/*
* Copyright (c) 1999 Bernardo Innocenti
* All rights reserved.
*
* Copyright (c) 1997 Stephan Thesing
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Stephan Thesing.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* TODO:
*
* - channel allocation is wrong for 14bit mono
* - perhaps use a calibration table for better 14bit output
* - set 31 kHz AGA video mode to allow 44.1 kHz even if grfcc is missing
* in the kernel
* - 14bit output requires maximum volume
*/
/*
* Software state.
*/
struct aucc_softc {
aucc_data_t sc_channel[4]; /* per channel freq, ... */
u_int sc_encoding; /* encoding AUDIO_ENCODING_.*/
int sc_channels; /* # of channels used */
int sc_precision; /* 8 or 16 bits */
int sc_14bit; /* 14bit output enabled */
int sc_intrcnt; /* interrupt count */
int sc_channelmask; /* which channels are used ? */
void (*sc_decodefunc)(u_char **, u_char *, int);
/* pointer to format conversion routine */
/*
* XXX *1 How lower limit of frequency should be? same as audio(4)?
* XXX *2 Should avoid a magic number at the upper limit of frequency.
* XXX *3 In fact, there is a number in this range that have minimal errors.
* It would be better if there is a mechanism which such frequency
* is prioritized.
* XXX *4 3/4ch modes use 8bits, 1/2ch modes use 14bits,
* so I imagined that 1/2ch modes are better.
*/
#define AUCC_FORMAT(prio, ch, chmask) \
{ \
.mode = AUMODE_PLAY, \
.priority = (prio), \
.encoding = AUDIO_ENCODING_SLINEAR_BE, \
.validbits = 16, \
.precision = 16, \
.channels = (ch), \
.channel_mask = (chmask), \
.frequency_type = 0, \
.frequency = { AUDIO_MIN_FREQUENCY, 28867 }, \
}
static const struct audio_format aucc_formats[] = {
AUCC_FORMAT(1, 1, AUFMT_MONAURAL),
AUCC_FORMAT(1, 2, AUFMT_STEREO),
AUCC_FORMAT(0, 3, AUFMT_UNKNOWN_POSITION),
AUCC_FORMAT(0, 4, AUFMT_UNKNOWN_POSITION),
};
#define AUCC_NFORMATS __arraycount(aucc_formats)
/* autoconfig routines */
int
auccmatch(device_t parent, cfdata_t cf, void *aux)
{
static int aucc_matched = 0;
int
aucc_commit_settings(void *addr)
{
struct aucc_softc *sc;
int i;
DPRINTF(("sa_commit.\n"));
sc = addr;
for (i = 0; i < 4; i++) {
custom.aud[i].vol = sc->sc_channel[i].nd_volume;
custom.aud[i].per = sc->sc_channel[i].nd_per;
}
DPRINTF(("commit done\n"));
return 0;
}
static int masks[4] = {1,3,7,15}; /* masks for n first channels */
static int masks2[4] = {1,2,4,8};
int
aucc_start_output(void *addr, void *p, int cc, void (*intr)(void *), void *arg)
{
struct aucc_softc *sc;
int mask;
int i, j, k, len;
u_char *dmap[4];
if (sc->sc_channels > 1)
mask &= masks[sc->sc_channels - 1];
/* we use first sc_channels channels */
if (mask == 0) /* active and used channels are disjoint */
return EINVAL;
for (i = 0; i < 4; i++) {
/* channels available ? */
if ((masks2[i] & mask) && (sc->sc_channel[i].nd_busy))
return EBUSY; /* channel is busy */
if (channel[i].isaudio == -1)
return EBUSY; /* system uses them */
}
/* enable interrupt on 1st channel */
for (i = j = 0; i < AUCC_MAXINT; i++) {
if (masks2[i] & mask) {
DPRINTF(("first channel is %d\n",i));
j = i;
sc->sc_channel[i].nd_intr = intr;
sc->sc_channel[i].nd_intrdata = arg;
break;
}
}
/*
* compute output length in bytes per channel.
* divide by two only for 16bit->8bit conversion.
*/
len = cc / sc->sc_channels;
if (!sc->sc_14bit && (sc->sc_precision == 16))
len /= 2;
/* audio int handler */
void
aucc_inthdl(int ch)
{
int i;
int mask;
mutex_spin_enter(&aucc->sc_intr_lock);
mask = aucc->sc_channel[ch].nd_mask;
/*
* for all channels in this maskgroup:
* disable DMA, int
* mark idle
*/
DPRINTF(("inthandler called, channel %d, mask 0x%x\n", ch, mask));
custom.intreq = mask << INTB_AUD0; /* clear request */
/*
* XXX: maybe we can leave ints and/or DMA on,
* if another sample has to be played?
*/
custom.intena = mask << INTB_AUD0;
/*
* XXX custom.dmacon=mask; NO!!!
*/
for (i = 0; i < 4; i++) {
if (masks2[i] && mask) {
DPRINTF(("marking channel %d idle\n",i));
aucc->sc_channel[i].nd_busy = 0;
aucc->sc_channel[i].nd_mask = 0;
channel[i].isaudio = channel[i].play_count = 0;
}
}
/* call handler */
if (aucc->sc_channel[ch].nd_intr) {
DPRINTF(("calling %p\n",aucc->sc_channel[ch].nd_intr));
(*(aucc->sc_channel[ch].nd_intr))
(aucc->sc_channel[ch].nd_intrdata);
} else
DPRINTF(("zero int handler\n"));
mutex_spin_exit(&aucc->sc_intr_lock);
DPRINTF(("ints done\n"));
}
/* transform frequency to period, adjust bounds */
static u_int
freqtoper(u_int freq)
{
u_int per;
per = eclockfreq * 5 / freq;
if (per < 124)
per = 124; /* must have at least 124 ticks between samples */
return per;
}
/* transform period to frequency */
static u_int
pertofreq(u_int per)
{