| @@ -1,213 +1,226 @@ | | | @@ -1,213 +1,226 @@ |
1 | /* $NetBSD: hdaudio_pci.c,v 1.5 2010/02/24 22:38:08 dyoung Exp $ */ | | 1 | /* $NetBSD: hdaudio_pci.c,v 1.6 2010/08/07 16:59:48 jmcneill Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk> | | 4 | * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk> |
5 | * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> | | 5 | * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> |
6 | * All rights reserved. | | 6 | * All rights reserved. |
7 | * | | 7 | * |
8 | * This code is derived from software contributed to The NetBSD Foundation | | 8 | * This code is derived from software contributed to The NetBSD Foundation |
9 | * by Precedence Technologies Ltd | | 9 | * by Precedence Technologies Ltd |
10 | * | | 10 | * |
11 | * Redistribution and use in source and binary forms, with or without | | 11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions | | 12 | * modification, are permitted provided that the following conditions |
13 | * are met: | | 13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright | | 14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. | | 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 | | 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. | | 17 | * derived from this software without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 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 | | 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 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 | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | /* | | 32 | /* |
33 | * Intel High Definition Audio (Revision 1.0) device driver. | | 33 | * Intel High Definition Audio (Revision 1.0) device driver. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | #include <sys/cdefs.h> | | 36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: hdaudio_pci.c,v 1.5 2010/02/24 22:38:08 dyoung Exp $"); | | 37 | __KERNEL_RCSID(0, "$NetBSD: hdaudio_pci.c,v 1.6 2010/08/07 16:59:48 jmcneill Exp $"); |
38 | | | 38 | |
39 | #include <sys/types.h> | | 39 | #include <sys/types.h> |
40 | #include <sys/param.h> | | 40 | #include <sys/param.h> |
41 | #include <sys/systm.h> | | 41 | #include <sys/systm.h> |
42 | #include <sys/device.h> | | 42 | #include <sys/device.h> |
43 | #include <sys/conf.h> | | 43 | #include <sys/conf.h> |
44 | #include <sys/bus.h> | | 44 | #include <sys/bus.h> |
45 | #include <sys/intr.h> | | 45 | #include <sys/intr.h> |
46 | | | 46 | |
47 | #include <dev/pci/pcidevs.h> | | 47 | #include <dev/pci/pcidevs.h> |
48 | #include <dev/pci/pcivar.h> | | 48 | #include <dev/pci/pcivar.h> |
49 | | | 49 | |
50 | #include <dev/pci/hdaudio/hdaudioreg.h> | | 50 | #include <dev/pci/hdaudio/hdaudioreg.h> |
51 | #include <dev/pci/hdaudio/hdaudiovar.h> | | 51 | #include <dev/pci/hdaudio/hdaudiovar.h> |
| | | 52 | #include <dev/pci/hdaudio/hdaudio_pci.h> |
52 | | | 53 | |
53 | struct hdaudio_pci_softc { | | 54 | struct hdaudio_pci_softc { |
54 | struct hdaudio_softc sc_hdaudio; /* must be first */ | | 55 | struct hdaudio_softc sc_hdaudio; /* must be first */ |
55 | pcitag_t sc_tag; | | 56 | pcitag_t sc_tag; |
56 | pci_chipset_tag_t sc_pc; | | 57 | pci_chipset_tag_t sc_pc; |
57 | void *sc_ih; | | 58 | void *sc_ih; |
58 | }; | | 59 | }; |
59 | | | 60 | |
60 | static int hdaudio_pci_match(device_t, cfdata_t, void *); | | 61 | static int hdaudio_pci_match(device_t, cfdata_t, void *); |
61 | static void hdaudio_pci_attach(device_t, device_t, void *); | | 62 | static void hdaudio_pci_attach(device_t, device_t, void *); |
62 | static int hdaudio_pci_detach(device_t, int); | | 63 | static int hdaudio_pci_detach(device_t, int); |
63 | static void hdaudio_pci_childdet(device_t, device_t); | | 64 | static void hdaudio_pci_childdet(device_t, device_t); |
64 | | | 65 | |
65 | static int hdaudio_pci_intr(void *); | | 66 | static int hdaudio_pci_intr(void *); |
66 | | | 67 | |
67 | /* power management */ | | 68 | /* power management */ |
68 | static bool hdaudio_pci_resume(device_t, const pmf_qual_t *); | | 69 | static bool hdaudio_pci_resume(device_t, const pmf_qual_t *); |
69 | | | 70 | |
70 | CFATTACH_DECL2_NEW( | | 71 | CFATTACH_DECL2_NEW( |
71 | hdaudio_pci, | | 72 | hdaudio_pci, |
72 | sizeof(struct hdaudio_pci_softc), | | 73 | sizeof(struct hdaudio_pci_softc), |
73 | hdaudio_pci_match, | | 74 | hdaudio_pci_match, |
74 | hdaudio_pci_attach, | | 75 | hdaudio_pci_attach, |
75 | hdaudio_pci_detach, | | 76 | hdaudio_pci_detach, |
76 | NULL, | | 77 | NULL, |
77 | NULL, | | 78 | NULL, |
78 | hdaudio_pci_childdet | | 79 | hdaudio_pci_childdet |
79 | ); | | 80 | ); |
80 | | | 81 | |
81 | /* | | 82 | /* |
82 | * NetBSD autoconfiguration | | 83 | * NetBSD autoconfiguration |
83 | */ | | 84 | */ |
84 | | | 85 | |
85 | static int | | 86 | static int |
86 | hdaudio_pci_match(device_t parent, cfdata_t match, void *opaque) | | 87 | hdaudio_pci_match(device_t parent, cfdata_t match, void *opaque) |
87 | { | | 88 | { |
88 | struct pci_attach_args *pa = opaque; | | 89 | struct pci_attach_args *pa = opaque; |
89 | | | 90 | |
90 | if (PCI_CLASS(pa->pa_class) != PCI_CLASS_MULTIMEDIA) | | 91 | if (PCI_CLASS(pa->pa_class) != PCI_CLASS_MULTIMEDIA) |
91 | return 0; | | 92 | return 0; |
92 | if (PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_MULTIMEDIA_HDAUDIO) | | 93 | if (PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_MULTIMEDIA_HDAUDIO) |
93 | return 0; | | 94 | return 0; |
94 | | | 95 | |
95 | return 10; | | 96 | return 10; |
96 | } | | 97 | } |
97 | | | 98 | |
98 | static void | | 99 | static void |
99 | hdaudio_pci_attach(device_t parent, device_t self, void *opaque) | | 100 | hdaudio_pci_attach(device_t parent, device_t self, void *opaque) |
100 | { | | 101 | { |
101 | struct hdaudio_pci_softc *sc = device_private(self); | | 102 | struct hdaudio_pci_softc *sc = device_private(self); |
102 | struct pci_attach_args *pa = opaque; | | 103 | struct pci_attach_args *pa = opaque; |
103 | pci_intr_handle_t ih; | | 104 | pci_intr_handle_t ih; |
104 | const char *intrstr; | | 105 | const char *intrstr; |
105 | pcireg_t csr; | | 106 | pcireg_t csr; |
106 | int err; | | 107 | int err; |
107 | | | 108 | |
108 | aprint_naive("\n"); | | 109 | aprint_naive("\n"); |
109 | aprint_normal(": HD Audio Controller\n"); | | 110 | aprint_normal(": HD Audio Controller\n"); |
110 | | | 111 | |
111 | sc->sc_pc = pa->pa_pc; | | 112 | sc->sc_pc = pa->pa_pc; |
112 | sc->sc_tag = pa->pa_tag; | | 113 | sc->sc_tag = pa->pa_tag; |
113 | | | 114 | |
114 | sc->sc_hdaudio.sc_subsystem = pci_conf_read(sc->sc_pc, sc->sc_tag, | | 115 | sc->sc_hdaudio.sc_subsystem = pci_conf_read(sc->sc_pc, sc->sc_tag, |
115 | PCI_SUBSYS_ID_REG); | | 116 | PCI_SUBSYS_ID_REG); |
116 | | | 117 | |
117 | /* Enable busmastering and MMIO access */ | | 118 | /* Enable busmastering and MMIO access */ |
118 | csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); | | 119 | 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 | csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE; |
120 | pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); | | 121 | pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); |
121 | | | 122 | |
122 | /* Map MMIO registers */ | | 123 | /* Map MMIO registers */ |
123 | err = pci_mapreg_map(pa, HDAUDIO_PCI_AZBARL, PCI_MAPREG_TYPE_MEM, 0, | | 124 | err = pci_mapreg_map(pa, HDAUDIO_PCI_AZBARL, PCI_MAPREG_TYPE_MEM, 0, |
124 | &sc->sc_hdaudio.sc_memt, | | 125 | &sc->sc_hdaudio.sc_memt, |
125 | &sc->sc_hdaudio.sc_memh, | | 126 | &sc->sc_hdaudio.sc_memh, |
126 | &sc->sc_hdaudio.sc_membase, | | 127 | &sc->sc_hdaudio.sc_membase, |
127 | &sc->sc_hdaudio.sc_memsize); | | 128 | &sc->sc_hdaudio.sc_memsize); |
128 | if (err) { | | 129 | if (err) { |
129 | aprint_error_dev(self, "couldn't map mmio space\n"); | | 130 | aprint_error_dev(self, "couldn't map mmio space\n"); |
130 | return; | | 131 | return; |
131 | } | | 132 | } |
132 | sc->sc_hdaudio.sc_memvalid = true; | | 133 | sc->sc_hdaudio.sc_memvalid = true; |
133 | sc->sc_hdaudio.sc_dmat = pa->pa_dmat; | | 134 | sc->sc_hdaudio.sc_dmat = pa->pa_dmat; |
134 | | | 135 | |
135 | /* Map interrupt and establish handler */ | | 136 | /* Map interrupt and establish handler */ |
136 | err = pci_intr_map(pa, &ih); | | 137 | err = pci_intr_map(pa, &ih); |
137 | if (err) { | | 138 | if (err) { |
138 | aprint_error_dev(self, "couldn't map interrupt\n"); | | 139 | aprint_error_dev(self, "couldn't map interrupt\n"); |
139 | return; | | 140 | return; |
140 | } | | 141 | } |
141 | intrstr = pci_intr_string(pa->pa_pc, ih); | | 142 | intrstr = pci_intr_string(pa->pa_pc, ih); |
142 | sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, | | 143 | sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, |
143 | hdaudio_pci_intr, sc); | | 144 | hdaudio_pci_intr, sc); |
144 | if (sc->sc_ih == NULL) { | | 145 | if (sc->sc_ih == NULL) { |
145 | aprint_error_dev(self, "couldn't establish interrupt"); | | 146 | aprint_error_dev(self, "couldn't establish interrupt"); |
146 | if (intrstr) | | 147 | if (intrstr) |
147 | aprint_error(" at %s", intrstr); | | 148 | aprint_error(" at %s", intrstr); |
148 | aprint_error("\n"); | | 149 | aprint_error("\n"); |
149 | return; | | 150 | return; |
150 | } | | 151 | } |
151 | aprint_normal_dev(self, "interrupting at %s\n", intrstr); | | 152 | aprint_normal_dev(self, "interrupting at %s\n", intrstr); |
152 | | | 153 | |
153 | if (!pmf_device_register(self, NULL, hdaudio_pci_resume)) | | 154 | if (!pmf_device_register(self, NULL, hdaudio_pci_resume)) |
154 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 155 | aprint_error_dev(self, "couldn't establish power handler\n"); |
155 | | | 156 | |
| | | 157 | switch (PCI_VENDOR(pa->pa_id)) { |
| | | 158 | case PCI_VENDOR_NVIDIA: |
| | | 159 | /* enable snooping */ |
| | | 160 | csr = pci_conf_read(sc->sc_pc, sc->sc_tag, |
| | | 161 | HDAUDIO_NV_REG_SNOOP); |
| | | 162 | csr &= ~HDAUDIO_NV_SNOOP_MASK; |
| | | 163 | csr |= HDAUDIO_NV_SNOOP_ENABLE; |
| | | 164 | pci_conf_write(sc->sc_pc, sc->sc_tag, |
| | | 165 | HDAUDIO_NV_REG_SNOOP, csr); |
| | | 166 | break; |
| | | 167 | } |
| | | 168 | |
156 | /* Attach bus-independent HD audio layer */ | | 169 | /* Attach bus-independent HD audio layer */ |
157 | hdaudio_attach(self, &sc->sc_hdaudio); | | 170 | hdaudio_attach(self, &sc->sc_hdaudio); |
158 | } | | 171 | } |
159 | | | 172 | |
160 | void | | 173 | void |
161 | hdaudio_pci_childdet(device_t self, device_t child) | | 174 | hdaudio_pci_childdet(device_t self, device_t child) |
162 | { | | 175 | { |
163 | #if notyet | | 176 | #if notyet |
164 | struct hdaudio_pci_softc *sc = device_private(self); | | 177 | struct hdaudio_pci_softc *sc = device_private(self); |
165 | | | 178 | |
166 | hdaudio_childdet(&sc->sc_hdaudio, child); | | 179 | hdaudio_childdet(&sc->sc_hdaudio, child); |
167 | #endif | | 180 | #endif |
168 | } | | 181 | } |
169 | | | 182 | |
170 | static int | | 183 | static int |
171 | hdaudio_pci_detach(device_t self, int flags) | | 184 | hdaudio_pci_detach(device_t self, int flags) |
172 | { | | 185 | { |
173 | struct hdaudio_pci_softc *sc = device_private(self); | | 186 | struct hdaudio_pci_softc *sc = device_private(self); |
174 | pcireg_t csr; | | 187 | pcireg_t csr; |
175 | | | 188 | |
176 | hdaudio_detach(&sc->sc_hdaudio, flags); | | 189 | hdaudio_detach(&sc->sc_hdaudio, flags); |
177 | | | 190 | |
178 | if (sc->sc_ih != NULL) { | | 191 | if (sc->sc_ih != NULL) { |
179 | pci_intr_disestablish(sc->sc_pc, sc->sc_ih); | | 192 | pci_intr_disestablish(sc->sc_pc, sc->sc_ih); |
180 | sc->sc_ih = NULL; | | 193 | sc->sc_ih = NULL; |
181 | } | | 194 | } |
182 | if (sc->sc_hdaudio.sc_memvalid == true) { | | 195 | if (sc->sc_hdaudio.sc_memvalid == true) { |
183 | bus_space_unmap(sc->sc_hdaudio.sc_memt, | | 196 | bus_space_unmap(sc->sc_hdaudio.sc_memt, |
184 | sc->sc_hdaudio.sc_memh, | | 197 | sc->sc_hdaudio.sc_memh, |
185 | sc->sc_hdaudio.sc_memsize); | | 198 | sc->sc_hdaudio.sc_memsize); |
186 | sc->sc_hdaudio.sc_memvalid = false; | | 199 | sc->sc_hdaudio.sc_memvalid = false; |
187 | } | | 200 | } |
188 | | | 201 | |
189 | /* Disable busmastering and MMIO access */ | | 202 | /* Disable busmastering and MMIO access */ |
190 | csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); | | 203 | csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); |
191 | csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE); | | 204 | csr &= ~(PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE); |
192 | pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); | | 205 | pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); |
193 | | | 206 | |
194 | pmf_device_deregister(self); | | 207 | pmf_device_deregister(self); |
195 | | | 208 | |
196 | return 0; | | 209 | return 0; |
197 | } | | 210 | } |
198 | | | 211 | |
199 | static int | | 212 | static int |
200 | hdaudio_pci_intr(void *opaque) | | 213 | hdaudio_pci_intr(void *opaque) |
201 | { | | 214 | { |
202 | struct hdaudio_pci_softc *sc = opaque; | | 215 | struct hdaudio_pci_softc *sc = opaque; |
203 | | | 216 | |
204 | return hdaudio_intr(&sc->sc_hdaudio); | | 217 | return hdaudio_intr(&sc->sc_hdaudio); |
205 | } | | 218 | } |
206 | | | 219 | |
207 | static bool | | 220 | static bool |
208 | hdaudio_pci_resume(device_t self, const pmf_qual_t *qual) | | 221 | hdaudio_pci_resume(device_t self, const pmf_qual_t *qual) |
209 | { | | 222 | { |
210 | struct hdaudio_pci_softc *sc = device_private(self); | | 223 | struct hdaudio_pci_softc *sc = device_private(self); |
211 | | | 224 | |
212 | return hdaudio_resume(&sc->sc_hdaudio); | | 225 | return hdaudio_resume(&sc->sc_hdaudio); |
213 | } | | 226 | } |