diff --git a/sys/dev/sound/pci/maestro.c b/sys/dev/sound/pci/maestro.c new file mode 100644 index 000000000000..c9926c4b8801 --- /dev/null +++ b/sys/dev/sound/pci/maestro.c @@ -0,0 +1,1188 @@ +/*- + * Copyright (c) 2000 Taku YAMAMOTO + * 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: maestro.c,v 1.12 2000/09/06 03:32:34 taku Exp $ + * $FreeBSD$ + */ + +/* + * Credits: + * + * Part of this code (especially in many magic numbers) was heavily inspired + * by the Linux driver originally written by + * Alan Cox , modified heavily by + * Zach Brown . + * + * busdma()-ize and buffer size reduction were suggested by + * Cameron Grant . + * Also he showed me the way to use busdma() suite. + * + * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 + * were looked at by + * Munehiro Matsuda , + * who brought patches based on the Linux driver with some simplification. + */ + +#include +#include +#include +#include + +#include + +#define inline __inline + +/* + * PCI IDs of supported chips: + * + * MAESTRO-1 0x01001285 + * MAESTRO-2 0x1968125d + * MAESTRO-2E 0x1978125d + */ + +#define MAESTRO_1_PCI_ID 0x01001285 +#define MAESTRO_2_PCI_ID 0x1968125d +#define MAESTRO_2E_PCI_ID 0x1978125d + +#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ +#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ + +#ifndef AGG_MAXPLAYCH +# define AGG_MAXPLAYCH 4 +#endif + +#define AGG_BUFSIZ 4096 + + +/* ----------------------------- + * Data structures. + */ +struct agg_chinfo { + struct agg_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + bus_addr_t offset; + u_int32_t blocksize; + int dir; + u_int num; + u_int16_t aputype; + u_int16_t wcreg_tpl; +}; + +struct agg_info { + device_t dev; + struct resource *reg; + int regid; + + bus_space_tag_t st; + bus_space_handle_t sh; + bus_dma_tag_t parent_dmat; + + struct resource *irq; + int irqid; + void *ih; + + u_int8_t *stat; + bus_addr_t baseaddr; + + struct ac97_info *codec; + + u_int playchns, active; + struct agg_chinfo pch[AGG_MAXPLAYCH]; + struct agg_chinfo rch; +}; + + +static u_int32_t agg_rdcodec(void *, int); +static void agg_wrcodec(void *, int, u_int32_t); + +static inline void ringbus_setdest(struct agg_info*, int, int); + +static inline u_int16_t wp_rdreg(struct agg_info*, u_int16_t); +static inline void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); +static inline u_int16_t wp_rdapu(struct agg_info*, int, u_int16_t); +static inline void wp_wrapu(struct agg_info*, int, u_int16_t, u_int16_t); +static inline void wp_settimer(struct agg_info*, u_int); +static inline void wp_starttimer(struct agg_info*); +static inline void wp_stoptimer(struct agg_info*); + +static inline u_int16_t wc_rdreg(struct agg_info*, u_int16_t); +static inline void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); +static inline u_int16_t wc_rdchctl(struct agg_info*, int); +static inline void wc_wrchctl(struct agg_info*, int, u_int16_t); + +static inline void agg_power(struct agg_info*, int); + +static void agg_init(struct agg_info*); +static u_int32_t agg_ac97_init(void *); + +static void aggch_start_dac(struct agg_chinfo*); +static void aggch_stop_dac(struct agg_chinfo*); + +static inline void suppress_jitter(struct agg_chinfo*); + +static inline u_int calc_timer_freq(struct agg_chinfo*); +static void set_timer(struct agg_info*); + +static pcmchan_init_t aggch_init; +static pcmchan_free_t aggch_free; +static pcmchan_setformat_t aggch_setplayformat; +static pcmchan_setspeed_t aggch_setspeed; +static pcmchan_setblocksize_t aggch_setblocksize; +static pcmchan_trigger_t aggch_trigger; +static pcmchan_getptr_t aggch_getplayptr; +static pcmchan_getcaps_t aggch_getcaps; + +static void agg_intr(void *); +static int agg_probe(device_t); +static int agg_attach(device_t); +static int agg_detach(device_t); +static int agg_suspend(device_t); +static int agg_resume(device_t); +static int agg_shutdown(device_t); + +static void *dma_malloc(struct agg_info*, u_int32_t, bus_addr_t*); +static void dma_free(struct agg_info*, void *); + +/* ----------------------------- + * Subsystems. + */ + +/* Codec/Ringbus */ + +static u_int32_t +agg_rdcodec(void *sc, int regno) +{ + struct agg_info *ess = sc; + unsigned t; + + /* We have to wait for a SAFE time to write addr/data */ + for (t = 0; t < 20; t++) { + if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) + & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) + break; + DELAY(2); /* 20.8us / 13 */ + } + if (t == 20) + device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); + + bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, + CODEC_CMD_READ | regno); + DELAY(21); /* AC97 cycle = 20.8usec */ + + /* Wait for data retrieve */ + for (t = 0; t < 20; t++) { + if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) + & CODEC_STAT_MASK) == CODEC_STAT_RW_DONE) + break; + DELAY(2); /* 20.8us / 13 */ + } + if (t == 20) + /* Timed out, but perform dummy read. */ + device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); + + return bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG); +} + +static void +agg_wrcodec(void *sc, int regno, u_int32_t data) +{ + unsigned t; + struct agg_info *ess = sc; + + /* We have to wait for a SAFE time to write addr/data */ + for (t = 0; t < 20; t++) { + if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) + & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) + break; + DELAY(2); /* 20.8us / 13 */ + } + if (t == 20) { + /* Timed out. Abort writing. */ + device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); + return; + } + + bus_space_write_2(ess->st, ess->sh, PORT_CODEC_REG, data); + bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, + CODEC_CMD_WRITE | regno); +} + +static inline void +ringbus_setdest(struct agg_info *ess, int src, int dest) +{ + u_int32_t data; + + data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL); + data &= ~(0xfU << src); + data |= (0xfU & dest) << src; + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); +} + +/* Wave Processor */ + +static inline u_int16_t +wp_rdreg(struct agg_info *ess, u_int16_t reg) +{ + bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); + return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA); +} + +static inline void +wp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) +{ + bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); + bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); +} + +static inline void +apu_setindex(struct agg_info *ess, u_int16_t reg) +{ + int t; + + wp_wrreg(ess, WPREG_CRAM_PTR, reg); + /* Sometimes WP fails to set apu register index. */ + for (t = 0; t < 1000; t++) { + if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg) + break; + bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg); + } + if (t == 1000) + device_printf(ess->dev, "apu_setindex() timed out.\n"); +} + +static inline u_int16_t +wp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) +{ + u_int16_t ret; + + apu_setindex(ess, ((unsigned)ch << 4) + reg); + ret = wp_rdreg(ess, WPREG_DATA_PORT); + return ret; +} + +static inline void +wp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) +{ + int t; + + apu_setindex(ess, ((unsigned)ch << 4) + reg); + wp_wrreg(ess, WPREG_DATA_PORT, data); + for (t = 0; t < 1000; t++) { + if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data) + break; + bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); + } + if (t == 1000) + device_printf(ess->dev, "wp_wrapu() timed out.\n"); +} + +static inline void +wp_settimer(struct agg_info *ess, u_int freq) +{ + u_int clock = 48000 << 2; + u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; + + RANGE(divide, 4, 32 << 8); + + for (; divide > 32 << 1; divide >>= 1) + prescale++; + divide = (divide + 1) >> 1; + + for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) + prescale++; + + wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); + wp_wrreg(ess, WPREG_TIMER_FREQ, + (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); + wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); +} + +static inline void +wp_starttimer(struct agg_info *ess) +{ + wp_wrreg(ess, WPREG_TIMER_START, 1); +} + +static inline void +wp_stoptimer(struct agg_info *ess) +{ + wp_wrreg(ess, WPREG_TIMER_START, 0); + bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); +} + +/* WaveCache */ + +static inline u_int16_t +wc_rdreg(struct agg_info *ess, u_int16_t reg) +{ + bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); + return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA); +} + +static inline void +wc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) +{ + bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); + bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data); +} + +static inline u_int16_t +wc_rdchctl(struct agg_info *ess, int ch) +{ + return wc_rdreg(ess, ch << 3); +} + +static inline void +wc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) +{ + wc_wrreg(ess, ch << 3, data); +} + +/* Power management */ + +static inline void +agg_power(struct agg_info *ess, int status) +{ + u_int8_t data; + + data = pci_read_config(ess->dev, CONF_PM_PTR, 1); + if (pci_read_config(ess->dev, data, 1) == PPMI_CID) + pci_write_config(ess->dev, data + PM_CTRL, status, 1); +} + + +/* ----------------------------- + * Controller. + */ + +static inline void +agg_initcodec(struct agg_info* ess) +{ + u_int16_t data; + + if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL) + & RINGBUS_CTRL_ACLINK_ENABLED) { + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); + DELAY(104); /* 20.8us * (4 + 1) */ + } + /* XXX - 2nd codec should be looked at. */ + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_AC97_SWRESET); + DELAY(2); + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_ACLINK_ENABLED); + DELAY(21); + + agg_rdcodec(ess, 0); + if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) + & CODEC_STAT_MASK) { + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); + DELAY(21); + + /* Try cold reset. */ + device_printf(ess->dev, "will perform cold reset.\n"); + data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR); + if (pci_read_config(ess->dev, 0x58, 2) & 1) + data |= 0x10; + data |= 0x009 & + ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, + data | 0x009); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000); + DELAY(2); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); + DELAY(1); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009); + DELAY(500000); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data); + DELAY(84); /* 20.8us * 4 */ + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_ACLINK_ENABLED); + DELAY(21); + } +} + +static void +agg_init(struct agg_info* ess) +{ + u_int32_t data; + + /* Setup PCI config registers. */ + + /* Disable all legacy emulations. */ + data = pci_read_config(ess->dev, CONF_LEGACY, 2); + data |= LEGACY_DISABLED; + pci_write_config(ess->dev, CONF_LEGACY, data, 2); + + /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) + * Enable posted write. + * Prefer PCI timing rather than that of ISA. + * Don't swap L/R. */ + data = pci_read_config(ess->dev, CONF_MAESTRO, 4); + data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; + data &= ~MAESTRO_SWAP_LR; + pci_write_config(ess->dev, CONF_MAESTRO, data, 4); + + /* Reset direct sound. */ + bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, + HOSTINT_CTRL_DSOUND_RESET); + DELAY(10000); /* XXX - too long? */ + bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); + DELAY(10000); + + /* Enable direct sound interruption. */ + bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, + HOSTINT_CTRL_DSOUND_INT_ENABLED); + + /* Setup Wave Processor. */ + + /* Enable WaveCache, set DMA base address. */ + wp_wrreg(ess, WPREG_WAVE_ROMRAM, + WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); + bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL, + WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB); + + for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) + wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); + + /* Setup Codec/Ringbus. */ + agg_initcodec(ess); + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED); + + wp_wrreg(ess, WPREG_BASE, 0x8500); /* Parallel I/O */ + ringbus_setdest(ess, RINGBUS_SRC_ADC, + RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); + ringbus_setdest(ess, RINGBUS_SRC_DSOUND, + RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); + + /* Setup ASSP. Needed for Dell Inspiron 7500? */ + bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00); + bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03); + bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00); + + /* + * Setup GPIO. + * There seems to be speciality with NEC systems. + */ + switch (pci_get_subvendor(ess->dev) + | (pci_get_subdevice(ess->dev) << 16)) { + case NEC_SUBID1: + case NEC_SUBID2: + /* Matthew Braithwaite reported that + * NEC Versa LX doesn't need GPIO operation. */ + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0x9ff); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, + bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 0x600); + bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x200); + break; + } +} + +/* Channel controller. */ + +static void +aggch_start_dac(struct agg_chinfo *ch) +{ + u_int wpwa = APU_USE_SYSMEM | (ch->offset >> 9); + u_int size = AGG_BUFSIZ >> 1; + u_int speed = ch->channel->speed; + u_int offset = ch->offset >> 1; + u_int cp = ch->buffer->rp >> 1; + u_int16_t apuch = ch->num << 1; + u_int dv; + int pan = 0; + + switch (ch->aputype) { + case APUTYPE_16BITSTEREO: + wpwa >>= 1; + size >>= 1; + offset >>= 1; + cp >>= 1; + /* FALLTHROUGH */ + case APUTYPE_8BITSTEREO: + pan = 8; + apuch++; + break; + case APUTYPE_8BITLINEAR: + speed >>= 1; + break; + } + + dv = (((speed % 48000) << 16) + 24000) / 48000 + + ((speed / 48000) << 16); + + do { + wp_wrapu(ch->parent, apuch, APUREG_WAVESPACE, wpwa & 0xff00); + wp_wrapu(ch->parent, apuch, APUREG_CURPTR, offset + cp); + wp_wrapu(ch->parent, apuch, APUREG_ENDPTR, offset + size); + wp_wrapu(ch->parent, apuch, APUREG_LOOPLEN, size); + wp_wrapu(ch->parent, apuch, APUREG_AMPLITUDE, 0xe800); + wp_wrapu(ch->parent, apuch, APUREG_POSITION, 0x8f00 + | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) + | ((PAN_FRONT + pan) << APU_PAN_SHIFT)); + wp_wrapu(ch->parent, apuch, APUREG_FREQ_LOBYTE, APU_plus6dB + | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); + wp_wrapu(ch->parent, apuch, APUREG_FREQ_HIWORD, dv >> 8); + + if (ch->aputype == APUTYPE_16BITSTEREO) + wpwa |= APU_STEREO >> 1; + pan = -pan; + } while (pan < 0 && apuch--); + + wc_wrchctl(ch->parent, apuch, ch->wcreg_tpl); + wc_wrchctl(ch->parent, apuch + 1, ch->wcreg_tpl); + + wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, + (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) + wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, + (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); +} + +static void +aggch_stop_dac(struct agg_chinfo *ch) +{ + wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE, + APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); + wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE, + APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); +} + +/* + * Stereo jitter suppressor. + * Sometimes playback pointers differ in stereo-paired channels. + * Calling this routine within intr fixes the problem. + */ +static inline void +suppress_jitter(struct agg_chinfo *ch) +{ + if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) { + int cp, diff, halfsize = AGG_BUFSIZ >> 2; + + if (ch->aputype == APUTYPE_16BITSTEREO) + halfsize >>= 1; + cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); + diff = wp_rdapu(ch->parent, (ch->num << 1) + 1, APUREG_CURPTR); + diff -= cp; + if (diff >> 1 && diff > -halfsize && diff < halfsize) + bus_space_write_2(ch->parent->st, ch->parent->sh, + PORT_DSP_DATA, cp); + } +} + +static inline u_int +calc_timer_freq(struct agg_chinfo *ch) +{ + u_int ss = 2; + + if (ch->aputype == APUTYPE_16BITSTEREO) + ss <<= 1; + if (ch->aputype == APUTYPE_8BITLINEAR) + ss >>= 1; + + return (ch->channel->speed * ss + ch->blocksize - 1) / ch->blocksize; +} + +static void +set_timer(struct agg_info *ess) +{ + int i; + u_int freq = 0; + + for (i = 0; i < ess->playchns; i++) + if ((ess->active & (1 << i)) && + (freq < calc_timer_freq(ess->pch + i))) + freq = calc_timer_freq(ess->pch + i); + + wp_settimer(ess, freq); +} + + +/* ----------------------------- + * Newpcm glue. + */ + +static u_int32_t +agg_ac97_init(void *sc) +{ + struct agg_info *ess = sc; + + return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; +} + +static void * +aggch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct agg_info *ess = devinfo; + struct agg_chinfo *ch; + bus_addr_t physaddr; + + ch = (dir == PCMDIR_PLAY)? ess->pch + ess->playchns : &ess->rch; + + ch->parent = ess; + ch->channel = c; + ch->buffer = b; + ch->num = ess->playchns; + ch->dir = dir; + + b->buf = dma_malloc(ess, AGG_BUFSIZ, &physaddr); + if (b->buf == NULL) + return NULL; + + ch->offset = physaddr - ess->baseaddr; + if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) { + device_printf(ess->dev, + "offset %#x exceeds limit. ", ch->offset); + dma_free(ess, b->buf); + b->buf = NULL; + return NULL; + } + + b->bufsize = AGG_BUFSIZ; + ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + + if (dir == PCMDIR_PLAY) { + ess->playchns++; + if (bootverbose) + device_printf(ess->dev, "pch[%d].offset = %#x\n", ch->num, ch->offset); + } else if (bootverbose) + device_printf(ess->dev, "rch.offset = %#x\n", ch->offset); + + return ch; +} + +static int +aggch_free(void *data) +{ + struct agg_chinfo *ch = data; + struct agg_info *ess = ch->parent; + + /* free up buffer - called after channel stopped */ + dma_free(ess, ch->buffer->buf); + + /* return 0 if ok */ + return 0; +} + +static int +aggch_setplayformat(void *data, u_int32_t format) +{ + struct agg_chinfo *ch = data; + u_int16_t wcreg_tpl; + u_int16_t aputype = APUTYPE_16BITLINEAR; + + wcreg_tpl = ch->wcreg_tpl & WAVCACHE_CHCTL_ADDRTAG_MASK; + + if (format & AFMT_STEREO) { + wcreg_tpl |= WAVCACHE_CHCTL_STEREO; + aputype += 1; + } + if (format & AFMT_U8 || format & AFMT_S8) { + aputype += 2; + if (format & AFMT_U8) + wcreg_tpl |= WAVCACHE_CHCTL_U8; + } + if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) { + format &= ~AFMT_BIGENDIAN & ~AFMT_U16_LE; + format |= AFMT_S16_LE; + } + ch->wcreg_tpl = wcreg_tpl; + ch->aputype = aputype; + return format; +} + +static int +aggch_setspeed(void *data, u_int32_t speed) +{ + return speed; +} + +static int +aggch_setblocksize(void *data, u_int32_t blocksize) +{ + return ((struct agg_chinfo*)data)->blocksize = blocksize; +} + +static int +aggch_trigger(void *data, int go) +{ + struct agg_chinfo *ch = data; + + switch (go) { + case PCMTRIG_EMLDMAWR: + return 0; + case PCMTRIG_START: + ch->parent->active |= (1 << ch->num); + if (ch->dir == PCMDIR_PLAY) + aggch_start_dac(ch); +#if 0 /* XXX - RECORDING */ + else + aggch_start_adc(ch); +#endif + break; + case PCMTRIG_ABORT: + case PCMTRIG_STOP: + ch->parent->active &= ~(1 << ch->num); + if (ch->dir == PCMDIR_PLAY) + aggch_stop_dac(ch); +#if 0 /* XXX - RECORDING */ + else + aggch_stop_adc(ch); +#endif + break; + } + + if (ch->parent->active) { + set_timer(ch->parent); + wp_starttimer(ch->parent); + } else + wp_stoptimer(ch->parent); + + return 0; +} + +static int +aggch_getplayptr(void *data) +{ + struct agg_chinfo *ch = data; + u_int cp; + + cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); + if (ch->aputype == APUTYPE_16BITSTEREO) + cp = (0xffff << 2) & ((cp << 2) - ch->offset); + else + cp = (0xffff << 1) & ((cp << 1) - ch->offset); + + return cp; +} + +static pcmchan_caps * +aggch_getcaps(void *data) +{ + static u_int32_t playfmt[] = { + AFMT_U8, + AFMT_STEREO | AFMT_U8, + AFMT_S8, + AFMT_STEREO | AFMT_S8, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + 0 + }; + static pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; + + static u_int32_t recfmt[] = { + AFMT_S8, + AFMT_STEREO | AFMT_S8, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + 0 + }; + static pcmchan_caps reccaps = {4000, 48000, recfmt, 0}; + + return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)? + &playcaps : &reccaps; +} + + +/* ----------------------------- + * Bus space. + */ + +static void +agg_intr(void *sc) +{ + struct agg_info* ess = sc; + u_int16_t status; + int i; + + status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); + if (!status) + return; + + /* Acknowledge all. */ + bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); + bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0); +#if 0 /* XXX - HWVOL */ + if (status & HOSTINT_STAT_HWVOL) { + u_int delta; + delta = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER) + - 0x88; + if (delta & 0x11) + mixer_set(device_get_softc(ess->dev), + SOUND_MIXER_VOLUME, 0); + else { + mixer_set(device_get_softc(ess->dev), + SOUND_MIXER_VOLUME, + mixer_get(device_get_softc(ess->dev), + SOUND_MIXER_VOLUME) + + ((delta >> 5) & 0x7) - 4 + + ((delta << 7) & 0x700) - 0x400); + } + bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 0x88); + } +#endif /* XXX - HWVOL */ + + for (i = 0; i < ess->playchns; i++) + if (ess->active & (1 << i)) { + suppress_jitter(ess->pch + i); + chn_intr(ess->pch[i].channel); + } +#if 0 /* XXX - RECORDING */ + if (ess->active & (1 << i)) + chn_intr(ess->rch.channel); +#endif +} + +static void +setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *phys = arg; + + *phys = error? 0 : segs->ds_addr; + + if (bootverbose) { + printf("setmap (%lx, %lx), nseg=%d, error=%d\n", + (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, + nseg, error); + } +} + +static void * +dma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys) +{ + void *buf; + bus_dmamap_t map; + + if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) + return NULL; + if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, setmap, phys, 0) + || !*phys) { + bus_dmamem_free(sc->parent_dmat, buf, map); + return NULL; + } + return buf; +} + +static void +dma_free(struct agg_info *sc, void *buf) +{ + bus_dmamem_free(sc->parent_dmat, buf, NULL); +} + +static int +agg_probe(device_t dev) +{ + char *s = NULL; + + switch (pci_get_devid(dev)) { + case MAESTRO_1_PCI_ID: + s = "ESS Technology Maestro-1"; + break; + + case MAESTRO_2_PCI_ID: + s = "ESS Technology Maestro-2"; + break; + + case MAESTRO_2E_PCI_ID: + s = "ESS Technology Maestro-2E"; + break; + } + + if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; +} + +static int +agg_attach(device_t dev) +{ + struct agg_info *ess = NULL; + u_int32_t data; + int mapped = 0; + int regid = PCIR_MAPS; + struct resource *reg = NULL; + struct ac97_info *codec = NULL; + int irqid = 0; + struct resource *irq = NULL; + void *ih = NULL; + char status[SND_STATUSLEN]; + static pcm_channel agg_pchtpl = { + aggch_init, + NULL, /* setdir */ + aggch_setplayformat, + aggch_setspeed, + aggch_setblocksize, + aggch_trigger, + aggch_getplayptr, + aggch_getcaps, + aggch_free, /* free */ + NULL, /* nop1 */ + NULL, /* nop2 */ + NULL, /* nop3 */ + NULL, /* nop4 */ + NULL, /* nop5 */ + NULL, /* nop6 */ + NULL, /* nop7 */ + }; + + if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + bzero(ess, sizeof *ess); + ess->dev = dev; + + if (bus_dma_tag_create(/*parent*/NULL, + /*alignment*/1 << WAVCACHE_BASEADDR_SHIFT, + /*boundary*/WPWA_MAXADDR + 1, + /*lowaddr*/MAESTRO_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/AGG_BUFSIZ * 2, /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, &ess->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + ess->stat = dma_malloc(ess, AGG_BUFSIZ, &ess->baseaddr); + if (ess->stat == NULL) { + device_printf(dev, "cannot allocate status buffer\n"); + goto bad; + } + if (bootverbose) + device_printf(dev, "Maestro DMA base: %#x\n", ess->baseaddr); + + agg_power(ess, PPMI_D0); + DELAY(100000); + + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + if (data & PCIM_CMD_PORTEN) { + reg = bus_alloc_resource(dev, SYS_RES_IOPORT, ®id, + 0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE); + if (reg != NULL) { + ess->reg = reg; + ess->regid = regid; + ess->st = rman_get_bustag(reg); + ess->sh = rman_get_bushandle(reg); + mapped++; + } + } + if (mapped == 0) { + device_printf(dev, "unable to map register space\n"); + goto bad; + } + + agg_init(ess); + if (agg_rdcodec(ess, 0) == 0x80) { + device_printf(dev, "PT101 codec detected!\n"); + goto bad; + } + codec = ac97_create(dev, ess, agg_ac97_init, agg_rdcodec, agg_wrcodec); + if (codec == NULL) + goto bad; + if (mixer_init(dev, &ac97_mixer, codec) == -1) + goto bad; + ess->codec = codec; + + irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, + 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE); + if (irq == NULL + || bus_setup_intr(dev, irq, INTR_TYPE_TTY, agg_intr, ess, &ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto bad; + } + ess->irq = irq; + ess->irqid = irqid; + ess->ih = ih; + + snprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld", + rman_get_start(reg), rman_get_start(irq)); + + if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1)) + goto bad; + + for (data = 0; data < AGG_MAXPLAYCH; data++) + pcm_addchan(dev, PCMDIR_PLAY, &agg_pchtpl, ess); +#if 0 /* XXX - RECORDING */ + pcm_addchan(dev, PCMDIR_REC, &agg_rchtpl, ess); +#endif + pcm_setstatus(dev, status); + + return 0; + + bad: + if (ih != NULL) + bus_teardown_intr(dev, irq, ih); + if (irq != NULL) + bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); + if (codec != NULL) + free(codec, M_DEVBUF); + if (reg != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); + if (ess != NULL) { + agg_power(ess, PPMI_D3); + if (ess->stat != NULL) + dma_free(ess, ess->stat); + if (ess->parent_dmat != NULL) + bus_dma_tag_destroy(ess->parent_dmat); + free(ess, M_DEVBUF); + } + + return ENXIO; +} + +static int +agg_detach(device_t dev) +{ + struct agg_info *ess = pcm_getdevinfo(dev); + int r; + + r = pcm_unregister(dev); + if (r) + return r; + + ess = pcm_getdevinfo(dev); + dma_free(ess, ess->stat); + + /* Power down everything except clock and vref. */ + agg_wrcodec(ess, AC97_REG_POWER, 0xd700); + DELAY(20); + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); + agg_power(ess, PPMI_D3); + + bus_teardown_intr(dev, ess->irq, ess->ih); + bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); + bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); + bus_dma_tag_destroy(ess->parent_dmat); + free(ess, M_DEVBUF); + return 0; +} + +static int +agg_suspend(device_t dev) +{ + struct agg_info *ess = pcm_getdevinfo(dev); + int i, x; + + x = spltty(); + wp_stoptimer(ess); + bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); + + for (i = 0; i < ess->playchns; i++) + aggch_stop_dac(ess->pch + i); + +#if 0 /* XXX - RECORDING */ + aggch_stop_adc(&ess->rch); +#endif + splx(x); + /* Power down everything except clock. */ + agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); + DELAY(20); + bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); + DELAY(1); + agg_power(ess, PPMI_D3); + + return 0; +} + +static int +agg_resume(device_t dev) +{ + int i, x; + struct agg_info *ess = pcm_getdevinfo(dev); + + agg_power(ess, PPMI_D0); + DELAY(100000); + agg_init(ess); + if (mixer_reinit(dev)) { + device_printf(dev, "unable to reinitialize the mixer\n"); + return ENXIO; + } + + x = spltty(); + for (i = 0; i < ess->playchns; i++) + if (ess->active & (1 << i)) + aggch_start_dac(ess->pch + i); +#if 0 /* XXX - RECORDING */ + if (ess->active & (1 << i)) + aggch_start_adc(&ess->rch); +#endif + if (ess->active) { + set_timer(ess); + wp_starttimer(ess); + } + splx(x); + return 0; +} + +static int +agg_shutdown(device_t dev) +{ + struct agg_info *ess = pcm_getdevinfo(dev); + int i; + + wp_stoptimer(ess); + bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); + + for (i = 0; i < ess->playchns; i++) + aggch_stop_dac(ess->pch + i); + +#if 0 /* XXX - RECORDING */ + aggch_stop_adc(&ess->rch); +#endif + return 0; +} + + +static device_method_t agg_methods[] = { + DEVMETHOD(device_probe, agg_probe), + DEVMETHOD(device_attach, agg_attach), + DEVMETHOD(device_detach, agg_detach), + DEVMETHOD(device_suspend, agg_suspend), + DEVMETHOD(device_resume, agg_resume), + DEVMETHOD(device_shutdown, agg_shutdown), + + { 0, 0 } +}; + +static driver_t agg_driver = { + "pcm", + agg_methods, + sizeof(snddev_info), +}; + +static devclass_t pcm_devclass; + +DRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_VERSION(snd_maestro, 1); diff --git a/sys/dev/sound/pci/maestro_reg.h b/sys/dev/sound/pci/maestro_reg.h new file mode 100644 index 000000000000..579723388d8e --- /dev/null +++ b/sys/dev/sound/pci/maestro_reg.h @@ -0,0 +1,345 @@ +/*- + * Copyright (c) 1999-2000 Taku YAMAMOTO + * 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: maestro_reg.h,v 1.10 2000/08/29 17:27:29 taku Exp $ + * $FreeBSD$ + */ + +#ifndef MAESTRO_REG_H_INCLUDED +#define MAESTRO_REG_H_INCLUDED + +/* ----------------------------- + * PCI config registers + */ + +/* Legacy emulation */ +#define CONF_LEGACY 0x40 + +#define LEGACY_DISABLED 0x8000 + +/* Chip configurations */ +#define CONF_MAESTRO 0x50 +#define MAESTRO_CHIBUS 0x00100000 +#define MAESTRO_POSTEDWRITE 0x00000080 +#define MAESTRO_DMA_PCITIMING 0x00000040 +#define MAESTRO_SWAP_LR 0x00000010 + +/* ACPI configurations */ +#define CONF_ACPI_STOPCLOCK 0x54 +#define ACPI_PART_2ndC_CLOCK 15 +#define ACPI_PART_CODEC_CLOCK 14 +#define ACPI_PART_978 13 /* Docking station or something */ +#define ACPI_PART_SPDIF 12 +#define ACPI_PART_GLUE 11 /* What? */ +#define ACPI_PART_DAA 10 +#define ACPI_PART_PCI_IF 9 +#define ACPI_PART_HW_VOL 8 +#define ACPI_PART_GPIO 7 +#define ACPI_PART_ASSP 6 +#define ACPI_PART_SB 5 +#define ACPI_PART_FM 4 +#define ACPI_PART_RINGBUS 3 +#define ACPI_PART_MIDI 2 +#define ACPI_PART_GAME_PORT 1 +#define ACPI_PART_WP 0 + +/* Power management */ +#define CONF_PM_PTR 0x34 /* BYTE R */ +#define PM_CID 0 /* BYTE R */ +#define PPMI_CID 1 +#define PM_CTRL 4 /* BYTE RW */ +#define PPMI_D0 0 /* Full power */ +#define PPMI_D1 1 /* Medium power */ +#define PPMI_D2 2 /* Low power */ +#define PPMI_D3 3 /* Turned off */ + + +/* ----------------------------- + * I/O ports + */ + +/* Direct Sound Processor (aka WP) */ +#define PORT_DSP_DATA 0x00 /* WORD RW */ +#define PORT_DSP_INDEX 0x02 /* WORD RW */ +#define PORT_INT_STAT 0x04 /* WORD RW */ +#define PORT_SAMPLE_CNT 0x06 /* WORD RO */ + +/* WaveCache */ +#define PORT_WAVCACHE_INDEX 0x10 /* WORD RW */ +#define PORT_WAVCACHE_DATA 0x12 /* WORD RW */ +#define WAVCACHE_PCMBAR 0x1fc +#define WAVCACHE_WTBAR 0x1f0 +#define WAVCACHE_BASEADDR_SHIFT 12 + +#define WAVCACHE_CHCTL_ADDRTAG_MASK 0xfff8 +#define WAVCACHE_CHCTL_U8 0x0004 +#define WAVCACHE_CHCTL_STEREO 0x0002 +#define WAVCACHE_CHCTL_DECREMENTAL 0x0001 + +#define PORT_WAVCACHE_CTRL 0x14 /* WORD RW */ +#define WAVCACHE_EXTRA_CH_ENABLED 0x0200 +#define WAVCACHE_ENABLED 0x0100 +#define WAVCACHE_CH_60_ENABLED 0x0080 +#define WAVCACHE_WTSIZE_MASK 0x0060 +#define WAVCACHE_WTSIZE_1MB 0x0000 +#define WAVCACHE_WTSIZE_2MB 0x0020 +#define WAVCACHE_WTSIZE_4MB 0x0040 +#define WAVCACHE_WTSIZE_8MB 0x0060 +#define WAVCACHE_SGC_MASK 0x000c +#define WAVCACHE_SGC_DISABLED 0x0000 +#define WAVCACHE_SGC_40_47 0x0004 +#define WAVCACHE_SGC_32_47 0x0008 +#define WAVCACHE_TESTMODE 0x0001 + +/* Host Interruption */ +#define PORT_HOSTINT_CTRL 0x18 /* WORD RW */ +#define HOSTINT_CTRL_SOFT_RESET 0x8000 +#define HOSTINT_CTRL_DSOUND_RESET 0x4000 +#define HOSTINT_CTRL_HW_VOL_TO_PME 0x0400 +#define HOSTINT_CTRL_CLKRUN_ENABLED 0x0100 +#define HOSTINT_CTRL_HWVOL_ENABLED 0x0040 +#define HOSTINT_CTRL_ASSP_INT_ENABLED 0x0010 +#define HOSTINT_CTRL_ISDN_INT_ENABLED 0x0008 +#define HOSTINT_CTRL_DSOUND_INT_ENABLED 0x0004 +#define HOSTINT_CTRL_MPU401_INT_ENABLED 0x0002 +#define HOSTINT_CTRL_SB_INT_ENABLED 0x0001 + +#define PORT_HOSTINT_STAT 0x1a /* BYTE RW */ +#define HOSTINT_STAT_HWVOL 0x40 +#define HOSTINT_STAT_ASSP 0x10 +#define HOSTINT_STAT_ISDN 0x08 +#define HOSTINT_STAT_DSOUND 0x04 +#define HOSTINT_STAT_MPU401 0x02 +#define HOSTINT_STAT_SB 0x01 + +/* Hardware volume */ +#define PORT_HWVOL_VOICE_SHADOW 0x1c /* BYTE RW */ +#define PORT_HWVOL_VOICE 0x1d /* BYTE RW */ +#define PORT_HWVOL_MASTER_SHADOW 0x1e /* BYTE RW */ +#define PORT_HWVOL_MASTER 0x1f /* BYTE RW */ + +/* CODEC */ +#define PORT_CODEC_CMD 0x30 /* BYTE W */ +#define CODEC_CMD_READ 0x80 +#define CODEC_CMD_WRITE 0x00 +#define CODEC_CMD_ADDR_MASK 0x7f + +#define PORT_CODEC_STAT 0x30 /* BYTE R */ +#define CODEC_STAT_MASK 0x01 +#define CODEC_STAT_RW_DONE 0x00 +#define CODEC_STAT_PROGLESS 0x01 + +#define PORT_CODEC_REG 0x32 /* WORD RW */ + +/* Ring bus control */ +#define PORT_RINGBUS_CTRL 0x34 /* DWORD RW */ +#define RINGBUS_CTRL_I2S_ENABLED 0x80000000 +#define RINGBUS_CTRL_RINGBUS_ENABLED 0x20000000 +#define RINGBUS_CTRL_ACLINK_ENABLED 0x10000000 +#define RINGBUS_CTRL_AC97_SWRESET 0x08000000 +#define RINGBUS_CTRL_IODMA_PLAYBACK_ENABLED 0x04000000 +#define RINGBUS_CTRL_IODMA_RECORD_ENABLED 0x02000000 + +#define RINGBUS_SRC_MIC 20 +#define RINGBUS_SRC_I2S 16 +#define RINGBUS_SRC_ADC 12 +#define RINGBUS_SRC_MODEM 8 +#define RINGBUS_SRC_DSOUND 4 +#define RINGBUS_SRC_ASSP 0 + +#define RINGBUS_DEST_MONORAL 000 +#define RINGBUS_DEST_STEREO 010 +#define RINGBUS_DEST_NONE 0 +#define RINGBUS_DEST_DAC 1 +#define RINGBUS_DEST_MODEM_IN 2 +#define RINGBUS_DEST_RESERVED3 3 +#define RINGBUS_DEST_DSOUND_IN 4 +#define RINGBUS_DEST_ASSP_IN 5 + +/* General Purpose I/O */ +#define PORT_GPIO_DATA 0x60 /* WORD RW */ +#define PORT_GPIO_MASK 0x64 /* WORD RW */ +#define PORT_GPIO_DIR 0x68 /* WORD RW */ + +/* Application Specific Signal Processor */ +#define PORT_ASSP_MEM_INDEX 0x80 /* DWORD RW */ +#define PORT_ASSP_MEM_DATA 0x84 /* WORD RW */ +#define PORT_ASSP_CTRL_A 0xa2 /* BYTE RW */ +#define PORT_ASSP_CTRL_B 0xa4 /* BYTE RW */ +#define PORT_ASSP_CTRL_C 0xa6 /* BYTE RW */ +#define PORT_ASSP_HOST_WR_INDEX 0xa8 /* BYTE W */ +#define PORT_ASSP_HOST_WR_DATA 0xaa /* BYTE RW */ +#define PORT_ASSP_INT_STAT 0xac /* BYTE RW */ + + +/* ----------------------------- + * Wave Processor Indexed Data Registers. + */ + +#define WPREG_DATA_PORT 0 +#define WPREG_CRAM_PTR 1 +#define WPREG_CRAM_DATA 2 +#define WPREG_WAVE_DATA 3 +#define WPREG_WAVE_PTR_LOW 4 +#define WPREG_WAVE_PTR_HIGH 5 + +#define WPREG_TIMER_FREQ 6 +#define WP_TIMER_FREQ_PRESCALE_MASK 0x00e0 /* actual - 9 */ +#define WP_TIMER_FREQ_PRESCALE_SHIFT 5 +#define WP_TIMER_FREQ_DIVIDE_MASK 0x001f +#define WP_TIMER_FREQ_DIVIDE_SHIFT 0 + +#define WPREG_WAVE_ROMRAM 7 +#define WP_WAVE_VIRTUAL_ENABLED 0x0400 +#define WP_WAVE_8BITRAM_ENABLED 0x0200 +#define WP_WAVE_DRAM_ENABLED 0x0100 +#define WP_WAVE_RAMSPLIT_MASK 0x00ff +#define WP_WAVE_RAMSPLIT_SHIFT 0 + +#define WPREG_BASE 12 +#define WP_PARAOUT_BASE_MASK 0xf000 +#define WP_PARAOUT_BASE_SHIFT 12 +#define WP_PARAIN_BASE_MASK 0x0f00 +#define WP_PARAIN_BASE_SHIFT 8 +#define WP_SERIAL0_BASE_MASK 0x00f0 +#define WP_SERIAL0_BASE_SHIFT 4 +#define WP_SERIAL1_BASE_MASK 0x000f +#define WP_SERIAL1_BASE_SHIFT 0 + +#define WPREG_TIMER_ENABLE 17 +#define WPREG_TIMER_START 23 + + +/* ----------------------------- + * Audio Processing Unit. + */ +#define APUREG_APUTYPE 0 +#define APU_DMA_ENABLED 0x4000 +#define APU_INT_ON_LOOP 0x2000 +#define APU_ENDCURVE 0x1000 +#define APU_APUTYPE_MASK 0x00f0 +#define APU_FILTERTYPE_MASK 0x000c +#define APU_FILTERQ_MASK 0x0003 + +/* APU types */ +#define APU_APUTYPE_SHIFT 4 + +#define APUTYPE_INACTIVE 0 +#define APUTYPE_16BITLINEAR 1 +#define APUTYPE_16BITSTEREO 2 +#define APUTYPE_8BITLINEAR 3 +#define APUTYPE_8BITSTEREO 4 +#define APUTYPE_8BITDIFF 5 +#define APUTYPE_DIGITALDELAY 6 +#define APUTYPE_DUALTAP_READER 7 +#define APUTYPE_CORRELATOR 8 +#define APUTYPE_INPUTMIXER 9 +#define APUTYPE_WAVETABLE 10 +#define APUTYPE_RATECONV 11 +#define APUTYPE_16BITPINGPONG 12 +/* APU type 13 through 15 are reserved. */ + +/* Filter types */ +#define APU_FILTERTYPE_SHIFT 2 + +#define FILTERTYPE_2POLE_LOPASS 0 +#define FILTERTYPE_2POLE_BANDPASS 1 +#define FILTERTYPE_2POLE_HIPASS 2 +#define FILTERTYPE_1POLE_LOPASS 3 +#define FILTERTYPE_1POLE_HIPASS 4 +#define FILTERTYPE_PASSTHROUGH 5 + +/* Filter Q */ +#define APU_FILTERQ_SHIFT 0 + +#define FILTERQ_LESSQ 0 +#define FILTERQ_MOREQ 3 + +/* APU register 2 */ +#define APUREG_FREQ_LOBYTE 2 +#define APU_FREQ_LOBYTE_MASK 0xff00 +#define APU_plus6dB 0x0010 + +/* APU register 3 */ +#define APUREG_FREQ_HIWORD 3 +#define APU_FREQ_HIWORD_MASK 0x0fff + +/* Frequency */ +#define APU_FREQ_LOBYTE_SHIFT 8 +#define APU_FREQ_HIWORD_SHIFT 0 +#define FREQ_Hz2DIV(freq) (((u_int64_t)(freq) << 16) / 48000) + +/* APU register 4 */ +#define APUREG_WAVESPACE 4 +#define APU_STEREO 0x8000 +#define APU_USE_SYSMEM 0x4000 +#define APU_PCMBAR_MASK 0x6000 +#define APU_64KPAGE_MASK 0xff00 + +/* PCM Base Address Register selection */ +#define APU_PCMBAR_SHIFT 13 + +/* 64KW (==128KB) Page */ +#define APU_64KPAGE_SHIFT 8 + +/* APU register 5 - 7 */ +#define APUREG_CURPTR 5 +#define APUREG_ENDPTR 6 +#define APUREG_LOOPLEN 7 + +/* APU register 9 */ +#define APUREG_AMPLITUDE 9 +#define APU_AMPLITUDE_NOW_MASK 0xff00 +#define APU_AMPLITUDE_DEST_MASK 0x00ff + +/* Amplitude now? */ +#define APU_AMPLITUDE_NOW_SHIFT 8 + +/* APU register 10 */ +#define APUREG_POSITION 10 +#define APU_RADIUS_MASK 0x00c0 +#define APU_PAN_MASK 0x003f + +/* Radius control. */ +#define APU_RADIUS_SHIFT 6 +#define RADIUS_CENTERCIRCLE 0 +#define RADIUS_MIDDLE 1 +#define RADIUS_OUTSIDE 2 + +/* Polar pan. */ +#define APU_PAN_SHIFT 0 +#define PAN_RIGHT 0x00 +#define PAN_FRONT 0x08 +#define PAN_LEFT 0x10 + + +/* ----------------------------- + * Limits. + */ +#define WPWA_MAX ((1 << 22) - 1) +#define WPWA_MAXADDR ((1 << 23) - 1) +#define MAESTRO_MAXADDR ((1 << 28) - 1) + +#endif /* MAESTRO_REG_H_INCLUDED */ diff --git a/sys/modules/sound/driver/maestro/Makefile b/sys/modules/sound/driver/maestro/Makefile new file mode 100644 index 000000000000..01de2704a9bb --- /dev/null +++ b/sys/modules/sound/driver/maestro/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci +KMOD = snd_maestro +SRCS = device_if.h bus_if.h isa_if.h pci_if.h +SRCS += maestro.c + +.include