Index: sys/i386/conf/files.i386 diff -u sys/i386/conf/files.i386:1.1.1.1 sys/i386/conf/files.i386:1.1.1.1.10.1 --- sys/i386/conf/files.i386:1.1.1.1 Fri Jul 2 01:33:53 1999 +++ sys/i386/conf/files.i386 Thu Sep 23 01:17:47 1999 @@ -356,4 +356,5 @@ gnu/i386/isa/dgm.c optional dgm device-driver gnu/i386/isa/sound/awe_wave.c optional awe device-driver pci/es1370.c optional pcm device-driver +pci/cmi.c optional pcm device-driver pci/ide_pci.c optional wd device-driver Index: sys/i386/conf/options.i386 diff -u sys/i386/conf/options.i386:1.1.1.1 sys/i386/conf/options.i386:1.1.1.1.10.1 --- sys/i386/conf/options.i386:1.1.1.1 Fri Jul 2 01:33:53 1999 +++ sys/i386/conf/options.i386 Thu Sep 23 06:03:08 1999 @@ -135,6 +135,10 @@ GUS_DMA opt_sound.h GUS_IRQ opt_sound.h +# CMI8x38 options +CMI_DEBUG opt_cmi.h +CMI_DEBUG_VERBOSE opt_cmi.h + # Video spigot SPIGOT_UNSECURE opt_spigot.h # ------------------------------- Index: sys/pci/cmi.c diff -u /dev/null sys/pci/cmi.c:1.1.2.2 --- /dev/null Thu Sep 23 06:13:25 1999 +++ sys/pci/cmi.c Thu Sep 23 06:05:38 1999 @@ -0,0 +1,1357 @@ +/*- + * This is the driver for C-Media PCI sound chip + * + * Copyright (c)1999 Takuya SHIOZAKI + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: cmi.c,v 1.1.2.2 1999/09/22 21:05:38 tshiozak Exp $ + */ + + +#include "opt_cmi.h" +#include "pci.h" + +/* XXX - sound.h should not be in such directory. */ +#include +#define DSP_ULAW_NOT_WANTED +#include + +#if (NPCI>0) && (NPCM>0) + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +#ifdef CMI_DEBUG +#define CMI_DPRINTF(arg) printf arg +#else +#define CMI_DPRINTF(arg) +#endif +#ifdef CMI_DEBUG_VERBOSE +#define CMI_DPRINTF_V(arg) printf arg +#else +#define CMI_DPRINTF_V(arg) +#endif + +#ifdef __alpha__ +#define IO_SPACE_MAPPING ALPHA_BUS_SPACE_IO +#define MEM_SPACE_MAPPING ALPHA_BUS_SPACE_MEM +#else /* not __alpha__ */ +#define IO_SPACE_MAPPING I386_BUS_SPACE_IO +#define MEM_SPACE_MAPPING I386_BUS_SPACE_MEM +#endif /* not __alpha__ */ +#define DMA_ALIGN_THRESHOLD 4 +#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) +#define DMA_READ_THRESHOLD 0x200 + +#define UNIT(minor) ((minor) >> 4) +#define DEV(minor) ((minor) & 0xf) +#define DTOCMI(d) ((struct cmi_info *)(d)->device_data) +#define MASKADDR(name) name ## _MASK, name ## _ADDR +#define FL_READ (1) +#define FL_WRITE (0) + +/* + */ + +struct cmi_info { + bus_space_tag_t st; + bus_space_handle_t sh; + bus_dma_tag_t parent_dmat; + bus_dmamap_t dmam_in, dmam_out; + bus_dma_segment_t seg_in, seg_out; + int isinit_in, isinit_out; /* XXX */ +}; + + +static void cmi_init __P((snddev_info *d)); +static void cmi_intr __P((void *)); + +/* sound methods */ +static d_open_t cmi_dsp_open; +static d_close_t cmi_dsp_close; +static d_read_t cmi_dsp_read; +static d_write_t cmi_dsp_write; +static d_ioctl_t cmi_dsp_ioctl; +static d_select_t cmi_select; +static snd_callback_t cmi_callback; + +/* */ +#define CODECREG_DECLARE(len) \ +__inline unsigned int cmi_codec_read_ ## len __P((snddev_info *d, int no)); \ +__inline void cmi_codec_write_ ## len __P((snddev_info *d, int no, \ + unsigned int val)); \ +__inline unsigned int cmi_codec_partial_read_ ## len __P((snddev_info *d, \ + int no, int mask, \ + int bitaddr)); \ +__inline void cmi_codec_partial_write_ ## len __P((snddev_info *d, \ + int no, \ + int mask,\ + int bitaddr, \ + unsigned int val)) +CODECREG_DECLARE(1); +CODECREG_DECLARE(2); +CODECREG_DECLARE(4); + +__inline int sb16_read __P((snddev_info *d, int no)); +__inline void sb16_write __P((snddev_info *d, int no, int val)); +__inline int sb16_partial_read __P((snddev_info *d, + int no, int mask, int bitaddr)); +__inline void sb16_partial_write __P((snddev_info *d, int no, + int mask, int bitaddr, int val)); +static int cmi_mixer_ioctl __P((snddev_info *d, u_long cmd, + caddr_t data, int fflag, + struct proc *p)); +__inline int cmi_index_to_speed __P((int index)); +__inline int cmi_index_to_divider __P((int index)); + +/* pci generic methods */ +static const char * cmi_probe __P((pcici_t, pcidi_t)); +static void cmi_attach __P((pcici_t, int)); + +/* local variables */ +static u_long cmi_count; + +/*******************************************************/ + +/* CODEC register read/write */ + +#define CODECREG_DEFINE(len) \ +__inline unsigned int \ +cmi_codec_read_ ## len (snddev_info *d, int no) \ +{ \ + int ret; \ + ret = bus_space_read_ ## len (DTOCMI(d)->st, DTOCMI(d)->sh, no); \ + DELAY(10); \ + return ret; \ +} \ +__inline void \ +cmi_codec_write_ ## len (snddev_info *d, int no, unsigned int val) \ +{ \ + bus_space_write_ ## len (DTOCMI(d)->st, DTOCMI(d)->sh, no, val); \ + DELAY(10); \ +} \ +__inline unsigned int \ +cmi_codec_partial_read_ ## len (snddev_info *d, int no, \ + int mask, int bitaddr) \ +{ \ + unsigned int ret; \ + ret = bus_space_read_ ## len (DTOCMI(d)->st, DTOCMI(d)->sh, no); \ + DELAY(10); \ + ret = (ret&mask)>>bitaddr; \ + return ret; \ +} \ +__inline void \ +cmi_codec_partial_write_ ## len (snddev_info *d, int no, \ + int mask, int bitaddr, unsigned int val) \ +{ \ + val = \ + ((val<st, DTOCMI(d)->sh, no) & ~mask); \ + DELAY(10); \ + bus_space_write_ ## len (DTOCMI(d)->st, DTOCMI(d)->sh, no, val); \ + DELAY(10); \ +} + +CODECREG_DEFINE(1); +CODECREG_DEFINE(2); +CODECREG_DEFINE(4); + +/* SB16 part register read/write */ +__inline int +sb16_read(snddev_info *d, int no) +{ + int ret; + + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_ADDRESS, no); + DELAY(10); + ret = bus_space_read_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_DATA); + DELAY(10); + + return ret; +} + +__inline void +sb16_write(snddev_info *d, int no, int val) +{ + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_ADDRESS, no); + DELAY(10); + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_DATA, val); + DELAY(10); +} + +__inline int +sb16_partial_read(snddev_info *d, int no, int mask, int bitaddr) +{ + int ret; + + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_ADDRESS, no); + DELAY(10); + ret = bus_space_read_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_DATA); + DELAY(10); + + ret = (ret & mask) >> bitaddr; + + return ret; +} + +__inline void +sb16_partial_write(snddev_info *d, int no, int mask, int bitaddr, int val) +{ + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_ADDRESS, no); + DELAY(10); + + val = + ((val<st, DTOCMI(d)->sh, + CMIREG_SB16_DATA) + &~mask); + DELAY(10); + bus_space_write_1(DTOCMI(d)->st, DTOCMI(d)->sh, CMIREG_SB16_DATA, val); + DELAY(10); +} + + +/* mixer */ +static struct { + unsigned left; + unsigned right; + unsigned recmask_l; + unsigned recmask_r; + unsigned playmask; + unsigned stereo; + unsigned avail; +} const mixer_desc[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = + { SB16_MIXER_MASTER_L, SB16_MIXER_MASTER_R, 0x00, 0x00, 0x00, 1, 1 }, + [SOUND_MIXER_PCM] = + { SB16_MIXER_VOICE_L, SB16_MIXER_VOICE_R, 0x00, 0x00, 0x00, 1, 1 }, + [SOUND_MIXER_SYNTH] = + { SB16_MIXER_FM_L, SB16_MIXER_FM_R, 0x40, 0x20, 0x00, 1, 1 }, + [SOUND_MIXER_CD] = + { SB16_MIXER_CDDA_L, SB16_MIXER_CDDA_R, 0x04, 0x02, 0x02, 1, 1 }, + [SOUND_MIXER_LINE] = + { SB16_MIXER_LINE_L, SB16_MIXER_LINE_R, 0x10, 0x08, 0x08, 1, 1 }, + [SOUND_MIXER_MIC] = + { SB16_MIXER_MIC, SB16_MIXER_MIC, 0x01, 0x01, 0x01, 0, 1 } +}; + +/* */ +static int +cmi_mixer_ioctl(snddev_info *d, u_long cmd, caddr_t data, int fflag, + struct proc *p) +{ + int ret = 0; + int cmdnum = cmd & 0xFF; + int *val = (int *)data; + + switch (cmd & IOC_DIRMASK) { + case IOC_IN | IOC_OUT: /* _IOWR */ + switch (cmdnum) { + case SOUND_MIXER_RECSRC: + { + int i, mask_l, mask_r; + + mask_l = mask_r = 0; + for (i=0; i= SOUND_MIXER_NRDEVICES || !mixer_desc[cmdnum].avail) { + ret = EINVAL; + } else { + int l, r, rl, rr; + l = *val & 0xFF; + r = (*val>>8) & 0xFF; + if ( l>100 ) + l = 100; + if ( r>100 ) + r = 100; + if ( l<4 ) + rl = 0; + else + rl = ((l-5)/3) << 3; + if ( mixer_desc[cmdnum].stereo ) { + if ( r<4 ) + rr = 0; + else + rr = ((r-5)/3) << 3; + sb16_write(d, mixer_desc[cmdnum].right, rr); + } else + r = l; + sb16_write(d, mixer_desc[cmdnum].left, rl); + *val = d->mix_levels[cmdnum] = ((u_int)r<<8) | l; + CMI_DPRINTF_V((CMI_DEVTAG ": mixer_ioctl set = %#x\n", + *val)); + } + } + break; + default: + ret = ENOSYS; + break; + } + return ret; +} + +/* speed */ +static struct { + int speed; + int divider; +} speed_table[CMIREG_NUMRATE] = { +#define _SPEED(n) { n, CMIREG_RATE_ ## n } + _SPEED(5512), + _SPEED(8000), + _SPEED(11025), + _SPEED(16000), + _SPEED(22050), + _SPEED(32000), + _SPEED(44100), + _SPEED(48000) +#undef _SPEED +}; + +static int +cmi_speed_to_index(int speed) +{ + int i; + for (i=0; irec_speed:&d->play_speed; + long s; + + CMI_DPRINTF_V(("cmi: set_speed isread=%d, argspeed=%d, ", + isread, *ref_speed)); + *ref_speed = cmi_index_to_speed(index=cmi_speed_to_index(*ref_speed)); + CMI_DPRINTF_V(("index=%d, realspeed=%d\n", index, *ref_speed)); + s = spltty(); + if ( isread ) + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL1_1, + MASKADDR(CMIREG_ADC_RATE), + cmi_index_to_divider(index)); + else + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL1_1, + MASKADDR(CMIREG_DAC_RATE), + cmi_index_to_divider(index)); + splx(s); +} + +/* format */ +static int +cmi_set_format(snddev_info *d, int isread) +{ + int ret = 0; + unsigned int val, mask, bitaddr; + long s; + + val = 0; + mask = CMIREG_DAC_STEREO_MASK|CMIREG_DAC_16BIT_MASK; + bitaddr = CMIREG_DAC_STEREO_ADDR; + + switch(isread?d->rec_fmt:d->play_fmt) { + case 0: + case AFMT_U8: + break; + + case AFMT_S16_LE: + val |= CMIREG_DAC_16BIT_MASK; + break; + + case AFMT_MU_LAW: + d->flags |= SND_F_XLAT8; + break; + + default: + CMI_DPRINTF(("cmi: unknown format=%d, isread=%d\n", + (int)(isread?d->rec_fmt:d->play_fmt), isread)); + ret = -1; + goto quit; + } + + if ( d->flags&SND_F_STEREO ) { + val |= CMIREG_DAC_STEREO_MASK; + } + + if ( isread ) { + mask <<= CMIREG_ADC_STEREO_ADDR-CMIREG_DAC_STEREO_ADDR; /* 2 */ + bitaddr += CMIREG_ADC_STEREO_ADDR-CMIREG_DAC_STEREO_ADDR; /* 2 */ + } + + s = spltty(); + CMI_DPRINTF(("cmi: format mask=%#x, bitaddr=%d, val=%#x\n", + mask, bitaddr, val)); + cmi_codec_partial_write_1(d, CMIREG_CHANNEL_FORMAT, mask, bitaddr, val); + splx(s); + +quit: + return ret; +} + +/* dma start/stop/addr */ +static void +cmi_start_dma(snddev_info *d, int isread) +{ + if ( isread ) { + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH1_INT_EN), 1); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH1_ENABLE), 1); + } else { + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH0_INT_EN), 1); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH0_ENABLE), 1); + } +} + +static void +cmi_stop_dma(snddev_info *d, int isread) +{ + if ( isread ) { + DTOCMI(d)->isinit_in = -1; + /* disable int/dma */ + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH1_INT_EN), 0); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH1_ENABLE), 0); + /* reset dma */ + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH1_RESET), 1); + DELAY(10); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH1_RESET), 0); + } else { + DTOCMI(d)->isinit_out = -1; + /* disable int/dma */ + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH0_INT_EN), 0); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH0_ENABLE), 0); + /* reset dma */ + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH0_RESET), 1); + DELAY(10); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH0_RESET), 0); + } +} + +/* set addr and count */ +static void +cmi_set_dma_addr_and_count(snddev_info *d, + unsigned int addr, unsigned int count, int isread) +{ + if ( isread ) { + DTOCMI(d)->isinit_in = -1; + cmi_codec_write_4(d, CMIREG_CHANNEL1_FRAME1, addr); + cmi_codec_write_2(d, CMIREG_CHANNEL1_FRAME2_0, count -1); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_0, + MASKADDR(CMIREG_CH1_DIR), 1); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH1_ENABLE), 1); + } else { + DTOCMI(d)->isinit_out = -1; + cmi_codec_write_4(d, CMIREG_CHANNEL0_FRAME1, addr); + cmi_codec_write_2(d, CMIREG_CHANNEL0_FRAME2_0, count -1); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_0, + MASKADDR(CMIREG_CH0_DIR), 0); + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL0_2, + MASKADDR(CMIREG_CH0_ENABLE), 1); + } +} + +/* set sample count */ +static void +cmi_set_sample_count(snddev_info *d, + unsigned int count, int isread) +{ + if ( isread ) { + cmi_codec_write_2(d, CMIREG_CHANNEL1_FRAME2_2, count -1); + } else { + cmi_codec_write_2(d, CMIREG_CHANNEL0_FRAME2_2, count -1); + } +} + +/* read count */ +static unsigned int +cmi_get_dma_count(snddev_info *d, int isread) +{ + unsigned int count; + + if ( isread ) { + count = cmi_codec_read_2(d, CMIREG_CHANNEL1_FRAME2_0); + count &= ~(d->dbuf_in.sample_size-1); + } else { + count = cmi_codec_read_2(d, CMIREG_CHANNEL0_FRAME2_0); + count &= ~(d->dbuf_out.sample_size-1); + } + return count; +} + + +/* initialize */ +static void +cmi_init(snddev_info *d) +{ + int i; + + cmi_codec_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, 0);/* interrupt disable */ + cmi_codec_write_1(d, CMIREG_FUNCTION_CONTROL0_2, 0);/* reset channel */ + sb16_write(d, SB16_MIXER_RESET, 0); /* reset mixer */ + /* SPDIF enable */ + cmi_codec_partial_write_1(d, CMIREG_FUNCTION_CONTROL1_0, + MASKADDR(CMIREG_SPDIF_ENABLE), 1); + d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED; + cmi_set_speed(d, FL_WRITE); + cmi_set_speed(d, FL_READ); + + DTOCMI(d)->isinit_in = DTOCMI(d)->isinit_out = 0; + + i = SOUND_MASK_MIC; + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_RECSRC, (caddr_t) &i, 0, NULL); + i = 0x4040; + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &i, 0, NULL); + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_PCM, (caddr_t) &i, 0, NULL); + i = 0; + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &i, 0, NULL); + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_CD, (caddr_t) &i, 0, NULL); + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_LINE, (caddr_t) &i, 0, NULL); + cmi_mixer_ioctl(d, SOUND_MIXER_WRITE_MIC, (caddr_t) &i, 0, NULL); +} + + +/*******************************************************/ + +/* map funcs */ +static void +cmi_map_write_buffer(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + snddev_info *d = (snddev_info *)arg; + + CMI_DPRINTF(("cmi: map_wr, dbuf->buf=%#x, ds_addr=%#x, ds_len=%d\n", + (unsigned)d->dbuf_out.buf, segs->ds_addr, segs->ds_len)); + DTOCMI(d)->seg_out = *segs; +} + +static void +cmi_map_read_buffer(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + snddev_info *d = (snddev_info *)arg; + + CMI_DPRINTF(("cmi: map_rd, dbuf->buf=%#x, ds_addr=%#x, ds_len=%d\n", + (unsigned)d->dbuf_in.buf, segs->ds_addr, segs->ds_len)); + DTOCMI(d)->seg_in = *segs; +} + + +/* allocation buffer */ +static int +cmi_alloc_dmabuf(snddev_info *d, int isread) +{ + snd_dbuf *b = isread ? &d->dbuf_in : &d->dbuf_out; + bus_dmamap_t *dmam = isread ? &DTOCMI(d)->dmam_in : &DTOCMI(d)->dmam_out; + + CMI_DPRINTF(("cmi: alloc_dmabuf, isread=%d\n", isread)); + + if ( bus_dmamem_alloc(DTOCMI(d)->parent_dmat, + (void **)&b->buf, BUS_DMA_NOWAIT, + dmam) ) + return -1; + bzero(b->buf, d->bufsize); + if ( bus_dmamap_load(DTOCMI(d)->parent_dmat, *dmam, b->buf, d->bufsize, + isread ? cmi_map_read_buffer : + cmi_map_write_buffer, d, 0) ) + return -1; + + b->rp = b->fp = b->dl = b->rl = 0; + b->fl = b->bufsize = d->bufsize; + return 0; +} + +/* update */ +static int +cmi_update_dma(snddev_info *d, int isread) +{ + snd_dbuf *b = isread ? &d->dbuf_in : &d->dbuf_out; + unsigned int hwptr, delta; + + hwptr = b->bufsize-cmi_get_dma_count(d, isread)*b->sample_size; + delta = (b->bufsize + hwptr - b->rp) % b->bufsize; + b->rp = hwptr; + b->rl -= delta; + b->fl += delta; + b->total += delta; + + return delta; +} + +/* abort */ +static int +cmi_abort(snddev_info *d, int isread) +{ + snd_dbuf *b = isread?&d->dbuf_in:&d->dbuf_out; + long s; + int missing; + + s = spltty(); + CMI_DPRINTF_V(("cmi: abort isread=%d, b->dl=%d\n", isread, b->dl)); + if (b->dl != 0) { + wakeup(b); + b->dl = 0; + } + d->callback(d, (isread ? SND_CB_RD:SND_CB_WR) | SND_CB_ABORT); + cmi_update_dma(d, isread); + missing = b->rl; + b->rl = 0; + b->fp = b->rp; + b->fl = b->bufsize; + splx(s); + return missing; +} + +/* dma intr */ +static void +cmi_dma_intr(snddev_info *d, int isread) +{ + snd_dbuf *b = isread?&d->dbuf_in:&d->dbuf_out; + unsigned int mask = isread?SND_CB_RD:SND_CB_WR; + + + if ( b->dl ) { + CMI_DPRINTF_V(("cmi: wakeup(isread=%d) dl=%d\n", isread, b->dl)); + cmi_update_dma(d, isread); + wakeup(b); + } + + if (b->rl >= (isread?DMA_READ_THRESHOLD:DMA_ALIGN_THRESHOLD) && + !(d->flags & SND_F_ABORTING)) { + int l = min(b->rl, isread?d->rec_blocksize:d->play_blocksize); +#if 0 + l &= DMA_ALIGN_MASK; +#endif + + if ( !b->dl ) { /* XXX */ + b->dl = l; + d->callback(d, mask | SND_CB_START); + } else if ( !b->rl ) { + b->dl = 0; + d->callback(d, mask | SND_CB_STOP); + } + +#if 0 + if (l != b->dl) { + if (b->dl != 0) { + d->callback(d, mask | SND_CB_STOP); + cmi_update_dma(d, isread); + l = min(b->rl, d->play_blocksize); + l &= DMA_ALIGN_MASK; + } + b->dl = l; + d->callback(d, mask | SND_CB_START); + } + } else if (b->dl != 0) { + b->dl = 0; + d->callback(d, mask | SND_CB_STOP); + cmi_update_dma(d, isread); +#endif + } +} + + +/* interrupt handler */ +static void +cmi_intr(void *p) +{ + snddev_info *d = (snddev_info *)p; + unsigned intsrc; + long s; +static unsigned int count=0; + + intsrc = cmi_codec_read_1(d, CMIREG_INTERRUPT_STATUS); + + CMI_DPRINTF_V(("cmi: intr(%d)(intsrc=%#x)", count, intsrc)); + count++; + if ( !(intsrc&CMIREG_CH0_INT_STAT_MASK) && + !(intsrc&CMIREG_CH1_INT_STAT_MASK) ) { + CMI_DPRINTF_V(("\n")); + return; + } + + /* disable intr */ + s = spltty(); + if ( intsrc&CMIREG_CH0_INT_STAT_MASK ) + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH0_INT_EN), 0); + if ( intsrc&CMIREG_CH1_INT_STAT_MASK ) + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH1_INT_EN), 0); + splx(s); + + /* */ + if ( intsrc&CMIREG_CH0_INT_STAT_MASK ) { + CMI_DPRINTF_V(("(wr)\n")); + cmi_dma_intr(d, FL_WRITE); + } + if ( intsrc&CMIREG_CH1_INT_STAT_MASK ) { + CMI_DPRINTF_V(("(rd)\n")); + cmi_dma_intr(d, FL_READ); + } + + /* enable intr */ + s = spltty(); + if ( intsrc&CMIREG_CH0_INT_STAT_MASK ) { + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH0_INT_EN), 1); + } + if ( intsrc&CMIREG_CH1_INT_STAT_MASK ) { + cmi_codec_partial_write_1(d, CMIREG_INTERRUPT_HLD_CLR_2, + MASKADDR(CMIREG_CH1_INT_EN), 1); + } + splx(s); +} + + +/********************************************************/ + +/* open */ +static int +cmi_dsp_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + int ret = 0; + int unit = UNIT(minor(dev)); + snddev_info *d = &pcm_info[unit]; + int fmt = 0; + int speed; + + CMI_DPRINTF_V((CMI_DEVTAG "%d: open\n", unit)); + + if ( d->flags & SND_F_BUSY ) { + ret = EBUSY; + } + d->flags = 0; + d->dbuf_out.total = d->dbuf_out.prev_total = + d->dbuf_in.total = d->dbuf_in.prev_total = 0; + + switch (DEV(minor(dev))) { + case SND_DEV_DSP16: + fmt = AFMT_S16_LE; + break; + + case SND_DEV_DSP: + fmt = AFMT_U8; + break; + + case SND_DEV_AUDIO: + fmt = AFMT_MU_LAW; + break; + + default: + ret = ENXIO; + goto quit; + } + + speed = DSP_DEFAULT_SPEED; + if ( oflags&FREAD ) { + d->rec_fmt = fmt; + d->rec_speed = speed; + cmi_set_speed(d, FL_READ); + DTOCMI(d)->isinit_in = 0; /* XXX */ + } + if ( oflags&FWRITE ) { + d->play_fmt = fmt; + d->play_speed = speed; + cmi_set_speed(d, FL_WRITE); + DTOCMI(d)->isinit_out = 0; /* XXX */ + } + + d->flags |= SND_F_BUSY; + + if ( oflags&O_NONBLOCK ) + d->flags |= SND_F_NBIO; + + ask_init(d); + +quit: + return ret; +} + +/* close */ +static int +cmi_dsp_close(dev_t dev, int cflags, int devtype, struct proc *p) +{ + int unit = UNIT(minor(dev)); + snddev_info *d = &pcm_info[unit]; + + CMI_DPRINTF_V((CMI_DEVTAG "%d: close\n", unit)); + + d->flags &= ~SND_F_BUSY; + + cmi_abort(d, FL_READ); + cmi_abort(d, FL_WRITE); + + return 0; +} + +/* read */ +static int +cmi_dsp_read(dev_t dev, struct uio *buf, int flag) +{ + int l, l1, limit, ret = 0, unit = UNIT(minor(dev)); + long s; + snddev_info *d = &pcm_info[unit]; + snd_dbuf *b = &d->dbuf_in; + + if (d->flags & SND_F_READING) { + /* This shouldn't happen and is actually silly */ + tsleep(&s, PZERO, "sndar", hz); + return (EBUSY); + } + d->flags |= SND_F_READING; + + /* + * XXX Check for SND_F_INIT. If set, wait for DMA to run empty and + * re-initialize the board + */ + + if (buf->uio_resid - d->rec_blocksize > 0) + limit = buf->uio_resid - d->rec_blocksize; + else + limit = 0; + + while ((l = buf->uio_resid) > limit) { + s = spltty(); + cmi_update_dma(d, FL_READ); + if ((l = min(l, b->rl)) == 0) { + int timeout; + if (b->dl == 0) + cmi_dma_intr(d, FL_READ); + if (d->flags & SND_F_NBIO) { + splx(s); + break; + } + timeout = hz; +#if 0 /* XXX */ + if (buf->uio_resid - limit > b->dl) + timeout = hz; + else + timeout = 1; +#endif + splx(s); + switch (ret = tsleep((caddr_t)b, PRIBIO | PCATCH, + "dsprd", timeout)) { + case EINTR: + cmi_abort(d, FL_READ); + /* FALLTHROUGH */ + + case ERESTART: + break; + + case EWOULDBLOCK: + printf(CMI_DEVTAG "%d: intr timeout (read)\n", unit); + /* FALLTHROUGH */ + + default: + continue; + } + break; + } + splx(s); + + if ((l1 = b->bufsize - b->rp) < l) { + if (d->flags & SND_F_XLAT8) { + translate_bytes(ulaw_dsp, b->buf + b->rp, l1); + translate_bytes(ulaw_dsp, b->buf, l - l1); + } + uiomove(b->buf + b->rp, l1, buf); + uiomove(b->buf, l - l1, buf); + } else { + if (d->flags & SND_F_XLAT8) + translate_bytes(ulaw_dsp, b->buf + b->rp, l); + uiomove(b->buf + b->rp, l, buf); + } + + s = spltty(); + b->fl += l; + b->rl -= l; + b->rp = (b->rp + l) % b->bufsize; + splx(s); + } + + d->flags &= ~SND_F_READING; + + return ret; +} + +/* write */ +static int +cmi_dsp_write(dev_t dev, struct uio *buf, int flag) +{ + int l, l1, ret = 0, unit = UNIT(minor(dev)); + long s; + snddev_info *d = &pcm_info[unit]; + snd_dbuf *b = &d->dbuf_out; + + if (d->flags & SND_F_WRITING) { + /* This shouldn't happen and is actually silly */ + tsleep(&s, PZERO, "sndaw", hz); + return (EBUSY); + } + d->flags |= SND_F_WRITING; + + /* + * XXX Check for SND_F_INIT. If set, wait for DMA to run empty and + * re-initialize the board + */ + + while ((l = buf->uio_resid) != 0) { + s = spltty(); + cmi_update_dma(d, FL_WRITE); + if ((l = min(l, b->fl)) == 0) { + int timeout; + if (d->flags & SND_F_NBIO) { + splx(s); + break; + } + + timeout = hz; +#if 0 /* XXX */ + if (buf->uio_resid >= b->dl) + timeout = hz; + else + timeout = 1; +#endif + splx(s); + switch (ret = tsleep((caddr_t)b, PRIBIO | PCATCH, + "dspwr", timeout)) { + case EINTR: + cmi_abort(d, FL_WRITE); + /* FALLTHROUGH */ + + case ERESTART: + break; + + case EWOULDBLOCK: + printf(CMI_DEVTAG "%d: intr timeout (write)\n", unit); + /* FALLTHROUGH */ + + default: + continue; + } + break; + } + splx(s); + + if ((l1 = b->bufsize - b->fp) < l) { + /* turn back */ + uiomove(b->buf + b->fp, l1, buf); + uiomove(b->buf, l - l1, buf); + if (d->flags & SND_F_XLAT8) { + translate_bytes(ulaw_dsp, b->buf + b->fp, l1); + translate_bytes(ulaw_dsp, b->buf, l - l1); + } + } else { + uiomove(b->buf + b->fp, l, buf); + if (d->flags & SND_F_XLAT8) + translate_bytes(ulaw_dsp, b->buf + b->fp, l); + } + + s = spltty(); + b->rl += l; + b->fl -= l; + b->fp = (b->fp + l) % b->bufsize; + + if (b->dl == 0) + cmi_dma_intr(d, FL_WRITE); + splx(s); + } + + d->flags &= ~SND_F_WRITING; + + return ret; +} + +/* ioctl */ +static int +cmi_dsp_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) +{ + int ret = 0; + int unit = UNIT(minor(dev)); + snddev_info *d = &pcm_info[unit]; + long s; + + CMI_DPRINTF_V((CMI_DEVTAG "%d: ioctl cmd=%ld\n", unit, cmd)); + + if ( (cmd&MIXER_WRITE(0)) == MIXER_WRITE(0) ) + return cmi_mixer_ioctl(d, cmd, data, fflag, p); + + switch(cmd) { + case AIONWRITE: + if (d->dbuf_out.dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_WRITE); + splx(s); + } + *(int *)data = d->dbuf_out.fl; + break; + + case FIONREAD: + if (d->dbuf_in.dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_READ); + splx(s); + } + *(int *)data = d->dbuf_in.rl; + break; + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info *a = (audio_buf_info *)data; + snd_dbuf *b = &d->dbuf_in; + if (b->dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_READ); + splx(s); + } + a->bytes = b->fl; + a->fragments = b->fl / d->rec_blocksize; + a->fragstotal = b->bufsize / d->rec_blocksize; + a->fragsize = d->rec_blocksize; + } + break; + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info *a = (audio_buf_info *)data; + snd_dbuf *b = &d->dbuf_out; + if (b->dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_WRITE); + splx(s); + } + a->bytes = b->fl; + a->fragments = b->fl / d->rec_blocksize; + a->fragstotal = b->bufsize / d->play_blocksize; + a->fragsize = d->play_blocksize; + } + break; + + case SNDCTL_DSP_GETIPTR: + { + count_info *c = (count_info *)data; + snd_dbuf *b = &d->dbuf_in; + if (b->dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_READ); + splx(s); + } + c->bytes = b->total; + c->blocks = (b->total - b->prev_total + + d->rec_blocksize - 1) / d->rec_blocksize; + c->ptr = b->fp; + b->prev_total = b->total; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info *c = (count_info *)data; + snd_dbuf *b = &d->dbuf_out; + if (b->dl != 0) { + s = spltty(); + cmi_update_dma(d, FL_WRITE); + splx(s); + } + c->bytes = b->total; + c->blocks = (b->total - b->prev_total + + d->play_blocksize - 1) / d->play_blocksize; + c->ptr = b->rp; + b->prev_total = b->total; + } + break; + + case AIOSTOP: + case SNDCTL_DSP_RESET: + case SNDCTL_DSP_SYNC: + ret = EINVAL; + break; + + default: + ret = ENOSYS; + break; + } + + return ret; +} + +/* select */ +static int +cmi_select(dev_t i_dev, int rw, struct proc * p) +{ + return ENOSYS; +} + +/* callback */ +static int +cmi_callback(snddev_info *d, int reason) +{ + int rd = reason & SND_CB_RD; + + switch (reason & SND_CB_REASON_MASK) { + case SND_CB_INIT: + CMI_DPRINTF_V(("cmi: callback(init)\n")); + d->flags &= ~SND_F_XLAT8; + if ( d->play_fmt ) { + cmi_set_speed(d, FL_WRITE); + cmi_set_format(d, FL_WRITE); + } + if ( d->rec_fmt ) { + cmi_set_speed(d, FL_READ); + cmi_set_format(d, FL_READ); + } + snd_set_blocksize(d); + CMI_DPRINTF_V(("cmi: blocksize : ")); + CMI_DPRINTF_V(("in.sample_size=%d, out.sample_size=%d, ", + d->dbuf_in.sample_size, d->dbuf_out.sample_size)); + CMI_DPRINTF_V(("rec_blocksize=%d, play_blocksize=%d\n", + d->rec_blocksize, d->play_blocksize)); + + break; + case SND_CB_START: + CMI_DPRINTF_V(("cmi: callback(start), rd=%d\n", rd)); + if ( rd ) + cmi_set_dma_addr_and_count(d, + DTOCMI(d)->seg_in.ds_addr, + DTOCMI(d)->seg_in.ds_len/d->dbuf_in.sample_size, rd); + else + cmi_set_dma_addr_and_count(d, + DTOCMI(d)->seg_out.ds_addr, + DTOCMI(d)->seg_out.ds_len/d->dbuf_out.sample_size, rd); +#if 0 + cmi_set_sample_count(d, + rd?d->dbuf_in.dl/d->dbuf_in.sample_size: + d->dbuf_out.dl/d->dbuf_out.sample_size, + rd); +#endif + cmi_set_sample_count(d, + rd?d->rec_blocksize/d->dbuf_in.sample_size: + d->play_blocksize/d->dbuf_out.sample_size, + rd); + cmi_start_dma(d, rd); + break; + case SND_CB_ABORT: + CMI_DPRINTF_V(("cmi: callback(abort)\n")); + cmi_stop_dma(d, rd); + break; + case SND_CB_STOP: + CMI_DPRINTF_V(("cmi: callback(stop)\n")); + cmi_stop_dma(d, rd); + break; + default: + CMI_DPRINTF_V(("cmi: unknown callback (reason no=%d)\n", reason)); + return -1; + } + + return 0; +} + + +/********************************************************* + * method tables + */ + +static snddev_info cmi_op_desc = { + "C-Media CMI8X38 PCI Sound Card", + + 0, /* type, apparently unused */ + NULL, /* ISA probe */ + NULL, /* ISA attach */ + + cmi_dsp_open, + cmi_dsp_close, + cmi_dsp_read, + cmi_dsp_write, + cmi_dsp_ioctl, + cmi_select, + + NULL, /* Interrupt Service Routine */ + cmi_callback, + + CMI_BUFFSIZE, + + AFMT_FULLDUPLEX | AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, +}; + + +static const char * +cmi_probe(config_id, device_id) + pcici_t config_id; + pcidi_t device_id; +{ + switch (device_id) { + case DEVID_CMI8338A: + return "C-Media CMI8338A PCI Sound Card"; + break; + case DEVID_CMI8338B: + return "C-Media CMI8338B PCI Sound Card"; + break; + case DEVID_CMI8738: + return "C-Media CMI8738 PCI Sound Card"; + break; + } + return NULL; +} + + +static void +cmi_attach(config_id, unit) + pcici_t config_id; + int unit; +{ + snddev_info *d; + struct cmi_info *cmi = NULL; + pci_port_t io_port; + int i; + + if ( unit>=NPCM_MAX ) + return; + + d = &pcm_info[unit]; + *d = cmi_op_desc; + if ( NULL == (cmi=malloc(sizeof(*cmi), M_DEVBUF, M_NOWAIT)) ) { + printf(CMI_DEVTAG "%d: cannot allocate softc\n", unit); + goto bad; + } + bzero(cmi, sizeof(*cmi)); + d->device_data = cmi; + + CMI_DPRINTF((CMI_DEVTAG "%d: STATUS_REG=0x%x\n", + unit, + (unsigned int)pci_conf_read(config_id, + PCI_COMMAND_STATUS_REG))); + + if ( pci_map_port(config_id, CMI_CONFIG_REG_IOBASE, &io_port) ) { + cmi->st = IO_SPACE_MAPPING; + cmi->sh = io_port; + } else { + printf(CMI_DEVTAG "%d: cannot map ports\n", unit); + goto bad; + } + printf(CMI_DEVTAG "%d: I/O space register mapped at %#x\n", + unit, cmi->sh); + d->io_base = cmi->sh; + + d->mix_devs = 0; + for (i=0; i< SOUND_MIXER_NRDEVICES; i++) + if ( mixer_desc[i].avail ) + d->mix_devs |= (1<mix_rec_devs = 0; + for (i=0; i< SOUND_MIXER_NRDEVICES; i++) + if ( mixer_desc[i].recmask_l ) + d->mix_rec_devs |= (1<bufsize, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &cmi->parent_dmat) != 0) { + printf(CMI_DEVTAG "%d: unable to create dma tag\n", unit); + goto bad; + } + + /* XXX */ + if ( cmi_alloc_dmabuf(d, 0) == -1 || + cmi_alloc_dmabuf(d, 1) == -1 ) { + printf(CMI_DEVTAG "%d: unable to allocate dma buffers\n", unit); + goto bad; + } + + pcminit(d, unit); + + return; +bad: + d->io_base = d->mix_devs = d->mix_rec_devs = 0; + if ( cmi ) + free(cmi, M_DEVBUF); + return; +} + +static struct pci_device cmi_device = { + CMI_DEVTAG, + cmi_probe, + cmi_attach, + &cmi_count, + NULL +}; +DATA_SET(pcidevice_set, cmi_device); + +#endif /* NPCI>0 && NPCM>0 */ + +/* end of file */ Index: sys/pci/cmi_reg.h diff -u /dev/null sys/pci/cmi_reg.h:1.1.2.1 --- /dev/null Thu Sep 23 06:13:25 1999 +++ sys/pci/cmi_reg.h Thu Sep 23 01:18:02 1999 @@ -0,0 +1,234 @@ +/*- + * This is the driver for C-Media PCI sound chip + * + * Copyright (c)1999 Takuya SHIOZAKI + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: cmi_reg.h,v 1.1.2.1 1999/09/22 16:18:02 tshiozak Exp $ + */ + +#ifndef _CMI_REG_H_ +#define _CMI_REG_H_ + +#define NCMI_MAX 4 + +#define CMI_DEVTAG "pcm" + +/* Device IDs */ +#define DEVID_CMI8338A 0x010013F6 +#define DEVID_CMI8338B 0x010113F6 +#define DEVID_CMI8738 0x011113F6 + +/* config register address */ +#define CMI_CONFIG_REG_IOBASE PCI_MAP_REG_START + + +/* register I/O frame offsets */ + +/* + * function control 0 + * 0x00 : + * bit0: DMA CH0 direction? 0:out(mem->dev) 1:in(dev->mem) + * bit1: DMA CH1 direction? 0:out(mem->dev) 1:in(dev->mem) + * 0x02 : + * bit0: DMA CH0 1:enable + * bit1: DMA CH1 1:enable + * bit2: DMA CH0 1:reset cycle + * bit3: DMA CH1 1:reset cycle + */ +#define CMIREG_FUNCTION_CONTROL0_0 0x00 +# define CMIREG_CH0_DIR_MASK 0x01 +# define CMIREG_CH0_DIR_ADDR 0 +# define CMIREG_CH1_DIR_MASK 0x02 +# define CMIREG_CH1_DIR_ADDR 1 +#define CMIREG_FUNCTION_CONTROL0_2 0x02 +# define CMIREG_CH0_ENABLE_MASK 0x01 +# define CMIREG_CH0_ENABLE_ADDR 0 +# define CMIREG_CH1_ENABLE_MASK 0x02 +# define CMIREG_CH1_ENABLE_ADDR 1 +# define CMIREG_CH0_RESET_MASK 0x04 +# define CMIREG_CH0_RESET_ADDR 2 +# define CMIREG_CH1_RESET_MASK 0x08 +# define CMIREG_CH1_RESET_ADDR 3 + +/* + * function control 1 + * 0x00 : + * bit2 : MPU-401 compatible 1:enable + * bit7 : SPDIF 1:enable + * 0x01 : + * bit2-4: D/A sample rate + * bit5-7: A/D sample rate + * each bit field means: + * 0: 5512 + * 1: 11025 + * 2: 22050 + * 3: 44100 + * 4: 8000 + * 5: 16000 + * 6: 32000 + * 7: 48000 + */ +#define CMIREG_FUNCTION_CONTROL1_0 0x04 +# define CMIREG_MPU401_ENABLE_MASK 0x04 +# define CMIREG_MPU401_ENABLE_ADDR 2 +# define CMIREG_SPDIF_ENABLE_MASK 0x80 +# define CMIREG_SPDIF_ENABLE_ADDR 7 +#define CMIREG_FUNCTION_CONTROL1_1 0x05 +# define CMIREG_DAC_RATE_MASK 0x1c +# define CMIREG_DAC_RATE_ADDR 2 +# define CMIREG_ADC_RATE_MASK 0xe0 +# define CMIREG_ADC_RATE_ADDR 5 +# define CMIREG_RATE_5512 0 +# define CMIREG_RATE_8000 4 +# define CMIREG_RATE_11025 1 +# define CMIREG_RATE_16000 5 +# define CMIREG_RATE_22050 2 +# define CMIREG_RATE_32000 6 +# define CMIREG_RATE_44100 3 +# define CMIREG_RATE_48000 7 +# define CMIREG_NUMRATE 8 + +/* + * channel format + * 0x00 : + * bit0: D/A channels 1:stereo + * bit1: D/A 8/16bit 1:16bit + * bit2: A/D channels 1:stereo + * bit3: A/D 8/16bit 1:16bit + * + */ +#define CMIREG_CHANNEL_FORMAT 0x08 +# define CMIREG_DAC_STEREO_MASK 0x01 +# define CMIREG_DAC_STEREO_ADDR 0 +# define CMIREG_DAC_16BIT_MASK 0x02 +# define CMIREG_DAC_16BIT_ADDR 1 +# define CMIREG_ADC_STEREO_MASK 0x04 +# define CMIREG_ADC_STEREO_ADDR 2 +# define CMIREG_ADC_16BIT_MASK 0x08 +# define CMIREG_ADC_16BIT_ADDR 3 + +/* + * DMA interrupt enable + * 0x02 : + * bit0: CH0 int 1:enable + * bit1: CH1 int 1:enable + * + */ +#define CMIREG_INTERRUPT_HLD_CLR_2 0x0E +# define CMIREG_CH0_INT_EN_MASK 0x01 +# define CMIREG_CH0_INT_EN_ADDR 0 +# define CMIREG_CH1_INT_EN_MASK 0x02 +# define CMIREG_CH1_INT_EN_ADDR 1 + + +/* + * DMA interrupt status + * 0x00 : + * bit0: CH0 int 1:signaled + * bit1: CH1 int 1:signaled + * + */ +#define CMIREG_INTERRUPT_STATUS 0x10 +# define CMIREG_CH0_INT_STAT_MASK 0x01 +# define CMIREG_CH0_INT_STAT_ADDR 0 +# define CMIREG_CH1_INT_STAT_MASK 0x02 +# define CMIREG_CH1_INT_STAT_ADDR 1 + +/* */ +#define CMIREG_LEGACY_CONTROL 0x14 +#define CMIREG_MISC_CONTROL 0x18 +#define CMIREG_TDMA_POSITION 0x1C +#define CMIREG_MIXER 0x20 + +/* SB16 legacy register */ +#define CMIREG_SB16_DATA 0x22 +#define CMIREG_SB16_ADDRESS 0x23 + +#define SB16_MIXER_RESET 0x00 +#define SB16_MIXER_MASTER_L 0x30 +#define SB16_MIXER_MASTER_R 0x31 +#define SB16_MIXER_VOICE_L 0x32 +#define SB16_MIXER_VOICE_R 0x33 +#define SB16_MIXER_FM_L 0x34 +#define SB16_MIXER_FM_R 0x35 +#define SB16_MIXER_CDDA_L 0x36 +#define SB16_MIXER_CDDA_R 0x37 +#define SB16_MIXER_LINE_L 0x38 +#define SB16_MIXER_LINE_R 0x39 + +#define SB16_MIXER_MIC 0x3A +#define SB16_MIXER_SPEAKER 0x3B + +#define SB16_MIXER_OUTMIX 0x3C + +#define SB16_MIXER_ADCMIX_L 0x3D +#define SB16_MIXER_ADCMIX_R 0x3E + +#define SB16_MIXER_INGAIN_L 0x3F +#define SB16_MIXER_INGAIN_R 0x40 +#define SB16_MIXER_OUTGAIN_L 0x41 +#define SB16_MIXER_OUTGAIN_R 0x42 + +#define SB16_MIXER_AGC 0x43 + +#define SB16_MIXER_TREBLE_L 0x44 +#define SB16_MIXER_TREBLE_R 0x45 +#define SB16_MIXER_BASS_L 0x46 +#define SB16_MIXER_BASS_R 0x47 + + +/* */ +#define CMIREG_MIXER1 0x24 +#define CMIREG_MIXER2 0x25 +#define CMIREG_AUX_VOLUME 0x26 +#define CMIREG_MISC 0x27 +#define CMIREG_AC97 0x28 + + +/* + * DMA registers : + * + * frame1(dword) : base address + * frame2 + * (high word) : bytes + * (low word) : samples-1 + */ +#define CMIREG_CHANNEL0_FRAME1 0x80 +#define CMIREG_CHANNEL0_FRAME2_0 0x84 +#define CMIREG_CHANNEL0_FRAME2_2 0x86 +#define CMIREG_CHANNEL1_FRAME1 0x88 +#define CMIREG_CHANNEL1_FRAME2_0 0x8C +#define CMIREG_CHANNEL1_FRAME2_2 0x8E + +/* */ +#define CMIREG_EXTENDED 0xF0 + + +#define CMI_BUFFSIZE 0x8000 /* XXX */ + + +#endif /* _CMI_REG_H_ */ + +/* end of file */