From owner-p4-projects@FreeBSD.ORG Wed Nov 10 04:48:56 2004 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id AFE2716A4D0; Wed, 10 Nov 2004 04:48:55 +0000 (GMT) Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 890F916A4CE for ; Wed, 10 Nov 2004 04:48:55 +0000 (GMT) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 5E5CB43D48 for ; Wed, 10 Nov 2004 04:48:55 +0000 (GMT) (envelope-from julian@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.12.11/8.12.11) with ESMTP id iAA4mt3i087313 for ; Wed, 10 Nov 2004 04:48:55 GMT (envelope-from julian@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.12.11/8.12.11/Submit) id iAA4msag087310 for perforce@freebsd.org; Wed, 10 Nov 2004 04:48:54 GMT (envelope-from julian@freebsd.org) Date: Wed, 10 Nov 2004 04:48:54 GMT Message-Id: <200411100448.iAA4msag087310@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to julian@freebsd.org using -f From: Julian Elischer To: Perforce Change Reviews Subject: PERFORCE change 64776 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 10 Nov 2004 04:48:56 -0000 http://perforce.freebsd.org/chv.cgi?CH=64776 Change 64776 by julian@julian_ref on 2004/11/10 04:48:22 IFC@64774 to get maestro changes Affected files ... .. //depot/projects/nsched/sys/dev/sound/pci/maestro.c#4 integrate .. //depot/projects/nsched/sys/dev/sound/pci/maestro_reg.h#2 integrate Differences ... ==== //depot/projects/nsched/sys/dev/sound/pci/maestro.c#4 (text+ko) ==== @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: maestro.c,v 1.18 2003/07/01 15:52:01 scottl Exp $ + * maestro.c,v 1.23.2.1 2003/10/03 18:21:38 taku Exp */ /* @@ -42,6 +42,9 @@ * were looked at by * Munehiro Matsuda , * who brought patches based on the Linux driver with some simplification. + * + * Hardware volume controller was implemented by + * John Baldwin . */ #include @@ -51,7 +54,8 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/maestro.c,v 1.24 2004/08/22 18:57:40 green Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/maestro.c,v 1.25 2004/11/10 04:29:09 julian Exp $"); + #define inline __inline @@ -70,60 +74,150 @@ #define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ #define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ -#ifndef AGG_MAXPLAYCH +#ifdef AGG_MAXPLAYCH +# if AGG_MAXPLAYCH > 4 +# undef AGG_MAXPLAYCH +# define AGG_MAXPLAYCH 4 +# endif +#else # define AGG_MAXPLAYCH 4 #endif #define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ +/* compatibility */ +#if __FreeBSD_version < 500000 +# define critical_enter() disable_intr() +# define critical_exit() enable_intr() +#endif + +#ifndef PCIR_BAR +#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) +#endif + + /* ----------------------------- * Data structures. */ struct agg_chinfo { + /* parent softc */ struct agg_info *parent; + + /* FreeBSD newpcm related */ struct pcm_channel *channel; struct snd_dbuf *buffer; - bus_addr_t offset; - u_int32_t blocksize; + + /* OS independent */ + bus_addr_t phys; /* channel buffer physical address */ + bus_addr_t base; /* channel buffer segment base */ + u_int32_t blklen; /* DMA block length in WORDs */ + u_int32_t buflen; /* channel buffer length in WORDs */ + u_int32_t speed; + unsigned num : 3; + unsigned stereo : 1; + unsigned qs16 : 1; /* quantum size is 16bit */ + unsigned us : 1; /* in unsigned format */ +}; + +struct agg_rchinfo { + /* parent softc */ + struct agg_info *parent; + + /* FreeBSD newpcm related */ + struct pcm_channel *channel; + struct snd_dbuf *buffer; + + /* OS independent */ + bus_addr_t phys; /* channel buffer physical address */ + bus_addr_t base; /* channel buffer segment base */ + u_int32_t blklen; /* DMA block length in WORDs */ + u_int32_t buflen; /* channel buffer length in WORDs */ u_int32_t speed; - int dir; - u_int num; - u_int16_t aputype; - u_int16_t wcreg_tpl; + unsigned : 3; + unsigned stereo : 1; + bus_addr_t srcphys; + int16_t *src; /* stereo peer buffer */ + int16_t *sink; /* channel buffer pointer */ + volatile u_int32_t hwptr; /* ready point in 16bit sample */ }; struct agg_info { + /* FreeBSD newbus related */ device_t dev; + + /* I wonder whether bus_space_* are in common in *BSD... */ 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; + bus_dma_tag_t buf_dmat; + bus_dma_tag_t stat_dmat; + /* FreeBSD SMPng related */ +#ifdef USING_MUTEX + struct mtx lock; /* mutual exclusion */ +#endif + /* FreeBSD newpcm related */ struct ac97_info *codec; - struct mtx *lock; - unsigned int bufsz; - u_int playchns, active; + /* OS independent */ + u_int8_t *stat; /* status buffer pointer */ + bus_addr_t phys; /* status buffer physical address */ + unsigned int bufsz; /* channel buffer size in bytes */ + u_int playchns; + volatile u_int active; struct agg_chinfo pch[AGG_MAXPLAYCH]; - struct agg_chinfo rch; + struct agg_rchinfo rch; + volatile u_int8_t curpwr; /* current power status: D[0-3] */ }; + +/* ----------------------------- + * Sysctls for debug. + */ +static unsigned int powerstate_active = PCI_POWERSTATE_D1; +#ifdef MAESTRO_AGGRESSIVE_POWERSAVE +static unsigned int powerstate_idle = PCI_POWERSTATE_D2; +#else +static unsigned int powerstate_idle = PCI_POWERSTATE_D1; +#endif +static unsigned int powerstate_init = PCI_POWERSTATE_D2; + +SYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, ""); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW, + &powerstate_active, 0, "The Dx power state when active (0-1)"); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW, + &powerstate_idle, 0, "The Dx power state when idle (0-2)"); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW, + &powerstate_init, 0, "The Dx power state prior to the first use (0-2)"); + + +/* ----------------------------- + * Prototypes + */ + +static inline void agg_lock(struct agg_info*); +static inline void agg_unlock(struct agg_info*); +static inline void agg_sleep(struct agg_info*, const char *wmesg, int msec); + +static inline u_int32_t agg_rd(struct agg_info*, int, int size); +static inline void agg_wr(struct agg_info*, int, u_int32_t data, int size); + +static inline int agg_rdcodec(struct agg_info*, int); +static inline int agg_wrcodec(struct agg_info*, 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 u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); +static inline void wp_wrapu(struct agg_info*, unsigned, 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*); @@ -133,16 +227,22 @@ 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 inline void agg_stopclock(struct agg_info*, int part, int st); +static inline void agg_initcodec(struct agg_info*); static void agg_init(struct agg_info*); +static void agg_power(struct agg_info*, int); static void aggch_start_dac(struct agg_chinfo*); static void aggch_stop_dac(struct agg_chinfo*); +static void aggch_start_adc(struct agg_rchinfo*); +static void aggch_stop_adc(struct agg_rchinfo*); +static void aggch_feed_adc_stereo(struct agg_rchinfo*); +static void aggch_feed_adc_mono(struct agg_rchinfo*); static inline void suppress_jitter(struct agg_chinfo*); +static inline void suppress_rec_jitter(struct agg_rchinfo*); -static inline u_int calc_timer_freq(struct agg_chinfo*); static void set_timer(struct agg_info*); static void agg_intr(void *); @@ -153,181 +253,260 @@ 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 *); +static void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*); +static void dma_free(bus_dma_tag_t, void *); + /* ----------------------------- * Subsystems. */ -/* Codec/Ringbus */ +/* locking */ + +static inline void +agg_lock(struct agg_info *sc) +{ +#ifdef USING_MUTEX + mtx_lock(&sc->lock); +#endif +} + +static inline void +agg_unlock(struct agg_info *sc) +{ +#ifdef USING_MUTEX + mtx_unlock(&sc->lock); +#endif +} + +static inline void +agg_sleep(struct agg_info *sc, const char *wmesg, int msec) +{ + int timo; + + timo = msec * hz / 1000; + if (timo == 0) + timo = 1; +#ifdef USING_MUTEX + msleep(sc, &sc->lock, PWAIT, wmesg, timo); +#else + tsleep(sc, PWAIT, wmesg, timo); +#endif +} + + +/* I/O port */ + +static inline u_int32_t +agg_rd(struct agg_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->st, sc->sh, regno); + case 2: + return bus_space_read_2(sc->st, sc->sh, regno); + case 4: + return bus_space_read_4(sc->st, sc->sh, regno); + default: + return ~(u_int32_t)0; + } +} + +#define AGG_RD(sc, regno, size) \ + bus_space_read_##size( \ + ((struct agg_info*)(sc))->st, \ + ((struct agg_info*)(sc))->sh, (regno)) + +static inline void +agg_wr(struct agg_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->st, sc->sh, regno, data); + break; + case 2: + bus_space_write_2(sc->st, sc->sh, regno, data); + break; + case 4: + bus_space_write_4(sc->st, sc->sh, regno, data); + break; + } +} + +#define AGG_WR(sc, regno, data, size) \ + bus_space_write_##size( \ + ((struct agg_info*)(sc))->st, \ + ((struct agg_info*)(sc))->sh, (regno), (data)) /* -------------------------------------------------------------------- */ -static u_int32_t -agg_ac97_init(kobj_t obj, void *sc) +/* Codec/Ringbus */ + +static inline int +agg_codec_wait4idle(struct agg_info *ess) { - struct agg_info *ess = sc; + unsigned t = 26; - return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; + while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { + if (--t == 0) + return EBUSY; + DELAY(2); /* 20.8us / 13 */ + } + return 0; } -static int -agg_rdcodec(kobj_t obj, void *sc, int regno) + +static inline int +agg_rdcodec(struct agg_info *ess, int regno) { - struct agg_info *ess = sc; - unsigned t; + int ret; /* 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 (agg_codec_wait4idle(ess)) { + /* Timed out. No read performed. */ + device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); + return -1; } - 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 */ + AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); + /*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 (!agg_codec_wait4idle(ess)) { + ret = AGG_RD(ess, PORT_CODEC_REG, 2); + } else { + /* Timed out. No read performed. */ + device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); + ret = -1; } - 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); + return ret; } -static int -agg_wrcodec(kobj_t obj, void *sc, int regno, u_int32_t data) +static inline int +agg_wrcodec(struct agg_info *ess, 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) { + if (agg_codec_wait4idle(ess)) { /* Timed out. Abort writing. */ device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); return -1; } - 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); + AGG_WR(ess, PORT_CODEC_REG, data, 2); + AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); + + /* Wait for write completion */ + if (agg_codec_wait4idle(ess)) { + /* Timed out. */ + device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); + return -1; + } return 0; } -static kobj_method_t agg_ac97_methods[] = { - KOBJMETHOD(ac97_init, agg_ac97_init), - KOBJMETHOD(ac97_read, agg_rdcodec), - KOBJMETHOD(ac97_write, agg_wrcodec), - { 0, 0 } -}; -AC97_DECLARE(agg_ac97); - -/* -------------------------------------------------------------------- */ - 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 = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); data &= ~(0xfU << src); data |= (0xfU & dest) << src; - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); + AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); } +/* -------------------------------------------------------------------- */ + /* 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); + AGG_WR(ess, PORT_DSP_INDEX, reg, 2); + return AGG_RD(ess, PORT_DSP_DATA, 2); } 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); + AGG_WR(ess, PORT_DSP_INDEX, reg, 2); + AGG_WR(ess, PORT_DSP_DATA, data, 2); } -static inline void -apu_setindex(struct agg_info *ess, u_int16_t reg) +static inline int +wp_wait_data(struct agg_info *ess, u_int16_t data) { - int t; + unsigned t = 0; - 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); + while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { + if (++t == 1000) { + return EAGAIN; + } + AGG_WR(ess, PORT_DSP_DATA, data, 2); } - if (t == 1000) - device_printf(ess->dev, "apu_setindex() timed out.\n"); + + return 0; } static inline u_int16_t -wp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) +wp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg) { - u_int16_t ret; + wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); + if (wp_wait_data(ess, reg | (ch << 4)) != 0) + device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); + return wp_rdreg(ess, WPREG_DATA_PORT); +} - 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, unsigned ch, u_int16_t reg, u_int16_t data) +{ + wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); + if (wp_wait_data(ess, reg | (ch << 4)) == 0) { + wp_wrreg(ess, WPREG_DATA_PORT, data); + if (wp_wait_data(ess, data) != 0) + device_printf(ess->dev, "wp_wrapu() write timed out.\n"); + } else { + device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); + } } static inline void -wp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) +apu_setparam(struct agg_info *ess, int apuch, + u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) { - 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"); + wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); + wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); + wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); + wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); + wp_wrapu(ess, apuch, APUREG_ROUTING, 0); + wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); + wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 + | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) + | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); + wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, + APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); + wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); } static inline void -wp_settimer(struct agg_info *ess, u_int freq) +wp_settimer(struct agg_info *ess, u_int divide) { - u_int clock = 48000 << 2; - u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; + u_int prescale = 0; - RANGE(divide, 4, 32 << 8); + RANGE(divide, 2, 32 << 7); - for (; divide > 32 << 1; divide >>= 1) + for (; divide > 32; divide >>= 1) { prescale++; - divide = (divide + 1) >> 1; + divide++; + } for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) prescale++; wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); - wp_wrreg(ess, WPREG_TIMER_FREQ, + wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); } @@ -335,30 +514,37 @@ static inline void wp_starttimer(struct agg_info *ess) { + AGG_WR(ess, PORT_INT_STAT, 1, 2); + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED + | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); wp_wrreg(ess, WPREG_TIMER_START, 1); } static inline void wp_stoptimer(struct agg_info *ess) { + AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED + & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); + AGG_WR(ess, PORT_INT_STAT, 1, 2); 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); + AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); + return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); } 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); + AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); + AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); } static inline u_int16_t @@ -373,16 +559,26 @@ wc_wrreg(ess, ch << 3, data); } +/* -------------------------------------------------------------------- */ + /* Power management */ - static inline void -agg_power(struct agg_info *ess, int status) +agg_stopclock(struct agg_info *ess, int part, int st) { - u_int8_t data; + u_int32_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); + data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); + if (part < 16) { + if (st == PCI_POWERSTATE_D1) + data &= ~(1 << part); + else + data |= (1 << part); + if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) + data |= (0x10000 << part); + else + data &= ~(0x10000 << part); + pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); + } } @@ -395,46 +591,38 @@ { 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); + if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 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); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); DELAY(2); - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, - RINGBUS_CTRL_ACLINK_ENABLED); - DELAY(21); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); + DELAY(50); - agg_rdcodec(NULL, 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); + if (agg_rdcodec(ess, 0) < 0) { + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 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); + data = AGG_RD(ess, PORT_GPIO_DIR, 2); 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); + data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); + AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); + AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); + AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); DELAY(2); - bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); + AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); 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); + AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); + agg_sleep(ess, "agginicd", 500); + AGG_WR(ess, PORT_GPIO_DIR, data, 2); DELAY(84); /* 20.8us * 4 */ - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, - RINGBUS_CTRL_ACLINK_ENABLED); - DELAY(21); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); + DELAY(50); } } @@ -455,47 +643,93 @@ * Prefer PCI timing rather than that of ISA. * Don't swap L/R. */ data = pci_read_config(ess->dev, CONF_MAESTRO, 4); + data |= MAESTRO_PMC; data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; data &= ~MAESTRO_SWAP_LR; pci_write_config(ess->dev, CONF_MAESTRO, data, 4); + /* Turn off unused parts if necessary. */ + /* consult CONF_MAESTRO. */ + if (data & MAESTRO_SPDIF) + agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); + else + agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); + if (data & MAESTRO_HWVOL) + agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); + else + agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); + + /* parts that never be used */ + agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); + + /* parts that will be used only when play/recording */ + agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); + + /* parts that should always be turned on */ + agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); + /* 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); + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + DELAY(100); - /* Enable direct sound interruption and hardware volume control. */ - bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, - HOSTINT_CTRL_DSOUND_INT_ENABLED | HOSTINT_CTRL_HWVOL_ENABLED); + /* Enable hardware volume control interruption. */ + if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ + AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); /* 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); + wp_wrreg(ess, WPREG_CRAM_DATA, 0); + + AGG_WR(ess, PORT_WAVCACHE_CTRL, + WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) - wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); + wc_wrreg(ess, data, ess->phys >> 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); + AGG_WR(ess, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); - wp_wrreg(ess, WPREG_BASE, 0x8500); /* Parallel I/O */ + wp_wrreg(ess, 0x08, 0xB004); + wp_wrreg(ess, 0x09, 0x001B); + wp_wrreg(ess, 0x0A, 0x8000); + wp_wrreg(ess, 0x0B, 0x3F37); + wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ + wp_wrreg(ess, WPREG_BASE + 1, 0x7632); 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); + /* Enable S/PDIF if necessary. */ + if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) + /* XXX - why not use device flags? */ + AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | + AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); + /* 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); + AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); + AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); + AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); /* * Setup GPIO. @@ -507,84 +741,400 @@ 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); + AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); + AGG_WR(ess, PORT_GPIO_DIR, + AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); + AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); + break; + } +} + +/* Deals power state transition. Must be called with softc->lock held. */ +static void +agg_power(struct agg_info *ess, int status) +{ + u_int8_t lastpwr; + + lastpwr = ess->curpwr; + if (lastpwr == status) + return; + + switch (status) { + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + switch (lastpwr) { + case PCI_POWERSTATE_D2: + pci_set_powerstate(ess->dev, status); + /* Turn on PCM-related parts. */ + agg_wrcodec(ess, AC97_REG_POWER, 0); + DELAY(100); +#if 0 + if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) + device_printf(ess->dev, "warning: codec not ready.\n"); +#endif + AGG_WR(ess, PORT_RINGBUS_CTRL, + (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + & ~RINGBUS_CTRL_ACLINK_ENABLED) + | RINGBUS_CTRL_RINGBUS_ENABLED, 4); + DELAY(50); + AGG_WR(ess, PORT_RINGBUS_CTRL, + AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + | RINGBUS_CTRL_ACLINK_ENABLED, 4); + break; + case PCI_POWERSTATE_D3: + /* Initialize. */ + pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); + DELAY(100); + agg_init(ess); + /* FALLTHROUGH */ + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + pci_set_powerstate(ess->dev, status); + break; + } + break; + case PCI_POWERSTATE_D2: + switch (lastpwr) { + case PCI_POWERSTATE_D3: + /* Initialize. */ + pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); + DELAY(100); + agg_init(ess); + /* FALLTHROUGH */ + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + /* Turn off PCM-related parts. */ + AGG_WR(ess, PORT_RINGBUS_CTRL, + AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); + DELAY(100); + agg_wrcodec(ess, AC97_REG_POWER, 0x300); + DELAY(100); + break; + } + pci_set_powerstate(ess->dev, status); + break; + case PCI_POWERSTATE_D3: + /* Entirely power down. */ + agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); + DELAY(100); + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); + /*DELAY(1);*/ + if (lastpwr != PCI_POWERSTATE_D2) + wp_stoptimer(ess); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); + pci_set_powerstate(ess->dev, status); + break; + default: + /* Invalid power state; let it ignored. */ + status = lastpwr; break; } + + ess->curpwr = status; } +/* -------------------------------------------------------------------- */ + /* Channel controller. */ static void aggch_start_dac(struct agg_chinfo *ch) { - bus_addr_t wpwa = APU_USE_SYSMEM | (ch->offset >> 9); - u_int size = ch->parent->bufsz >> 1; - u_int speed = ch->speed; - bus_addr_t offset = ch->offset >> 1; - u_int cp = 0; - u_int16_t apuch = ch->num << 1; - u_int dv; - int pan = 0; + bus_addr_t wpwa; + u_int32_t speed; + u_int16_t size, apuch, wtbar, wcreg, aputype; + u_int dv; + int pan; + + speed = ch->speed; + wpwa = (ch->phys - ch->base) >> 1; + wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); + wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + size = ch->buflen; + apuch = (ch->num << 1) | 32; + pan = PAN_RIGHT - PAN_FRONT; - 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; + if (ch->stereo) { + wcreg |= WAVCACHE_CHCTL_STEREO; + if (ch->qs16) { + aputype = APUTYPE_16BITSTEREO; + wpwa >>= 1; + size >>= 1; + pan = -pan; + } else + aputype = APUTYPE_8BITSTEREO; + } else { + pan = 0; >>> TRUNCATED FOR MAIL (1000 lines) <<<