1/* $NetBSD: hdaudio_pci.c,v 1.13 2022/09/13 11:47:54 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.13 2022/09/13 11:47:54 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
55struct 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#define	HDAUDIO_PCI_IS_INTEL(sc)	\
65	(PCI_VENDOR(sc->sc_id) == PCI_VENDOR_INTEL)
66#define	HDAUDIO_PCI_IS_NVIDIA(sc)	\
67	(PCI_VENDOR(sc->sc_id) == PCI_VENDOR_NVIDIA)
68
69static int	hdaudio_pci_match(device_t, cfdata_t, void *);
70static void	hdaudio_pci_attach(device_t, device_t, void *);
71static int	hdaudio_pci_detach(device_t, int);
72static int	hdaudio_pci_rescan(device_t, const char *, const int *);
73static void	hdaudio_pci_childdet(device_t, device_t);
74
75static int	hdaudio_pci_intr(void *);
76static void	hdaudio_pci_init(struct hdaudio_pci_softc *);
77
78/* power management */
79static bool	hdaudio_pci_resume(device_t, const pmf_qual_t *);
80
81CFATTACH_DECL2_NEW(
82    hdaudio_pci,
83    sizeof(struct hdaudio_pci_softc),
84    hdaudio_pci_match,
85    hdaudio_pci_attach,
86    hdaudio_pci_detach,
87    NULL,
88    hdaudio_pci_rescan,
89    hdaudio_pci_childdet
90);
91
92/* Some devices' sublcass is not PCI_SUBCLASS_MULTIMEDIA_HDAUDIO. */
93static const struct device_compatible_entry compat_data[] = {
94	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_2HS_U_HDA) },
95	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3HS_U_HDA) },
96	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_4HS_H_CAVS) },
97	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_5HS_LP_HDA) },
98	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6HS_LP_HDA) },
99	{ .id = PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_JSL_CAVS) },
100
101	PCI_COMPAT_EOL
102};
103
104/*
105 * NetBSD autoconfiguration
106 */
107
108static int
109hdaudio_pci_match(device_t parent, cfdata_t match, void *opaque)
110{
111	struct pci_attach_args *pa = opaque;
112
113	if ((PCI_CLASS(pa->pa_class) == PCI_CLASS_MULTIMEDIA) &&
114	    (PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MULTIMEDIA_HDAUDIO))
115		return 10;
116	if (pci_compatible_match(pa, compat_data) != 0)
117		return 10;
118
119	return 0;
120}
121
122static void
123hdaudio_pci_attach(device_t parent, device_t self, void *opaque)
124{
125	struct hdaudio_pci_softc *sc = device_private(self);
126	struct pci_attach_args *pa = opaque;
127	const char *intrstr;
128	pcireg_t csr, maptype;
129	int err, reg;
130	char intrbuf[PCI_INTRSTR_LEN];
131
132	aprint_naive("\n");
133	aprint_normal(": HD Audio Controller\n");
134
135	sc->sc_pc = pa->pa_pc;
136	sc->sc_tag = pa->pa_tag;
137	sc->sc_id = pa->pa_id;
138
139	sc->sc_hdaudio.sc_subsystem = pci_conf_read(sc->sc_pc, sc->sc_tag,
140	    PCI_SUBSYS_ID_REG);
141
142	/* Enable busmastering and MMIO access */
143	csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
144	csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE;
145	pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
146
147	/* Map MMIO registers */
148	reg = PCI_BAR0;
149	maptype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, reg);
150	err = pci_mapreg_map(pa, reg, maptype, 0,
151			     &sc->sc_hdaudio.sc_memt,
152			     &sc->sc_hdaudio.sc_memh,
153			     &sc->sc_hdaudio.sc_membase,
154			     &sc->sc_hdaudio.sc_memsize);
155	if (err) {
156		aprint_error_dev(self, "couldn't map mmio space\n");
157		return;
158	}
159	sc->sc_hdaudio.sc_memvalid = true;
160	if (pci_dma64_available(pa))
161		sc->sc_hdaudio.sc_dmat = pa->pa_dmat64;
162	else
163		sc->sc_hdaudio.sc_dmat = pa->pa_dmat;
164
165	/* Map interrupt and establish handler */
166	if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) {
167		aprint_error_dev(self, "couldn't map interrupt\n");
168		return;
169	}
170	intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf,
171	    sizeof(intrbuf));
172	sc->sc_ih = pci_intr_establish_xname(pa->pa_pc, sc->sc_pihp[0],
173	    IPL_AUDIO, hdaudio_pci_intr, sc, device_xname(self));
174	if (sc->sc_ih == NULL) {
175		aprint_error_dev(self, "couldn't establish interrupt");
176		if (intrstr)
177			aprint_error(" at %s", intrstr);
178		aprint_error("\n");
179		return;
180	}
181	aprint_normal_dev(self, "interrupting at %s\n", intrstr);
182
183	hdaudio_pci_init(sc);
184
185	/* Attach bus-independent HD audio layer */
186	if (hdaudio_attach(self, &sc->sc_hdaudio)) {
187		pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
188		pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
189		sc->sc_ih = NULL;
190		bus_space_unmap(sc->sc_hdaudio.sc_memt,
191				sc->sc_hdaudio.sc_memh,
192				sc->sc_hdaudio.sc_memsize);
193		sc->sc_hdaudio.sc_memvalid = false;
194		csr = pci_conf_read(sc->sc_pc, sc->sc_tag,
195		    PCI_COMMAND_STATUS_REG);
196		csr &= ~(PCI_COMMAND_MASTER_ENABLE |
197			 PCI_COMMAND_BACKTOBACK_ENABLE);
198		pci_conf_write(sc->sc_pc, sc->sc_tag,
199		    PCI_COMMAND_STATUS_REG, csr);
200
201		if (!pmf_device_register(self, NULL, NULL)) {
202			aprint_error_dev(self,
203			    "couldn't establish power handler\n");
204		}
205	} else if (!pmf_device_register(self, NULL, hdaudio_pci_resume)) {
206		aprint_error_dev(self, "couldn't establish power handler\n");
207	}
208}
209
210static int
211hdaudio_pci_rescan(device_t self, const char *ifattr, const int *locs)
212{
213	struct hdaudio_pci_softc *sc = device_private(self);
214
215	return hdaudio_rescan(&sc->sc_hdaudio, ifattr, locs);
216}
217
218void
219hdaudio_pci_childdet(device_t self, device_t child)
220{
221	struct hdaudio_pci_softc *sc = device_private(self);
222
223	hdaudio_childdet(&sc->sc_hdaudio, child);
224}
225
226static int
227hdaudio_pci_detach(device_t self, int flags)
228{
229	struct hdaudio_pci_softc *sc = device_private(self);
230	pcireg_t csr;
231
232	hdaudio_detach(&sc->sc_hdaudio, flags);
233
234	if (sc->sc_ih != NULL) {
235		pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
236		pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
237		sc->sc_ih = NULL;
238	}
239	if (sc->sc_hdaudio.sc_memvalid == true) {
240		bus_space_unmap(sc->sc_hdaudio.sc_memt,
241				sc->sc_hdaudio.sc_memh,
242				sc->sc_hdaudio.sc_memsize);
243		sc->sc_hdaudio.sc_memvalid = false;
244	}
245
246	/* Disable busmastering and MMIO access */
247	csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
248	csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
249	pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
250
251	pmf_device_deregister(self);
252
253	return 0;
254}
255
256static int
257hdaudio_pci_intr(void *opaque)
258{
259	struct hdaudio_pci_softc *sc = opaque;
260
261	return hdaudio_intr(&sc->sc_hdaudio);
262}
263
264static void
265hdaudio_pci_init(struct hdaudio_pci_softc *sc)
266{
267	pcireg_t val;
268
269	if (HDAUDIO_PCI_IS_INTEL(sc)) {
270		/*
271		 * ICH: Set traffic class for input/output/buf descriptors
272		 * to TC0. For PCH without a TCSEL register, PGCTL is in
273		 * the same location and clearing these bits is harmless.
274		 */
275		val = pci_conf_read(sc->sc_pc, sc->sc_tag,
276		    HDAUDIO_INTEL_REG_ICH_TCSEL);
277		val &= ~HDAUDIO_INTEL_ICH_TCSEL_MASK;
278		val |= HDAUDIO_INTEL_ICH_TCSEL_TC0;
279		pci_conf_write(sc->sc_pc, sc->sc_tag,
280		    HDAUDIO_INTEL_REG_ICH_TCSEL, val);
281
282		/*
283		 * PCH: Disable dynamic clock gating logic. Implementations
284		 * without a CGCTL register do not appear to have anything
285		 * else in its place.
286		 */
287		val = pci_conf_read(sc->sc_pc, sc->sc_tag,
288		    HDAUDIO_INTEL_REG_PCH_CGCTL);
289		val &= ~HDAUDIO_INTEL_PCH_CGCTL_MISCBDCGE;
290		pci_conf_write(sc->sc_pc, sc->sc_tag,
291		    HDAUDIO_INTEL_REG_PCH_CGCTL, val);
292
293		/* ICH/PCH: Enable snooping. */
294		val = pci_conf_read(sc->sc_pc, sc->sc_tag,
295		    HDAUDIO_INTEL_REG_PCH_DEVC);
296		val &= ~HDAUDIO_INTEL_PCH_DEVC_NSNPEN;
297		pci_conf_write(sc->sc_pc, sc->sc_tag,
298		    HDAUDIO_INTEL_REG_PCH_DEVC, val);
299	}
300
301	if (HDAUDIO_PCI_IS_NVIDIA(sc)) {
302		/* Enable snooping. */
303		val = pci_conf_read(sc->sc_pc, sc->sc_tag,
304		    HDAUDIO_NV_REG_SNOOP);
305		val &= ~HDAUDIO_NV_SNOOP_MASK;
306		val |= HDAUDIO_NV_SNOOP_ENABLE;
307		pci_conf_write(sc->sc_pc, sc->sc_tag,
308		    HDAUDIO_NV_REG_SNOOP, val);
309	}
310}
311
312static bool
313hdaudio_pci_resume(device_t self, const pmf_qual_t *qual)
314{
315	struct hdaudio_pci_softc *sc = device_private(self);
316
317	hdaudio_pci_init(sc);
318
319	return hdaudio_resume(&sc->sc_hdaudio);
320}
321
322MODULE(MODULE_CLASS_DRIVER, hdaudio_pci, "pci,hdaudio,audio");
323
324#ifdef _MODULE
325/*
326 * XXX Don't allow ioconf.c to redefine the "struct cfdriver hdaudio_cd"
327 * XXX it will be defined in the common hdaudio module
328 */
329
330#undef CFDRIVER_DECL
331#define CFDRIVER_DECL(name, class, attr) /* nothing */
332#include "ioconf.c"
333#endif
334
335static int
336hdaudio_pci_modcmd(modcmd_t cmd, void *opaque)
337{
338#ifdef _MODULE
339	/*
340	 * We ignore the cfdriver_vec[] that ioconf provides, since
341	 * the cfdrivers are attached already.
342	 */
343	static struct cfdriver * const no_cfdriver_vec[] = { NULL };
344#endif
345	int error = 0;
346
347	switch (cmd) {
348	case MODULE_CMD_INIT:
349#ifdef _MODULE
350		error = config_init_component(no_cfdriver_vec,
351		    cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
352#endif
353		return error;
354	case MODULE_CMD_FINI:
355#ifdef _MODULE
356		error = config_fini_component(no_cfdriver_vec,
357		    cfattach_ioconf_hdaudio_pci, cfdata_ioconf_hdaudio_pci);
358#endif
359		return error;
360	default:
361		return ENOTTY;
362	}
363}
364