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