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