xref: /netbsd-src/sys/arch/x68k/dev/vs.c (revision 220b5c059a84c51ea44107ea8951a57ffaecdc8c)
1 /*	$NetBSD: vs.c,v 1.11 2001/11/25 16:00:06 minoura Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 Tetsuya Isaki. 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 Tetsuya Isaki.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * VS - OKI MSM6258 ADPCM voice synthesizer device driver.
35  */
36 
37 #include "audio.h"
38 #include "vs.h"
39 #if NAUDIO > 0 && NVS > 0
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 
45 #include <sys/audioio.h>
46 #include <dev/audio_if.h>
47 
48 #include <machine/bus.h>
49 #include <machine/cpu.h>
50 
51 #include <dev/ic/msm6258var.h>
52 
53 #include <arch/x68k/dev/dmacvar.h>
54 #include <arch/x68k/dev/intiovar.h>
55 #include <arch/x68k/dev/opmreg.h>
56 
57 #include <arch/x68k/dev/vsvar.h>
58 
59 #ifdef VS_DEBUG
60 #define DPRINTF(y,x)	if(vs_debug>=(y))printf x
61 static int vs_debug;
62 #ifdef AUDIO_DEBUG
63 extern int audiodebug;
64 #endif
65 #else
66 #define DPRINTF(y,x)
67 #endif
68 
69 static int  vs_match __P((struct device *, struct cfdata *, void *));
70 static void vs_attach __P((struct device *, struct device *, void *));
71 
72 static int  vs_dmaintr __P((void *));
73 static int  vs_dmaerrintr __P((void *));
74 
75 /* MI audio layer interface */
76 static int  vs_open __P((void *, int));
77 static void vs_close __P((void *));
78 static int  vs_query_encoding __P((void *, struct audio_encoding *));
79 static int  vs_set_params __P((void *, int, int, struct audio_params *,
80 	struct audio_params *));
81 static int  vs_trigger_output __P((void *, void *, void *, int,
82 				   void (*)(void *), void *,
83 				   struct audio_params *));
84 static int  vs_trigger_input __P((void *, void *, void *, int,
85 				   void (*)(void *), void *,
86 				   struct audio_params *));
87 static int  vs_halt_output __P((void *));
88 static int  vs_halt_input __P((void *));
89 static int  vs_allocmem __P((struct vs_softc *, size_t, size_t, size_t, int,
90 			    struct vs_dma *));
91 static void vs_freemem __P((struct vs_dma *));
92 static int  vs_getdev __P((void *, struct audio_device *));
93 static int  vs_set_port __P((void *, mixer_ctrl_t *));
94 static int  vs_get_port __P((void *, mixer_ctrl_t *));
95 static int  vs_query_devinfo __P((void *, mixer_devinfo_t *));
96 static void *vs_allocm __P((void *, int, size_t, int, int));
97 static void vs_freem __P((void *, void *, int));
98 static size_t vs_round_buffersize __P((void *, int, size_t));
99 static int  vs_get_props __P((void *));
100 
101 /* lower functions */
102 static int vs_round_sr(u_long);
103 static void vs_set_sr(struct vs_softc *sc, int);
104 static inline void vs_set_po(struct vs_softc *sc, u_long);
105 
106 extern struct cfdata vs_cd;
107 
108 struct cfattach vs_ca = {
109 	sizeof(struct vs_softc), vs_match, vs_attach
110 };
111 
112 static struct audio_hw_if vs_hw_if = {
113 	vs_open,
114 	vs_close,
115 	NULL,			/* drain */
116 
117 	vs_query_encoding,
118 	vs_set_params,
119 	NULL,			/* round_blocksize */
120 	NULL,			/* commit_settings */
121 
122 	NULL, 			/* init_output */
123 	NULL, 			/* init_input */
124 	NULL, 			/* start_output */
125 	NULL,			/* start_input */
126 
127 	vs_halt_output,
128 	vs_halt_input,
129 	NULL,			/* speaker_ctl */
130 
131 	vs_getdev,
132 	NULL,			/* setfd */
133 
134 	vs_set_port,
135 	vs_get_port,
136 	vs_query_devinfo,
137 
138 	vs_allocm,
139 	vs_freem,
140 	vs_round_buffersize,
141 	NULL,			/* mappage */
142 
143 	vs_get_props,
144 
145 	vs_trigger_output,
146 	vs_trigger_input,
147 
148 	NULL,
149 };
150 
151 static struct audio_device vs_device = {
152 	"OKI MSM6258",
153 	"",
154 	"vs"
155 };
156 
157 struct {
158 	u_long rate;
159 	u_char clk;
160 	u_char den;
161 } vs_l2r[] = {
162 	{ VS_RATE_15K, VS_CLK_8MHZ, VS_SRATE_512 },
163 	{ VS_RATE_10K, VS_CLK_8MHZ, VS_SRATE_768 },
164 	{ VS_RATE_7K,  VS_CLK_8MHZ, VS_SRATE_1024},
165 	{ VS_RATE_5K,  VS_CLK_4MHZ, VS_SRATE_768 },
166 	{ VS_RATE_3K,  VS_CLK_4MHZ, VS_SRATE_1024}
167 };
168 
169 #define NUM_RATE	(sizeof(vs_l2r)/sizeof(vs_l2r[0]))
170 
171 struct audio_encoding vs_encodings[] = {
172 	{0, AudioEadpcm, AUDIO_ENCODING_ADPCM, 4, 0},
173 	{1, AudioEulinear, AUDIO_ENCODING_ULINEAR, 8,
174 	    AUDIO_ENCODINGFLAG_EMULATED},
175 	{2, AudioEmulaw, AUDIO_ENCODING_ULAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
176 };
177 
178 static int
179 vs_match(struct device *parent, struct cfdata *cf, void *aux)
180 {
181 	struct intio_attach_args *ia = aux;
182 
183 	if (strcmp(ia->ia_name, "vs") || cf->cf_unit > 0)
184 		return 0;
185 
186 	if (ia->ia_addr == INTIOCF_ADDR_DEFAULT)
187 		ia->ia_addr = VS_ADDR;
188 	if (ia->ia_dma == INTIOCF_DMA_DEFAULT)
189 		ia->ia_dma = VS_DMA;
190 	if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT)
191 		ia->ia_dmaintr = VS_DMAINTR;
192 
193 	/* fixed parameters */
194 	if (ia->ia_addr != VS_ADDR)
195 		return 0;
196 	if (ia->ia_dma != VS_DMA)
197 		return 0;
198 	if (ia->ia_dmaintr != VS_DMAINTR)
199 		return 0;
200 
201 #ifdef VS_DEBUG
202 	vs_debug = 1;
203 #ifdef AUDIO_DEBUG
204 	audiodebug = 2;
205 #endif
206 #endif
207 
208 	return 1;
209 }
210 
211 static void
212 vs_attach(struct device *parent, struct device *self, void *aux)
213 {
214 	struct vs_softc *sc = (struct vs_softc *)self;
215 	bus_space_tag_t iot;
216 	bus_space_handle_t ioh;
217 	struct intio_attach_args *ia = aux;
218 
219 	printf("\n");
220 
221 	/* Re-map the I/O space */
222 	iot = ia->ia_bst;
223 	bus_space_map(iot, ia->ia_addr, 0x2000, BUS_SPACE_MAP_SHIFTED, &ioh);
224 
225 	/* Initialize sc */
226 	sc->sc_iot = iot;
227 	sc->sc_ioh = ioh;
228 	sc->sc_hw_if = &vs_hw_if;
229 	sc->sc_addr = (caddr_t) ia->ia_addr;
230 	sc->sc_dmas = NULL;
231 
232 	/* Initialize codec */
233 	sc->sc_codec = msm6258_codec_init();
234 	if (sc->sc_codec == NULL) {
235 		printf ("Could not init codec\n");
236 		return;
237 	}
238 
239 	/* XXX */
240 	bus_space_map(iot, PPI_ADDR, PPI_MAPSIZE, BUS_SPACE_MAP_SHIFTED,
241 		      &sc->sc_ppi);
242 
243 	/* Initialize DMAC */
244 	sc->sc_dmat = ia->ia_dmat;
245 	sc->sc_dma_ch = dmac_alloc_channel(parent, ia->ia_dma, "vs",
246 		ia->ia_dmaintr,   vs_dmaintr, sc,
247 		ia->ia_dmaintr+1, vs_dmaerrintr, sc);
248 
249 	printf("%s: MSM6258V ADPCM voice synthesizer\n", sc->sc_dev.dv_xname);
250 
251 	audio_attach_mi(&vs_hw_if, sc, &sc->sc_dev);
252 }
253 
254 /*
255  * vs interrupt handler
256  */
257 static int
258 vs_dmaintr(void *hdl)
259 {
260 	struct vs_softc *sc = hdl;
261 
262 	DPRINTF(2, ("vs_dmaintr\n"));
263 
264 	if (sc->sc_pintr) {
265 		/* start next transfer */
266 		sc->sc_current.dmap += sc->sc_current.blksize;
267 		if (sc->sc_current.dmap + sc->sc_current.blksize
268 			> sc->sc_current.bufsize)
269 			sc->sc_current.dmap -= sc->sc_current.bufsize;
270 		dmac_start_xfer_offset (sc->sc_dma_ch->ch_softc,
271 					sc->sc_current.xfer,
272 					sc->sc_current.dmap,
273 					sc->sc_current.blksize);
274 		sc->sc_pintr(sc->sc_parg);
275 	} else if (sc->sc_rintr) {
276 		/* start next transfer */
277 		sc->sc_current.dmap += sc->sc_current.blksize;
278 		if (sc->sc_current.dmap + sc->sc_current.blksize
279 			> sc->sc_current.bufsize)
280 			sc->sc_current.dmap -= sc->sc_current.bufsize;
281 		dmac_start_xfer_offset (sc->sc_dma_ch->ch_softc,
282 					sc->sc_current.xfer,
283 					sc->sc_current.dmap,
284 					sc->sc_current.blksize);
285 		sc->sc_rintr(sc->sc_rarg);
286 	} else {
287 		printf ("vs_dmaintr: spurious interrupt\n");
288 	}
289 
290 	return 1;
291 }
292 
293 static int
294 vs_dmaerrintr(void *hdl)
295 {
296 	struct vs_softc *sc = hdl;
297 
298 	DPRINTF(1, ("%s: DMA transfer error.\n", sc->sc_dev.dv_xname));
299 	/* XXX */
300 	vs_dmaintr(sc);
301 
302 	return 1;
303 }
304 
305 
306 /*
307  * audio MD layer interfaces
308  */
309 
310 static int
311 vs_open(void *hdl, int flags)
312 {
313 	struct vs_softc *sc = hdl;
314 
315 	DPRINTF(1, ("vs_open: flags=%d\n", flags));
316 
317 	sc->sc_pintr = NULL;
318 	sc->sc_rintr = NULL;
319 
320 	return 0;
321 }
322 
323 static void
324 vs_close(void *hdl)
325 {
326 	DPRINTF(1, ("vs_close\n"));
327 }
328 
329 static int
330 vs_query_encoding(void *hdl, struct audio_encoding *fp)
331 {
332 	DPRINTF(1, ("vs_query_encoding\n"));
333 
334 	if (fp->index >= sizeof(vs_encodings) / sizeof(vs_encodings[0]))
335 		return EINVAL;
336 
337 	*fp = vs_encodings[fp->index];
338 	return 0;
339 }
340 
341 static int
342 vs_round_sr(u_long rate)
343 {
344 	int i;
345 	int diff = rate;
346 	int nearest = 0;
347 
348 	for (i = 0; i < NUM_RATE; i++) {
349 		if (rate >= vs_l2r[i].rate) {
350 			if (rate - vs_l2r[i].rate < diff) {
351 				diff = rate - vs_l2r[i].rate;
352 				nearest = i;
353 			}
354 		} else {
355 			if (vs_l2r[i].rate - rate < diff) {
356 				diff = vs_l2r[i].rate - rate;
357 				nearest = i;
358 			}
359 		}
360 	}
361 	if (diff * 100 / rate > 15)
362 		return -1;
363 	else
364 		return nearest;
365 }
366 
367 static int
368 vs_set_params(void *hdl, int setmode, int usemode,
369 	struct audio_params *play, struct audio_params *rec)
370 {
371 	struct vs_softc *sc = hdl;
372 	struct audio_params *p;
373 	int mode;
374 	int rate;
375 
376 	DPRINTF(1, ("vs_set_params: setmode=%d, usemode=%d\n", setmode, usemode));
377 
378 	/* set first record info, then play info */
379 	for (mode = AUMODE_RECORD; mode != -1;
380 	     mode = (mode == AUMODE_RECORD) ? AUMODE_PLAY : -1) {
381 		if ((setmode & mode) == 0)
382 			continue;
383 
384 		p = (mode == AUMODE_PLAY) ? play : rec;
385 
386 		if (p->channels != 1)
387 			return (EINVAL);
388 
389 		rate = p->sample_rate;
390 		p->sw_code = NULL;
391 		p->factor = 1;
392 		switch (p->encoding) {
393 		case AUDIO_ENCODING_ULAW:
394 			if (p->precision != 8)
395 				return EINVAL;
396 			if (mode == AUMODE_PLAY) {
397 				p->sw_code = msm6258_mulaw_to_adpcm;
398 				rate = p->sample_rate * 2;
399 			} else {
400 				p->sw_code = msm6258_adpcm_to_mulaw;
401 				p->factor = 2;
402 			}
403 			break;
404 		case AUDIO_ENCODING_ULINEAR_LE:
405 		case AUDIO_ENCODING_ULINEAR_BE:
406 			if (p->precision != 8)
407 				return EINVAL;
408 			if (mode == AUMODE_PLAY) {
409 				p->sw_code = msm6258_ulinear8_to_adpcm;
410 				rate = p->sample_rate * 2;
411 			} else {
412 				p->sw_code = msm6258_adpcm_to_ulinear8;
413 				p->factor = 2;
414 			}
415 			break;
416 		case AUDIO_ENCODING_ADPCM:
417 			if (p->precision != 4)
418 				return EINVAL;
419 			break;
420 		default:
421 			DPRINTF(1, ("vs_set_params: mode=%d, encoding=%d\n",
422 				mode, p->encoding));
423 			return (EINVAL);
424 		}
425 		DPRINTF(1, ("vs_set_params: rate=%d -> ", rate));
426 		rate = vs_round_sr(rate);
427 		DPRINTF(1, ("%d\n", rate));
428 		if (rate < 0)
429 			return (EINVAL);
430 		if (mode == AUMODE_PLAY)
431 			sc->sc_current.prate = rate;
432 		else
433 			sc->sc_current.rrate = rate;
434 	}
435 
436 	return 0;
437 }
438 
439 static void
440 vs_set_sr(struct vs_softc *sc, int rate)
441 {
442 	DPRINTF(1, ("setting sample rate to %d, %d\n",
443 		 rate, (int)vs_l2r[rate].rate));
444 	bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC,
445 			  (bus_space_read_1 (sc->sc_iot, sc->sc_ppi,
446 					     PPI_PORTC) & 0xf0)
447 			  | vs_l2r[rate].den);
448 	adpcm_chgclk(vs_l2r[rate].clk);
449 }
450 
451 static inline void
452 vs_set_po(struct vs_softc *sc, u_long po)
453 {
454 	bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC,
455 			  (bus_space_read_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC)
456 			   & 0xfc) | po);
457 }
458 
459 static int
460 vs_trigger_output(void *hdl, void *start, void *end, int bsize,
461 		  void (*intr)(void *), void *arg,
462 		  struct audio_params *p)
463 {
464 	struct vs_softc *sc = hdl;
465 	struct vs_dma *vd;
466 	struct dmac_dma_xfer *xf;
467 	struct dmac_channel_stat *chan = sc->sc_dma_ch;
468 
469 	DPRINTF(2, ("vs_trigger_output: start=%p, bsize=%d, intr=%p, arg=%p\n",
470 		 start, bsize, intr, arg));
471 
472 	sc->sc_pintr = intr;
473 	sc->sc_parg  = arg;
474 	sc->sc_current.blksize = bsize;
475 	sc->sc_current.bufsize = (char*)end - (char*)start;
476 	sc->sc_current.dmap = 0;
477 
478 	/* Find DMA buffer. */
479 	for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start;
480 	     vd = vd->vd_next)
481 		;
482 	if (vd == NULL) {
483 		printf("%s: trigger_output: bad addr %p\n",
484 		    sc->sc_dev.dv_xname, start);
485 		return (EINVAL);
486 	}
487 
488 	vs_set_sr(sc, sc->sc_current.prate);
489 	vs_set_po(sc, VS_PANOUT_LR);
490 
491 	xf = dmac_alloc_xfer (chan, sc->sc_dmat, vd->vd_map);
492 	sc->sc_current.xfer = xf;
493 	chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC |
494 			DMAC_DCR_OPS_8BIT);
495 	chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL;
496 	xf->dx_ocr = DMAC_OCR_DIR_MTD;
497 	xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT;
498 	xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1;
499 
500 	dmac_load_xfer (chan->ch_softc, xf);
501 	dmac_start_xfer_offset (chan->ch_softc, xf, 0, sc->sc_current.blksize);
502 	bus_space_write_1 (sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 2);
503 
504 	return 0;
505 }
506 
507 static int
508 vs_trigger_input(void *hdl, void *start, void *end, int bsize,
509 		 void (*intr)(void *), void *arg,
510 		 struct audio_params *p)
511 {
512 	struct vs_softc *sc = hdl;
513 	struct vs_dma *vd;
514 	struct dmac_dma_xfer *xf;
515 	struct dmac_channel_stat *chan = sc->sc_dma_ch;
516 
517 	DPRINTF(2, ("vs_trigger_input: start=%p, bsize=%d, intr=%p, arg=%p\n",
518 		 start, bsize, intr, arg));
519 
520 	sc->sc_rintr = intr;
521 	sc->sc_rarg  = arg;
522 	sc->sc_current.blksize = bsize;
523 	sc->sc_current.bufsize = (char*)end - (char*)start;
524 	sc->sc_current.dmap = 0;
525 
526 	/* Find DMA buffer. */
527 	for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start;
528 	     vd = vd->vd_next)
529 		;
530 	if (vd == NULL) {
531 		printf("%s: trigger_output: bad addr %p\n",
532 		    sc->sc_dev.dv_xname, start);
533 		return (EINVAL);
534 	}
535 
536 	vs_set_sr(sc, sc->sc_current.rrate);
537 	xf = dmac_alloc_xfer (chan, sc->sc_dmat, vd->vd_map);
538 	sc->sc_current.xfer = xf;
539 	chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC |
540 			DMAC_DCR_OPS_8BIT);
541 	chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL;
542 	xf->dx_ocr = DMAC_OCR_DIR_DTM;
543 	xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT;
544 	xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1;
545 
546 	dmac_load_xfer (chan->ch_softc, xf);
547 	dmac_start_xfer_offset (chan->ch_softc, xf, 0, sc->sc_current.blksize);
548 	bus_space_write_1 (sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 4);
549 
550 	return 0;
551 }
552 
553 static int
554 vs_halt_output(void *hdl)
555 {
556 	struct vs_softc *sc = hdl;
557 
558 	DPRINTF(1, ("vs_halt_output\n"));
559 
560 	/* stop ADPCM play */
561 	dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer);
562 	bus_space_write_1 (sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1);
563 
564 	return 0;
565 }
566 
567 static int
568 vs_halt_input(void *hdl)
569 {
570 	struct vs_softc *sc = hdl;
571 
572 	DPRINTF(1, ("vs_halt_input\n"));
573 
574 	/* stop ADPCM recoding */
575 	dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer);
576 	bus_space_write_1 (sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1);
577 
578 	return 0;
579 }
580 
581 static int
582 vs_allocmem(sc, size, align, boundary, flags, vd)
583 	struct vs_softc *sc;
584 	size_t size;
585 	size_t align;
586 	size_t boundary;
587 	int flags;
588 	struct vs_dma *vd;
589 {
590 	int error, wait;
591 
592 #ifdef DIAGNOSTIC
593 	if (size > DMAC_MAXSEGSZ)
594 		panic ("vs_allocmem: maximum size exceeded, %d", (int) size);
595 #endif
596 
597 	wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
598 	vd->vd_size = size;
599 
600 	error = bus_dmamem_alloc(vd->vd_dmat, vd->vd_size, align, boundary,
601 				 vd->vd_segs,
602 				 sizeof (vd->vd_segs) / sizeof (vd->vd_segs[0]),
603 				 &vd->vd_nsegs, wait);
604 	if (error)
605 		goto out;
606 
607 	error = bus_dmamem_map(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs,
608 			       vd->vd_size, &vd->vd_addr,
609 			       wait | BUS_DMA_COHERENT);
610 	if (error)
611 		goto free;
612 
613 	error = bus_dmamap_create(vd->vd_dmat, vd->vd_size, 1, DMAC_MAXSEGSZ,
614 				  0, wait, &vd->vd_map);
615 	if (error)
616 		goto unmap;
617 
618 	error = bus_dmamap_load(vd->vd_dmat, vd->vd_map, vd->vd_addr,
619 				vd->vd_size, NULL, wait);
620 	if (error)
621 		goto destroy;
622 
623 	return (0);
624 
625  destroy:
626 	bus_dmamap_destroy(vd->vd_dmat, vd->vd_map);
627  unmap:
628 	bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size);
629  free:
630 	bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs);
631  out:
632 	return (error);
633 }
634 
635 static void
636 vs_freemem(vd)
637 	struct vs_dma *vd;
638 {
639 
640 	bus_dmamap_unload(vd->vd_dmat, vd->vd_map);
641 	bus_dmamap_destroy(vd->vd_dmat, vd->vd_map);
642 	bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size);
643 	bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs);
644 }
645 
646 static int
647 vs_getdev(void *hdl, struct audio_device *retp)
648 {
649 	DPRINTF(1, ("vs_getdev\n"));
650 
651 	*retp = vs_device;
652 	return 0;
653 }
654 
655 static int
656 vs_set_port(void *hdl, mixer_ctrl_t *cp)
657 {
658 	DPRINTF(1, ("vs_set_port\n"));
659 	return 0;
660 }
661 
662 static int
663 vs_get_port(void *hdl, mixer_ctrl_t *cp)
664 {
665 	DPRINTF(1, ("vs_get_port\n"));
666 	return 0;
667 }
668 
669 static int
670 vs_query_devinfo(void *hdl, mixer_devinfo_t *mi)
671 {
672 	DPRINTF(1, ("vs_query_devinfo\n"));
673 	switch (mi->index) {
674 	default:
675 		return EINVAL;
676 	}
677 	return 0;
678 }
679 
680 static void *
681 vs_allocm(hdl, direction, size, type, flags)
682 	void *hdl;
683 	int direction;
684 	size_t size;
685 	int type, flags;
686 {
687 	struct vs_softc *sc = hdl;
688 	struct vs_dma *vd;
689 	int error;
690 
691 	if ((vd = malloc(size, type, flags)) == NULL)
692 		return (NULL);
693 
694 	vd->vd_dmat = sc->sc_dmat;
695 
696 	error = vs_allocmem(sc, size, 32, 0, flags, vd);
697 	if (error) {
698 		free(vd, type);
699 		return (NULL);
700 	}
701 	vd->vd_next = sc->sc_dmas;
702 	sc->sc_dmas = vd;
703 
704 	return (KVADDR(vd));
705 }
706 
707 static void
708 vs_freem(hdl, addr, type)
709 	void *hdl;
710 	void *addr;
711 	int type;
712 {
713 	struct vs_softc *sc = hdl;
714 	struct vs_dma *p, **pp;
715 
716 	for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->vd_next) {
717 		if (KVADDR(p) == addr) {
718 			vs_freemem(p);
719 			*pp = p->vd_next;
720 			free(p, type);
721 			return;
722 		}
723 	}
724 }
725 
726 static size_t
727 vs_round_buffersize(void *hdl, int direction, size_t bufsize)
728 {
729 	if (bufsize > DMAC_MAXSEGSZ)
730 		bufsize = DMAC_MAXSEGSZ;
731 
732 	return bufsize;
733 }
734 
735 #if 0
736 paddr_t
737 vs_mappage(addr, mem, off, prot)
738 	void *addr;
739 	void *mem;
740 	off_t off;
741 	int prot;
742 {
743 	struct vs_softc *sc = addr;
744 	struct vs_dma *p;
745 
746 	if (off < 0)
747 		return (-1);
748 	for (p = sc->sc_dmas; p != NULL && KVADDR(p) != mem;
749 	     p = p->vd_next)
750 		;
751 	if (p == NULL) {
752 		printf("%s: mappage: bad addr %p\n",
753 		    sc->sc_dev.dv_xname, start);
754 		return (-1);
755 	}
756 
757 	return (bus_dmamem_mmap(sc->sc_dmat, p->vd_segs, p->vd_nsegs,
758 				off, prot, BUS_DMA_WAITOK));
759 }
760 #endif
761 
762 static int
763 vs_get_props(void *hdl)
764 {
765 	DPRINTF(1, ("vs_get_props\n"));
766 	return 0 /* | dependent | half duplex | no mmap */;
767 }
768 #endif /* NAUDIO > 0 && NVS > 0*/
769