hdaudio_pci.c revision 1.9 1 /* $NetBSD: hdaudio_pci.c,v 1.9 2018/09/12 03:23:38 mrg Exp $ */
2
3 /*
4 * Copyright (c) 2009 Precedence Technologies Ltd <support (at) precedence.co.uk>
5 * Copyright (c) 2009 Jared D. McNeill <jmcneill (at) 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.9 2018/09/12 03:23:38 mrg 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 /*
88 * NetBSD autoconfiguration
89 */
90
91 static int
92 hdaudio_pci_match(device_t parent, cfdata_t match, void *opaque)
93 {
94 struct pci_attach_args *pa = opaque;
95
96 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_MULTIMEDIA)
97 return 0;
98 if (PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_MULTIMEDIA_HDAUDIO)
99 return 0;
100
101 return 10;
102 }
103
104 static void
105 hdaudio_pci_attach(device_t parent, device_t self, void *opaque)
106 {
107 struct hdaudio_pci_softc *sc = device_private(self);
108 struct pci_attach_args *pa = opaque;
109 const char *intrstr;
110 pcireg_t csr, maptype;
111 int err, reg;
112 char intrbuf[PCI_INTRSTR_LEN];
113
114 aprint_naive("\n");
115 aprint_normal(": HD Audio Controller\n");
116
117 sc->sc_pc = pa->pa_pc;
118 sc->sc_tag = pa->pa_tag;
119 sc->sc_id = pa->pa_id;
120
121 sc->sc_hdaudio.sc_subsystem = pci_conf_read(sc->sc_pc, sc->sc_tag,
122 PCI_SUBSYS_ID_REG);
123
124 /* Enable busmastering and MMIO access */
125 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
126 csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE;
127 pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
128
129 /* Map MMIO registers */
130 reg = HDAUDIO_PCI_AZBARL;
131 maptype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, reg);
132 err = pci_mapreg_map(pa, reg, maptype, 0,
133 &sc->sc_hdaudio.sc_memt,
134 &sc->sc_hdaudio.sc_memh,
135 &sc->sc_hdaudio.sc_membase,
136 &sc->sc_hdaudio.sc_memsize);
137 if (err) {
138 aprint_error_dev(self, "couldn't map mmio space\n");
139 return;
140 }
141 sc->sc_hdaudio.sc_memvalid = true;
142 sc->sc_hdaudio.sc_dmat = pa->pa_dmat;
143
144 /* Map interrupt and establish handler */
145 if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) {
146 aprint_error_dev(self, "couldn't map interrupt\n");
147 return;
148 }
149 intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf,
150 sizeof(intrbuf));
151 sc->sc_ih = pci_intr_establish_xname(pa->pa_pc, sc->sc_pihp[0],
152 IPL_AUDIO, hdaudio_pci_intr, sc, device_xname(self));
153 if (sc->sc_ih == NULL) {
154 aprint_error_dev(self, "couldn't establish interrupt");
155 if (intrstr)
156 aprint_error(" at %s", intrstr);
157 aprint_error("\n");
158 return;
159 }
160 aprint_normal_dev(self, "interrupting at %s\n", intrstr);
161
162 hdaudio_pci_reinit(sc);
163
164 /* Attach bus-independent HD audio layer */
165 if (hdaudio_attach(self, &sc->sc_hdaudio)) {
166 pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
167 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
168 sc->sc_ih = NULL;
169 bus_space_unmap(sc->sc_hdaudio.sc_memt,
170 sc->sc_hdaudio.sc_memh,
171 sc->sc_hdaudio.sc_memsize);
172 sc->sc_hdaudio.sc_memvalid = false;
173 csr = pci_conf_read(sc->sc_pc, sc->sc_tag,
174 PCI_COMMAND_STATUS_REG);
175 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
176 pci_conf_write(sc->sc_pc, sc->sc_tag,
177 PCI_COMMAND_STATUS_REG, csr);
178
179 if (!pmf_device_register(self, NULL, NULL))
180 aprint_error_dev(self, "couldn't establish power handler\n");
181 }
182 else if (!pmf_device_register(self, NULL, hdaudio_pci_resume))
183 aprint_error_dev(self, "couldn't establish power handler\n");
184 }
185
186 static int
187 hdaudio_pci_rescan(device_t self, const char *ifattr, const int *locs)
188 {
189 struct hdaudio_pci_softc *sc = device_private(self);
190
191 return hdaudio_rescan(&sc->sc_hdaudio, ifattr, locs);
192 }
193
194 void
195 hdaudio_pci_childdet(device_t self, device_t child)
196 {
197 struct hdaudio_pci_softc *sc = device_private(self);
198
199 hdaudio_childdet(&sc->sc_hdaudio, child);
200 }
201
202 static int
203 hdaudio_pci_detach(device_t self, int flags)
204 {
205 struct hdaudio_pci_softc *sc = device_private(self);
206 pcireg_t csr;
207
208 hdaudio_detach(&sc->sc_hdaudio, flags);
209
210 if (sc->sc_ih != NULL) {
211 pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
212 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
213 sc->sc_ih = NULL;
214 }
215 if (sc->sc_hdaudio.sc_memvalid == true) {
216 bus_space_unmap(sc->sc_hdaudio.sc_memt,
217 sc->sc_hdaudio.sc_memh,
218 sc->sc_hdaudio.sc_memsize);
219 sc->sc_hdaudio.sc_memvalid = false;
220 }
221
222 /* Disable busmastering and MMIO access */
223 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
224 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
225 pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
226
227 pmf_device_deregister(self);
228
229 return 0;
230 }
231
232 static int
233 hdaudio_pci_intr(void *opaque)
234 {
235 struct hdaudio_pci_softc *sc = opaque;
236
237 return hdaudio_intr(&sc->sc_hdaudio);
238 }
239
240
241 static void
242 hdaudio_pci_reinit(struct hdaudio_pci_softc *sc)
243 {
244 pcireg_t val;
245
246 /* stops playback static */
247 val = pci_conf_read(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL);
248 val &= ~7;
249 val |= 0;
250 pci_conf_write(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL, val);
251
252 switch (PCI_VENDOR(sc->sc_id)) {
253 case PCI_VENDOR_NVIDIA:
254 /* enable snooping */
255 val = pci_conf_read(sc->sc_pc, sc->sc_tag,
256 HDAUDIO_NV_REG_SNOOP);
257 val &= ~HDAUDIO_NV_SNOOP_MASK;
258 val |= HDAUDIO_NV_SNOOP_ENABLE;
259 pci_conf_write(sc->sc_pc, sc->sc_tag,
260 HDAUDIO_NV_REG_SNOOP, val);
261 break;
262 }
263 }
264
265 static bool
266 hdaudio_pci_resume(device_t self, const pmf_qual_t *qual)
267 {
268 struct hdaudio_pci_softc *sc = device_private(self);
269
270 hdaudio_pci_reinit(sc);
271 return hdaudio_resume(&sc->sc_hdaudio);
272 }
273
274 MODULE(MODULE_CLASS_DRIVER, hdaudio_pci, "pci,hdaudio,audio");
275
276 #ifdef _MODULE
277 /*
278 * XXX Don't allow ioconf.c to redefine the "struct cfdriver hdaudio_cd"
279 * XXX it will be defined in the common hdaudio module
280 */
281
282 #undef CFDRIVER_DECL
283 #define CFDRIVER_DECL(name, class, attr) /* nothing */
284 #include "ioconf.c"
285 #endif
286
287 static int
288 hdaudio_pci_modcmd(modcmd_t cmd, void *opaque)
289 {
290 #ifdef _MODULE
291 /*
292 * We ignore the cfdriver_vec[] that ioconf provides, since
293 * the cfdrivers are attached already.
294 */
295 static struct cfdriver * const no_cfdriver_vec[] = { NULL };
296 #endif
297 int error = 0;
298
299 switch (cmd) {
300 case MODULE_CMD_INIT:
301 #ifdef _MODULE
302 error = config_init_component(no_cfdriver_vec,
303 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
304 #endif
305 return error;
306 case MODULE_CMD_FINI:
307 #ifdef _MODULE
308 error = config_fini_component(no_cfdriver_vec,
309 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
310 #endif
311 return error;
312 default:
313 return ENOTTY;
314 }
315 }
316