hdaudio_pci.c revision 1.7.2.1 1 /* $NetBSD: hdaudio_pci.c,v 1.7.2.1 2017/06/05 08:13:05 snj 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.7.2.1 2017/06/05 08:13:05 snj 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;
111 int err;
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 err = pci_mapreg_map(pa, HDAUDIO_PCI_AZBARL, PCI_MAPREG_TYPE_MEM, 0,
131 &sc->sc_hdaudio.sc_memt,
132 &sc->sc_hdaudio.sc_memh,
133 &sc->sc_hdaudio.sc_membase,
134 &sc->sc_hdaudio.sc_memsize);
135 if (err) {
136 aprint_error_dev(self, "couldn't map mmio space\n");
137 return;
138 }
139 sc->sc_hdaudio.sc_memvalid = true;
140 sc->sc_hdaudio.sc_dmat = pa->pa_dmat;
141
142 /* Map interrupt and establish handler */
143 if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) {
144 aprint_error_dev(self, "couldn't map interrupt\n");
145 return;
146 }
147 intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf,
148 sizeof(intrbuf));
149 sc->sc_ih = pci_intr_establish_xname(pa->pa_pc, sc->sc_pihp[0],
150 IPL_AUDIO, hdaudio_pci_intr, sc, device_xname(self));
151 if (sc->sc_ih == NULL) {
152 aprint_error_dev(self, "couldn't establish interrupt");
153 if (intrstr)
154 aprint_error(" at %s", intrstr);
155 aprint_error("\n");
156 return;
157 }
158 aprint_normal_dev(self, "interrupting at %s\n", intrstr);
159
160 hdaudio_pci_reinit(sc);
161
162 /* Attach bus-independent HD audio layer */
163 if (hdaudio_attach(self, &sc->sc_hdaudio)) {
164 pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
165 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
166 sc->sc_ih = NULL;
167 bus_space_unmap(sc->sc_hdaudio.sc_memt,
168 sc->sc_hdaudio.sc_memh,
169 sc->sc_hdaudio.sc_memsize);
170 sc->sc_hdaudio.sc_memvalid = false;
171 csr = pci_conf_read(sc->sc_pc, sc->sc_tag,
172 PCI_COMMAND_STATUS_REG);
173 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
174 pci_conf_write(sc->sc_pc, sc->sc_tag,
175 PCI_COMMAND_STATUS_REG, csr);
176
177 if (!pmf_device_register(self, NULL, NULL))
178 aprint_error_dev(self, "couldn't establish power handler\n");
179 }
180 else if (!pmf_device_register(self, NULL, hdaudio_pci_resume))
181 aprint_error_dev(self, "couldn't establish power handler\n");
182 }
183
184 static int
185 hdaudio_pci_rescan(device_t self, const char *ifattr, const int *locs)
186 {
187 struct hdaudio_pci_softc *sc = device_private(self);
188
189 return hdaudio_rescan(&sc->sc_hdaudio, ifattr, locs);
190 }
191
192 void
193 hdaudio_pci_childdet(device_t self, device_t child)
194 {
195 struct hdaudio_pci_softc *sc = device_private(self);
196
197 hdaudio_childdet(&sc->sc_hdaudio, child);
198 }
199
200 static int
201 hdaudio_pci_detach(device_t self, int flags)
202 {
203 struct hdaudio_pci_softc *sc = device_private(self);
204 pcireg_t csr;
205
206 hdaudio_detach(&sc->sc_hdaudio, flags);
207
208 if (sc->sc_ih != NULL) {
209 pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
210 pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
211 sc->sc_ih = NULL;
212 }
213 if (sc->sc_hdaudio.sc_memvalid == true) {
214 bus_space_unmap(sc->sc_hdaudio.sc_memt,
215 sc->sc_hdaudio.sc_memh,
216 sc->sc_hdaudio.sc_memsize);
217 sc->sc_hdaudio.sc_memvalid = false;
218 }
219
220 /* Disable busmastering and MMIO access */
221 csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
222 csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
223 pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
224
225 pmf_device_deregister(self);
226
227 return 0;
228 }
229
230 static int
231 hdaudio_pci_intr(void *opaque)
232 {
233 struct hdaudio_pci_softc *sc = opaque;
234
235 return hdaudio_intr(&sc->sc_hdaudio);
236 }
237
238
239 static void
240 hdaudio_pci_reinit(struct hdaudio_pci_softc *sc)
241 {
242 pcireg_t val;
243
244 /* stops playback static */
245 val = pci_conf_read(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL);
246 val &= ~7;
247 val |= 0;
248 pci_conf_write(sc->sc_pc, sc->sc_tag, HDAUDIO_PCI_TCSEL, val);
249
250 switch (PCI_VENDOR(sc->sc_id)) {
251 case PCI_VENDOR_NVIDIA:
252 /* enable snooping */
253 val = pci_conf_read(sc->sc_pc, sc->sc_tag,
254 HDAUDIO_NV_REG_SNOOP);
255 val &= ~HDAUDIO_NV_SNOOP_MASK;
256 val |= HDAUDIO_NV_SNOOP_ENABLE;
257 pci_conf_write(sc->sc_pc, sc->sc_tag,
258 HDAUDIO_NV_REG_SNOOP, val);
259 break;
260 }
261 }
262
263 static bool
264 hdaudio_pci_resume(device_t self, const pmf_qual_t *qual)
265 {
266 struct hdaudio_pci_softc *sc = device_private(self);
267
268 hdaudio_pci_reinit(sc);
269 return hdaudio_resume(&sc->sc_hdaudio);
270 }
271
272 MODULE(MODULE_CLASS_DRIVER, hdaudio_pci, "pci,hdaudio,audio");
273
274 #ifdef _MODULE
275 /*
276 * XXX Don't allow ioconf.c to redefine the "struct cfdriver hdaudio_cd"
277 * XXX it will be defined in the common hdaudio module
278 */
279
280 #undef CFDRIVER_DECL
281 #define CFDRIVER_DECL(name, class, attr) /* nothing */
282 #include "ioconf.c"
283 #endif
284
285 static int
286 hdaudio_pci_modcmd(modcmd_t cmd, void *opaque)
287 {
288 #ifdef _MODULE
289 /*
290 * We ignore the cfdriver_vec[] that ioconf provides, since
291 * the cfdrivers are attached already.
292 */
293 static struct cfdriver * const no_cfdriver_vec[] = { NULL };
294 #endif
295 int error = 0;
296
297 switch (cmd) {
298 case MODULE_CMD_INIT:
299 #ifdef _MODULE
300 error = config_init_component(no_cfdriver_vec,
301 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
302 #endif
303 return error;
304 case MODULE_CMD_FINI:
305 #ifdef _MODULE
306 error = config_fini_component(no_cfdriver_vec,
307 cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
308 #endif
309 return error;
310 default:
311 return ENOTTY;
312 }
313 }
314