1*3204498cSisaki /* $NetBSD: aucc.c,v 1.48 2020/02/29 06:03:55 isaki Exp $ */
2e7029fc0Saugustss
3b14e6082Sis /*
4946db35cSis * Copyright (c) 1999 Bernardo Innocenti
5946db35cSis * All rights reserved.
6946db35cSis *
7b14e6082Sis * Copyright (c) 1997 Stephan Thesing
8b14e6082Sis * All rights reserved.
9b14e6082Sis *
10b14e6082Sis * Redistribution and use in source and binary forms, with or without
11b14e6082Sis * modification, are permitted provided that the following conditions
12b14e6082Sis * are met:
13b14e6082Sis * 1. Redistributions of source code must retain the above copyright
14b14e6082Sis * notice, this list of conditions and the following disclaimer.
15b14e6082Sis * 2. Redistributions in binary form must reproduce the above copyright
16b14e6082Sis * notice, this list of conditions and the following disclaimer in the
17b14e6082Sis * documentation and/or other materials provided with the distribution.
18b14e6082Sis * 3. All advertising materials mentioning features or use of this software
19b14e6082Sis * must display the following acknowledgement:
20b14e6082Sis * This product includes software developed by Stephan Thesing.
21b14e6082Sis * 4. The name of the author may not be used to endorse or promote products
22b14e6082Sis * derived from this software without specific prior written permission
23b14e6082Sis *
24b14e6082Sis * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25b14e6082Sis * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26b14e6082Sis * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27b14e6082Sis * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28b14e6082Sis * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29b14e6082Sis * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30b14e6082Sis * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31b14e6082Sis * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32b14e6082Sis * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33b14e6082Sis * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34b14e6082Sis */
35b14e6082Sis
36946db35cSis /* TODO:
37946db35cSis *
38946db35cSis * - channel allocation is wrong for 14bit mono
39946db35cSis * - perhaps use a calibration table for better 14bit output
40a1f606d3Slukem * - set 31 kHz AGA video mode to allow 44.1 kHz even if grfcc is missing
41946db35cSis * in the kernel
42946db35cSis * - 14bit output requires maximum volume
43946db35cSis */
44946db35cSis
45b14e6082Sis #include "aucc.h"
46b14e6082Sis #if NAUCC > 0
47b14e6082Sis
481ea4df81Saymeric #include <sys/cdefs.h>
49*3204498cSisaki __KERNEL_RCSID(0, "$NetBSD: aucc.c,v 1.48 2020/02/29 06:03:55 isaki Exp $");
501ea4df81Saymeric
51b14e6082Sis #include <sys/param.h>
52b14e6082Sis #include <sys/systm.h>
53b14e6082Sis #include <sys/errno.h>
54b14e6082Sis #include <sys/ioctl.h>
55b14e6082Sis #include <sys/device.h>
56b14e6082Sis #include <sys/proc.h>
57b14e6082Sis #include <machine/cpu.h>
58b14e6082Sis
59b14e6082Sis #include <sys/audioio.h>
60e622eac4Sisaki #include <dev/audio/audio_if.h>
61e622eac4Sisaki #include <dev/audio/audiovar.h> /* for AUDIO_MIN_FREQUENCY */
62e622eac4Sisaki
63b14e6082Sis #include <amiga/amiga/cc.h>
64b14e6082Sis #include <amiga/amiga/custom.h>
65b14e6082Sis #include <amiga/amiga/device.h>
66b14e6082Sis #include <amiga/dev/auccvar.h>
67b14e6082Sis
6809a3dd5fSis #include "opt_lev6_defer.h"
6909a3dd5fSis
707fe30657Sis
717fe30657Sis #ifdef LEV6_DEFER
727fe30657Sis #define AUCC_MAXINT 3
737fe30657Sis #define AUCC_ALLINTF (INTF_AUD0|INTF_AUD1|INTF_AUD2)
747fe30657Sis #else
757fe30657Sis #define AUCC_MAXINT 4
767fe30657Sis #define AUCC_ALLINTF (INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3)
777fe30657Sis #endif
787fe30657Sis /* this unconditionally; we may use AUD3 as slave channel with LEV6_DEFER */
797fe30657Sis #define AUCC_ALLDMAF (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3)
807fe30657Sis
81b14e6082Sis #ifdef AUDIO_DEBUG
829382c873Saymeric /*extern printf(const char *,...);*/
83b14e6082Sis int auccdebug = 1;
84b14e6082Sis #define DPRINTF(x) if (auccdebug) printf x
85b14e6082Sis #else
86b14e6082Sis #define DPRINTF(x)
87b14e6082Sis #endif
88b14e6082Sis
89b14e6082Sis /* clock frequency.. */
90b14e6082Sis extern int eclockfreq;
91b14e6082Sis
92b14e6082Sis
93b14e6082Sis /* hw audio ch */
94b14e6082Sis extern struct audio_channel channel[4];
95b14e6082Sis
96b14e6082Sis
97b14e6082Sis /*
98b14e6082Sis * Software state.
99b14e6082Sis */
100b14e6082Sis struct aucc_softc {
101b14e6082Sis aucc_data_t sc_channel[4]; /* per channel freq, ... */
102b14e6082Sis u_int sc_encoding; /* encoding AUDIO_ENCODING_.*/
103b14e6082Sis int sc_channels; /* # of channels used */
104946db35cSis int sc_precision; /* 8 or 16 bits */
105946db35cSis int sc_14bit; /* 14bit output enabled */
106b14e6082Sis
107b14e6082Sis int sc_intrcnt; /* interrupt count */
108b14e6082Sis int sc_channelmask; /* which channels are used ? */
1099382c873Saymeric void (*sc_decodefunc)(u_char **, u_char *, int);
110946db35cSis /* pointer to format conversion routine */
1118a962f23Sjmcneill
1128a962f23Sjmcneill kmutex_t sc_lock;
1138a962f23Sjmcneill kmutex_t sc_intr_lock;
114b14e6082Sis };
115b14e6082Sis
116b14e6082Sis /* interrupt interfaces */
1179382c873Saymeric void aucc_inthdl(int);
118b14e6082Sis
119b14e6082Sis /* forward declarations */
1209382c873Saymeric static int init_aucc(struct aucc_softc *);
1219382c873Saymeric static u_int freqtoper(u_int);
1229382c873Saymeric static u_int pertofreq(u_int);
123b14e6082Sis
124b14e6082Sis /* autoconfiguration driver */
125cbab9cadSchs void auccattach(device_t, device_t, void *);
126cbab9cadSchs int auccmatch(device_t, cfdata_t, void *);
127b14e6082Sis
128cbab9cadSchs CFATTACH_DECL_NEW(aucc, sizeof(struct aucc_softc),
129c5e91d44Sthorpej auccmatch, auccattach, NULL, NULL);
130b14e6082Sis
131b14e6082Sis struct audio_device aucc_device = {
132b14e6082Sis "Amiga-audio",
133946db35cSis "2.0",
134b14e6082Sis "aucc"
135b14e6082Sis };
136b14e6082Sis
137b14e6082Sis
138b14e6082Sis struct aucc_softc *aucc = NULL;
139b14e6082Sis
140b14e6082Sis
141b14e6082Sis /*
142b14e6082Sis * Define our interface to the higher level audio driver.
143b14e6082Sis */
1449382c873Saymeric int aucc_open(void *, int);
1459382c873Saymeric void aucc_close(void *);
14623b5d914Skent int aucc_set_out_sr(void *, u_int);
147e622eac4Sisaki int aucc_query_format(void *, audio_format_query_t *);
14823b5d914Skent int aucc_round_blocksize(void *, int, int, const audio_params_t *);
1499382c873Saymeric int aucc_commit_settings(void *);
1509382c873Saymeric int aucc_start_output(void *, void *, int, void (*)(void *), void *);
1519382c873Saymeric int aucc_start_input(void *, void *, int, void (*)(void *), void *);
1529382c873Saymeric int aucc_halt_output(void *);
1539382c873Saymeric int aucc_halt_input(void *);
1549382c873Saymeric int aucc_getdev(void *, struct audio_device *);
1559382c873Saymeric int aucc_set_port(void *, mixer_ctrl_t *);
1569382c873Saymeric int aucc_get_port(void *, mixer_ctrl_t *);
1579382c873Saymeric int aucc_query_devinfo(void *, mixer_devinfo_t *);
1589382c873Saymeric void aucc_encode(int, int, int, int, u_char *, u_short **);
159e622eac4Sisaki int aucc_set_format(void *, int,
160e622eac4Sisaki const audio_params_t *, const audio_params_t *,
161e622eac4Sisaki audio_filter_reg_t *, audio_filter_reg_t *);
1629382c873Saymeric int aucc_get_props(void *);
1638a962f23Sjmcneill void aucc_get_locks(void *, kmutex_t **, kmutex_t **);
164b14e6082Sis
165946db35cSis
1669382c873Saymeric static void aucc_decode_slinear16_1ch(u_char **, u_char *, int);
1679382c873Saymeric static void aucc_decode_slinear16_2ch(u_char **, u_char *, int);
1689382c873Saymeric static void aucc_decode_slinear16_3ch(u_char **, u_char *, int);
1699382c873Saymeric static void aucc_decode_slinear16_4ch(u_char **, u_char *, int);
170946db35cSis
171946db35cSis
17218f717bbSyamt const struct audio_hw_if sa_hw_if = {
1736291b134Sisaki .open = aucc_open,
1746291b134Sisaki .close = aucc_close,
175e622eac4Sisaki .query_format = aucc_query_format,
176e622eac4Sisaki .set_format = aucc_set_format,
1776291b134Sisaki .round_blocksize = aucc_round_blocksize,
1786291b134Sisaki .commit_settings = aucc_commit_settings,
1796291b134Sisaki .start_output = aucc_start_output,
1806291b134Sisaki .start_input = aucc_start_input,
1816291b134Sisaki .halt_output = aucc_halt_output,
1826291b134Sisaki .halt_input = aucc_halt_input,
1836291b134Sisaki .getdev = aucc_getdev,
1846291b134Sisaki .set_port = aucc_set_port,
1856291b134Sisaki .get_port = aucc_get_port,
1866291b134Sisaki .query_devinfo = aucc_query_devinfo,
1876291b134Sisaki .get_props = aucc_get_props,
1886291b134Sisaki .get_locks = aucc_get_locks,
189b14e6082Sis };
190b14e6082Sis
191e622eac4Sisaki /*
192e622eac4Sisaki * XXX *1 How lower limit of frequency should be? same as audio(4)?
193e622eac4Sisaki * XXX *2 Should avoid a magic number at the upper limit of frequency.
194e622eac4Sisaki * XXX *3 In fact, there is a number in this range that have minimal errors.
195e622eac4Sisaki * It would be better if there is a mechanism which such frequency
196e622eac4Sisaki * is prioritized.
197e622eac4Sisaki * XXX *4 3/4ch modes use 8bits, 1/2ch modes use 14bits,
198e622eac4Sisaki * so I imagined that 1/2ch modes are better.
199e622eac4Sisaki */
200e622eac4Sisaki #define AUCC_FORMAT(prio, ch, chmask) \
201e622eac4Sisaki { \
202e622eac4Sisaki .mode = AUMODE_PLAY, \
203e622eac4Sisaki .priority = (prio), \
204e622eac4Sisaki .encoding = AUDIO_ENCODING_SLINEAR_BE, \
205e622eac4Sisaki .validbits = 16, \
206e622eac4Sisaki .precision = 16, \
207e622eac4Sisaki .channels = (ch), \
208e622eac4Sisaki .channel_mask = (chmask), \
209e622eac4Sisaki .frequency_type = 0, \
210e622eac4Sisaki .frequency = { AUDIO_MIN_FREQUENCY, 28867 }, \
211e622eac4Sisaki }
212e622eac4Sisaki static const struct audio_format aucc_formats[] = {
213e622eac4Sisaki AUCC_FORMAT(1, 1, AUFMT_MONAURAL),
214e622eac4Sisaki AUCC_FORMAT(1, 2, AUFMT_STEREO),
215e622eac4Sisaki AUCC_FORMAT(0, 3, AUFMT_UNKNOWN_POSITION),
216e622eac4Sisaki AUCC_FORMAT(0, 4, AUFMT_UNKNOWN_POSITION),
217e622eac4Sisaki };
218e622eac4Sisaki #define AUCC_NFORMATS __arraycount(aucc_formats)
219e622eac4Sisaki
220b14e6082Sis /* autoconfig routines */
221b14e6082Sis
222b14e6082Sis int
auccmatch(device_t parent,cfdata_t cf,void * aux)223cbab9cadSchs auccmatch(device_t parent, cfdata_t cf, void *aux)
224b14e6082Sis {
2258bc084e1Skleink static int aucc_matched = 0;
226b14e6082Sis
2278bc084e1Skleink if (!matchname((char *)aux, "aucc") ||
2288bc084e1Skleink #ifdef DRACO
2298bc084e1Skleink is_draco() ||
2308bc084e1Skleink #endif
2318bc084e1Skleink aucc_matched)
232b14e6082Sis return 0;
2338bc084e1Skleink
2348bc084e1Skleink aucc_matched = 1;
2358bc084e1Skleink return 1;
236b14e6082Sis }
237b14e6082Sis
238b14e6082Sis /*
239b14e6082Sis * Audio chip found.
240b14e6082Sis */
241b14e6082Sis void
auccattach(device_t parent,device_t self,void * args)242cbab9cadSchs auccattach(device_t parent, device_t self, void *args)
243b14e6082Sis {
24493293b9eSkent struct aucc_softc *sc;
24593293b9eSkent int i;
246b14e6082Sis
247cbab9cadSchs sc = device_private(self);
248b14e6082Sis printf("\n");
249b14e6082Sis
250b14e6082Sis if ((i=init_aucc(sc))) {
251b14e6082Sis printf("audio: no chipmem\n");
252b14e6082Sis return;
253b14e6082Sis }
254b14e6082Sis
255cbab9cadSchs audio_attach_mi(&sa_hw_if, sc, self);
256b14e6082Sis }
257b14e6082Sis
258b14e6082Sis
259b14e6082Sis static int
init_aucc(struct aucc_softc * sc)2609382c873Saymeric init_aucc(struct aucc_softc *sc)
261b14e6082Sis {
26293293b9eSkent int i, err;
263b14e6082Sis
26493293b9eSkent err = 0;
265b14e6082Sis /* init values per channel */
266b14e6082Sis for (i = 0; i < 4; i++) {
267b14e6082Sis sc->sc_channel[i].nd_freq = 8000;
268b14e6082Sis sc->sc_channel[i].nd_per = freqtoper(8000);
269b14e6082Sis sc->sc_channel[i].nd_busy = 0;
270b14e6082Sis sc->sc_channel[i].nd_dma = alloc_chipmem(AUDIO_BUF_SIZE*2);
271b14e6082Sis if (sc->sc_channel[i].nd_dma == NULL)
272b14e6082Sis err = 1;
273b14e6082Sis sc->sc_channel[i].nd_dmalength = 0;
274b14e6082Sis sc->sc_channel[i].nd_volume = 64;
275b14e6082Sis sc->sc_channel[i].nd_intr = NULL;
276b14e6082Sis sc->sc_channel[i].nd_intrdata = NULL;
2775171612dSis sc->sc_channel[i].nd_doublebuf = 0;
2781ffa7b76Swiz DPRINTF(("DMA buffer for channel %d is %p\n", i,
279b14e6082Sis sc->sc_channel[i].nd_dma));
280b14e6082Sis }
281b14e6082Sis
282b14e6082Sis if (err) {
283b14e6082Sis for (i = 0; i < 4; i++)
284b14e6082Sis if (sc->sc_channel[i].nd_dma)
285b14e6082Sis free_chipmem(sc->sc_channel[i].nd_dma);
286b14e6082Sis }
287b14e6082Sis
2885171612dSis sc->sc_channels = 1;
289b14e6082Sis sc->sc_channelmask = 0xf;
290e622eac4Sisaki sc->sc_precision = 16;
291e622eac4Sisaki sc->sc_14bit = 1;
292e622eac4Sisaki sc->sc_encoding = AUDIO_ENCODING_SLINEAR_BE;
293e622eac4Sisaki sc->sc_decodefunc = aucc_decode_slinear16_2ch;
294b14e6082Sis
2951ffa7b76Swiz /* clear interrupts and DMA: */
2967fe30657Sis custom.intena = AUCC_ALLINTF;
297946db35cSis custom.dmacon = AUCC_ALLDMAF;
298b14e6082Sis
2998a962f23Sjmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
3008a962f23Sjmcneill mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
3018a962f23Sjmcneill
302b14e6082Sis return err;
303b14e6082Sis }
304b14e6082Sis
305b14e6082Sis int
aucc_open(void * addr,int flags)3069382c873Saymeric aucc_open(void *addr, int flags)
307b14e6082Sis {
30893293b9eSkent struct aucc_softc *sc;
309658656bbSaugustss int i;
310b14e6082Sis
31193293b9eSkent sc = addr;
312658656bbSaugustss DPRINTF(("sa_open: unit %p\n",sc));
313b14e6082Sis
3147fe30657Sis for (i = 0; i < AUCC_MAXINT; i++) {
315b14e6082Sis sc->sc_channel[i].nd_intr = NULL;
316b14e6082Sis sc->sc_channel[i].nd_intrdata = NULL;
317b14e6082Sis }
318b14e6082Sis aucc = sc;
319b14e6082Sis sc->sc_channelmask = 0xf;
320b14e6082Sis
3217cdea212Schristos DPRINTF(("saopen: ok -> sc=%p\n",sc));
322b14e6082Sis
32393293b9eSkent return 0;
324b14e6082Sis }
325b14e6082Sis
326b14e6082Sis void
aucc_close(void * addr)3279382c873Saymeric aucc_close(void *addr)
328b14e6082Sis {
329b14e6082Sis
330b14e6082Sis DPRINTF(("sa_close: closed.\n"));
331b14e6082Sis }
332b14e6082Sis
333b14e6082Sis int
aucc_set_out_sr(void * addr,u_int sr)33423b5d914Skent aucc_set_out_sr(void *addr, u_int sr)
335b14e6082Sis {
33693293b9eSkent struct aucc_softc *sc;
337b14e6082Sis u_long per;
33893293b9eSkent int i;
339b14e6082Sis
34093293b9eSkent sc = addr;
341b14e6082Sis per = freqtoper(sr);
342b14e6082Sis if (per > 0xffff)
343b14e6082Sis return EINVAL;
344b14e6082Sis sr = pertofreq(per);
345b14e6082Sis
346b14e6082Sis for (i = 0; i < 4; i++) {
347b14e6082Sis sc->sc_channel[i].nd_freq = sr;
348b14e6082Sis sc->sc_channel[i].nd_per = per;
349b14e6082Sis }
350b14e6082Sis
35193293b9eSkent return 0;
352b14e6082Sis }
353b14e6082Sis
354b14e6082Sis int
aucc_query_format(void * addr,audio_format_query_t * afp)355e622eac4Sisaki aucc_query_format(void *addr, audio_format_query_t *afp)
356b14e6082Sis {
35793293b9eSkent
358e622eac4Sisaki return audio_query_format(aucc_formats, AUCC_NFORMATS, afp);
359b14e6082Sis }
360b14e6082Sis
361b14e6082Sis int
aucc_set_format(void * addr,int setmode,const audio_params_t * p,const audio_params_t * r,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)362e622eac4Sisaki aucc_set_format(void *addr, int setmode,
363e622eac4Sisaki const audio_params_t *p, const audio_params_t *r,
364e622eac4Sisaki audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
365b14e6082Sis {
36693293b9eSkent struct aucc_softc *sc;
3675171612dSis
36893293b9eSkent sc = addr;
369e622eac4Sisaki KASSERT((setmode & AUMODE_RECORD) == 0);
3705171612dSis
371e7609638Sis #ifdef AUCCDEBUG
372e622eac4Sisaki printf("%s(setmode 0x%x,"
373e622eac4Sisaki "enc %u bits %u, chn %u, sr %u)\n", setmode,
374946db35cSis p->encoding, p->precision, p->channels, p->sample_rate);
375e7609638Sis #endif
3764b718644Sis
377946db35cSis switch (p->channels) {
378946db35cSis case 1:
379946db35cSis sc->sc_decodefunc = aucc_decode_slinear16_1ch;
380946db35cSis break;
381946db35cSis case 2:
382946db35cSis sc->sc_decodefunc = aucc_decode_slinear16_2ch;
383946db35cSis break;
384946db35cSis case 3:
385946db35cSis sc->sc_decodefunc = aucc_decode_slinear16_3ch;
386946db35cSis break;
387946db35cSis case 4:
388946db35cSis sc->sc_decodefunc = aucc_decode_slinear16_4ch;
389946db35cSis break;
390946db35cSis default:
391946db35cSis return EINVAL;
392946db35cSis }
393b14e6082Sis
3945171612dSis sc->sc_encoding = p->encoding;
395946db35cSis sc->sc_precision = p->precision;
396946db35cSis sc->sc_14bit = ((p->precision == 16) && (p->channels <= 2));
397946db35cSis sc->sc_channels = sc->sc_14bit ? (p->channels * 2) : p->channels;
3985171612dSis
3995171612dSis return aucc_set_out_sr(addr, p->sample_rate);
400b14e6082Sis }
401b14e6082Sis
402b14e6082Sis int
aucc_round_blocksize(void * addr,int blk,int mode,const audio_params_t * param)40323b5d914Skent aucc_round_blocksize(void *addr, int blk,
40423b5d914Skent int mode, const audio_params_t *param)
405b14e6082Sis {
40693293b9eSkent
407*3204498cSisaki if (blk > AUDIO_BUF_SIZE)
408*3204498cSisaki blk = AUDIO_BUF_SIZE;
409*3204498cSisaki
410*3204498cSisaki blk = rounddown(blk, param->channels * param->precision / NBBY);
411*3204498cSisaki return blk;
412b14e6082Sis }
413b14e6082Sis
414b14e6082Sis int
aucc_commit_settings(void * addr)4159382c873Saymeric aucc_commit_settings(void *addr)
416b14e6082Sis {
41793293b9eSkent struct aucc_softc *sc;
41893293b9eSkent int i;
419b14e6082Sis
420b14e6082Sis DPRINTF(("sa_commit.\n"));
421b14e6082Sis
42293293b9eSkent sc = addr;
423b14e6082Sis for (i = 0; i < 4; i++) {
424b14e6082Sis custom.aud[i].vol = sc->sc_channel[i].nd_volume;
425b14e6082Sis custom.aud[i].per = sc->sc_channel[i].nd_per;
426b14e6082Sis }
427b14e6082Sis
428b14e6082Sis DPRINTF(("commit done\n"));
429b14e6082Sis
43093293b9eSkent return 0;
431b14e6082Sis }
432b14e6082Sis
433b14e6082Sis static int masks[4] = {1,3,7,15}; /* masks for n first channels */
434b14e6082Sis static int masks2[4] = {1,2,4,8};
435b14e6082Sis
436b14e6082Sis int
aucc_start_output(void * addr,void * p,int cc,void (* intr)(void *),void * arg)4379382c873Saymeric aucc_start_output(void *addr, void *p, int cc, void (*intr)(void *), void *arg)
438b14e6082Sis {
4395171612dSis struct aucc_softc *sc;
4405171612dSis int mask;
441946db35cSis int i, j, k, len;
442946db35cSis u_char *dmap[4];
443b14e6082Sis
444b14e6082Sis
4455171612dSis sc = addr;
4465171612dSis mask = sc->sc_channelmask;
4475171612dSis
4485171612dSis dmap[0] = dmap[1] = dmap[2] = dmap[3] = NULL;
449b14e6082Sis
450b14e6082Sis DPRINTF(("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
451b14e6082Sis
452c7e3ab13Sis if (sc->sc_channels > 1)
453c7e3ab13Sis mask &= masks[sc->sc_channels - 1];
454c7e3ab13Sis /* we use first sc_channels channels */
455b14e6082Sis if (mask == 0) /* active and used channels are disjoint */
456b14e6082Sis return EINVAL;
457b14e6082Sis
458946db35cSis for (i = 0; i < 4; i++) {
459946db35cSis /* channels available ? */
460b14e6082Sis if ((masks2[i] & mask) && (sc->sc_channel[i].nd_busy))
461b14e6082Sis return EBUSY; /* channel is busy */
462b14e6082Sis if (channel[i].isaudio == -1)
463b14e6082Sis return EBUSY; /* system uses them */
464b14e6082Sis }
465b14e6082Sis
466b14e6082Sis /* enable interrupt on 1st channel */
4675171612dSis for (i = j = 0; i < AUCC_MAXINT; i++) {
468b14e6082Sis if (masks2[i] & mask) {
469b14e6082Sis DPRINTF(("first channel is %d\n",i));
470b14e6082Sis j = i;
471b14e6082Sis sc->sc_channel[i].nd_intr = intr;
472b14e6082Sis sc->sc_channel[i].nd_intrdata = arg;
473b14e6082Sis break;
474b14e6082Sis }
475b14e6082Sis }
476b14e6082Sis
4775171612dSis DPRINTF(("dmap is %p %p %p %p, mask=0x%x\n", dmap[0], dmap[1],
4785171612dSis dmap[2], dmap[3], mask));
4795171612dSis
4801ffa7b76Swiz /* disable ints, DMA for channels, until all parameters set */
4815171612dSis /* XXX dont disable DMA! custom.dmacon=mask;*/
4825171612dSis custom.intreq = mask << INTB_AUD0;
4835171612dSis custom.intena = mask << INTB_AUD0;
484b14e6082Sis
4851ffa7b76Swiz /* copy data to DMA buffer */
486b14e6082Sis
4875171612dSis if (sc->sc_channels == 1) {
4885171612dSis dmap[0] =
4895171612dSis dmap[1] =
4905171612dSis dmap[2] =
491946db35cSis dmap[3] = (u_char *)sc->sc_channel[j].nd_dma;
49293293b9eSkent } else {
4935171612dSis for (k = 0; k < 4; k++) {
4945171612dSis if (masks2[k+j] & mask)
495946db35cSis dmap[k] = (u_char *)sc->sc_channel[k+j].nd_dma;
4965171612dSis }
4975171612dSis }
498b14e6082Sis
4995171612dSis sc->sc_channel[j].nd_doublebuf ^= 1;
5005171612dSis if (sc->sc_channel[j].nd_doublebuf) {
501946db35cSis dmap[0] += AUDIO_BUF_SIZE;
502946db35cSis dmap[1] += AUDIO_BUF_SIZE;
503946db35cSis dmap[2] += AUDIO_BUF_SIZE;
504946db35cSis dmap[3] += AUDIO_BUF_SIZE;
5055171612dSis }
506b14e6082Sis
50793293b9eSkent /*
50893293b9eSkent * compute output length in bytes per channel.
509946db35cSis * divide by two only for 16bit->8bit conversion.
510946db35cSis */
511946db35cSis len = cc / sc->sc_channels;
512946db35cSis if (!sc->sc_14bit && (sc->sc_precision == 16))
513946db35cSis len /= 2;
514b14e6082Sis
515946db35cSis /* call audio decoding routine */
516946db35cSis sc->sc_decodefunc (dmap, (u_char *)p, len);
517946db35cSis
5181ffa7b76Swiz /* DMA buffers: we use same buffer 4 all channels
5191ffa7b76Swiz * write DMA location and length
520946db35cSis */
5215171612dSis for (i = k = 0; i < 4; i++) {
522b14e6082Sis if (masks2[i] & mask) {
523b14e6082Sis DPRINTF(("turning channel %d on\n",i));
524b14e6082Sis /* sc->sc_channel[i].nd_busy=1; */
525b14e6082Sis channel[i].isaudio = 1;
526b14e6082Sis channel[i].play_count = 1;
527b14e6082Sis channel[i].handler = NULL;
528b14e6082Sis custom.aud[i].per = sc->sc_channel[i].nd_per;
529946db35cSis if (sc->sc_14bit && (i > 1))
530946db35cSis custom.aud[i].vol = 1;
531946db35cSis else
532b14e6082Sis custom.aud[i].vol = sc->sc_channel[i].nd_volume;
5335171612dSis custom.aud[i].lc = PREP_DMA_MEM(dmap[k++]);
534946db35cSis custom.aud[i].len = len / 2;
535b14e6082Sis sc->sc_channel[i].nd_mask = mask;
536b14e6082Sis DPRINTF(("per is %d, vol is %d, len is %d\n",\
5375171612dSis sc->sc_channel[i].nd_per,
538946db35cSis sc->sc_channel[i].nd_volume, len));
539b14e6082Sis }
540b14e6082Sis }
541b14e6082Sis
542b14e6082Sis channel[j].handler = aucc_inthdl;
543b14e6082Sis
544b14e6082Sis /* enable ints */
5457fe30657Sis custom.intena = INTF_SETCLR | INTF_INTEN | (masks2[j] << INTB_AUD0);
546b14e6082Sis
5477fe30657Sis DPRINTF(("enabled ints: 0x%x\n", (masks2[j] << INTB_AUD0)));
548b14e6082Sis
5491ffa7b76Swiz /* enable DMA */
550b14e6082Sis custom.dmacon = DMAF_SETCLR | DMAF_MASTER | mask;
551b14e6082Sis
5521ffa7b76Swiz DPRINTF(("enabled DMA, mask=0x%x\n",mask));
553b14e6082Sis
55493293b9eSkent return 0;
555b14e6082Sis }
556b14e6082Sis
557b14e6082Sis /* ARGSUSED */
558b14e6082Sis int
aucc_start_input(void * addr,void * p,int cc,void (* intr)(void *),void * arg)5599382c873Saymeric aucc_start_input(void *addr, void *p, int cc, void (*intr)(void *), void *arg)
560b14e6082Sis {
561b14e6082Sis
562b14e6082Sis return ENXIO; /* no input */
563b14e6082Sis }
564b14e6082Sis
565b14e6082Sis int
aucc_halt_output(void * addr)5669382c873Saymeric aucc_halt_output(void *addr)
567b14e6082Sis {
56893293b9eSkent struct aucc_softc *sc;
56993293b9eSkent int i;
570b14e6082Sis
571b14e6082Sis /* XXX only halt, if input is also halted ?? */
57293293b9eSkent sc = addr;
5731ffa7b76Swiz /* stop DMA, etc */
5747fe30657Sis custom.intena = AUCC_ALLINTF;
5757fe30657Sis custom.dmacon = AUCC_ALLDMAF;
576b14e6082Sis /* mark every busy unit idle */
577b14e6082Sis for (i = 0; i < 4; i++) {
578b14e6082Sis sc->sc_channel[i].nd_busy = sc->sc_channel[i].nd_mask = 0;
579b14e6082Sis channel[i].isaudio = 0;
580b14e6082Sis channel[i].play_count = 0;
581b14e6082Sis }
582b14e6082Sis
58393293b9eSkent return 0;
584b14e6082Sis }
585b14e6082Sis
586b14e6082Sis int
aucc_halt_input(void * addr)5879382c873Saymeric aucc_halt_input(void *addr)
588b14e6082Sis {
589b14e6082Sis
59093293b9eSkent /* no input */
591b14e6082Sis return ENXIO;
592b14e6082Sis }
593b14e6082Sis
594b14e6082Sis int
aucc_getdev(void * addr,struct audio_device * retp)5959382c873Saymeric aucc_getdev(void *addr, struct audio_device *retp)
596b14e6082Sis {
59793293b9eSkent
598b14e6082Sis *retp = aucc_device;
599b14e6082Sis return 0;
600b14e6082Sis }
601b14e6082Sis
602b14e6082Sis int
aucc_set_port(void * addr,mixer_ctrl_t * cp)6039382c873Saymeric aucc_set_port(void *addr, mixer_ctrl_t *cp)
604b14e6082Sis {
60593293b9eSkent struct aucc_softc *sc;
60693293b9eSkent int i,j;
607b14e6082Sis
608b14e6082Sis DPRINTF(("aucc_set_port: port=%d", cp->dev));
60993293b9eSkent sc = addr;
610b14e6082Sis switch (cp->type) {
611b14e6082Sis case AUDIO_MIXER_SET:
612b14e6082Sis if (cp->dev != AUCC_CHANNELS)
613b14e6082Sis return EINVAL;
614b14e6082Sis i = cp->un.mask;
615b14e6082Sis if ((i < 1) || (i > 15))
616b14e6082Sis return EINVAL;
617946db35cSis
618b14e6082Sis sc->sc_channelmask = i;
619b14e6082Sis break;
620b14e6082Sis
621b14e6082Sis case AUDIO_MIXER_VALUE:
622b14e6082Sis i = cp->un.value.num_channels;
623b14e6082Sis if ((i < 1) || (i > 4))
624b14e6082Sis return EINVAL;
625b14e6082Sis
626c7e3ab13Sis #ifdef __XXXwhatsthat
627b14e6082Sis if (cp->dev != AUCC_VOLUME)
628b14e6082Sis return EINVAL;
629c7e3ab13Sis #endif
630b14e6082Sis
631b14e6082Sis /* set volume for channel 0..i-1 */
632a90ace97Sis
633a90ace97Sis /* evil workaround for xanim bug, IMO */
634a90ace97Sis if ((sc->sc_channels == 1) && (i == 2)) {
635a90ace97Sis sc->sc_channel[0].nd_volume =
636a90ace97Sis sc->sc_channel[3].nd_volume =
637a90ace97Sis cp->un.value.level[0] >> 2;
638a90ace97Sis sc->sc_channel[1].nd_volume =
639a90ace97Sis sc->sc_channel[2].nd_volume =
640a90ace97Sis cp->un.value.level[1] >> 2;
641a90ace97Sis } else if (i > 1) {
642b14e6082Sis for (j = 0; j < i; j++)
643c7e3ab13Sis sc->sc_channel[j].nd_volume =
644c7e3ab13Sis cp->un.value.level[j] >> 2;
645a90ace97Sis } else if (sc->sc_channels > 1)
646c7e3ab13Sis for (j = 0; j < sc->sc_channels; j++)
647c7e3ab13Sis sc->sc_channel[j].nd_volume =
648c7e3ab13Sis cp->un.value.level[0] >> 2;
649c7e3ab13Sis else
650c7e3ab13Sis for (j = 0; j < 4; j++)
651c7e3ab13Sis sc->sc_channel[j].nd_volume =
652c7e3ab13Sis cp->un.value.level[0] >> 2;
653b14e6082Sis break;
654b14e6082Sis
655b14e6082Sis default:
656b14e6082Sis return EINVAL;
657b14e6082Sis break;
658b14e6082Sis }
659b14e6082Sis return 0;
660b14e6082Sis }
661b14e6082Sis
662b14e6082Sis
663b14e6082Sis int
aucc_get_port(void * addr,mixer_ctrl_t * cp)6649382c873Saymeric aucc_get_port(void *addr, mixer_ctrl_t *cp)
665b14e6082Sis {
66693293b9eSkent struct aucc_softc *sc;
66793293b9eSkent int i,j;
668b14e6082Sis
669b14e6082Sis DPRINTF(("aucc_get_port: port=%d", cp->dev));
67093293b9eSkent sc = addr;
671b14e6082Sis switch (cp->type) {
672b14e6082Sis case AUDIO_MIXER_SET:
673b14e6082Sis if (cp->dev != AUCC_CHANNELS)
674b14e6082Sis return EINVAL;
675b14e6082Sis cp->un.mask = sc->sc_channelmask;
676b14e6082Sis break;
677b14e6082Sis
678b14e6082Sis case AUDIO_MIXER_VALUE:
679b14e6082Sis i = cp->un.value.num_channels;
680b14e6082Sis if ((i < 1) || (i > 4))
681b14e6082Sis return EINVAL;
682b14e6082Sis
683b14e6082Sis for (j = 0; j < i; j++)
684c7e3ab13Sis cp->un.value.level[j] =
685c7e3ab13Sis (sc->sc_channel[j].nd_volume << 2) +
686c7e3ab13Sis (sc->sc_channel[j].nd_volume >> 4);
687b14e6082Sis break;
688b14e6082Sis
689b14e6082Sis default:
690b14e6082Sis return EINVAL;
691b14e6082Sis }
692b14e6082Sis return 0;
693b14e6082Sis }
694b14e6082Sis
695b14e6082Sis
696b14e6082Sis int
aucc_get_props(void * addr)6979382c873Saymeric aucc_get_props(void *addr)
698658656bbSaugustss {
699ede47d01Sisaki
7007abe87f1Srin return AUDIO_PROP_PLAYBACK;
701658656bbSaugustss }
702658656bbSaugustss
7038a962f23Sjmcneill
7048a962f23Sjmcneill void
aucc_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)7058a962f23Sjmcneill aucc_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
7068a962f23Sjmcneill {
7078a962f23Sjmcneill struct aucc_softc *sc = opaque;
7088a962f23Sjmcneill
7098a962f23Sjmcneill *intr = &sc->sc_intr_lock;
7108a962f23Sjmcneill *thread = &sc->sc_lock;
7118a962f23Sjmcneill }
7128a962f23Sjmcneill
713658656bbSaugustss int
aucc_query_devinfo(void * addr,register mixer_devinfo_t * dip)7149382c873Saymeric aucc_query_devinfo(void *addr, register mixer_devinfo_t *dip)
715b14e6082Sis {
71693293b9eSkent int i;
717b14e6082Sis
718b14e6082Sis switch(dip->index) {
719b14e6082Sis case AUCC_CHANNELS:
720b14e6082Sis dip->type = AUDIO_MIXER_SET;
721b14e6082Sis dip->mixer_class = AUCC_OUTPUT_CLASS;
722b14e6082Sis dip->prev = dip->next = AUDIO_MIXER_LAST;
723889cc98cSchristos #define setname(a) strlcpy(dip->label.name, (a), sizeof(dip->label.name))
724889cc98cSchristos setname(AudioNspeaker);
725b14e6082Sis for (i = 0; i < 16; i++) {
726889cc98cSchristos snprintf(dip->un.s.member[i].label.name,
727889cc98cSchristos sizeof(dip->un.s.member[i].label.name),
728b14e6082Sis "channelmask%d", i);
729b14e6082Sis dip->un.s.member[i].mask = i;
730b14e6082Sis }
731b14e6082Sis dip->un.s.num_mem = 16;
732b14e6082Sis break;
733b14e6082Sis
734b14e6082Sis case AUCC_VOLUME:
735b14e6082Sis dip->type = AUDIO_MIXER_VALUE;
736b14e6082Sis dip->mixer_class = AUCC_OUTPUT_CLASS;
737b14e6082Sis dip->prev = dip->next = AUDIO_MIXER_LAST;
738889cc98cSchristos setname(AudioNmaster);
739b14e6082Sis dip->un.v.num_channels = 4;
740b14e6082Sis strcpy(dip->un.v.units.name, AudioNvolume);
741b14e6082Sis break;
742b14e6082Sis
743b14e6082Sis case AUCC_OUTPUT_CLASS:
744b14e6082Sis dip->type = AUDIO_MIXER_CLASS;
745b14e6082Sis dip->mixer_class = AUCC_OUTPUT_CLASS;
746b14e6082Sis dip->next = dip->prev = AUDIO_MIXER_LAST;
747889cc98cSchristos setname(AudioCoutputs);
748b14e6082Sis break;
749e7029fc0Saugustss
750b14e6082Sis default:
751b14e6082Sis return ENXIO;
752b14e6082Sis }
753b14e6082Sis
754b14e6082Sis DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
755b14e6082Sis
75693293b9eSkent return 0;
757b14e6082Sis }
758b14e6082Sis
759b14e6082Sis /* audio int handler */
760b14e6082Sis void
aucc_inthdl(int ch)761b14e6082Sis aucc_inthdl(int ch)
762b14e6082Sis {
76393293b9eSkent int i;
76493293b9eSkent int mask;
765b14e6082Sis
7668a962f23Sjmcneill mutex_spin_enter(&aucc->sc_intr_lock);
76793293b9eSkent mask = aucc->sc_channel[ch].nd_mask;
76893293b9eSkent /*
76993293b9eSkent * for all channels in this maskgroup:
77093293b9eSkent * disable DMA, int
77193293b9eSkent * mark idle
77293293b9eSkent */
773b14e6082Sis DPRINTF(("inthandler called, channel %d, mask 0x%x\n", ch, mask));
774b14e6082Sis
7757fe30657Sis custom.intreq = mask << INTB_AUD0; /* clear request */
776946db35cSis /*
777946db35cSis * XXX: maybe we can leave ints and/or DMA on,
778946db35cSis * if another sample has to be played?
779946db35cSis */
7807fe30657Sis custom.intena = mask << INTB_AUD0;
781b14e6082Sis /*
782b14e6082Sis * XXX custom.dmacon=mask; NO!!!
783b14e6082Sis */
784b14e6082Sis for (i = 0; i < 4; i++) {
785b14e6082Sis if (masks2[i] && mask) {
786b14e6082Sis DPRINTF(("marking channel %d idle\n",i));
787b14e6082Sis aucc->sc_channel[i].nd_busy = 0;
788b14e6082Sis aucc->sc_channel[i].nd_mask = 0;
789b14e6082Sis channel[i].isaudio = channel[i].play_count = 0;
790b14e6082Sis }
791b14e6082Sis }
792b14e6082Sis
793b14e6082Sis /* call handler */
794b14e6082Sis if (aucc->sc_channel[ch].nd_intr) {
795b14e6082Sis DPRINTF(("calling %p\n",aucc->sc_channel[ch].nd_intr));
796946db35cSis (*(aucc->sc_channel[ch].nd_intr))
797946db35cSis (aucc->sc_channel[ch].nd_intrdata);
79893293b9eSkent } else
799946db35cSis DPRINTF(("zero int handler\n"));
8008a962f23Sjmcneill mutex_spin_exit(&aucc->sc_intr_lock);
801b14e6082Sis DPRINTF(("ints done\n"));
802b14e6082Sis }
803b14e6082Sis
804b14e6082Sis /* transform frequency to period, adjust bounds */
805b14e6082Sis static u_int
freqtoper(u_int freq)8069382c873Saymeric freqtoper(u_int freq)
8079382c873Saymeric {
80893293b9eSkent u_int per;
809b14e6082Sis
81093293b9eSkent per = eclockfreq * 5 / freq;
811b14e6082Sis if (per < 124)
812b14e6082Sis per = 124; /* must have at least 124 ticks between samples */
813b14e6082Sis
814b14e6082Sis return per;
815b14e6082Sis }
816b14e6082Sis
817b14e6082Sis /* transform period to frequency */
818b14e6082Sis static u_int
pertofreq(u_int per)8199382c873Saymeric pertofreq(u_int per)
8209382c873Saymeric {
821b14e6082Sis
82293293b9eSkent return eclockfreq * 5 / per;
823b14e6082Sis }
824b14e6082Sis
825946db35cSis
826946db35cSis /* 14bit output */
827946db35cSis static void
aucc_decode_slinear16_1ch(u_char ** dmap,u_char * p,int i)8289382c873Saymeric aucc_decode_slinear16_1ch(u_char **dmap, u_char *p, int i)
829946db35cSis {
83093293b9eSkent u_char *ch0;
83193293b9eSkent u_char *ch3;
832946db35cSis
83393293b9eSkent ch0 = dmap[0];
83493293b9eSkent ch3 = dmap[1]; /* XXX should be 3 */
835946db35cSis while (i--) {
836946db35cSis *ch0++ = *p++;
837946db35cSis *ch3++ = *p++ >> 2;
838946db35cSis }
839946db35cSis }
840946db35cSis
841946db35cSis /* 14bit stereo output */
842946db35cSis static void
aucc_decode_slinear16_2ch(u_char ** dmap,u_char * p,int i)8439382c873Saymeric aucc_decode_slinear16_2ch(u_char **dmap, u_char *p, int i)
844946db35cSis {
84593293b9eSkent u_char *ch0;
84693293b9eSkent u_char *ch1;
84793293b9eSkent u_char *ch2;
84893293b9eSkent u_char *ch3;
849946db35cSis
85093293b9eSkent ch0 = dmap[0];
85193293b9eSkent ch1 = dmap[1];
85293293b9eSkent ch2 = dmap[2];
85393293b9eSkent ch3 = dmap[3];
854946db35cSis while (i--) {
855946db35cSis *ch0++ = *p++;
856946db35cSis *ch3++ = *p++ >> 2;
857946db35cSis *ch1++ = *p++;
858946db35cSis *ch2++ = *p++ >> 2;
859946db35cSis }
860946db35cSis }
861946db35cSis
862946db35cSis static void
aucc_decode_slinear16_3ch(u_char ** dmap,u_char * p,int i)8639382c873Saymeric aucc_decode_slinear16_3ch(u_char **dmap, u_char *p, int i)
864946db35cSis {
86593293b9eSkent u_char *ch0;
86693293b9eSkent u_char *ch1;
86793293b9eSkent u_char *ch2;
868946db35cSis
86993293b9eSkent ch0 = dmap[0];
87093293b9eSkent ch1 = dmap[1];
87193293b9eSkent ch2 = dmap[2];
872946db35cSis while (i--) {
873946db35cSis *ch0++ = *p++; p++;
874946db35cSis *ch1++ = *p++; p++;
875946db35cSis *ch2++ = *p++; p++;
876946db35cSis }
877946db35cSis }
878946db35cSis
879946db35cSis static void
aucc_decode_slinear16_4ch(u_char ** dmap,u_char * p,int i)8809382c873Saymeric aucc_decode_slinear16_4ch(u_char **dmap, u_char *p, int i)
881946db35cSis {
88293293b9eSkent u_char *ch0;
88393293b9eSkent u_char *ch1;
88493293b9eSkent u_char *ch2;
88593293b9eSkent u_char *ch3;
886946db35cSis
88793293b9eSkent ch0 = dmap[0];
88893293b9eSkent ch1 = dmap[1];
88993293b9eSkent ch2 = dmap[2];
89093293b9eSkent ch3 = dmap[3];
891946db35cSis while (i--) {
892946db35cSis *ch0++ = *p++; p++;
893946db35cSis *ch1++ = *p++; p++;
894946db35cSis *ch2++ = *p++; p++;
895946db35cSis *ch3++ = *p++; p++;
896946db35cSis }
897946db35cSis }
898946db35cSis
899b14e6082Sis #endif /* NAUCC > 0 */
900