xref: /netbsd-src/sys/dev/pci/hdaudio_pci.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
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