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