1 /* $NetBSD: vidcaudio.c,v 1.45 2007/02/22 05:14:05 thorpej Exp $ */
4 * Copyright (c) 1995 Melvin Tang-Richardson
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. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the RiscBSD team.
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.
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, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 2003 Ben Harris
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. The name of the author may not be used to endorse or promote products
45 * derived from this software without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 * audio driver for the RiscPC 8/16 bit sound
62 * Interfaces with the NetBSD generic audio driver to provide SUN
63 * /dev/audio (partial) compatibility.
66 #include <sys/param.h> /* proc.h */
68 __KERNEL_RCSID(0, "$NetBSD: vidcaudio.c,v 1.45 2007/02/22 05:14:05 thorpej Exp $");
70 #include <sys/audioio.h>
71 #include <sys/conf.h> /* autoconfig functions */
72 #include <sys/device.h> /* device calls */
73 #include <sys/errno.h>
74 #include <sys/malloc.h>
75 #include <sys/proc.h> /* device calls */
76 #include <sys/systm.h>
78 #include <uvm/uvm_extern.h>
80 #include <dev/audio_if.h>
81 #include <dev/audiobellvar.h>
82 #include <dev/auconv.h>
83 #include <dev/mulaw.h>
85 #include <machine/intr.h>
86 #include <machine/machdep.h>
87 #include <arm/arm32/katelib.h>
89 #include <arm/iomd/vidcaudiovar.h>
90 #include <arm/iomd/iomdreg.h>
91 #include <arm/iomd/iomdvar.h>
92 #include <arm/iomd/vidc.h>
93 #include <arm/mainbus/mainbus.h>
97 #include <dev/pckbport/pckbdvar.h>
100 extern int *vidc_base
;
102 #ifdef VIDCAUDIO_DEBUG
103 #define DPRINTF(x) printf x
108 struct vidcaudio_softc
{
109 struct device sc_dev
;
117 vm_offset_t sc_poffset
;
118 vm_offset_t sc_pbufsize
;
120 void (*sc_pintr
)(void *);
125 static int vidcaudio_probe(struct device
*, struct cfdata
*, void *);
126 static void vidcaudio_attach(struct device
*, struct device
*, void *);
127 static void vidcaudio_close(void *);
129 static int vidcaudio_intr(void *);
130 static void vidcaudio_rate(int);
131 static void vidcaudio_ctrl(int);
132 static void vidcaudio_stereo(int, int);
133 static stream_filter_factory_t mulaw_to_vidc
;
134 static stream_filter_factory_t mulaw_to_vidc_stereo
;
135 static int mulaw_to_vidc_fetch_to(stream_fetcher_t
*, audio_stream_t
*, int);
136 static int mulaw_to_vidc_stereo_fetch_to(stream_fetcher_t
*,
137 audio_stream_t
*, int);
139 CFATTACH_DECL(vidcaudio
, sizeof(struct vidcaudio_softc
),
140 vidcaudio_probe
, vidcaudio_attach
, NULL
, NULL
);
142 static int vidcaudio_query_encoding(void *, struct audio_encoding
*);
143 static int vidcaudio_set_params(void *, int, int, audio_params_t
*,
144 audio_params_t
*, stream_filter_list_t
*, stream_filter_list_t
*);
145 static int vidcaudio_round_blocksize(void *, int, int, const audio_params_t
*);
146 static int vidcaudio_trigger_output(void *, void *, void *, int,
147 void (*)(void *), void *, const audio_params_t
*);
148 static int vidcaudio_trigger_input(void *, void *, void *, int,
149 void (*)(void *), void *, const audio_params_t
*);
150 static int vidcaudio_halt_output(void *);
151 static int vidcaudio_halt_input(void *);
152 static int vidcaudio_getdev(void *, struct audio_device
*);
153 static int vidcaudio_set_port(void *, mixer_ctrl_t
*);
154 static int vidcaudio_get_port(void *, mixer_ctrl_t
*);
155 static int vidcaudio_query_devinfo(void *, mixer_devinfo_t
*);
156 static int vidcaudio_get_props(void *);
158 static struct audio_device vidcaudio_device
= {
164 static const struct audio_hw_if vidcaudio_hw_if
= {
168 vidcaudio_query_encoding
,
169 vidcaudio_set_params
,
170 vidcaudio_round_blocksize
,
176 vidcaudio_halt_output
,
177 vidcaudio_halt_input
,
183 vidcaudio_query_devinfo
,
189 vidcaudio_trigger_output
,
190 vidcaudio_trigger_input
,
195 vidcaudio_probe(struct device
*parent
, struct cfdata
*cf
, void *aux
)
201 /* So far I only know about this IOMD */
205 case ARM7500FE_IOC_ID
:
208 aprint_error("vidcaudio: Unknown IOMD id=%04x", id
);
215 vidcaudio_attach(struct device
*parent
, struct device
*self
, void *aux
)
217 struct vidcaudio_softc
*sc
;
218 struct device
*beepdev
;
224 sc
->sc_is16bit
= (cmos_read(0xc4) >> 5) & 1;
225 sc
->sc_dma_intr
= IRQ_DMASCH0
;
229 case ARM7500FE_IOC_ID
:
230 sc
->sc_is16bit
= true;
231 sc
->sc_dma_intr
= IRQ_SDMA
;
234 aprint_error(": strange IOMD\n");
239 aprint_normal(": 16-bit external DAC\n");
241 aprint_normal(": 8-bit internal DAC\n");
243 /* Install the irq handler for the DMA interrupt */
244 sc
->sc_ih
.ih_func
= vidcaudio_intr
;
245 sc
->sc_ih
.ih_arg
= sc
;
246 sc
->sc_ih
.ih_level
= IPL_AUDIO
;
247 sc
->sc_ih
.ih_name
= self
->dv_xname
;
249 if (irq_claim(sc
->sc_dma_intr
, &sc
->sc_ih
) != 0) {
250 aprint_error("%s: couldn't claim IRQ %d\n",
251 self
->dv_xname
, sc
->sc_dma_intr
);
255 disable_irq(sc
->sc_dma_intr
);
257 beepdev
= audio_attach_mi(&vidcaudio_hw_if
, sc
, self
);
259 pckbd_hookup_bell(audiobell
, beepdev
);
264 vidcaudio_close(void *addr
)
266 struct vidcaudio_softc
*sc
;
268 DPRINTF(("DEBUG: vidcaudio_close called\n"));
271 * We do this here rather than in vidcaudio_halt_output()
272 * because the latter can be called from interrupt context
273 * (audio_pint()->audio_clear()->vidcaudio_halt_output()).
275 if (sc
->sc_ppages
!= NULL
) {
276 free(sc
->sc_ppages
, M_DEVBUF
);
277 sc
->sc_ppages
= NULL
;
282 * Interface to the generic audio driver
286 vidcaudio_query_encoding(void *addr
, struct audio_encoding
*fp
)
288 struct vidcaudio_softc
*sc
;
293 strcpy(fp
->name
, AudioEmulaw
);
294 fp
->encoding
= AUDIO_ENCODING_ULAW
;
296 fp
->flags
= AUDIO_ENCODINGFLAG_EMULATED
;
300 if (sc
->sc_is16bit
) {
301 strcpy(fp
->name
, AudioEslinear_le
);
302 fp
->encoding
= AUDIO_ENCODING_SLINEAR_LE
;
314 #define MULAW_TO_VIDC(m) (~((m) << 1 | (m) >> 7))
316 static stream_filter_t
*
317 mulaw_to_vidc(struct audio_softc
*sc
, const audio_params_t
*from
,
318 const audio_params_t
*to
)
321 return auconv_nocontext_filter_factory(mulaw_to_vidc_fetch_to
);
325 mulaw_to_vidc_fetch_to(stream_fetcher_t
*self
, audio_stream_t
*dst
, int max_used
)
327 stream_filter_t
*this;
330 this = (stream_filter_t
*)self
;
331 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
)))
333 m
= dst
->end
- dst
->start
;
334 m
= min(m
, max_used
);
335 FILTER_LOOP_PROLOGUE(this->src
, 1, dst
, 1, m
) {
336 *d
= MULAW_TO_VIDC(*s
);
337 } FILTER_LOOP_EPILOGUE(this->src
, dst
);
341 static stream_filter_t
*
342 mulaw_to_vidc_stereo(struct audio_softc
*sc
, const audio_params_t
*from
,
343 const audio_params_t
*to
)
346 return auconv_nocontext_filter_factory(mulaw_to_vidc_stereo_fetch_to
);
350 mulaw_to_vidc_stereo_fetch_to(stream_fetcher_t
*self
, audio_stream_t
*dst
,
353 stream_filter_t
*this;
356 this = (stream_filter_t
*)self
;
357 max_used
= (max_used
+ 1) & ~1;
358 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
/ 2)))
360 m
= (dst
->end
- dst
->start
) & ~1;
361 m
= min(m
, max_used
);
362 FILTER_LOOP_PROLOGUE(this->src
, 1, dst
, 2, m
) {
363 d
[0] = d
[1] = MULAW_TO_VIDC(*s
);
364 } FILTER_LOOP_EPILOGUE(this->src
, dst
);
369 vidcaudio_set_params(void *addr
, int setmode
, int usemode
,
370 audio_params_t
*p
, audio_params_t
*r
,
371 stream_filter_list_t
*pfil
, stream_filter_list_t
*rfil
)
374 struct vidcaudio_softc
*sc
;
375 int sample_period
, ch
;
377 if ((setmode
& AUMODE_PLAY
) == 0)
381 if (sc
->sc_is16bit
) {
382 /* ARM7500ish, 16-bit, two-channel */
384 if (p
->encoding
== AUDIO_ENCODING_ULAW
&& p
->precision
== 8) {
385 hw
.encoding
= AUDIO_ENCODING_SLINEAR_LE
;
386 hw
.precision
= hw
.validbits
= 16;
387 pfil
->append(pfil
, mulaw_to_linear16
, &hw
);
388 } else if (p
->encoding
!= AUDIO_ENCODING_SLINEAR_LE
||
391 sample_period
= 705600 / 4 / p
->sample_rate
;
392 if (sample_period
< 3) sample_period
= 3;
393 vidcaudio_rate(sample_period
- 2);
394 vidcaudio_ctrl(SCR_SERIAL
);
395 hw
.sample_rate
= 705600 / 4 / sample_period
;
397 pfil
->prepend(pfil
, aurateconv
, &hw
);
399 /* VIDC20ish, u-law, 8-channel */
400 if (p
->encoding
!= AUDIO_ENCODING_ULAW
|| p
->precision
!= 8)
403 * We always use two hardware channels, because using
404 * one at 8kHz gives a nasty whining sound from the
405 * speaker. The aurateconv mechanism doesn't support
406 * ulaw, so we do the channel duplication ourselves,
407 * and don't try to do rate conversion.
409 sample_period
= 1000000 / 2 / p
->sample_rate
;
410 if (sample_period
< 3) sample_period
= 3;
411 p
->sample_rate
= 1000000 / 2 / sample_period
;
413 hw
.encoding
= AUDIO_ENCODING_NONE
;
415 vidcaudio_rate(sample_period
- 2);
416 vidcaudio_ctrl(SCR_SDAC
| SCR_CLKSEL
);
417 if (p
->channels
== 1) {
418 pfil
->append(pfil
, mulaw_to_vidc_stereo
, &hw
);
419 for (ch
= 0; ch
< 8; ch
++)
420 vidcaudio_stereo(ch
, SIR_CENTRE
);
422 pfil
->append(pfil
, mulaw_to_vidc
, &hw
);
423 for (ch
= 0; ch
< 8; ch
+= 2)
424 vidcaudio_stereo(ch
, SIR_LEFT_100
);
425 for (ch
= 1; ch
< 8; ch
+= 2)
426 vidcaudio_stereo(ch
, SIR_RIGHT_100
);
433 vidcaudio_round_blocksize(void *addr
, int wantblk
,
434 int mode
, const audio_params_t
*param
)
439 * Find the smallest power of two that's larger than the
440 * requested block size, but don't allow < 32 (DMA burst is 16
441 * bytes, and single bursts are tricky) or > PAGE_SIZE (DMA is
442 * confined to a page).
445 for (blk
= 32; blk
< PAGE_SIZE
; blk
<<= 1)
452 vidcaudio_trigger_output(void *addr
, void *start
, void *end
, int blksize
,
453 void (*intr
)(void *), void *arg
, const audio_params_t
*params
)
455 struct vidcaudio_softc
*sc
;
458 DPRINTF(("vidcaudio_trigger_output %p-%p/0x%x\n",
459 start
, end
, blksize
));
462 KASSERT(blksize
== vidcaudio_round_blocksize(addr
, blksize
, 0, NULL
));
463 KASSERT((vaddr_t
)start
% blksize
== 0);
465 sc
->sc_pblksize
= blksize
;
466 sc
->sc_pbufsize
= (char *)end
- (char *)start
;
467 npages
= sc
->sc_pbufsize
>> PGSHIFT
;
468 if (sc
->sc_ppages
!= NULL
)
469 free(sc
->sc_ppages
, M_DEVBUF
);
470 sc
->sc_ppages
= malloc(npages
* sizeof(paddr_t
), M_DEVBUF
, M_WAITOK
);
471 if (sc
->sc_ppages
== NULL
) return ENOMEM
;
472 for (i
= 0; i
< npages
; i
++)
473 if (!pmap_extract(pmap_kernel(),
474 (vaddr_t
)start
+ i
* PAGE_SIZE
, &sc
->sc_ppages
[i
]))
480 IOMD_WRITE_WORD(IOMD_SD0CR
, IOMD_DMACR_CLEAR
| IOMD_DMACR_QUADWORD
);
481 IOMD_WRITE_WORD(IOMD_SD0CR
, IOMD_DMACR_ENABLE
| IOMD_DMACR_QUADWORD
);
484 * Queue up the first two blocks, but don't tell audio code
485 * we're finished with them yet.
487 sc
->sc_pcountdown
= 2;
489 enable_irq(sc
->sc_dma_intr
);
495 vidcaudio_trigger_input(void *addr
, void *start
, void *end
, int blksize
,
496 void (*intr
)(void *), void *arg
, const audio_params_t
*params
)
503 vidcaudio_halt_output(void *addr
)
505 struct vidcaudio_softc
*sc
;
507 DPRINTF(("vidcaudio_halt_output\n"));
509 disable_irq(sc
->sc_dma_intr
);
510 IOMD_WRITE_WORD(IOMD_SD0CR
, IOMD_DMACR_CLEAR
| IOMD_DMACR_QUADWORD
);
515 vidcaudio_halt_input(void *addr
)
522 vidcaudio_getdev(void *addr
, struct audio_device
*retp
)
525 *retp
= vidcaudio_device
;
531 vidcaudio_set_port(void *addr
, mixer_ctrl_t
*cp
)
538 vidcaudio_get_port(void *addr
, mixer_ctrl_t
*cp
)
545 vidcaudio_query_devinfo(void *addr
, mixer_devinfo_t
*dip
)
552 vidcaudio_get_props(void *addr
)
559 vidcaudio_rate(int rate
)
562 WriteWord(vidc_base
, VIDC_SFR
| rate
);
566 vidcaudio_ctrl(int ctrl
)
569 WriteWord(vidc_base
, VIDC_SCR
| ctrl
);
573 vidcaudio_stereo(int channel
, int position
)
576 channel
= channel
<< 24 | VIDC_SIR0
;
577 WriteWord(vidc_base
, channel
| position
);
581 vidcaudio_intr(void *arg
)
583 struct vidcaudio_softc
*sc
;
588 status
= IOMD_READ_BYTE(IOMD_SD0ST
);
589 DPRINTF(("I[%x]", status
));
590 if ((status
& IOMD_DMAST_INT
) == 0)
593 pnext
= sc
->sc_ppages
[sc
->sc_poffset
>> PGSHIFT
] |
594 (sc
->sc_poffset
& PGOFSET
);
595 pend
= (pnext
+ sc
->sc_pblksize
- 16) & IOMD_DMAEND_OFFSET
;
598 (IOMD_DMAST_OVERRUN
| IOMD_DMAST_INT
| IOMD_DMAST_BANKB
)) {
600 case (IOMD_DMAST_INT
| IOMD_DMAST_BANKA
):
601 case (IOMD_DMAST_OVERRUN
| IOMD_DMAST_INT
| IOMD_DMAST_BANKB
):
602 DPRINTF(("B<0x%08lx,0x%03lx>", pnext
, pend
));
603 IOMD_WRITE_WORD(IOMD_SD0CURB
, pnext
);
604 IOMD_WRITE_WORD(IOMD_SD0ENDB
, pend
);
607 case (IOMD_DMAST_INT
| IOMD_DMAST_BANKB
):
608 case (IOMD_DMAST_OVERRUN
| IOMD_DMAST_INT
| IOMD_DMAST_BANKA
):
609 DPRINTF(("A<0x%08lx,0x%03lx>", pnext
, pend
));
610 IOMD_WRITE_WORD(IOMD_SD0CURA
, pnext
);
611 IOMD_WRITE_WORD(IOMD_SD0ENDA
, pend
);
615 sc
->sc_poffset
+= sc
->sc_pblksize
;
616 if (sc
->sc_poffset
>= sc
->sc_pbufsize
)
619 if (sc
->sc_pcountdown
> 0)
622 (*sc
->sc_pintr
)(sc
->sc_parg
);