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