1 /* $NetBSD: cs428x.c,v 1.20 2019/06/08 08:02:38 isaki Exp $ */
2
3 /*
4 * Copyright (c) 2000 Tatoku Ogaito. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Tatoku Ogaito
17 * for the NetBSD Project.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /* Common functions for CS4280 and CS4281 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: cs428x.c,v 1.20 2019/06/08 08:02:38 isaki Exp $");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/kmem.h>
42 #include <sys/device.h>
43 #include <sys/audioio.h>
44 #include <sys/bus.h>
45
46 #include <dev/audio/audio_if.h>
47 #include <dev/midi_if.h>
48
49 #include <dev/ic/ac97reg.h>
50 #include <dev/ic/ac97var.h>
51
52 #include <dev/pci/pcidevs.h>
53 #include <dev/pci/pcivar.h>
54 #include <dev/pci/cs428xreg.h>
55 #include <dev/pci/cs428x.h>
56
57 #if defined(CS4280_DEBUG) || defined(CS4281_DEBUG)
58 int cs428x_debug = 0;
59 #endif
60
61 int
cs428x_round_blocksize(void * addr,int blk,int mode,const audio_params_t * param)62 cs428x_round_blocksize(void *addr, int blk,
63 int mode, const audio_params_t *param)
64 {
65 struct cs428x_softc *sc;
66 int retval;
67
68 DPRINTFN(5,("cs428x_round_blocksize blk=%d -> ", blk));
69
70 sc = addr;
71 if (blk < sc->hw_blocksize)
72 retval = sc->hw_blocksize;
73 else
74 retval = blk & -(sc->hw_blocksize);
75
76 DPRINTFN(5,("%d\n", retval));
77
78 return retval;
79 }
80
81 int
cs428x_mixer_set_port(void * addr,mixer_ctrl_t * cp)82 cs428x_mixer_set_port(void *addr, mixer_ctrl_t *cp)
83 {
84 struct cs428x_softc *sc;
85 int val;
86
87 sc = addr;
88 val = sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
89 DPRINTFN(3,("mixer_set_port: val=%d\n", val));
90 return (val);
91 }
92
93 int
cs428x_mixer_get_port(void * addr,mixer_ctrl_t * cp)94 cs428x_mixer_get_port(void *addr, mixer_ctrl_t *cp)
95 {
96 struct cs428x_softc *sc;
97
98 sc = addr;
99 return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
100 }
101
102 int
cs428x_query_devinfo(void * addr,mixer_devinfo_t * dip)103 cs428x_query_devinfo(void *addr, mixer_devinfo_t *dip)
104 {
105 struct cs428x_softc *sc;
106
107 sc = addr;
108 return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
109 }
110
111 void *
cs428x_malloc(void * addr,int direction,size_t size)112 cs428x_malloc(void *addr, int direction, size_t size)
113 {
114 struct cs428x_softc *sc;
115 struct cs428x_dma *p;
116 int error;
117
118 sc = addr;
119
120 p = kmem_alloc(sizeof(*p), KM_SLEEP);
121 error = cs428x_allocmem(sc, size, p);
122 if (error) {
123 kmem_free(p, sizeof(*p));
124 return 0;
125 }
126
127 p->next = sc->sc_dmas;
128 sc->sc_dmas = p;
129 return BUFADDR(p);
130 }
131
132 void
cs428x_free(void * addr,void * ptr,size_t size)133 cs428x_free(void *addr, void *ptr, size_t size)
134 {
135 struct cs428x_softc *sc;
136 struct cs428x_dma **pp, *p;
137
138 sc = addr;
139 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
140 if (BUFADDR(p) == ptr) {
141 bus_dmamap_unload(sc->sc_dmatag, p->map);
142 bus_dmamap_destroy(sc->sc_dmatag, p->map);
143 bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
144 bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
145 kmem_free(p->dum, p->size);
146 *pp = p->next;
147 kmem_free(p, sizeof(*p));
148 return;
149 }
150 }
151 }
152
153 size_t
cs428x_round_buffersize(void * addr,int direction,size_t size)154 cs428x_round_buffersize(void *addr, int direction,
155 size_t size)
156 {
157 /* The real DMA buffersize are 4KB for CS4280
158 * and 64kB/MAX_CHANNELS for CS4281.
159 * But they are too small for high quality audio,
160 * let the upper layer(audio) use a larger buffer.
161 * (originally suggested by Lennart Augustsson.)
162 */
163 return size;
164 }
165
166 int
cs428x_get_props(void * addr)167 cs428x_get_props(void *addr)
168 {
169
170 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
171 AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
172 }
173
174 /* AC97 */
175 int
cs428x_attach_codec(void * addr,struct ac97_codec_if * codec_if)176 cs428x_attach_codec(void *addr, struct ac97_codec_if *codec_if)
177 {
178 struct cs428x_softc *sc;
179
180 DPRINTF(("cs428x_attach_codec:\n"));
181 sc = addr;
182 sc->codec_if = codec_if;
183 return 0;
184 }
185
186 int
cs428x_read_codec(void * addr,uint8_t ac97_addr,uint16_t * ac97_data)187 cs428x_read_codec(void *addr, uint8_t ac97_addr, uint16_t *ac97_data)
188 {
189 struct cs428x_softc *sc;
190 uint32_t acctl;
191 int n;
192
193 sc = addr;
194
195 DPRINTFN(5,("read_codec: add=0x%02x ", ac97_addr));
196 /*
197 * Make sure that there is not data sitting around from a previous
198 * uncompleted access.
199 */
200 BA0READ4(sc, CS428X_ACSDA);
201
202 /* Set up AC97 control registers. */
203 BA0WRITE4(sc, CS428X_ACCAD, ac97_addr);
204 BA0WRITE4(sc, CS428X_ACCDA, 0);
205
206 acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_CRW | ACCTL_DCV;
207 if (sc->type == TYPE_CS4280)
208 acctl |= ACCTL_RSTN;
209 BA0WRITE4(sc, CS428X_ACCTL, acctl);
210
211 if (cs428x_src_wait(sc) < 0) {
212 printf("%s: AC97 read prob. (DCV!=0) for add=0x%0x\n",
213 device_xname(sc->sc_dev), ac97_addr);
214 return 1;
215 }
216
217 /* wait for valid status bit is active */
218 n = 0;
219 while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_VSTS) == 0) {
220 delay(1);
221 while (++n > 1000) {
222 printf("%s: AC97 read fail (VSTS==0) for add=0x%0x\n",
223 device_xname(sc->sc_dev), ac97_addr);
224 return 1;
225 }
226 }
227 *ac97_data = BA0READ4(sc, CS428X_ACSDA);
228 DPRINTFN(5,("data=0x%04x\n", *ac97_data));
229 return 0;
230 }
231
232 int
cs428x_write_codec(void * addr,uint8_t ac97_addr,uint16_t ac97_data)233 cs428x_write_codec(void *addr, uint8_t ac97_addr, uint16_t ac97_data)
234 {
235 struct cs428x_softc *sc;
236 uint32_t acctl;
237
238 sc = addr;
239
240 DPRINTFN(5,("write_codec: add=0x%02x data=0x%04x\n", ac97_addr, ac97_data));
241 BA0WRITE4(sc, CS428X_ACCAD, ac97_addr);
242 BA0WRITE4(sc, CS428X_ACCDA, ac97_data);
243
244 acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_DCV;
245 if (sc->type == TYPE_CS4280)
246 acctl |= ACCTL_RSTN;
247 BA0WRITE4(sc, CS428X_ACCTL, acctl);
248
249 if (cs428x_src_wait(sc) < 0) {
250 printf("%s: AC97 write fail (DCV!=0) for add=0x%02x data="
251 "0x%04x\n", device_xname(sc->sc_dev), ac97_addr, ac97_data);
252 return 1;
253 }
254 return 0;
255 }
256
257 /* Internal functions */
258 int
cs428x_allocmem(struct cs428x_softc * sc,size_t size,struct cs428x_dma * p)259 cs428x_allocmem(struct cs428x_softc *sc, size_t size, struct cs428x_dma *p)
260 {
261 int error;
262 size_t align;
263
264 align = sc->dma_align;
265 p->size = sc->dma_size;
266 /* allocate memory for upper audio driver */
267 p->dum = kmem_alloc(size, KM_SLEEP);
268
269 error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
270 p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
271 &p->nsegs, BUS_DMA_WAITOK);
272 if (error) {
273 aprint_error_dev(sc->sc_dev, "unable to allocate DMA. error=%d\n",
274 error);
275 goto allfree;
276 }
277
278 error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
279 &p->addr, BUS_DMA_WAITOK|BUS_DMA_COHERENT);
280 if (error) {
281 aprint_error_dev(sc->sc_dev, "unable to map DMA, error=%d\n",
282 error);
283 goto free;
284 }
285
286 error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
287 0, BUS_DMA_WAITOK, &p->map);
288 if (error) {
289 aprint_error_dev(sc->sc_dev, "unable to create DMA map, error=%d\n",
290 error);
291 goto unmap;
292 }
293
294 error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
295 BUS_DMA_WAITOK);
296 if (error) {
297 aprint_error_dev(sc->sc_dev, "unable to load DMA map, error=%d\n",
298 error);
299 goto destroy;
300 }
301 return 0;
302
303 destroy:
304 bus_dmamap_destroy(sc->sc_dmatag, p->map);
305 unmap:
306 bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
307 free:
308 bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
309 allfree:
310 kmem_free(p->dum, size);
311
312 return error;
313 }
314
315 int
cs428x_src_wait(struct cs428x_softc * sc)316 cs428x_src_wait(struct cs428x_softc *sc)
317 {
318 int n;
319
320 n = 0;
321 while ((BA0READ4(sc, CS428X_ACCTL) & ACCTL_DCV)) {
322 delay(1000);
323 while (++n > 1000) {
324 printf("cs428x_src_wait: 0x%08x\n",
325 BA0READ4(sc, CS428X_ACCTL));
326 return -1;
327 }
328 }
329 return 0;
330 }
331
332 void
cs428x_get_locks(void * addr,kmutex_t ** intr,kmutex_t ** thread)333 cs428x_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread)
334 {
335 struct cs428x_softc *sc;
336
337 sc = addr;
338 *intr = &sc->sc_intr_lock;
339 *thread = &sc->sc_lock;
340 }
341