1 /* $NetBSD: hdaudio_pci.c,v 1.11 2021/10/28 09:15:35 msaitoh Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk> 5 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Precedence Technologies Ltd 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Intel High Definition Audio (Revision 1.0a) device driver. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: hdaudio_pci.c,v 1.11 2021/10/28 09:15:35 msaitoh Exp $"); 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/device.h> 43 #include <sys/conf.h> 44 #include <sys/bus.h> 45 #include <sys/intr.h> 46 #include <sys/module.h> 47 48 #include <dev/pci/pcidevs.h> 49 #include <dev/pci/pcivar.h> 50 51 #include <dev/hdaudio/hdaudioreg.h> 52 #include <dev/hdaudio/hdaudiovar.h> 53 #include <dev/pci/hdaudio_pci.h> 54 55 struct hdaudio_pci_softc { 56 struct hdaudio_softc sc_hdaudio; /* must be first */ 57 pcitag_t sc_tag; 58 pci_chipset_tag_t sc_pc; 59 void *sc_ih; 60 pcireg_t sc_id; 61 pci_intr_handle_t *sc_pihp; 62 }; 63 64 static int hdaudio_pci_match(device_t, cfdata_t, void *); 65 static void hdaudio_pci_attach(device_t, device_t, void *); 66 static int hdaudio_pci_detach(device_t, int); 67 static int hdaudio_pci_rescan(device_t, const char *, const int *); 68 static void hdaudio_pci_childdet(device_t, device_t); 69 70 static int hdaudio_pci_intr(void *); 71 static void hdaudio_pci_reinit(struct hdaudio_pci_softc *); 72 73 /* power management */ 74 static bool hdaudio_pci_resume(device_t, const pmf_qual_t *); 75 76 CFATTACH_DECL2_NEW( 77 hdaudio_pci, 78 sizeof(struct hdaudio_pci_softc), 79 hdaudio_pci_match, 80 hdaudio_pci_attach, 81 hdaudio_pci_detach, 82 NULL, 83 hdaudio_pci_rescan, 84 hdaudio_pci_childdet 85 ); 86 87 /* Some devices' sublcass is not PCI_SUBCLASS_MULTIMEDIA_HDAUDIO. */ 88 static const struct device_compatible_entry compat_data[] = { 89 { .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_2HS_U_HDA) }, 90 { .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3HS_U_HDA) }, 91 { .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_4HS_H_CAVS) }, 92 { .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_5HS_LP_HDA) }, 93 { .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_JSL_CAVS) }, 94 95 PCI_COMPAT_EOL 96 }; 97 98 /* 99 * NetBSD autoconfiguration 100 */ 101 102 static int 103 hdaudio_pci_match(device_t parent, cfdata_t match, void *opaque) 104 { 105 struct pci_attach_args *pa = opaque; 106 107 if ((PCI_CLASS(pa->pa_class) == PCI_CLASS_MULTIMEDIA) && 108 (PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MULTIMEDIA_HDAUDIO)) 109 return 10; 110 if (pci_compatible_match(pa, compat_data) != 0) 111 return 10; 112 113 return 0; 114 } 115 116 static void 117 hdaudio_pci_attach(device_t parent, device_t self, void *opaque) 118 { 119 struct hdaudio_pci_softc *sc = device_private(self); 120 struct pci_attach_args *pa = opaque; 121 const char *intrstr; 122 pcireg_t csr, maptype; 123 int err, reg; 124 char intrbuf[PCI_INTRSTR_LEN]; 125 126 aprint_naive("\n"); 127 aprint_normal(": HD Audio Controller\n"); 128 129 sc->sc_pc = pa->pa_pc; 130 sc->sc_tag = pa->pa_tag; 131 sc->sc_id = pa->pa_id; 132 133 sc->sc_hdaudio.sc_subsystem = pci_conf_read(sc->sc_pc, sc->sc_tag, 134 PCI_SUBSYS_ID_REG); 135 136 /* Enable busmastering and MMIO access */ 137 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); 138 csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE; 139 pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); 140 141 /* Map MMIO registers */ 142 reg = HDAUDIO_PCI_AZBARL; 143 maptype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, reg); 144 err = pci_mapreg_map(pa, reg, maptype, 0, 145 &sc->sc_hdaudio.sc_memt, 146 &sc->sc_hdaudio.sc_memh, 147 &sc->sc_hdaudio.sc_membase, 148 &sc->sc_hdaudio.sc_memsize); 149 if (err) { 150 aprint_error_dev(self, "couldn't map mmio space\n"); 151 return; 152 } 153 sc->sc_hdaudio.sc_memvalid = true; 154 if (pci_dma64_available(pa)) 155 sc->sc_hdaudio.sc_dmat = pa->pa_dmat64; 156 else 157 sc->sc_hdaudio.sc_dmat = pa->pa_dmat; 158 159 /* Map interrupt and establish handler */ 160 if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) { 161 aprint_error_dev(self, "couldn't map interrupt\n"); 162 return; 163 } 164 intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf, 165 sizeof(intrbuf)); 166 sc->sc_ih = pci_intr_establish_xname(pa->pa_pc, sc->sc_pihp[0], 167 IPL_AUDIO, hdaudio_pci_intr, sc, device_xname(self)); 168 if (sc->sc_ih == NULL) { 169 aprint_error_dev(self, "couldn't establish interrupt"); 170 if (intrstr) 171 aprint_error(" at %s", intrstr); 172 aprint_error("\n"); 173 return; 174 } 175 aprint_normal_dev(self, "interrupting at %s\n", intrstr); 176 177 hdaudio_pci_reinit(sc); 178 179 /* Attach bus-independent HD audio layer */ 180 if (hdaudio_attach(self, &sc->sc_hdaudio)) { 181 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 182 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); 183 sc->sc_ih = NULL; 184 bus_space_unmap(sc->sc_hdaudio.sc_memt, 185 sc->sc_hdaudio.sc_memh, 186 sc->sc_hdaudio.sc_memsize); 187 sc->sc_hdaudio.sc_memvalid = false; 188 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, 189 PCI_COMMAND_STATUS_REG); 190 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE); 191 pci_conf_write(sc->sc_pc, sc->sc_tag, 192 PCI_COMMAND_STATUS_REG, csr); 193 194 if (!pmf_device_register(self, NULL, NULL)) 195 aprint_error_dev(self, "couldn't establish power handler\n"); 196 } 197 else if (!pmf_device_register(self, NULL, hdaudio_pci_resume)) 198 aprint_error_dev(self, "couldn't establish power handler\n"); 199 } 200 201 static int 202 hdaudio_pci_rescan(device_t self, const char *ifattr, const int *locs) 203 { 204 struct hdaudio_pci_softc *sc = device_private(self); 205 206 return hdaudio_rescan(&sc->sc_hdaudio, ifattr, locs); 207 } 208 209 void 210 hdaudio_pci_childdet(device_t self, device_t child) 211 { 212 struct hdaudio_pci_softc *sc = device_private(self); 213 214 hdaudio_childdet(&sc->sc_hdaudio, child); 215 } 216 217 static int 218 hdaudio_pci_detach(device_t self, int flags) 219 { 220 struct hdaudio_pci_softc *sc = device_private(self); 221 pcireg_t csr; 222 223 hdaudio_detach(&sc->sc_hdaudio, flags); 224 225 if (sc->sc_ih != NULL) { 226 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 227 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); 228 sc->sc_ih = NULL; 229 } 230 if (sc->sc_hdaudio.sc_memvalid == true) { 231 bus_space_unmap(sc->sc_hdaudio.sc_memt, 232 sc->sc_hdaudio.sc_memh, 233 sc->sc_hdaudio.sc_memsize); 234 sc->sc_hdaudio.sc_memvalid = false; 235 } 236 237 /* Disable busmastering and MMIO access */ 238 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); 239 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE); 240 pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); 241 242 pmf_device_deregister(self); 243 244 return 0; 245 } 246 247 static int 248 hdaudio_pci_intr(void *opaque) 249 { 250 struct hdaudio_pci_softc *sc = opaque; 251 252 return hdaudio_intr(&sc->sc_hdaudio); 253 } 254 255 256 static void 257 hdaudio_pci_reinit(struct hdaudio_pci_softc *sc) 258 { 259 pcireg_t val; 260 261 /* stops playback static */ 262 val = pci_conf_read(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL); 263 val &= ~7; 264 val |= 0; 265 pci_conf_write(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL, val); 266 267 switch (PCI_VENDOR(sc->sc_id)) { 268 case PCI_VENDOR_NVIDIA: 269 /* enable snooping */ 270 val = pci_conf_read(sc->sc_pc, sc->sc_tag, 271 HDAUDIO_NV_REG_SNOOP); 272 val &= ~HDAUDIO_NV_SNOOP_MASK; 273 val |= HDAUDIO_NV_SNOOP_ENABLE; 274 pci_conf_write(sc->sc_pc, sc->sc_tag, 275 HDAUDIO_NV_REG_SNOOP, val); 276 break; 277 } 278 } 279 280 static bool 281 hdaudio_pci_resume(device_t self, const pmf_qual_t *qual) 282 { 283 struct hdaudio_pci_softc *sc = device_private(self); 284 285 hdaudio_pci_reinit(sc); 286 return hdaudio_resume(&sc->sc_hdaudio); 287 } 288 289 MODULE(MODULE_CLASS_DRIVER, hdaudio_pci, "pci,hdaudio,audio"); 290 291 #ifdef _MODULE 292 /* 293 * XXX Don't allow ioconf.c to redefine the "struct cfdriver hdaudio_cd" 294 * XXX it will be defined in the common hdaudio module 295 */ 296 297 #undef CFDRIVER_DECL 298 #define CFDRIVER_DECL(name, class, attr) /* nothing */ 299 #include "ioconf.c" 300 #endif 301 302 static int 303 hdaudio_pci_modcmd(modcmd_t cmd, void *opaque) 304 { 305 #ifdef _MODULE 306 /* 307 * We ignore the cfdriver_vec[] that ioconf provides, since 308 * the cfdrivers are attached already. 309 */ 310 static struct cfdriver * const no_cfdriver_vec[] = { NULL }; 311 #endif 312 int error = 0; 313 314 switch (cmd) { 315 case MODULE_CMD_INIT: 316 #ifdef _MODULE 317 error = config_init_component(no_cfdriver_vec, 318 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci); 319 #endif 320 return error; 321 case MODULE_CMD_FINI: 322 #ifdef _MODULE 323 error = config_fini_component(no_cfdriver_vec, 324 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci); 325 #endif 326 return error; 327 default: 328 return ENOTTY; 329 } 330 } 331