1 /* $NetBSD: vs.c,v 1.32 2007/03/11 08:09:25 isaki Exp $ */
4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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
31 * VS - OKI MSM6258 ADPCM voice synthesizer device driver.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: vs.c,v 1.32 2007/03/11 08:09:25 isaki Exp $");
39 #if NAUDIO > 0 && NVS > 0
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
45 #include <sys/audioio.h>
46 #include <dev/audio_if.h>
47 #include <dev/mulaw.h>
49 #include <machine/bus.h>
50 #include <machine/cpu.h>
52 #include <dev/ic/msm6258var.h>
54 #include <arch/x68k/dev/dmacvar.h>
55 #include <arch/x68k/dev/intiovar.h>
56 #include <arch/x68k/dev/opmvar.h>
58 #include <arch/x68k/dev/vsvar.h>
61 #define DPRINTF(y,x) if (vs_debug >= (y)) printf x
64 extern int audiodebug
;
70 static int vs_match(device_t
, cfdata_t
, void *);
71 static void vs_attach(device_t
, device_t
, void *);
73 static int vs_dmaintr(void *);
74 static int vs_dmaerrintr(void *);
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,
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 *);
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
);
105 extern struct cfdriver vs_cd
;
107 CFATTACH_DECL_NEW(vs
, sizeof(struct vs_softc
),
108 vs_match
, vs_attach
, NULL
, NULL
);
110 static int vs_attached
;
112 static const struct audio_hw_if vs_hw_if
= {
118 NULL
, /* round_blocksize */
119 NULL
, /* commit_settings */
120 NULL
, /* init_output */
121 NULL
, /* init_input */
122 NULL
, /* start_output */
123 NULL
, /* start_input */
126 NULL
, /* speaker_ctl */
142 static struct audio_device vs_device
= {
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
}
160 #define NUM_RATE (sizeof(vs_l2r)/sizeof(vs_l2r[0]))
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},
176 vs_match(device_t parent
, cfdata_t cf
, void *aux
)
178 struct intio_attach_args
*ia
;
181 if (strcmp(ia
->ia_name
, "vs") || vs_attached
)
184 if (ia
->ia_addr
== INTIOCF_ADDR_DEFAULT
)
185 ia
->ia_addr
= VS_ADDR
;
186 if (ia
->ia_dma
== INTIOCF_DMA_DEFAULT
)
188 if (ia
->ia_dmaintr
== INTIOCF_DMAINTR_DEFAULT
)
189 ia
->ia_dmaintr
= VS_DMAINTR
;
191 /* fixed parameters */
192 if (ia
->ia_addr
!= VS_ADDR
)
194 if (ia
->ia_dma
!= VS_DMA
)
196 if (ia
->ia_dmaintr
!= VS_DMAINTR
)
210 vs_attach(device_t parent
, device_t self
, void *aux
)
214 bus_space_handle_t ioh
;
215 struct intio_attach_args
*ia
;
217 sc
= device_private(self
);
224 /* Re-map the I/O space */
226 bus_space_map(iot
, ia
->ia_addr
, 0x2000, BUS_SPACE_MAP_SHIFTED
, &ioh
);
231 sc
->sc_hw_if
= &vs_hw_if
;
232 sc
->sc_addr
= (void *) ia
->ia_addr
;
236 bus_space_map(iot
, PPI_ADDR
, PPI_MAPSIZE
, BUS_SPACE_MAP_SHIFTED
,
239 /* Initialize DMAC */
240 sc
->sc_dmat
= ia
->ia_dmat
;
241 sc
->sc_dma_ch
= dmac_alloc_channel(parent
, ia
->ia_dma
, "vs",
242 ia
->ia_dmaintr
, vs_dmaintr
, sc
,
243 ia
->ia_dmaintr
+1, vs_dmaerrintr
, sc
);
245 aprint_normal_dev(self
, "MSM6258V ADPCM voice synthesizer\n");
247 audio_attach_mi(&vs_hw_if
, sc
, sc
->sc_dev
);
251 * vs interrupt handler
254 vs_dmaintr(void *hdl
)
258 DPRINTF(2, ("vs_dmaintr\n"));
261 /* start next transfer */
262 sc
->sc_current
.dmap
+= sc
->sc_current
.blksize
;
263 if (sc
->sc_current
.dmap
+ sc
->sc_current
.blksize
264 > sc
->sc_current
.bufsize
)
265 sc
->sc_current
.dmap
-= sc
->sc_current
.bufsize
;
266 dmac_start_xfer_offset(sc
->sc_dma_ch
->ch_softc
,
269 sc
->sc_current
.blksize
);
270 sc
->sc_pintr(sc
->sc_parg
);
271 } else if (sc
->sc_rintr
) {
272 /* start next transfer */
273 sc
->sc_current
.dmap
+= sc
->sc_current
.blksize
;
274 if (sc
->sc_current
.dmap
+ sc
->sc_current
.blksize
275 > sc
->sc_current
.bufsize
)
276 sc
->sc_current
.dmap
-= sc
->sc_current
.bufsize
;
277 dmac_start_xfer_offset(sc
->sc_dma_ch
->ch_softc
,
280 sc
->sc_current
.blksize
);
281 sc
->sc_rintr(sc
->sc_rarg
);
283 printf("vs_dmaintr: spurious interrupt\n");
290 vs_dmaerrintr(void *hdl
)
295 DPRINTF(1, ("%s: DMA transfer error.\n", device_xname(sc
->sc_dev
)));
304 * audio MD layer interfaces
308 vs_open(void *hdl
, int flags
)
312 DPRINTF(1, ("vs_open: flags=%d\n", flags
));
324 DPRINTF(1, ("vs_close\n"));
328 vs_query_encoding(void *hdl
, struct audio_encoding
*fp
)
331 DPRINTF(1, ("vs_query_encoding\n"));
332 if (fp
->index
>= sizeof(vs_encodings
) / sizeof(vs_encodings
[0]))
335 strcpy(fp
->name
, vs_encodings
[fp
->index
].name
);
336 fp
->encoding
= vs_encodings
[fp
->index
].encoding
;
337 fp
->precision
= vs_encodings
[fp
->index
].precision
;
338 if (fp
->encoding
== AUDIO_ENCODING_ADPCM
)
341 fp
->flags
= AUDIO_ENCODINGFLAG_EMULATED
;
346 vs_round_sr(u_long rate
)
354 for (i
= 0; i
< NUM_RATE
; i
++) {
355 if (rate
>= vs_l2r
[i
].rate
) {
356 if (rate
- vs_l2r
[i
].rate
< diff
) {
357 diff
= rate
- vs_l2r
[i
].rate
;
361 if (vs_l2r
[i
].rate
- rate
< diff
) {
362 diff
= vs_l2r
[i
].rate
- rate
;
367 if (diff
* 100 / rate
> 15)
374 vs_set_params(void *hdl
, int setmode
, int usemode
,
375 audio_params_t
*play
, audio_params_t
*rec
,
376 stream_filter_list_t
*pfil
, stream_filter_list_t
*rfil
)
379 struct audio_params
*p
;
382 stream_filter_factory_t
*pswcode
;
383 stream_filter_factory_t
*rswcode
;
387 DPRINTF(1, ("vs_set_params: setmode=%d, usemode=%d\n",
391 /* set first record info, then play info */
392 for (mode
= AUMODE_RECORD
; mode
!= -1;
393 mode
= (mode
== AUMODE_RECORD
) ? AUMODE_PLAY
: -1) {
394 if ((setmode
& mode
) == 0)
397 p
= (mode
== AUMODE_PLAY
) ? play
: rec
;
399 if (p
->channels
!= 1)
402 rate
= p
->sample_rate
;
406 hw
.encoding
= AUDIO_ENCODING_ADPCM
;
407 hw
.precision
= hw
.validbits
= 4;
408 DPRINTF(1, ("vs_set_params: encoding=%d, precision=%d\n",
409 p
->encoding
, p
->precision
));
411 switch (p
->precision
) {
413 if (p
->encoding
== AUDIO_ENCODING_ADPCM
)
417 switch (p
->encoding
) {
418 case AUDIO_ENCODING_ULAW
:
420 hw
.encoding
= AUDIO_ENCODING_ULINEAR_LE
;
421 hw
.precision
= hw
.validbits
= 8;
422 pfil
->prepend(pfil
, mulaw_to_linear8
, &hw
);
423 hw
.encoding
= AUDIO_ENCODING_ADPCM
;
424 hw
.precision
= hw
.validbits
= 4;
425 pfil
->prepend(pfil
, msm6258_linear8_to_adpcm
, &hw
);
426 rfil
->append(rfil
, msm6258_adpcm_to_linear8
, &hw
);
427 hw
.encoding
= AUDIO_ENCODING_ULINEAR_LE
;
428 hw
.precision
= hw
.validbits
= 8;
429 rfil
->append(rfil
, linear8_to_mulaw
, &hw
);
431 case AUDIO_ENCODING_SLINEAR
:
432 case AUDIO_ENCODING_SLINEAR_LE
:
433 case AUDIO_ENCODING_SLINEAR_BE
:
434 case AUDIO_ENCODING_ULINEAR
:
435 case AUDIO_ENCODING_ULINEAR_LE
:
436 case AUDIO_ENCODING_ULINEAR_BE
:
438 pfil
->append(pfil
, msm6258_linear8_to_adpcm
, &hw
);
439 rfil
->append(rfil
, msm6258_adpcm_to_linear8
, &hw
);
444 switch (p
->encoding
) {
445 case AUDIO_ENCODING_SLINEAR_LE
:
446 case AUDIO_ENCODING_SLINEAR_BE
:
448 pfil
->append(pfil
, msm6258_slinear16_to_adpcm
, &hw
);
449 rfil
->append(rfil
, msm6258_adpcm_to_slinear16
, &hw
);
455 DPRINTF(1, ("vs_set_params: mode=%d, encoding=%d\n",
460 DPRINTF(1, ("vs_set_params: rate=%d -> ", rate
));
461 rate
= vs_round_sr(rate
);
462 DPRINTF(1, ("%d\n", rate
));
465 if (mode
== AUMODE_PLAY
) {
466 sc
->sc_current
.prate
= rate
;
468 sc
->sc_current
.rrate
= rate
;
476 vs_set_sr(struct vs_softc
*sc
, int rate
)
479 DPRINTF(1, ("setting sample rate to %d, %d\n",
480 rate
, (int)vs_l2r
[rate
].rate
));
481 bus_space_write_1(sc
->sc_iot
, sc
->sc_ppi
, PPI_PORTC
,
482 (bus_space_read_1 (sc
->sc_iot
, sc
->sc_ppi
,
485 adpcm_chgclk(vs_l2r
[rate
].clk
);
489 vs_set_po(struct vs_softc
*sc
, u_long po
)
491 bus_space_write_1(sc
->sc_iot
, sc
->sc_ppi
, PPI_PORTC
,
492 (bus_space_read_1(sc
->sc_iot
, sc
->sc_ppi
, PPI_PORTC
)
497 vs_trigger_output(void *hdl
, void *start
, void *end
, int bsize
,
498 void (*intr
)(void *), void *arg
,
499 const audio_params_t
*p
)
503 struct dmac_dma_xfer
*xf
;
504 struct dmac_channel_stat
*chan
;
506 DPRINTF(2, ("vs_trigger_output: start=%p, bsize=%d, intr=%p, arg=%p\n",
507 start
, bsize
, intr
, arg
));
509 chan
= sc
->sc_dma_ch
;
512 sc
->sc_current
.blksize
= bsize
;
513 sc
->sc_current
.bufsize
= (char *)end
- (char *)start
;
514 sc
->sc_current
.dmap
= 0;
516 /* Find DMA buffer. */
517 for (vd
= sc
->sc_dmas
; vd
!= NULL
&& KVADDR(vd
) != start
;
521 printf("%s: trigger_output: bad addr %p\n",
522 device_xname(sc
->sc_dev
), start
);
526 vs_set_sr(sc
, sc
->sc_current
.prate
);
527 vs_set_po(sc
, VS_PANOUT_LR
);
529 xf
= dmac_alloc_xfer(chan
, sc
->sc_dmat
, vd
->vd_map
);
530 sc
->sc_current
.xfer
= xf
;
531 chan
->ch_dcr
= (DMAC_DCR_XRM_CSWOH
| DMAC_DCR_OTYP_EASYNC
|
533 chan
->ch_ocr
= DMAC_OCR_REQG_EXTERNAL
;
534 xf
->dx_ocr
= DMAC_OCR_DIR_MTD
;
535 xf
->dx_scr
= DMAC_SCR_MAC_COUNT_UP
| DMAC_SCR_DAC_NO_COUNT
;
536 xf
->dx_device
= sc
->sc_addr
+ MSM6258_DATA
*2 + 1;
538 dmac_load_xfer(chan
->ch_softc
, xf
);
539 dmac_start_xfer_offset(chan
->ch_softc
, xf
, 0, sc
->sc_current
.blksize
);
540 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, MSM6258_STAT
, 2);
546 vs_trigger_input(void *hdl
, void *start
, void *end
, int bsize
,
547 void (*intr
)(void *), void *arg
,
548 const audio_params_t
*p
)
552 struct dmac_dma_xfer
*xf
;
553 struct dmac_channel_stat
*chan
;
555 DPRINTF(2, ("vs_trigger_input: start=%p, bsize=%d, intr=%p, arg=%p\n",
556 start
, bsize
, intr
, arg
));
558 chan
= sc
->sc_dma_ch
;
561 sc
->sc_current
.blksize
= bsize
;
562 sc
->sc_current
.bufsize
= (char *)end
- (char *)start
;
563 sc
->sc_current
.dmap
= 0;
565 /* Find DMA buffer. */
566 for (vd
= sc
->sc_dmas
; vd
!= NULL
&& KVADDR(vd
) != start
;
570 printf("%s: trigger_output: bad addr %p\n",
571 device_xname(sc
->sc_dev
), start
);
575 vs_set_sr(sc
, sc
->sc_current
.rrate
);
576 xf
= dmac_alloc_xfer(chan
, sc
->sc_dmat
, vd
->vd_map
);
577 sc
->sc_current
.xfer
= xf
;
578 chan
->ch_dcr
= (DMAC_DCR_XRM_CSWOH
| DMAC_DCR_OTYP_EASYNC
|
580 chan
->ch_ocr
= DMAC_OCR_REQG_EXTERNAL
;
581 xf
->dx_ocr
= DMAC_OCR_DIR_DTM
;
582 xf
->dx_scr
= DMAC_SCR_MAC_COUNT_UP
| DMAC_SCR_DAC_NO_COUNT
;
583 xf
->dx_device
= sc
->sc_addr
+ MSM6258_DATA
*2 + 1;
585 dmac_load_xfer(chan
->ch_softc
, xf
);
586 dmac_start_xfer_offset(chan
->ch_softc
, xf
, 0, sc
->sc_current
.blksize
);
587 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, MSM6258_STAT
, 4);
593 vs_halt_output(void *hdl
)
597 DPRINTF(1, ("vs_halt_output\n"));
599 /* stop ADPCM play */
600 dmac_abort_xfer(sc
->sc_dma_ch
->ch_softc
, sc
->sc_current
.xfer
);
601 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, MSM6258_STAT
, 1);
607 vs_halt_input(void *hdl
)
611 DPRINTF(1, ("vs_halt_input\n"));
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);
621 vs_allocmem(struct vs_softc
*sc
, size_t size
, size_t align
, size_t boundary
,
622 int flags
, struct vs_dma
*vd
)
627 if (size
> DMAC_MAXSEGSZ
)
628 panic ("vs_allocmem: maximum size exceeded, %d", (int) size
);
631 wait
= (flags
& M_NOWAIT
) ? BUS_DMA_NOWAIT
: BUS_DMA_WAITOK
;
634 error
= bus_dmamem_alloc(vd
->vd_dmat
, vd
->vd_size
, align
, boundary
,
636 sizeof (vd
->vd_segs
) / sizeof (vd
->vd_segs
[0]),
637 &vd
->vd_nsegs
, wait
);
641 error
= bus_dmamem_map(vd
->vd_dmat
, vd
->vd_segs
, vd
->vd_nsegs
,
642 vd
->vd_size
, &vd
->vd_addr
,
643 wait
| BUS_DMA_COHERENT
);
647 error
= bus_dmamap_create(vd
->vd_dmat
, vd
->vd_size
, 1, DMAC_MAXSEGSZ
,
648 0, wait
, &vd
->vd_map
);
652 error
= bus_dmamap_load(vd
->vd_dmat
, vd
->vd_map
, vd
->vd_addr
,
653 vd
->vd_size
, NULL
, wait
);
660 bus_dmamap_destroy(vd
->vd_dmat
, vd
->vd_map
);
662 bus_dmamem_unmap(vd
->vd_dmat
, vd
->vd_addr
, vd
->vd_size
);
664 bus_dmamem_free(vd
->vd_dmat
, vd
->vd_segs
, vd
->vd_nsegs
);
670 vs_freemem(struct vs_dma
*vd
)
673 bus_dmamap_unload(vd
->vd_dmat
, vd
->vd_map
);
674 bus_dmamap_destroy(vd
->vd_dmat
, vd
->vd_map
);
675 bus_dmamem_unmap(vd
->vd_dmat
, vd
->vd_addr
, vd
->vd_size
);
676 bus_dmamem_free(vd
->vd_dmat
, vd
->vd_segs
, vd
->vd_nsegs
);
680 vs_getdev(void *hdl
, struct audio_device
*retp
)
683 DPRINTF(1, ("vs_getdev\n"));
689 vs_set_port(void *hdl
, mixer_ctrl_t
*cp
)
692 DPRINTF(1, ("vs_set_port\n"));
697 vs_get_port(void *hdl
, mixer_ctrl_t
*cp
)
700 DPRINTF(1, ("vs_get_port\n"));
705 vs_query_devinfo(void *hdl
, mixer_devinfo_t
*mi
)
708 DPRINTF(1, ("vs_query_devinfo\n"));
717 vs_allocm(void *hdl
, int direction
, size_t size
, struct malloc_type
*type
,
724 if ((vd
= malloc(size
, type
, flags
)) == NULL
)
727 vd
->vd_dmat
= sc
->sc_dmat
;
729 error
= vs_allocmem(sc
, size
, 32, 0, flags
, vd
);
734 vd
->vd_next
= sc
->sc_dmas
;
741 vs_freem(void *hdl
, void *addr
, struct malloc_type
*type
)
744 struct vs_dma
*p
, **pp
;
747 for (pp
= &sc
->sc_dmas
; (p
= *pp
) != NULL
; pp
= &p
->vd_next
) {
748 if (KVADDR(p
) == addr
) {
758 vs_round_buffersize(void *hdl
, int direction
, size_t bufsize
)
761 if (bufsize
> DMAC_MAXSEGSZ
)
762 bufsize
= DMAC_MAXSEGSZ
;
768 vs_mappage(void *addr
, void *mem
, off_t off
, int prot
)
776 for (p
= sc
->sc_dmas
; p
!= NULL
&& KVADDR(p
) != mem
;
780 printf("%s: mappage: bad addr %p\n",
781 device_xname(sc
->sc_dev
), start
);
785 return bus_dmamem_mmap(sc
->sc_dmat
, p
->vd_segs
, p
->vd_nsegs
,
786 off
, prot
, BUS_DMA_WAITOK
);
791 vs_get_props(void *hdl
)
794 DPRINTF(1, ("vs_get_props\n"));
795 return 0 /* | dependent | half duplex | no mmap */;
797 #endif /* NAUDIO > 0 && NVS > 0*/