1 /* $NetBSD: hdaudio_pci.c,v 1.3 2009/12/04 11:13:05 njoly Exp $ */
4 * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk>
5 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Precedence Technologies Ltd
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
33 * Intel High Definition Audio (Revision 1.0) device driver.
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: hdaudio_pci.c,v 1.3 2009/12/04 11:13:05 njoly Exp $");
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/device.h>
47 #include <dev/pci/pcidevs.h>
48 #include <dev/pci/pcivar.h>
50 #include <dev/pci/hdaudio/hdaudioreg.h>
51 #include <dev/pci/hdaudio/hdaudiovar.h>
53 struct hdaudio_pci_softc
{
54 struct hdaudio_softc sc_hdaudio
; /* must be first */
56 pci_chipset_tag_t sc_pc
;
60 static int hdaudio_pci_match(device_t
, cfdata_t
, void *);
61 static void hdaudio_pci_attach(device_t
, device_t
, void *);
62 static int hdaudio_pci_detach(device_t
, int);
63 static void hdaudio_pci_childdet(device_t
, device_t
);
65 static int hdaudio_pci_intr(void *);
67 /* power management */
68 static bool hdaudio_pci_resume(device_t
, pmf_qual_t
);
72 sizeof(struct hdaudio_pci_softc
),
82 * NetBSD autoconfiguration
86 hdaudio_pci_match(device_t parent
, cfdata_t match
, void *opaque
)
88 struct pci_attach_args
*pa
= opaque
;
90 if (PCI_CLASS(pa
->pa_class
) != PCI_CLASS_MULTIMEDIA
)
92 if (PCI_SUBCLASS(pa
->pa_class
) != PCI_SUBCLASS_MULTIMEDIA_HDAUDIO
)
99 hdaudio_pci_attach(device_t parent
, device_t self
, void *opaque
)
101 struct hdaudio_pci_softc
*sc
= device_private(self
);
102 struct pci_attach_args
*pa
= opaque
;
103 pci_intr_handle_t ih
;
109 aprint_normal(": HD Audio Controller\n");
111 sc
->sc_pc
= pa
->pa_pc
;
112 sc
->sc_tag
= pa
->pa_tag
;
114 sc
->sc_hdaudio
.sc_subsystem
= pci_conf_read(sc
->sc_pc
, sc
->sc_tag
,
117 /* Enable busmastering and MMIO access */
118 csr
= pci_conf_read(sc
->sc_pc
, sc
->sc_tag
, PCI_COMMAND_STATUS_REG
);
119 csr
|= PCI_COMMAND_MASTER_ENABLE
| PCI_COMMAND_BACKTOBACK_ENABLE
;
120 pci_conf_write(sc
->sc_pc
, sc
->sc_tag
, PCI_COMMAND_STATUS_REG
, csr
);
122 /* Map MMIO registers */
123 err
= pci_mapreg_map(pa
, HDAUDIO_PCI_AZBARL
, PCI_MAPREG_TYPE_MEM
, 0,
124 &sc
->sc_hdaudio
.sc_memt
,
125 &sc
->sc_hdaudio
.sc_memh
,
126 &sc
->sc_hdaudio
.sc_membase
,
127 &sc
->sc_hdaudio
.sc_memsize
);
129 aprint_error_dev(self
, "couldn't map mmio space\n");
132 sc
->sc_hdaudio
.sc_memvalid
= true;
133 sc
->sc_hdaudio
.sc_dmat
= pa
->pa_dmat
;
135 /* Map interrupt and establish handler */
136 err
= pci_intr_map(pa
, &ih
);
138 aprint_error_dev(self
, "couldn't map interrupt\n");
141 intrstr
= pci_intr_string(pa
->pa_pc
, ih
);
142 sc
->sc_ih
= pci_intr_establish(pa
->pa_pc
, ih
, IPL_AUDIO
,
143 hdaudio_pci_intr
, sc
);
144 if (sc
->sc_ih
== NULL
) {
145 aprint_error_dev(self
, "couldn't establish interrupt");
147 aprint_error(" at %s", intrstr
);
151 aprint_normal_dev(self
, "interrupting at %s\n", intrstr
);
153 if (!pmf_device_register(self
, NULL
, hdaudio_pci_resume
))
154 aprint_error_dev(self
, "couldn't establish power handler\n");
156 /* Attach bus-independent HD audio layer */
157 hdaudio_attach(self
, &sc
->sc_hdaudio
);
161 hdaudio_pci_childdet(device_t self
, device_t child
)
164 struct hdaudio_pci_softc
*sc
= device_private(self
);
166 hdaudio_childdet(&sc
->sc_hdaudio
, child
);
171 hdaudio_pci_detach(device_t self
, int flags
)
173 struct hdaudio_pci_softc
*sc
= device_private(self
);
176 hdaudio_detach(&sc
->sc_hdaudio
, flags
);
178 if (sc
->sc_ih
!= NULL
) {
179 pci_intr_disestablish(sc
->sc_pc
, sc
->sc_ih
);
182 if (sc
->sc_hdaudio
.sc_memvalid
== true) {
183 bus_space_unmap(sc
->sc_hdaudio
.sc_memt
,
184 sc
->sc_hdaudio
.sc_memh
,
185 sc
->sc_hdaudio
.sc_memsize
);
186 sc
->sc_hdaudio
.sc_memvalid
= false;
189 /* Disable busmastering and MMIO access */
190 csr
= pci_conf_read(sc
->sc_pc
, sc
->sc_tag
, PCI_COMMAND_STATUS_REG
);
191 csr
&= ~(PCI_COMMAND_MASTER_ENABLE
| PCI_COMMAND_BACKTOBACK_ENABLE
);
192 pci_conf_write(sc
->sc_pc
, sc
->sc_tag
, PCI_COMMAND_STATUS_REG
, csr
);
194 pmf_device_deregister(self
);
200 hdaudio_pci_intr(void *opaque
)
202 struct hdaudio_pci_softc
*sc
= opaque
;
204 return hdaudio_intr(&sc
->sc_hdaudio
);
208 hdaudio_pci_resume(device_t self
, pmf_qual_t qual
)
210 struct hdaudio_pci_softc
*sc
= device_private(self
);
212 return hdaudio_resume(&sc
->sc_hdaudio
);