root/sys/arch/amiga/dev/aucc.c
/*      $NetBSD: aucc.c,v 1.48 2020/02/29 06:03:55 isaki Exp $ */

/*
 * 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
 */

#include "aucc.h"
#if NAUCC > 0

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: aucc.c,v 1.48 2020/02/29 06:03:55 isaki Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <machine/cpu.h>

#include <sys/audioio.h>
#include <dev/audio/audio_if.h>
#include <dev/audio/audiovar.h> /* for AUDIO_MIN_FREQUENCY */

#include <amiga/amiga/cc.h>
#include <amiga/amiga/custom.h>
#include <amiga/amiga/device.h>
#include <amiga/dev/auccvar.h>

#include "opt_lev6_defer.h"


#ifdef LEV6_DEFER
#define AUCC_MAXINT 3
#define AUCC_ALLINTF (INTF_AUD0|INTF_AUD1|INTF_AUD2)
#else
#define AUCC_MAXINT 4
#define AUCC_ALLINTF (INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3)
#endif
/* this unconditionally; we may use AUD3 as slave channel with LEV6_DEFER */
#define AUCC_ALLDMAF (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3)

#ifdef AUDIO_DEBUG
/*extern printf(const char *,...);*/
int     auccdebug = 1;
#define DPRINTF(x)      if (auccdebug) printf x
#else
#define DPRINTF(x)
#endif

/* clock frequency.. */
extern int eclockfreq;


/* hw audio ch */
extern struct audio_channel channel[4];


/*
 * 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 */

        kmutex_t sc_lock;
        kmutex_t sc_intr_lock;
};

/* interrupt interfaces */
void aucc_inthdl(int);

/* forward declarations */
static int init_aucc(struct aucc_softc *);
static u_int freqtoper(u_int);
static u_int pertofreq(u_int);

/* autoconfiguration driver */
void    auccattach(device_t, device_t, void *);
int     auccmatch(device_t, cfdata_t, void *);

CFATTACH_DECL_NEW(aucc, sizeof(struct aucc_softc),
    auccmatch, auccattach, NULL, NULL);

struct audio_device aucc_device = {
        "Amiga-audio",
        "2.0",
        "aucc"
};


struct aucc_softc *aucc = NULL;


/*
 * Define our interface to the higher level audio driver.
 */
int     aucc_open(void *, int);
void    aucc_close(void *);
int     aucc_set_out_sr(void *, u_int);
int     aucc_query_format(void *, audio_format_query_t *);
int     aucc_round_blocksize(void *, int, int, const audio_params_t *);
int     aucc_commit_settings(void *);
int     aucc_start_output(void *, void *, int, void (*)(void *), void *);
int     aucc_start_input(void *, void *, int, void (*)(void *), void *);
int     aucc_halt_output(void *);
int     aucc_halt_input(void *);
int     aucc_getdev(void *, struct audio_device *);
int     aucc_set_port(void *, mixer_ctrl_t *);
int     aucc_get_port(void *, mixer_ctrl_t *);
int     aucc_query_devinfo(void *, mixer_devinfo_t *);
void    aucc_encode(int, int, int, int, u_char *, u_short **);
int     aucc_set_format(void *, int,
                        const audio_params_t *, const audio_params_t *,
                        audio_filter_reg_t *, audio_filter_reg_t *);
int     aucc_get_props(void *);
void    aucc_get_locks(void *, kmutex_t **, kmutex_t **);


static void aucc_decode_slinear16_1ch(u_char **, u_char *, int);
static void aucc_decode_slinear16_2ch(u_char **, u_char *, int);
static void aucc_decode_slinear16_3ch(u_char **, u_char *, int);
static void aucc_decode_slinear16_4ch(u_char **, u_char *, int);


const struct audio_hw_if sa_hw_if = {
        .open                   = aucc_open,
        .close                  = aucc_close,
        .query_format           = aucc_query_format,
        .set_format             = aucc_set_format,
        .round_blocksize        = aucc_round_blocksize,
        .commit_settings        = aucc_commit_settings,
        .start_output           = aucc_start_output,
        .start_input            = aucc_start_input,
        .halt_output            = aucc_halt_output,
        .halt_input             = aucc_halt_input,
        .getdev                 = aucc_getdev,
        .set_port               = aucc_set_port,
        .get_port               = aucc_get_port,
        .query_devinfo          = aucc_query_devinfo,
        .get_props              = aucc_get_props,
        .get_locks              = aucc_get_locks,
};

/*
 * 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;

        if (!matchname((char *)aux, "aucc") ||
#ifdef DRACO
            is_draco() ||
#endif
            aucc_matched)
                return 0;

        aucc_matched = 1;
        return 1;
}

/*
 * Audio chip found.
 */
void
auccattach(device_t parent, device_t self, void *args)
{
        struct aucc_softc *sc;
        int i;

        sc = device_private(self);
        printf("\n");

        if ((i=init_aucc(sc))) {
                printf("audio: no chipmem\n");
                return;
        }

        audio_attach_mi(&sa_hw_if, sc, self);
}


static int
init_aucc(struct aucc_softc *sc)
{
        int i, err;

        err = 0;
        /* init values per channel */
        for (i = 0; i < 4; i++) {
                sc->sc_channel[i].nd_freq = 8000;
                sc->sc_channel[i].nd_per = freqtoper(8000);
                sc->sc_channel[i].nd_busy = 0;
                sc->sc_channel[i].nd_dma = alloc_chipmem(AUDIO_BUF_SIZE*2);
                if (sc->sc_channel[i].nd_dma == NULL)
                        err = 1;
                sc->sc_channel[i].nd_dmalength = 0;
                sc->sc_channel[i].nd_volume = 64;
                sc->sc_channel[i].nd_intr = NULL;
                sc->sc_channel[i].nd_intrdata = NULL;
                sc->sc_channel[i].nd_doublebuf = 0;
                DPRINTF(("DMA buffer for channel %d is %p\n", i,
                    sc->sc_channel[i].nd_dma));
        }

        if (err) {
                for (i = 0; i < 4; i++)
                        if (sc->sc_channel[i].nd_dma)
                                free_chipmem(sc->sc_channel[i].nd_dma);
        }

        sc->sc_channels = 1;
        sc->sc_channelmask = 0xf;
        sc->sc_precision = 16;
        sc->sc_14bit = 1;
        sc->sc_encoding = AUDIO_ENCODING_SLINEAR_BE;
        sc->sc_decodefunc = aucc_decode_slinear16_2ch;

        /* clear interrupts and DMA: */
        custom.intena = AUCC_ALLINTF;
        custom.dmacon = AUCC_ALLDMAF;

        mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
        mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);

        return err;
}

int
aucc_open(void *addr, int flags)
{
        struct aucc_softc *sc;
        int i;

        sc = addr;
        DPRINTF(("sa_open: unit %p\n",sc));

        for (i = 0; i < AUCC_MAXINT; i++) {
                sc->sc_channel[i].nd_intr = NULL;
                sc->sc_channel[i].nd_intrdata = NULL;
        }
        aucc = sc;
        sc->sc_channelmask = 0xf;

        DPRINTF(("saopen: ok -> sc=%p\n",sc));

        return 0;
}

void
aucc_close(void *addr)
{

        DPRINTF(("sa_close: closed.\n"));
}

int
aucc_set_out_sr(void *addr, u_int sr)
{
        struct aucc_softc *sc;
        u_long per;
        int i;

        sc = addr;
        per = freqtoper(sr);
        if (per > 0xffff)
                return EINVAL;
        sr = pertofreq(per);

        for (i = 0; i < 4; i++) {
                sc->sc_channel[i].nd_freq = sr;
                sc->sc_channel[i].nd_per = per;
        }

        return 0;
}

int
aucc_query_format(void *addr, audio_format_query_t *afp)
{

        return audio_query_format(aucc_formats, AUCC_NFORMATS, afp);
}

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)
{
        struct aucc_softc *sc;

        sc = addr;
        KASSERT((setmode & AUMODE_RECORD) == 0);

#ifdef AUCCDEBUG
        printf("%s(setmode 0x%x,"
            "enc %u bits %u, chn %u, sr %u)\n", setmode,
            p->encoding, p->precision, p->channels, p->sample_rate);
#endif

        switch (p->channels) {
        case 1:
                sc->sc_decodefunc = aucc_decode_slinear16_1ch;
                break;
        case 2:
                sc->sc_decodefunc = aucc_decode_slinear16_2ch;
                break;
        case 3:
                sc->sc_decodefunc = aucc_decode_slinear16_3ch;
                break;
        case 4:
                sc->sc_decodefunc = aucc_decode_slinear16_4ch;
                break;
        default:
                return EINVAL;
        }

        sc->sc_encoding = p->encoding;
        sc->sc_precision = p->precision;
        sc->sc_14bit = ((p->precision == 16) && (p->channels <= 2));
        sc->sc_channels = sc->sc_14bit ? (p->channels * 2) : p->channels;

        return aucc_set_out_sr(addr, p->sample_rate);
}

int
aucc_round_blocksize(void *addr, int blk,
                     int mode, const audio_params_t *param)
{

        if (blk > AUDIO_BUF_SIZE)
                blk = AUDIO_BUF_SIZE;

        blk = rounddown(blk, param->channels * param->precision / NBBY);
        return blk;
}

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];


        sc = addr;
        mask = sc->sc_channelmask;

        dmap[0] = dmap[1] = dmap[2] = dmap[3] = NULL;

        DPRINTF(("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));

        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;
                }
        }

        DPRINTF(("dmap is %p %p %p %p, mask=0x%x\n", dmap[0], dmap[1],
                 dmap[2], dmap[3], mask));

        /* disable ints, DMA for channels, until all parameters set */
        /* XXX dont disable DMA! custom.dmacon=mask;*/
        custom.intreq = mask << INTB_AUD0;
        custom.intena = mask << INTB_AUD0;

        /* copy data to DMA buffer */

        if (sc->sc_channels == 1) {
                dmap[0] =
                dmap[1] =
                dmap[2] =
                dmap[3] = (u_char *)sc->sc_channel[j].nd_dma;
        } else {
                for (k = 0; k < 4; k++) {
                        if (masks2[k+j] & mask)
                                dmap[k] = (u_char *)sc->sc_channel[k+j].nd_dma;
                }
        }

        sc->sc_channel[j].nd_doublebuf ^= 1;
        if (sc->sc_channel[j].nd_doublebuf) {
                dmap[0] += AUDIO_BUF_SIZE;
                dmap[1] += AUDIO_BUF_SIZE;
                dmap[2] += AUDIO_BUF_SIZE;
                dmap[3] += AUDIO_BUF_SIZE;
        }

        /*
         * 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;

        /* call audio decoding routine */
        sc->sc_decodefunc (dmap, (u_char *)p, len);

        /* DMA buffers: we use same buffer 4 all channels
         * write DMA location and length
         */
        for (i = k = 0; i < 4; i++) {
                if (masks2[i] & mask) {
                        DPRINTF(("turning channel %d on\n",i));
                        /* sc->sc_channel[i].nd_busy=1; */
                        channel[i].isaudio = 1;
                        channel[i].play_count = 1;
                        channel[i].handler = NULL;
                        custom.aud[i].per = sc->sc_channel[i].nd_per;
                        if (sc->sc_14bit && (i > 1))
                                custom.aud[i].vol = 1;
                        else
                                custom.aud[i].vol = sc->sc_channel[i].nd_volume;
                        custom.aud[i].lc = PREP_DMA_MEM(dmap[k++]);
                        custom.aud[i].len = len / 2;
                        sc->sc_channel[i].nd_mask = mask;
                        DPRINTF(("per is %d, vol is %d, len is %d\n",\
                            sc->sc_channel[i].nd_per,
                            sc->sc_channel[i].nd_volume, len));
                }
        }

        channel[j].handler = aucc_inthdl;

        /* enable ints */
        custom.intena = INTF_SETCLR | INTF_INTEN | (masks2[j] << INTB_AUD0);

        DPRINTF(("enabled ints: 0x%x\n", (masks2[j] << INTB_AUD0)));

        /* enable DMA */
        custom.dmacon = DMAF_SETCLR | DMAF_MASTER | mask;

        DPRINTF(("enabled DMA, mask=0x%x\n",mask));

        return 0;
}

/* ARGSUSED */
int
aucc_start_input(void *addr, void *p, int cc, void (*intr)(void *), void *arg)
{

        return ENXIO; /* no input */
}

int
aucc_halt_output(void *addr)
{
        struct aucc_softc *sc;
        int i;

        /* XXX only halt, if input is also halted ?? */
        sc = addr;
        /* stop DMA, etc */
        custom.intena = AUCC_ALLINTF;
        custom.dmacon = AUCC_ALLDMAF;
        /* mark every busy unit idle */
        for (i = 0; i < 4; i++) {
                sc->sc_channel[i].nd_busy = sc->sc_channel[i].nd_mask = 0;
                channel[i].isaudio = 0;
                channel[i].play_count = 0;
        }

        return 0;
}

int
aucc_halt_input(void *addr)
{

        /* no input */
        return ENXIO;
}

int
aucc_getdev(void *addr, struct audio_device *retp)
{

        *retp = aucc_device;
        return 0;
}

int
aucc_set_port(void *addr, mixer_ctrl_t *cp)
{
        struct aucc_softc *sc;
        int i,j;

        DPRINTF(("aucc_set_port: port=%d", cp->dev));
        sc = addr;
        switch (cp->type) {
        case AUDIO_MIXER_SET:
                if (cp->dev != AUCC_CHANNELS)
                        return EINVAL;
                i = cp->un.mask;
                if ((i < 1) || (i > 15))
                        return EINVAL;

                sc->sc_channelmask = i;
                break;

        case AUDIO_MIXER_VALUE:
                i = cp->un.value.num_channels;
                if ((i < 1) || (i > 4))
                        return EINVAL;

#ifdef __XXXwhatsthat
                if (cp->dev != AUCC_VOLUME)
                        return EINVAL;
#endif

                /* set volume for channel 0..i-1 */

                /* evil workaround for xanim bug, IMO */
                if ((sc->sc_channels == 1) && (i == 2)) {
                        sc->sc_channel[0].nd_volume =
                            sc->sc_channel[3].nd_volume =
                            cp->un.value.level[0] >> 2;
                        sc->sc_channel[1].nd_volume =
                            sc->sc_channel[2].nd_volume =
                            cp->un.value.level[1] >> 2;
                } else if (i > 1) {
                        for (j = 0; j < i; j++)
                                sc->sc_channel[j].nd_volume =
                                    cp->un.value.level[j] >> 2;
                } else if (sc->sc_channels > 1)
                        for (j = 0; j < sc->sc_channels; j++)
                                sc->sc_channel[j].nd_volume =
                                    cp->un.value.level[0] >> 2;
                else
                        for (j = 0; j < 4; j++)
                                sc->sc_channel[j].nd_volume =
                                    cp->un.value.level[0] >> 2;
                break;

        default:
                return EINVAL;
                break;
        }
        return 0;
}


int
aucc_get_port(void *addr, mixer_ctrl_t *cp)
{
        struct aucc_softc *sc;
        int i,j;

        DPRINTF(("aucc_get_port: port=%d", cp->dev));
        sc = addr;
        switch (cp->type) {
        case AUDIO_MIXER_SET:
                if (cp->dev != AUCC_CHANNELS)
                        return EINVAL;
                cp->un.mask = sc->sc_channelmask;
                break;

        case AUDIO_MIXER_VALUE:
                i = cp->un.value.num_channels;
                if ((i < 1) || (i > 4))
                        return EINVAL;

                for (j = 0; j < i; j++)
                        cp->un.value.level[j] =
                            (sc->sc_channel[j].nd_volume << 2) +
                            (sc->sc_channel[j].nd_volume >> 4);
                break;

        default:
                return EINVAL;
        }
        return 0;
}


int
aucc_get_props(void *addr)
{

        return AUDIO_PROP_PLAYBACK;
}


void
aucc_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
{
        struct aucc_softc *sc = opaque;

        *intr = &sc->sc_intr_lock;
        *thread = &sc->sc_lock;
}

int
aucc_query_devinfo(void *addr, register mixer_devinfo_t *dip)
{
        int i;

        switch(dip->index) {
        case AUCC_CHANNELS:
                dip->type = AUDIO_MIXER_SET;
                dip->mixer_class = AUCC_OUTPUT_CLASS;
                dip->prev = dip->next = AUDIO_MIXER_LAST;
#define setname(a) strlcpy(dip->label.name, (a), sizeof(dip->label.name))
                setname(AudioNspeaker);
                for (i = 0; i < 16; i++) {
                        snprintf(dip->un.s.member[i].label.name,
                            sizeof(dip->un.s.member[i].label.name),
                            "channelmask%d", i);
                        dip->un.s.member[i].mask = i;
                }
                dip->un.s.num_mem = 16;
                break;

        case AUCC_VOLUME:
                dip->type = AUDIO_MIXER_VALUE;
                dip->mixer_class = AUCC_OUTPUT_CLASS;
                dip->prev = dip->next = AUDIO_MIXER_LAST;
                setname(AudioNmaster);
                dip->un.v.num_channels = 4;
                strcpy(dip->un.v.units.name, AudioNvolume);
                break;

        case AUCC_OUTPUT_CLASS:
                dip->type = AUDIO_MIXER_CLASS;
                dip->mixer_class = AUCC_OUTPUT_CLASS;
                dip->next = dip->prev = AUDIO_MIXER_LAST;
                setname(AudioCoutputs);
                break;

        default:
                return ENXIO;
        }

        DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));

        return 0;
}

/* 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)
{

        return eclockfreq * 5 / per;
}


/* 14bit output */
static void
aucc_decode_slinear16_1ch(u_char **dmap, u_char *p, int i)
{
        u_char *ch0;
        u_char *ch3;

        ch0 = dmap[0];
        ch3 = dmap[1];          /* XXX should be 3 */
        while (i--) {
                *ch0++ = *p++;
                *ch3++ = *p++ >> 2;
        }
}

/* 14bit stereo output */
static void
aucc_decode_slinear16_2ch(u_char **dmap, u_char *p, int i)
{
        u_char *ch0;
        u_char *ch1;
        u_char *ch2;
        u_char *ch3;

        ch0 = dmap[0];
        ch1 = dmap[1];
        ch2 = dmap[2];
        ch3 = dmap[3];
        while (i--) {
                *ch0++ = *p++;
                *ch3++ = *p++ >> 2;
                *ch1++ = *p++;
                *ch2++ = *p++ >> 2;
        }
}

static void
aucc_decode_slinear16_3ch(u_char **dmap, u_char *p, int i)
{
        u_char *ch0;
        u_char *ch1;
        u_char *ch2;

        ch0 = dmap[0];
        ch1 = dmap[1];
        ch2 = dmap[2];
        while (i--) {
                *ch0++ = *p++; p++;
                *ch1++ = *p++; p++;
                *ch2++ = *p++; p++;
        }
}

static void
aucc_decode_slinear16_4ch(u_char **dmap, u_char *p, int i)
{
        u_char *ch0;
        u_char *ch1;
        u_char *ch2;
        u_char *ch3;

        ch0 = dmap[0];
        ch1 = dmap[1];
        ch2 = dmap[2];
        ch3 = dmap[3];
        while (i--) {
                *ch0++ = *p++; p++;
                *ch1++ = *p++; p++;
                *ch2++ = *p++; p++;
                *ch3++ = *p++; p++;
        }
}

#endif /* NAUCC > 0 */