maestro.c revision 141095
165543Scg/*- 2137573Sru * Copyright (c) 2000-2004 Taku YAMAMOTO <taku@tackymt.homeip.net> 365543Scg * All rights reserved. 465543Scg * 565543Scg * Redistribution and use in source and binary forms, with or without 665543Scg * modification, are permitted provided that the following conditions 765543Scg * are met: 865543Scg * 1. Redistributions of source code must retain the above copyright 965543Scg * notice, this list of conditions and the following disclaimer. 1065543Scg * 2. Redistributions in binary form must reproduce the above copyright 1165543Scg * notice, this list of conditions and the following disclaimer in the 1265543Scg * documentation and/or other materials provided with the distribution. 1365543Scg * 1465543Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1565543Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1665543Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1765543Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1865543Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1965543Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2065543Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2165543Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2265543Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2365543Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2465543Scg * SUCH DAMAGE. 2565543Scg * 26137500Sjulian * maestro.c,v 1.23.2.1 2003/10/03 18:21:38 taku Exp 2765543Scg */ 2865543Scg 2965543Scg/* 3065543Scg * Credits: 3165543Scg * 3265543Scg * Part of this code (especially in many magic numbers) was heavily inspired 3365543Scg * by the Linux driver originally written by 3465543Scg * Alan Cox <alan.cox@linux.org>, modified heavily by 3565543Scg * Zach Brown <zab@zabbo.net>. 3665543Scg * 3765543Scg * busdma()-ize and buffer size reduction were suggested by 38119853Scg * Cameron Grant <cg@freebsd.org>. 3965543Scg * Also he showed me the way to use busdma() suite. 4065543Scg * 4165543Scg * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 4265543Scg * were looked at by 4365543Scg * Munehiro Matsuda <haro@tk.kubota.co.jp>, 4465543Scg * who brought patches based on the Linux driver with some simplification. 45137500Sjulian * 46137500Sjulian * Hardware volume controller was implemented by 47137500Sjulian * John Baldwin <jhb@freebsd.org>. 4865543Scg */ 4965543Scg 5065543Scg#include <dev/sound/pcm/sound.h> 5165543Scg#include <dev/sound/pcm/ac97.h> 52119287Simp#include <dev/pci/pcireg.h> 53119287Simp#include <dev/pci/pcivar.h> 5465543Scg 5565543Scg#include <dev/sound/pci/maestro_reg.h> 5665543Scg 5782180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/maestro.c 141095 2005-02-01 07:43:34Z imp $"); 5882180Scg 59137500Sjulian 6065543Scg#define inline __inline 6165543Scg 6265543Scg/* 6365543Scg * PCI IDs of supported chips: 6465543Scg * 6565543Scg * MAESTRO-1 0x01001285 6665543Scg * MAESTRO-2 0x1968125d 6765543Scg * MAESTRO-2E 0x1978125d 6865543Scg */ 6965543Scg 7065543Scg#define MAESTRO_1_PCI_ID 0x01001285 7165543Scg#define MAESTRO_2_PCI_ID 0x1968125d 7265543Scg#define MAESTRO_2E_PCI_ID 0x1978125d 7365543Scg 7465543Scg#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ 7565543Scg#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ 7665543Scg 77137500Sjulian#ifdef AGG_MAXPLAYCH 78137500Sjulian# if AGG_MAXPLAYCH > 4 79137500Sjulian# undef AGG_MAXPLAYCH 80137500Sjulian# define AGG_MAXPLAYCH 4 81137500Sjulian# endif 82137500Sjulian#else 8365543Scg# define AGG_MAXPLAYCH 4 8465543Scg#endif 8565543Scg 8684658Scg#define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ 8765543Scg 8865543Scg 89137500Sjulian/* compatibility */ 90137500Sjulian#if __FreeBSD_version < 500000 91137500Sjulian# define critical_enter() disable_intr() 92137500Sjulian# define critical_exit() enable_intr() 93137500Sjulian#endif 94137500Sjulian 95137500Sjulian#ifndef PCIR_BAR 96137500Sjulian#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) 97137500Sjulian#endif 98137500Sjulian 99137500Sjulian 10065543Scg/* ----------------------------- 10165543Scg * Data structures. 10265543Scg */ 10365543Scgstruct agg_chinfo { 104137500Sjulian /* parent softc */ 10565543Scg struct agg_info *parent; 106137500Sjulian 107137500Sjulian /* FreeBSD newpcm related */ 10874763Scg struct pcm_channel *channel; 10974763Scg struct snd_dbuf *buffer; 110137500Sjulian 111137500Sjulian /* OS independent */ 112137500Sjulian bus_addr_t phys; /* channel buffer physical address */ 113137500Sjulian bus_addr_t base; /* channel buffer segment base */ 114137500Sjulian u_int32_t blklen; /* DMA block length in WORDs */ 115137500Sjulian u_int32_t buflen; /* channel buffer length in WORDs */ 11670291Scg u_int32_t speed; 117137500Sjulian unsigned num : 3; 118137500Sjulian unsigned stereo : 1; 119137500Sjulian unsigned qs16 : 1; /* quantum size is 16bit */ 120137500Sjulian unsigned us : 1; /* in unsigned format */ 12165543Scg}; 12265543Scg 123137500Sjulianstruct agg_rchinfo { 124137500Sjulian /* parent softc */ 125137500Sjulian struct agg_info *parent; 126137500Sjulian 127137500Sjulian /* FreeBSD newpcm related */ 128137500Sjulian struct pcm_channel *channel; 129137500Sjulian struct snd_dbuf *buffer; 130137500Sjulian 131137500Sjulian /* OS independent */ 132137500Sjulian bus_addr_t phys; /* channel buffer physical address */ 133137500Sjulian bus_addr_t base; /* channel buffer segment base */ 134137500Sjulian u_int32_t blklen; /* DMA block length in WORDs */ 135137500Sjulian u_int32_t buflen; /* channel buffer length in WORDs */ 136137500Sjulian u_int32_t speed; 137137500Sjulian unsigned : 3; 138137500Sjulian unsigned stereo : 1; 139137500Sjulian bus_addr_t srcphys; 140137500Sjulian int16_t *src; /* stereo peer buffer */ 141137500Sjulian int16_t *sink; /* channel buffer pointer */ 142137500Sjulian volatile u_int32_t hwptr; /* ready point in 16bit sample */ 143137500Sjulian}; 144137500Sjulian 14565543Scgstruct agg_info { 146137500Sjulian /* FreeBSD newbus related */ 14765543Scg device_t dev; 148137500Sjulian 149137500Sjulian /* I wonder whether bus_space_* are in common in *BSD... */ 15065543Scg struct resource *reg; 15165543Scg int regid; 15265543Scg bus_space_tag_t st; 15365543Scg bus_space_handle_t sh; 15465543Scg 15565543Scg struct resource *irq; 15665543Scg int irqid; 15765543Scg void *ih; 15865543Scg 159137500Sjulian bus_dma_tag_t buf_dmat; 160137500Sjulian bus_dma_tag_t stat_dmat; 16165543Scg 162137500Sjulian /* FreeBSD SMPng related */ 163137500Sjulian#ifdef USING_MUTEX 164137500Sjulian struct mtx lock; /* mutual exclusion */ 165137500Sjulian#endif 166137500Sjulian /* FreeBSD newpcm related */ 16765543Scg struct ac97_info *codec; 16865543Scg 169137500Sjulian /* OS independent */ 170137500Sjulian u_int8_t *stat; /* status buffer pointer */ 171137500Sjulian bus_addr_t phys; /* status buffer physical address */ 172137500Sjulian unsigned int bufsz; /* channel buffer size in bytes */ 173137500Sjulian u_int playchns; 174137500Sjulian volatile u_int active; 17565543Scg struct agg_chinfo pch[AGG_MAXPLAYCH]; 176137500Sjulian struct agg_rchinfo rch; 177137500Sjulian volatile u_int8_t curpwr; /* current power status: D[0-3] */ 17865543Scg}; 17965543Scg 180137500Sjulian 181137500Sjulian/* ----------------------------- 182137500Sjulian * Sysctls for debug. 183137500Sjulian */ 184137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1; 185137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE 186137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D2; 187137500Sjulian#else 188137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D1; 189137500Sjulian#endif 190137500Sjulianstatic unsigned int powerstate_init = PCI_POWERSTATE_D2; 191137500Sjulian 192137500SjulianSYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, ""); 193137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW, 194137500Sjulian &powerstate_active, 0, "The Dx power state when active (0-1)"); 195137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW, 196137500Sjulian &powerstate_idle, 0, "The Dx power state when idle (0-2)"); 197137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW, 198137500Sjulian &powerstate_init, 0, "The Dx power state prior to the first use (0-2)"); 199137500Sjulian 200137500Sjulian 201137500Sjulian/* ----------------------------- 202137500Sjulian * Prototypes 203137500Sjulian */ 204137500Sjulian 205137500Sjulianstatic inline void agg_lock(struct agg_info*); 206137500Sjulianstatic inline void agg_unlock(struct agg_info*); 207137500Sjulianstatic inline void agg_sleep(struct agg_info*, const char *wmesg, int msec); 208137500Sjulian 209137500Sjulianstatic inline u_int32_t agg_rd(struct agg_info*, int, int size); 210137500Sjulianstatic inline void agg_wr(struct agg_info*, int, u_int32_t data, int size); 211137500Sjulian 212137500Sjulianstatic inline int agg_rdcodec(struct agg_info*, int); 213137500Sjulianstatic inline int agg_wrcodec(struct agg_info*, int, u_int32_t); 214137500Sjulian 21565543Scgstatic inline void ringbus_setdest(struct agg_info*, int, int); 21665543Scg 21765543Scgstatic inline u_int16_t wp_rdreg(struct agg_info*, u_int16_t); 21865543Scgstatic inline void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); 219137500Sjulianstatic inline u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); 220137500Sjulianstatic inline void wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t); 22165543Scgstatic inline void wp_settimer(struct agg_info*, u_int); 22265543Scgstatic inline void wp_starttimer(struct agg_info*); 22365543Scgstatic inline void wp_stoptimer(struct agg_info*); 22465543Scg 22565543Scgstatic inline u_int16_t wc_rdreg(struct agg_info*, u_int16_t); 22665543Scgstatic inline void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); 22765543Scgstatic inline u_int16_t wc_rdchctl(struct agg_info*, int); 22865543Scgstatic inline void wc_wrchctl(struct agg_info*, int, u_int16_t); 22965543Scg 230137500Sjulianstatic inline void agg_stopclock(struct agg_info*, int part, int st); 23165543Scg 232137500Sjulianstatic inline void agg_initcodec(struct agg_info*); 23365543Scgstatic void agg_init(struct agg_info*); 234137500Sjulianstatic void agg_power(struct agg_info*, int); 23565543Scg 23665543Scgstatic void aggch_start_dac(struct agg_chinfo*); 23765543Scgstatic void aggch_stop_dac(struct agg_chinfo*); 238137500Sjulianstatic void aggch_start_adc(struct agg_rchinfo*); 239137500Sjulianstatic void aggch_stop_adc(struct agg_rchinfo*); 240137500Sjulianstatic void aggch_feed_adc_stereo(struct agg_rchinfo*); 241137500Sjulianstatic void aggch_feed_adc_mono(struct agg_rchinfo*); 24265543Scg 24365543Scgstatic inline void suppress_jitter(struct agg_chinfo*); 244137500Sjulianstatic inline void suppress_rec_jitter(struct agg_rchinfo*); 24565543Scg 24665543Scgstatic void set_timer(struct agg_info*); 24765543Scg 24865543Scgstatic void agg_intr(void *); 24965543Scgstatic int agg_probe(device_t); 25065543Scgstatic int agg_attach(device_t); 25165543Scgstatic int agg_detach(device_t); 25265543Scgstatic int agg_suspend(device_t); 25365543Scgstatic int agg_resume(device_t); 25465543Scgstatic int agg_shutdown(device_t); 25565543Scg 256137500Sjulianstatic void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*); 257137500Sjulianstatic void dma_free(bus_dma_tag_t, void *); 25865543Scg 259137500Sjulian 26065543Scg/* ----------------------------- 26165543Scg * Subsystems. 26265543Scg */ 26365543Scg 264137500Sjulian/* locking */ 26565543Scg 266137500Sjulianstatic inline void 267137500Sjulianagg_lock(struct agg_info *sc) 268137500Sjulian{ 269137500Sjulian#ifdef USING_MUTEX 270137500Sjulian mtx_lock(&sc->lock); 271137500Sjulian#endif 272137500Sjulian} 273137500Sjulian 274137500Sjulianstatic inline void 275137500Sjulianagg_unlock(struct agg_info *sc) 276137500Sjulian{ 277137500Sjulian#ifdef USING_MUTEX 278137500Sjulian mtx_unlock(&sc->lock); 279137500Sjulian#endif 280137500Sjulian} 281137500Sjulian 282137500Sjulianstatic inline void 283137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec) 284137500Sjulian{ 285137500Sjulian int timo; 286137500Sjulian 287137500Sjulian timo = msec * hz / 1000; 288137500Sjulian if (timo == 0) 289137500Sjulian timo = 1; 290137500Sjulian#ifdef USING_MUTEX 291137500Sjulian msleep(sc, &sc->lock, PWAIT, wmesg, timo); 292137500Sjulian#else 293137500Sjulian tsleep(sc, PWAIT, wmesg, timo); 294137500Sjulian#endif 295137500Sjulian} 296137500Sjulian 297137500Sjulian 298137500Sjulian/* I/O port */ 299137500Sjulian 300137500Sjulianstatic inline u_int32_t 301137500Sjulianagg_rd(struct agg_info *sc, int regno, int size) 302137500Sjulian{ 303137500Sjulian switch (size) { 304137500Sjulian case 1: 305137500Sjulian return bus_space_read_1(sc->st, sc->sh, regno); 306137500Sjulian case 2: 307137500Sjulian return bus_space_read_2(sc->st, sc->sh, regno); 308137500Sjulian case 4: 309137500Sjulian return bus_space_read_4(sc->st, sc->sh, regno); 310137500Sjulian default: 311137500Sjulian return ~(u_int32_t)0; 312137500Sjulian } 313137500Sjulian} 314137500Sjulian 315137500Sjulian#define AGG_RD(sc, regno, size) \ 316137500Sjulian bus_space_read_##size( \ 317137500Sjulian ((struct agg_info*)(sc))->st, \ 318137500Sjulian ((struct agg_info*)(sc))->sh, (regno)) 319137500Sjulian 320137500Sjulianstatic inline void 321137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size) 322137500Sjulian{ 323137500Sjulian switch (size) { 324137500Sjulian case 1: 325137500Sjulian bus_space_write_1(sc->st, sc->sh, regno, data); 326137500Sjulian break; 327137500Sjulian case 2: 328137500Sjulian bus_space_write_2(sc->st, sc->sh, regno, data); 329137500Sjulian break; 330137500Sjulian case 4: 331137500Sjulian bus_space_write_4(sc->st, sc->sh, regno, data); 332137500Sjulian break; 333137500Sjulian } 334137500Sjulian} 335137500Sjulian 336137500Sjulian#define AGG_WR(sc, regno, data, size) \ 337137500Sjulian bus_space_write_##size( \ 338137500Sjulian ((struct agg_info*)(sc))->st, \ 339137500Sjulian ((struct agg_info*)(sc))->sh, (regno), (data)) 340137500Sjulian 34170134Scg/* -------------------------------------------------------------------- */ 34270134Scg 343137500Sjulian/* Codec/Ringbus */ 344137500Sjulian 345137500Sjulianstatic inline int 346137500Sjulianagg_codec_wait4idle(struct agg_info *ess) 34765543Scg{ 348137500Sjulian unsigned t = 26; 34970134Scg 350137500Sjulian while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { 351137500Sjulian if (--t == 0) 352137500Sjulian return EBUSY; 353137500Sjulian DELAY(2); /* 20.8us / 13 */ 354137500Sjulian } 355137500Sjulian return 0; 35670134Scg} 35770134Scg 358137500Sjulian 359137500Sjulianstatic inline int 360137500Sjulianagg_rdcodec(struct agg_info *ess, int regno) 36170134Scg{ 362137500Sjulian int ret; 36365543Scg 36465543Scg /* We have to wait for a SAFE time to write addr/data */ 365137500Sjulian if (agg_codec_wait4idle(ess)) { 366137500Sjulian /* Timed out. No read performed. */ 367137500Sjulian device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); 368137500Sjulian return -1; 36965543Scg } 37065543Scg 371137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); 372137500Sjulian /*DELAY(21); * AC97 cycle = 20.8usec */ 37365543Scg 37465543Scg /* Wait for data retrieve */ 375137500Sjulian if (!agg_codec_wait4idle(ess)) { 376137500Sjulian ret = AGG_RD(ess, PORT_CODEC_REG, 2); 377137500Sjulian } else { 378137500Sjulian /* Timed out. No read performed. */ 379137500Sjulian device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); 380137500Sjulian ret = -1; 38165543Scg } 38265543Scg 383137500Sjulian return ret; 38465543Scg} 38565543Scg 386137500Sjulianstatic inline int 387137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data) 38865543Scg{ 38965543Scg /* We have to wait for a SAFE time to write addr/data */ 390137500Sjulian if (agg_codec_wait4idle(ess)) { 39165543Scg /* Timed out. Abort writing. */ 39265543Scg device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); 39370134Scg return -1; 39465543Scg } 39565543Scg 396137500Sjulian AGG_WR(ess, PORT_CODEC_REG, data, 2); 397137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); 39870134Scg 399137500Sjulian /* Wait for write completion */ 400137500Sjulian if (agg_codec_wait4idle(ess)) { 401137500Sjulian /* Timed out. */ 402137500Sjulian device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); 403137500Sjulian return -1; 404137500Sjulian } 405137500Sjulian 40670134Scg return 0; 40765543Scg} 40865543Scg 40965543Scgstatic inline void 41065543Scgringbus_setdest(struct agg_info *ess, int src, int dest) 41165543Scg{ 41265543Scg u_int32_t data; 41365543Scg 414137500Sjulian data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); 41565543Scg data &= ~(0xfU << src); 41665543Scg data |= (0xfU & dest) << src; 417137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); 41865543Scg} 41965543Scg 420137500Sjulian/* -------------------------------------------------------------------- */ 421137500Sjulian 42265543Scg/* Wave Processor */ 42365543Scg 42465543Scgstatic inline u_int16_t 42565543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg) 42665543Scg{ 427137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 428137500Sjulian return AGG_RD(ess, PORT_DSP_DATA, 2); 42965543Scg} 43065543Scg 43165543Scgstatic inline void 43265543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 43365543Scg{ 434137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 435137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 43665543Scg} 43765543Scg 438137500Sjulianstatic inline int 439137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data) 44065543Scg{ 441137500Sjulian unsigned t = 0; 44265543Scg 443137500Sjulian while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { 444137500Sjulian if (++t == 1000) { 445137500Sjulian return EAGAIN; 446137500Sjulian } 447137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 44865543Scg } 449137500Sjulian 450137500Sjulian return 0; 45165543Scg} 45265543Scg 45365543Scgstatic inline u_int16_t 454137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg) 45565543Scg{ 456137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 457137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) != 0) 458137500Sjulian device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); 459137500Sjulian return wp_rdreg(ess, WPREG_DATA_PORT); 46065543Scg} 46165543Scg 46265543Scgstatic inline void 463137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data) 46465543Scg{ 465137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 466137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) == 0) { 467137500Sjulian wp_wrreg(ess, WPREG_DATA_PORT, data); 468137500Sjulian if (wp_wait_data(ess, data) != 0) 469137500Sjulian device_printf(ess->dev, "wp_wrapu() write timed out.\n"); 470137500Sjulian } else { 471137500Sjulian device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); 47265543Scg } 47365543Scg} 47465543Scg 475137573Srustatic void 476137500Sjulianapu_setparam(struct agg_info *ess, int apuch, 477137500Sjulian u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) 47865543Scg{ 479137500Sjulian wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); 480137500Sjulian wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); 481137500Sjulian wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); 482137500Sjulian wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); 483137500Sjulian wp_wrapu(ess, apuch, APUREG_ROUTING, 0); 484137500Sjulian wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); 485137500Sjulian wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 486137500Sjulian | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) 487137500Sjulian | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); 488137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, 489137500Sjulian APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 490137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); 491137500Sjulian} 49265543Scg 493137500Sjulianstatic inline void 494137500Sjulianwp_settimer(struct agg_info *ess, u_int divide) 495137500Sjulian{ 496137500Sjulian u_int prescale = 0; 49765543Scg 498137500Sjulian RANGE(divide, 2, 32 << 7); 499137500Sjulian 500137500Sjulian for (; divide > 32; divide >>= 1) { 50165543Scg prescale++; 502137500Sjulian divide++; 503137500Sjulian } 50465543Scg 50565543Scg for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 50665543Scg prescale++; 50765543Scg 50865543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 509137500Sjulian wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | 51065543Scg (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 51165543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 51265543Scg} 51365543Scg 51465543Scgstatic inline void 51565543Scgwp_starttimer(struct agg_info *ess) 51665543Scg{ 517137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 518137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED 519137500Sjulian | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 52065543Scg wp_wrreg(ess, WPREG_TIMER_START, 1); 52165543Scg} 52265543Scg 52365543Scgstatic inline void 52465543Scgwp_stoptimer(struct agg_info *ess) 52565543Scg{ 526137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED 527137500Sjulian & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 528137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 52965543Scg wp_wrreg(ess, WPREG_TIMER_START, 0); 53065543Scg} 53165543Scg 532137500Sjulian/* -------------------------------------------------------------------- */ 533137500Sjulian 53465543Scg/* WaveCache */ 53565543Scg 53665543Scgstatic inline u_int16_t 53765543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg) 53865543Scg{ 539137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 540137500Sjulian return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); 54165543Scg} 54265543Scg 54365543Scgstatic inline void 54465543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 54565543Scg{ 546137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 547137500Sjulian AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); 54865543Scg} 54965543Scg 55065543Scgstatic inline u_int16_t 55165543Scgwc_rdchctl(struct agg_info *ess, int ch) 55265543Scg{ 55365543Scg return wc_rdreg(ess, ch << 3); 55465543Scg} 55565543Scg 55665543Scgstatic inline void 55765543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) 55865543Scg{ 55965543Scg wc_wrreg(ess, ch << 3, data); 56065543Scg} 56165543Scg 562137500Sjulian/* -------------------------------------------------------------------- */ 563137500Sjulian 56465543Scg/* Power management */ 56565543Scgstatic inline void 566137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st) 56765543Scg{ 568137500Sjulian u_int32_t data; 56965543Scg 570137500Sjulian data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); 571137500Sjulian if (part < 16) { 572137500Sjulian if (st == PCI_POWERSTATE_D1) 573137500Sjulian data &= ~(1 << part); 574137500Sjulian else 575137500Sjulian data |= (1 << part); 576137500Sjulian if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) 577137500Sjulian data |= (0x10000 << part); 578137500Sjulian else 579137500Sjulian data &= ~(0x10000 << part); 580137500Sjulian pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); 581137500Sjulian } 58265543Scg} 58365543Scg 58465543Scg 58565543Scg/* ----------------------------- 58665543Scg * Controller. 58765543Scg */ 58865543Scg 58965543Scgstatic inline void 59065543Scgagg_initcodec(struct agg_info* ess) 59165543Scg{ 59265543Scg u_int16_t data; 59365543Scg 594137500Sjulian if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { 595137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 59665543Scg DELAY(104); /* 20.8us * (4 + 1) */ 59765543Scg } 59865543Scg /* XXX - 2nd codec should be looked at. */ 599137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); 60065543Scg DELAY(2); 601137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 602137500Sjulian DELAY(50); 60365543Scg 604137500Sjulian if (agg_rdcodec(ess, 0) < 0) { 605137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 60665543Scg DELAY(21); 60765543Scg 60865543Scg /* Try cold reset. */ 60965543Scg device_printf(ess->dev, "will perform cold reset.\n"); 610137500Sjulian data = AGG_RD(ess, PORT_GPIO_DIR, 2); 61165543Scg if (pci_read_config(ess->dev, 0x58, 2) & 1) 61265543Scg data |= 0x10; 613137500Sjulian data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); 614137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); 615137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); 616137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); 61765543Scg DELAY(2); 618137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); 61965543Scg DELAY(1); 620137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); 621137500Sjulian agg_sleep(ess, "agginicd", 500); 622137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data, 2); 62365543Scg DELAY(84); /* 20.8us * 4 */ 624137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 625137500Sjulian DELAY(50); 62665543Scg } 62765543Scg} 62865543Scg 62965543Scgstatic void 63065543Scgagg_init(struct agg_info* ess) 63165543Scg{ 63265543Scg u_int32_t data; 63365543Scg 63465543Scg /* Setup PCI config registers. */ 63565543Scg 63665543Scg /* Disable all legacy emulations. */ 63765543Scg data = pci_read_config(ess->dev, CONF_LEGACY, 2); 63865543Scg data |= LEGACY_DISABLED; 63965543Scg pci_write_config(ess->dev, CONF_LEGACY, data, 2); 64065543Scg 64165543Scg /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 64265543Scg * Enable posted write. 64365543Scg * Prefer PCI timing rather than that of ISA. 64465543Scg * Don't swap L/R. */ 64565543Scg data = pci_read_config(ess->dev, CONF_MAESTRO, 4); 646137500Sjulian data |= MAESTRO_PMC; 64765543Scg data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 64865543Scg data &= ~MAESTRO_SWAP_LR; 64965543Scg pci_write_config(ess->dev, CONF_MAESTRO, data, 4); 65065543Scg 651137500Sjulian /* Turn off unused parts if necessary. */ 652137500Sjulian /* consult CONF_MAESTRO. */ 653137500Sjulian if (data & MAESTRO_SPDIF) 654137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); 655137500Sjulian else 656137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); 657137500Sjulian if (data & MAESTRO_HWVOL) 658137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); 659137500Sjulian else 660137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); 661137500Sjulian 662137500Sjulian /* parts that never be used */ 663137500Sjulian agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); 664137500Sjulian agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); 665137500Sjulian agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); 666137500Sjulian agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); 667137500Sjulian agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); 668137500Sjulian agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); 669137500Sjulian agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); 670137500Sjulian 671137500Sjulian /* parts that will be used only when play/recording */ 672137500Sjulian agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); 673137500Sjulian 674137500Sjulian /* parts that should always be turned on */ 675137500Sjulian agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); 676137500Sjulian agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); 677137500Sjulian agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); 678137500Sjulian agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); 679137500Sjulian 68065543Scg /* Reset direct sound. */ 681137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); 682137500Sjulian DELAY(100); 683137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 684137500Sjulian DELAY(100); 685137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); 686137500Sjulian DELAY(100); 687137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 688137500Sjulian DELAY(100); 68965543Scg 690137500Sjulian /* Enable hardware volume control interruption. */ 691137500Sjulian if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ 692137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); 69365543Scg 69465543Scg /* Setup Wave Processor. */ 69565543Scg 69665543Scg /* Enable WaveCache, set DMA base address. */ 69765543Scg wp_wrreg(ess, WPREG_WAVE_ROMRAM, 69865543Scg WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 699137500Sjulian wp_wrreg(ess, WPREG_CRAM_DATA, 0); 70065543Scg 701137500Sjulian AGG_WR(ess, PORT_WAVCACHE_CTRL, 702137500Sjulian WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); 703137500Sjulian 70465543Scg for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) 705137500Sjulian wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT); 70665543Scg 70765543Scg /* Setup Codec/Ringbus. */ 70865543Scg agg_initcodec(ess); 709137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 710137500Sjulian RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); 71165543Scg 712137500Sjulian wp_wrreg(ess, 0x08, 0xB004); 713137500Sjulian wp_wrreg(ess, 0x09, 0x001B); 714137500Sjulian wp_wrreg(ess, 0x0A, 0x8000); 715137500Sjulian wp_wrreg(ess, 0x0B, 0x3F37); 716137500Sjulian wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ 717137500Sjulian wp_wrreg(ess, WPREG_BASE + 1, 0x7632); 71865543Scg ringbus_setdest(ess, RINGBUS_SRC_ADC, 71965543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 72065543Scg ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 72165543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 72265543Scg 723137500Sjulian /* Enable S/PDIF if necessary. */ 724137500Sjulian if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) 725137500Sjulian /* XXX - why not use device flags? */ 726137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | 727137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); 728137500Sjulian 72965543Scg /* Setup ASSP. Needed for Dell Inspiron 7500? */ 730137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); 731137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); 732137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); 73365543Scg 73465543Scg /* 73565543Scg * Setup GPIO. 73665543Scg * There seems to be speciality with NEC systems. 73765543Scg */ 73865543Scg switch (pci_get_subvendor(ess->dev) 73965543Scg | (pci_get_subdevice(ess->dev) << 16)) { 74065543Scg case NEC_SUBID1: 74165543Scg case NEC_SUBID2: 74265543Scg /* Matthew Braithwaite <matt@braithwaite.net> reported that 74365543Scg * NEC Versa LX doesn't need GPIO operation. */ 744137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); 745137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, 746137500Sjulian AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); 747137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); 74865543Scg break; 74965543Scg } 75065543Scg} 75165543Scg 752137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */ 753137500Sjulianstatic void 754137500Sjulianagg_power(struct agg_info *ess, int status) 755137500Sjulian{ 756137500Sjulian u_int8_t lastpwr; 757137500Sjulian 758137500Sjulian lastpwr = ess->curpwr; 759137500Sjulian if (lastpwr == status) 760137500Sjulian return; 761137500Sjulian 762137500Sjulian switch (status) { 763137500Sjulian case PCI_POWERSTATE_D0: 764137500Sjulian case PCI_POWERSTATE_D1: 765137500Sjulian switch (lastpwr) { 766137500Sjulian case PCI_POWERSTATE_D2: 767137500Sjulian pci_set_powerstate(ess->dev, status); 768137500Sjulian /* Turn on PCM-related parts. */ 769137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0); 770137500Sjulian DELAY(100); 771137500Sjulian#if 0 772137500Sjulian if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) 773137500Sjulian device_printf(ess->dev, "warning: codec not ready.\n"); 774137500Sjulian#endif 775137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 776137500Sjulian (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 777137500Sjulian & ~RINGBUS_CTRL_ACLINK_ENABLED) 778137500Sjulian | RINGBUS_CTRL_RINGBUS_ENABLED, 4); 779137500Sjulian DELAY(50); 780137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 781137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 782137500Sjulian | RINGBUS_CTRL_ACLINK_ENABLED, 4); 783137500Sjulian break; 784137500Sjulian case PCI_POWERSTATE_D3: 785137500Sjulian /* Initialize. */ 786137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 787137500Sjulian DELAY(100); 788137500Sjulian agg_init(ess); 789137500Sjulian /* FALLTHROUGH */ 790137500Sjulian case PCI_POWERSTATE_D0: 791137500Sjulian case PCI_POWERSTATE_D1: 792137500Sjulian pci_set_powerstate(ess->dev, status); 793137500Sjulian break; 794137500Sjulian } 795137500Sjulian break; 796137500Sjulian case PCI_POWERSTATE_D2: 797137500Sjulian switch (lastpwr) { 798137500Sjulian case PCI_POWERSTATE_D3: 799137500Sjulian /* Initialize. */ 800137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 801137500Sjulian DELAY(100); 802137500Sjulian agg_init(ess); 803137500Sjulian /* FALLTHROUGH */ 804137500Sjulian case PCI_POWERSTATE_D0: 805137500Sjulian case PCI_POWERSTATE_D1: 806137500Sjulian /* Turn off PCM-related parts. */ 807137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 808137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 809137500Sjulian & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); 810137500Sjulian DELAY(100); 811137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0x300); 812137500Sjulian DELAY(100); 813137500Sjulian break; 814137500Sjulian } 815137500Sjulian pci_set_powerstate(ess->dev, status); 816137500Sjulian break; 817137500Sjulian case PCI_POWERSTATE_D3: 818137500Sjulian /* Entirely power down. */ 819137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); 820137500Sjulian DELAY(100); 821137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 822137500Sjulian /*DELAY(1);*/ 823137500Sjulian if (lastpwr != PCI_POWERSTATE_D2) 824137500Sjulian wp_stoptimer(ess); 825137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 826137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); 827137500Sjulian pci_set_powerstate(ess->dev, status); 828137500Sjulian break; 829137500Sjulian default: 830137500Sjulian /* Invalid power state; let it ignored. */ 831137500Sjulian status = lastpwr; 832137500Sjulian break; 833137500Sjulian } 834137500Sjulian 835137500Sjulian ess->curpwr = status; 836137500Sjulian} 837137500Sjulian 838137500Sjulian/* -------------------------------------------------------------------- */ 839137500Sjulian 84065543Scg/* Channel controller. */ 84165543Scg 84265543Scgstatic void 84365543Scgaggch_start_dac(struct agg_chinfo *ch) 84465543Scg{ 845137500Sjulian bus_addr_t wpwa; 846137500Sjulian u_int32_t speed; 847137500Sjulian u_int16_t size, apuch, wtbar, wcreg, aputype; 848137500Sjulian u_int dv; 849137500Sjulian int pan; 85065543Scg 851137500Sjulian speed = ch->speed; 852137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 853137500Sjulian wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); 854137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 855137500Sjulian size = ch->buflen; 856137500Sjulian apuch = (ch->num << 1) | 32; 857137500Sjulian pan = PAN_RIGHT - PAN_FRONT; 858137500Sjulian 859137500Sjulian if (ch->stereo) { 860137500Sjulian wcreg |= WAVCACHE_CHCTL_STEREO; 861137500Sjulian if (ch->qs16) { 862137500Sjulian aputype = APUTYPE_16BITSTEREO; 863137500Sjulian wpwa >>= 1; 864137500Sjulian size >>= 1; 865137500Sjulian pan = -pan; 866137500Sjulian } else 867137500Sjulian aputype = APUTYPE_8BITSTEREO; 868137500Sjulian } else { 869137500Sjulian pan = 0; 870137500Sjulian if (ch->qs16) 871137500Sjulian aputype = APUTYPE_16BITLINEAR; 872137500Sjulian else { 873137500Sjulian aputype = APUTYPE_8BITLINEAR; 874137500Sjulian speed >>= 1; 875137500Sjulian } 87665543Scg } 877137500Sjulian if (ch->us) 878137500Sjulian wcreg |= WAVCACHE_CHCTL_U8; 87965543Scg 880137500Sjulian if (wtbar > 8) 881137500Sjulian wtbar = (wtbar >> 1) + 4; 882137500Sjulian 88365543Scg dv = (((speed % 48000) << 16) + 24000) / 48000 88465543Scg + ((speed / 48000) << 16); 88565543Scg 886137500Sjulian agg_lock(ch->parent); 887137500Sjulian agg_power(ch->parent, powerstate_active); 88865543Scg 889137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar, 890137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 891137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1, 892137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 893137500Sjulian if (wtbar < 8) { 894137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2, 895137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 896137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3, 897137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 898137500Sjulian } 899137500Sjulian wc_wrchctl(ch->parent, apuch, wcreg); 900137500Sjulian wc_wrchctl(ch->parent, apuch + 1, wcreg); 90165543Scg 902137500Sjulian apu_setparam(ch->parent, apuch, wpwa, size, pan, dv); 903137500Sjulian if (ch->stereo) { 904137500Sjulian if (ch->qs16) 905137500Sjulian wpwa |= (WPWA_STEREO >> 1); 906137500Sjulian apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv); 90765543Scg 908137500Sjulian critical_enter(); 909137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 910137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 91165543Scg wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, 912137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 913137500Sjulian critical_exit(); 914137500Sjulian } else { 915137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 916137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 917137500Sjulian } 918137500Sjulian 919137500Sjulian /* to mark that this channel is ready for intr. */ 920137500Sjulian ch->parent->active |= (1 << ch->num); 921137500Sjulian 922137500Sjulian set_timer(ch->parent); 923137500Sjulian wp_starttimer(ch->parent); 924137500Sjulian agg_unlock(ch->parent); 92565543Scg} 92665543Scg 92765543Scgstatic void 92865543Scgaggch_stop_dac(struct agg_chinfo *ch) 92965543Scg{ 930137500Sjulian agg_lock(ch->parent); 931137500Sjulian 932137500Sjulian /* to mark that this channel no longer needs further intrs. */ 933137500Sjulian ch->parent->active &= ~(1 << ch->num); 934137500Sjulian 935137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE, 93665543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 937137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE, 93865543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 939137500Sjulian 940137500Sjulian if (ch->parent->active) { 941137500Sjulian set_timer(ch->parent); 942137500Sjulian wp_starttimer(ch->parent); 943137500Sjulian } else { 944137500Sjulian wp_stoptimer(ch->parent); 945137500Sjulian agg_power(ch->parent, powerstate_idle); 946137500Sjulian } 947137500Sjulian agg_unlock(ch->parent); 94865543Scg} 94965543Scg 950137500Sjulianstatic void 951137500Sjulianaggch_start_adc(struct agg_rchinfo *ch) 952137500Sjulian{ 953137500Sjulian bus_addr_t wpwa, wpwa2; 954137500Sjulian u_int16_t wcreg, wcreg2; 955137500Sjulian u_int dv; 956137500Sjulian int pan; 957137500Sjulian 958137500Sjulian /* speed > 48000 not cared */ 959137500Sjulian dv = ((ch->speed << 16) + 24000) / 48000; 960137500Sjulian 961137500Sjulian /* RATECONV doesn't seem to like dv == 0x10000. */ 962137500Sjulian if (dv == 0x10000) 963137500Sjulian dv--; 964137500Sjulian 965137500Sjulian if (ch->stereo) { 966137500Sjulian wpwa = (ch->srcphys - ch->base) >> 1; 967137500Sjulian wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1; 968137500Sjulian wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 969137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 970137500Sjulian pan = PAN_LEFT - PAN_FRONT; 971137500Sjulian } else { 972137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 973137500Sjulian wpwa2 = (ch->srcphys - ch->base) >> 1; 974137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 975137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 976137500Sjulian pan = 0; 977137500Sjulian } 978137500Sjulian 979137500Sjulian agg_lock(ch->parent); 980137500Sjulian 981137500Sjulian ch->hwptr = 0; 982137500Sjulian agg_power(ch->parent, powerstate_active); 983137500Sjulian 984137500Sjulian /* Invalidate WaveCache. */ 985137500Sjulian wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO); 986137500Sjulian wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO); 987137500Sjulian wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO); 988137500Sjulian wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO); 989137500Sjulian 990137500Sjulian /* Load APU registers. */ 991137500Sjulian /* APU #0 : Sample rate converter for left/center. */ 992137500Sjulian apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa, 993137500Sjulian ch->buflen >> ch->stereo, 0, dv); 994137500Sjulian wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0); 995137500Sjulian wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT); 996137500Sjulian 997137500Sjulian /* APU #1 : Sample rate converter for right. */ 998137500Sjulian apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2, 999137500Sjulian ch->buflen >> ch->stereo, 0, dv); 1000137500Sjulian wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0); 1001137500Sjulian wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT); 1002137500Sjulian 1003137500Sjulian /* APU #2 : Input mixer for left. */ 1004137500Sjulian apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0, 1005137500Sjulian ch->parent->bufsz >> 2, pan, 0x10000); 1006137500Sjulian wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0); 1007137500Sjulian wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0); 1008137500Sjulian wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT); 1009137500Sjulian 1010137500Sjulian /* APU #3 : Input mixer for right. */ 1011137500Sjulian apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2), 1012137500Sjulian ch->parent->bufsz >> 2, -pan, 0x10000); 1013137500Sjulian wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0); 1014137500Sjulian wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0); 1015137500Sjulian wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT); 1016137500Sjulian 1017137500Sjulian /* to mark this channel ready for intr. */ 1018137500Sjulian ch->parent->active |= (1 << ch->parent->playchns); 1019137500Sjulian 1020137500Sjulian /* start adc */ 1021137500Sjulian critical_enter(); 1022137500Sjulian wp_wrapu(ch->parent, 0, APUREG_APUTYPE, 1023137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1024137500Sjulian wp_wrapu(ch->parent, 1, APUREG_APUTYPE, 1025137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1026137500Sjulian wp_wrapu(ch->parent, 2, APUREG_APUTYPE, 1027137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1028137500Sjulian wp_wrapu(ch->parent, 3, APUREG_APUTYPE, 1029137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1030137500Sjulian critical_exit(); 1031137500Sjulian 1032137500Sjulian set_timer(ch->parent); 1033137500Sjulian wp_starttimer(ch->parent); 1034137500Sjulian agg_unlock(ch->parent); 1035137500Sjulian} 1036137500Sjulian 1037137500Sjulianstatic void 1038137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch) 1039137500Sjulian{ 1040137500Sjulian int apuch; 1041137500Sjulian 1042137500Sjulian agg_lock(ch->parent); 1043137500Sjulian 1044137500Sjulian /* to mark that this channel no longer needs further intrs. */ 1045137500Sjulian ch->parent->active &= ~(1 << ch->parent->playchns); 1046137500Sjulian 1047137500Sjulian for (apuch = 0; apuch < 4; apuch++) 1048137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 1049137500Sjulian APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1050137500Sjulian 1051137500Sjulian if (ch->parent->active) { 1052137500Sjulian set_timer(ch->parent); 1053137500Sjulian wp_starttimer(ch->parent); 1054137500Sjulian } else { 1055137500Sjulian wp_stoptimer(ch->parent); 1056137500Sjulian agg_power(ch->parent, powerstate_idle); 1057137500Sjulian } 1058137500Sjulian agg_unlock(ch->parent); 1059137500Sjulian} 1060137500Sjulian 106165543Scg/* 1062137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving. 1063137500Sjulian * This function expects n not overwrapping the buffer boundary. 1064137500Sjulian * Note that n is measured in sample unit. 1065137500Sjulian * 1066137500Sjulian * XXX - this function works in 16bit stereo format only. 1067137500Sjulian */ 1068137500Sjulianstatic inline void 1069137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n) 1070137500Sjulian{ 1071137500Sjulian int16_t *end; 1072137500Sjulian 1073137500Sjulian for (end = l + n; l < end; ) { 1074137500Sjulian *p++ = *l++; 1075137500Sjulian *p++ = *r++; 1076137500Sjulian } 1077137500Sjulian} 1078137500Sjulian 1079137500Sjulianstatic void 1080137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch) 1081137500Sjulian{ 1082137500Sjulian unsigned cur, last; 1083137500Sjulian int16_t *src2; 1084137500Sjulian 1085137500Sjulian agg_lock(ch->parent); 1086137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1087137500Sjulian agg_unlock(ch->parent); 1088137500Sjulian cur -= 0xffff & ((ch->srcphys - ch->base) >> 1); 1089137500Sjulian last = ch->hwptr; 1090137500Sjulian src2 = ch->src + ch->parent->bufsz/4; 1091137500Sjulian 1092137500Sjulian if (cur < last) { 1093137500Sjulian interleave(ch->src + last, src2 + last, 1094137500Sjulian ch->sink + 2*last, ch->buflen/2 - last); 1095137500Sjulian interleave(ch->src, src2, 1096137500Sjulian ch->sink, cur); 1097137500Sjulian } else if (cur > last) 1098137500Sjulian interleave(ch->src + last, src2 + last, 1099137500Sjulian ch->sink + 2*last, cur - last); 1100137500Sjulian ch->hwptr = cur; 1101137500Sjulian} 1102137500Sjulian 1103137500Sjulian/* 1104137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center. 1105137500Sjulian * This function expects n not overwrapping the buffer boundary. 1106137500Sjulian * Note that n is measured in sample unit. 1107137500Sjulian * 1108137500Sjulian * XXX - this function works in 16bit monoral format only. 1109137500Sjulian */ 1110137500Sjulianstatic inline void 1111137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n) 1112137500Sjulian{ 1113137500Sjulian int16_t *end; 1114137500Sjulian 1115137500Sjulian for (end = dest + n; dest < end; dest++) 1116137500Sjulian *dest = (int16_t)(((int)*dest - (int)*src++) / 2); 1117137500Sjulian} 1118137500Sjulian 1119137500Sjulianstatic void 1120137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch) 1121137500Sjulian{ 1122137500Sjulian unsigned cur, last; 1123137500Sjulian 1124137500Sjulian agg_lock(ch->parent); 1125137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1126137500Sjulian agg_unlock(ch->parent); 1127137500Sjulian cur -= 0xffff & ((ch->phys - ch->base) >> 1); 1128137500Sjulian last = ch->hwptr; 1129137500Sjulian 1130137500Sjulian if (cur < last) { 1131137500Sjulian mixdown(ch->src + last, ch->sink + last, ch->buflen - last); 1132137500Sjulian mixdown(ch->src, ch->sink, cur); 1133137500Sjulian } else if (cur > last) 1134137500Sjulian mixdown(ch->src + last, ch->sink + last, cur - last); 1135137500Sjulian ch->hwptr = cur; 1136137500Sjulian} 1137137500Sjulian 1138137500Sjulian/* 113965543Scg * Stereo jitter suppressor. 114065543Scg * Sometimes playback pointers differ in stereo-paired channels. 114165543Scg * Calling this routine within intr fixes the problem. 114265543Scg */ 114365543Scgstatic inline void 114465543Scgsuppress_jitter(struct agg_chinfo *ch) 114565543Scg{ 1146137500Sjulian if (ch->stereo) { 1147137500Sjulian int cp1, cp2, diff /*, halfsize*/ ; 114865543Scg 1149137500Sjulian /*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/ 1150137500Sjulian cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); 1151137500Sjulian cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR); 1152137500Sjulian if (cp1 != cp2) { 1153137500Sjulian diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); 1154137500Sjulian if (diff > 1 /* && diff < halfsize*/ ) 1155137500Sjulian AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); 1156137500Sjulian } 115765543Scg } 115865543Scg} 115965543Scg 1160137500Sjulianstatic inline void 1161137500Sjuliansuppress_rec_jitter(struct agg_rchinfo *ch) 1162137500Sjulian{ 1163137500Sjulian int cp1, cp2, diff /*, halfsize*/ ; 1164137500Sjulian 1165137500Sjulian /*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/ 1166137500Sjulian cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1) 1167137500Sjulian + wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1168137500Sjulian cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR); 1169137500Sjulian if (cp1 != cp2) { 1170137500Sjulian diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); 1171137500Sjulian if (diff > 1 /* && diff < halfsize*/ ) 1172137500Sjulian AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); 1173137500Sjulian } 1174137500Sjulian} 1175137500Sjulian 117665543Scgstatic inline u_int 1177137500Sjuliancalc_timer_div(struct agg_chinfo *ch) 117865543Scg{ 1179137500Sjulian u_int speed; 118065543Scg 1181137500Sjulian speed = ch->speed; 1182137500Sjulian#ifdef INVARIANTS 1183137500Sjulian if (speed == 0) { 1184137500Sjulian printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n", 1185137500Sjulian ch->num); 1186137500Sjulian speed = 1; 1187137500Sjulian } 1188137500Sjulian#endif 1189137500Sjulian return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo)) 1190137500Sjulian + speed - 1) / speed; 1191137500Sjulian} 119265543Scg 1193137500Sjulianstatic inline u_int 1194137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch) 1195137500Sjulian{ 1196137500Sjulian u_int speed; 1197137500Sjulian 1198137500Sjulian speed = ch->speed; 1199137500Sjulian#ifdef INVARIANTS 1200137500Sjulian if (speed == 0) { 1201137500Sjulian printf("snd_maestro: rch.speed == 0, which shouldn't\n"); 1202137500Sjulian speed = 1; 1203137500Sjulian } 1204137500Sjulian#endif 1205137500Sjulian return (48000 * (ch->blklen << (!ch->stereo)) 1206137500Sjulian + speed - 1) / speed; 120765543Scg} 120865543Scg 120965543Scgstatic void 121065543Scgset_timer(struct agg_info *ess) 121165543Scg{ 121265543Scg int i; 1213137500Sjulian u_int dv = 32 << 7, newdv; 121465543Scg 121565543Scg for (i = 0; i < ess->playchns; i++) 121665543Scg if ((ess->active & (1 << i)) && 1217137500Sjulian (dv > (newdv = calc_timer_div(ess->pch + i)))) 1218137500Sjulian dv = newdv; 1219137500Sjulian if ((ess->active & (1 << i)) && 1220137500Sjulian (dv > (newdv = calc_timer_div_rch(&ess->rch)))) 1221137500Sjulian dv = newdv; 122265543Scg 1223137500Sjulian wp_settimer(ess, dv); 122465543Scg} 122565543Scg 122665543Scg 122765543Scg/* ----------------------------- 122865543Scg * Newpcm glue. 122965543Scg */ 123065543Scg 1231137500Sjulian/* AC97 mixer interface. */ 1232137500Sjulian 1233137500Sjulianstatic u_int32_t 1234137500Sjulianagg_ac97_init(kobj_t obj, void *sc) 1235137500Sjulian{ 1236137500Sjulian struct agg_info *ess = sc; 1237137500Sjulian 1238137500Sjulian return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1; 1239137500Sjulian} 1240137500Sjulian 1241137500Sjulianstatic int 1242137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno) 1243137500Sjulian{ 1244137500Sjulian struct agg_info *ess = sc; 1245137500Sjulian int ret; 1246137500Sjulian 1247137500Sjulian agg_lock(ess); 1248137500Sjulian ret = agg_rdcodec(ess, regno); 1249137500Sjulian agg_unlock(ess); 1250137500Sjulian return ret; 1251137500Sjulian} 1252137500Sjulian 1253137500Sjulianstatic int 1254137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data) 1255137500Sjulian{ 1256137500Sjulian struct agg_info *ess = sc; 1257137500Sjulian int ret; 1258137500Sjulian 1259137500Sjulian agg_lock(ess); 1260137500Sjulian ret = agg_wrcodec(ess, regno, data); 1261137500Sjulian agg_unlock(ess); 1262137500Sjulian return ret; 1263137500Sjulian} 1264137500Sjulian 1265137500Sjulian 1266137500Sjulianstatic kobj_method_t agg_ac97_methods[] = { 1267137500Sjulian KOBJMETHOD(ac97_init, agg_ac97_init), 1268137500Sjulian KOBJMETHOD(ac97_read, agg_ac97_read), 1269137500Sjulian KOBJMETHOD(ac97_write, agg_ac97_write), 1270137500Sjulian { 0, 0 } 1271137500Sjulian}; 1272137500SjulianAC97_DECLARE(agg_ac97); 1273137500Sjulian 1274137500Sjulian 1275137500Sjulian/* -------------------------------------------------------------------- */ 1276137500Sjulian 1277137500Sjulian/* Playback channel. */ 1278137500Sjulian 127965543Scgstatic void * 1280137500Sjulianaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 128165543Scg{ 128265543Scg struct agg_info *ess = devinfo; 128365543Scg struct agg_chinfo *ch; 128465543Scg bus_addr_t physaddr; 128570291Scg void *p; 128665543Scg 1287137500Sjulian KASSERT((dir == PCMDIR_PLAY), 1288137500Sjulian ("aggpch_init() called for RECORDING channel!")); 1289137500Sjulian ch = ess->pch + ess->playchns; 129065543Scg 129165543Scg ch->parent = ess; 129265543Scg ch->channel = c; 129365543Scg ch->buffer = b; 129465543Scg ch->num = ess->playchns; 129565543Scg 1296137500Sjulian p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr); 1297137500Sjulian if (p == NULL) 1298137500Sjulian return NULL; 1299137500Sjulian ch->phys = physaddr; 1300137500Sjulian ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT); 1301137500Sjulian 130284658Scg sndbuf_setup(b, p, ess->bufsz); 1303137500Sjulian ch->blklen = sndbuf_getblksz(b) / 2; 1304137500Sjulian ch->buflen = sndbuf_getsize(b) / 2; 1305137500Sjulian ess->playchns++; 130665543Scg 1307137500Sjulian return ch; 1308137500Sjulian} 130965543Scg 1310137500Sjulianstatic void 1311137500Sjulianadjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size) 1312137500Sjulian{ 1313137500Sjulian struct agg_chinfo *pchs[AGG_MAXPLAYCH]; 1314137500Sjulian u_int i, j, k; 1315137500Sjulian bus_addr_t base; 131665543Scg 1317137500Sjulian /* sort pchs by phys address */ 1318137500Sjulian for (i = 0; i < n; i++) { 1319137500Sjulian for (j = 0; j < i; j++) 1320137500Sjulian if (chans[i].phys < pchs[j]->phys) { 1321137500Sjulian for (k = i; k > j; k--) 1322137500Sjulian pchs[k] = pchs[k - 1]; 1323137500Sjulian break; 1324137500Sjulian } 1325137500Sjulian pchs[j] = chans + i; 1326137500Sjulian } 1327137500Sjulian 1328137500Sjulian /* use new base register if next buffer can not be addressed 1329137500Sjulian via current base. */ 1330137500Sjulian#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1) 1331137500Sjulian base = pchs[0]->base; 1332137500Sjulian for (k = 1, i = 1; i < n; i++) { 1333137500Sjulian if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT) 1334137500Sjulian /* not addressable: assign new base */ 1335137500Sjulian base = (pchs[i]->base -= k++ << BASE_SHIFT); 1336137500Sjulian else 1337137500Sjulian pchs[i]->base = base; 1338137500Sjulian } 1339137500Sjulian#undef BASE_SHIFT 1340137500Sjulian 1341137500Sjulian if (bootverbose) { 1342137500Sjulian printf("Total of %d bases are assigned.\n", k); 1343137500Sjulian for (i = 0; i < n; i++) { 1344137500Sjulian printf("ch.%d: phys 0x%llx, wpwa 0x%llx\n", 1345137500Sjulian i, (long long)chans[i].phys, 1346137500Sjulian (long long)(chans[i].phys - 1347137500Sjulian chans[i].base) >> 1); 1348137500Sjulian } 1349137500Sjulian } 135065543Scg} 135165543Scg 135265543Scgstatic int 1353137500Sjulianaggpch_free(kobj_t obj, void *data) 135465543Scg{ 1355137500Sjulian struct agg_chinfo *ch = data; 1356137500Sjulian struct agg_info *ess = ch->parent; 1357137500Sjulian 1358137500Sjulian /* free up buffer - called after channel stopped */ 1359137500Sjulian dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer)); 1360137500Sjulian 136165543Scg /* return 0 if ok */ 136265543Scg return 0; 136365543Scg} 136465543Scg 136565543Scgstatic int 1366137500Sjulianaggpch_setformat(kobj_t obj, void *data, u_int32_t format) 136765543Scg{ 136865543Scg struct agg_chinfo *ch = data; 136965543Scg 1370137500Sjulian if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) 1371137500Sjulian return EINVAL; 1372137500Sjulian ch->stereo = ch->qs16 = ch->us = 0; 1373137500Sjulian if (format & AFMT_STEREO) 1374137500Sjulian ch->stereo = 1; 137565543Scg 137665543Scg if (format & AFMT_U8 || format & AFMT_S8) { 137765543Scg if (format & AFMT_U8) 1378137500Sjulian ch->us = 1; 1379137500Sjulian } else 1380137500Sjulian ch->qs16 = 1; 138189887Sscottl return 0; 138265543Scg} 138365543Scg 138465543Scgstatic int 1385137500Sjulianaggpch_setspeed(kobj_t obj, void *data, u_int32_t speed) 138665543Scg{ 1387137500Sjulian return ((struct agg_chinfo*)data)->speed = speed; 138865543Scg} 138965543Scg 139065543Scgstatic int 1391137500Sjulianaggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 139265543Scg{ 1393137500Sjulian struct agg_chinfo *ch = data; 1394137500Sjulian int blkcnt; 1395137500Sjulian 1396137500Sjulian /* try to keep at least 20msec DMA space */ 1397137500Sjulian blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize); 1398137500Sjulian RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); 1399137500Sjulian 1400137500Sjulian if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { 1401137500Sjulian sndbuf_resize(ch->buffer, blkcnt, blocksize); 1402137500Sjulian blkcnt = sndbuf_getblkcnt(ch->buffer); 1403137500Sjulian blocksize = sndbuf_getblksz(ch->buffer); 1404137500Sjulian } else { 1405137500Sjulian sndbuf_setblkcnt(ch->buffer, blkcnt); 1406137500Sjulian sndbuf_setblksz(ch->buffer, blocksize); 1407137500Sjulian } 1408137500Sjulian 1409137500Sjulian ch->blklen = blocksize / 2; 1410137500Sjulian ch->buflen = blkcnt * blocksize / 2; 1411137500Sjulian return blocksize; 141265543Scg} 141365543Scg 141465543Scgstatic int 1415137500Sjulianaggpch_trigger(kobj_t obj, void *data, int go) 141665543Scg{ 141765543Scg struct agg_chinfo *ch = data; 141865543Scg 141965543Scg switch (go) { 142065543Scg case PCMTRIG_EMLDMAWR: 1421137500Sjulian break; 142265543Scg case PCMTRIG_START: 1423137500Sjulian aggch_start_dac(ch); 142465543Scg break; 142565543Scg case PCMTRIG_ABORT: 142665543Scg case PCMTRIG_STOP: 1427137500Sjulian aggch_stop_dac(ch); 142865543Scg break; 142965543Scg } 143065543Scg return 0; 143165543Scg} 143265543Scg 143365543Scgstatic int 1434137500Sjulianaggpch_getptr(kobj_t obj, void *data) 143565543Scg{ 143665543Scg struct agg_chinfo *ch = data; 143765543Scg u_int cp; 143865543Scg 1439137500Sjulian agg_lock(ch->parent); 1440137500Sjulian cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); 1441137500Sjulian agg_unlock(ch->parent); 144265543Scg 1443137500Sjulian return ch->qs16 && ch->stereo 1444137500Sjulian ? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base)) 1445137500Sjulian : (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base)); 144665543Scg} 144765543Scg 144874763Scgstatic struct pcmchan_caps * 1449137500Sjulianaggpch_getcaps(kobj_t obj, void *data) 145065543Scg{ 145165543Scg static u_int32_t playfmt[] = { 145265543Scg AFMT_U8, 145365543Scg AFMT_STEREO | AFMT_U8, 145465543Scg AFMT_S8, 145565543Scg AFMT_STEREO | AFMT_S8, 145665543Scg AFMT_S16_LE, 145765543Scg AFMT_STEREO | AFMT_S16_LE, 145865543Scg 0 145965543Scg }; 1460137500Sjulian static struct pcmchan_caps playcaps = {2000, 767999, playfmt, 0}; 146165543Scg 1462137500Sjulian return &playcaps; 1463137500Sjulian} 1464137500Sjulian 1465137500Sjulian 1466137500Sjulianstatic kobj_method_t aggpch_methods[] = { 1467137500Sjulian KOBJMETHOD(channel_init, aggpch_init), 1468137500Sjulian KOBJMETHOD(channel_free, aggpch_free), 1469137500Sjulian KOBJMETHOD(channel_setformat, aggpch_setformat), 1470137500Sjulian KOBJMETHOD(channel_setspeed, aggpch_setspeed), 1471137500Sjulian KOBJMETHOD(channel_setblocksize, aggpch_setblocksize), 1472137500Sjulian KOBJMETHOD(channel_trigger, aggpch_trigger), 1473137500Sjulian KOBJMETHOD(channel_getptr, aggpch_getptr), 1474137500Sjulian KOBJMETHOD(channel_getcaps, aggpch_getcaps), 1475137500Sjulian { 0, 0 } 1476137500Sjulian}; 1477137500SjulianCHANNEL_DECLARE(aggpch); 1478137500Sjulian 1479137500Sjulian 1480137500Sjulian/* -------------------------------------------------------------------- */ 1481137500Sjulian 1482137500Sjulian/* Recording channel. */ 1483137500Sjulian 1484137500Sjulianstatic void * 1485137500Sjulianaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 1486137500Sjulian{ 1487137500Sjulian struct agg_info *ess = devinfo; 1488137500Sjulian struct agg_rchinfo *ch; 1489137500Sjulian u_int8_t *p; 1490137500Sjulian 1491137500Sjulian KASSERT((dir == PCMDIR_REC), 1492137500Sjulian ("aggrch_init() called for PLAYBACK channel!")); 1493137500Sjulian ch = &ess->rch; 1494137500Sjulian 1495137500Sjulian ch->parent = ess; 1496137500Sjulian ch->channel = c; 1497137500Sjulian ch->buffer = b; 1498137500Sjulian 1499137500Sjulian /* Uses the bottom-half of the status buffer. */ 1500137500Sjulian p = ess->stat + ess->bufsz; 1501137500Sjulian ch->phys = ess->phys + ess->bufsz; 1502137500Sjulian ch->base = ess->phys; 1503137500Sjulian ch->src = (int16_t *)(p + ess->bufsz); 1504137500Sjulian ch->srcphys = ch->phys + ess->bufsz; 1505137500Sjulian ch->sink = (int16_t *)p; 1506137500Sjulian 1507137500Sjulian sndbuf_setup(b, p, ess->bufsz); 1508137500Sjulian ch->blklen = sndbuf_getblksz(b) / 2; 1509137500Sjulian ch->buflen = sndbuf_getsize(b) / 2; 1510137500Sjulian 1511137500Sjulian return ch; 1512137500Sjulian} 1513137500Sjulian 1514137500Sjulianstatic int 1515137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format) 1516137500Sjulian{ 1517137500Sjulian struct agg_rchinfo *ch = data; 1518137500Sjulian 1519137500Sjulian if (!(format & AFMT_S16_LE)) 1520137500Sjulian return EINVAL; 1521137500Sjulian if (format & AFMT_STEREO) 1522137500Sjulian ch->stereo = 1; 1523137500Sjulian else 1524137500Sjulian ch->stereo = 0; 1525137500Sjulian return 0; 1526137500Sjulian} 1527137500Sjulian 1528137500Sjulianstatic int 1529137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) 1530137500Sjulian{ 1531137500Sjulian return ((struct agg_rchinfo*)data)->speed = speed; 1532137500Sjulian} 1533137500Sjulian 1534137500Sjulianstatic int 1535137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 1536137500Sjulian{ 1537137500Sjulian struct agg_rchinfo *ch = data; 1538137500Sjulian int blkcnt; 1539137500Sjulian 1540137500Sjulian /* try to keep at least 20msec DMA space */ 1541137500Sjulian blkcnt = (ch->speed << ch->stereo) / (25 * blocksize); 1542137500Sjulian RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); 1543137500Sjulian 1544137500Sjulian if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { 1545137500Sjulian sndbuf_resize(ch->buffer, blkcnt, blocksize); 1546137500Sjulian blkcnt = sndbuf_getblkcnt(ch->buffer); 1547137500Sjulian blocksize = sndbuf_getblksz(ch->buffer); 1548137500Sjulian } else { 1549137500Sjulian sndbuf_setblkcnt(ch->buffer, blkcnt); 1550137500Sjulian sndbuf_setblksz(ch->buffer, blocksize); 1551137500Sjulian } 1552137500Sjulian 1553137500Sjulian ch->blklen = blocksize / 2; 1554137500Sjulian ch->buflen = blkcnt * blocksize / 2; 1555137500Sjulian return blocksize; 1556137500Sjulian} 1557137500Sjulian 1558137500Sjulianstatic int 1559137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go) 1560137500Sjulian{ 1561137500Sjulian struct agg_rchinfo *ch = sc; 1562137500Sjulian 1563137500Sjulian switch (go) { 1564137500Sjulian case PCMTRIG_EMLDMARD: 1565137500Sjulian if (ch->stereo) 1566137500Sjulian aggch_feed_adc_stereo(ch); 1567137500Sjulian else 1568137500Sjulian aggch_feed_adc_mono(ch); 1569137500Sjulian break; 1570137500Sjulian case PCMTRIG_START: 1571137500Sjulian aggch_start_adc(ch); 1572137500Sjulian break; 1573137500Sjulian case PCMTRIG_ABORT: 1574137500Sjulian case PCMTRIG_STOP: 1575137500Sjulian aggch_stop_adc(ch); 1576137500Sjulian break; 1577137500Sjulian } 1578137500Sjulian return 0; 1579137500Sjulian} 1580137500Sjulian 1581137500Sjulianstatic int 1582137500Sjulianaggrch_getptr(kobj_t obj, void *sc) 1583137500Sjulian{ 1584137500Sjulian struct agg_rchinfo *ch = sc; 1585137500Sjulian 1586137500Sjulian return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1; 1587137500Sjulian} 1588137500Sjulian 1589137500Sjulianstatic struct pcmchan_caps * 1590137500Sjulianaggrch_getcaps(kobj_t obj, void *sc) 1591137500Sjulian{ 159265543Scg static u_int32_t recfmt[] = { 159365543Scg AFMT_S16_LE, 159465543Scg AFMT_STEREO | AFMT_S16_LE, 159565543Scg 0 159665543Scg }; 1597137500Sjulian static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; 159865543Scg 1599137500Sjulian return &reccaps; 160065543Scg} 160165543Scg 1602137500Sjulianstatic kobj_method_t aggrch_methods[] = { 1603137500Sjulian KOBJMETHOD(channel_init, aggrch_init), 1604137500Sjulian /* channel_free: no-op */ 1605137500Sjulian KOBJMETHOD(channel_setformat, aggrch_setformat), 1606137500Sjulian KOBJMETHOD(channel_setspeed, aggrch_setspeed), 1607137500Sjulian KOBJMETHOD(channel_setblocksize, aggrch_setblocksize), 1608137500Sjulian KOBJMETHOD(channel_trigger, aggrch_trigger), 1609137500Sjulian KOBJMETHOD(channel_getptr, aggrch_getptr), 1610137500Sjulian KOBJMETHOD(channel_getcaps, aggrch_getcaps), 161170134Scg { 0, 0 } 161270134Scg}; 1613137500SjulianCHANNEL_DECLARE(aggrch); 161465543Scg 1615137500Sjulian 161665543Scg/* ----------------------------- 161765543Scg * Bus space. 161865543Scg */ 161965543Scg 162065543Scgstatic void 162165543Scgagg_intr(void *sc) 162265543Scg{ 162365543Scg struct agg_info* ess = sc; 1624137500Sjulian register u_int8_t status; 162565543Scg int i; 1626137500Sjulian u_int m; 162765543Scg 1628137500Sjulian status = AGG_RD(ess, PORT_HOSTINT_STAT, 1); 162965543Scg if (!status) 163065543Scg return; 163165543Scg 1632137500Sjulian /* Acknowledge intr. */ 1633137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, status, 1); 163470619Sjhb 1635137500Sjulian if (status & HOSTINT_STAT_DSOUND) { 1636137500Sjulian#ifdef AGG_JITTER_CORRECTION 1637137500Sjulian agg_lock(ess); 1638137500Sjulian#endif 1639137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) { 1640137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 1641137500Sjulian#ifdef AGG_JITTER_CORRECTION 1642137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1643137500Sjulian if (ess->active & m) 1644137500Sjulian suppress_jitter(ess->pch + i); 1645137500Sjulian } 1646137500Sjulian if (ess->active & m) 1647137500Sjulian suppress_rec_jitter(&ess->rch); 1648137500Sjulian agg_unlock(ess); 1649137500Sjulian#endif 1650137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1651137500Sjulian if (ess->active & m) { 1652137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) 1653137500Sjulian chn_intr(ess->pch[i].channel); 1654137500Sjulian else { 1655137500Sjulian m = 0; 1656137500Sjulian break; 1657137500Sjulian } 1658137500Sjulian } 1659137500Sjulian } 1660137500Sjulian if ((ess->active & m) 1661137500Sjulian && ess->curpwr <= PCI_POWERSTATE_D1) 1662137500Sjulian chn_intr(ess->rch.channel); 1663137500Sjulian } 1664137500Sjulian#ifdef AGG_JITTER_CORRECTION 1665137500Sjulian else 1666137500Sjulian agg_unlock(ess); 1667137500Sjulian#endif 1668137500Sjulian } 1669137500Sjulian 167065543Scg if (status & HOSTINT_STAT_HWVOL) { 1671137500Sjulian register u_int8_t event; 167270619Sjhb 1673137500Sjulian agg_lock(ess); 1674137500Sjulian event = AGG_RD(ess, PORT_HWVOL_MASTER, 1); 1675137500Sjulian AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1); 1676137500Sjulian agg_unlock(ess); 1677137500Sjulian 167870619Sjhb switch (event) { 167970619Sjhb case HWVOL_UP: 168070945Sjhb mixer_hwvol_step(ess->dev, 1, 1); 168170619Sjhb break; 168270619Sjhb case HWVOL_DOWN: 168370945Sjhb mixer_hwvol_step(ess->dev, -1, -1); 168470619Sjhb break; 168570619Sjhb case HWVOL_NOP: 168670619Sjhb break; 168770619Sjhb default: 1688137500Sjulian if (event & HWVOL_MUTE) { 1689137500Sjulian mixer_hwvol_mute(ess->dev); 1690137500Sjulian break; 1691137500Sjulian } 1692137500Sjulian device_printf(ess->dev, 1693137500Sjulian "%s: unknown HWVOL event 0x%x\n", 1694137500Sjulian device_get_nameunit(ess->dev), event); 169565543Scg } 169665543Scg } 169765543Scg} 169865543Scg 169965543Scgstatic void 170065543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 170165543Scg{ 170265543Scg bus_addr_t *phys = arg; 170365543Scg 170465543Scg *phys = error? 0 : segs->ds_addr; 170565543Scg 170665543Scg if (bootverbose) { 170765543Scg printf("setmap (%lx, %lx), nseg=%d, error=%d\n", 170865543Scg (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 170965543Scg nseg, error); 171065543Scg } 171165543Scg} 171265543Scg 171365543Scgstatic void * 1714137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys) 171565543Scg{ 171665543Scg void *buf; 171765543Scg bus_dmamap_t map; 171865543Scg 1719137500Sjulian if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map)) 172065543Scg return NULL; 1721137500Sjulian if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0) 1722137500Sjulian || !*phys || map) { 1723137500Sjulian bus_dmamem_free(dmat, buf, map); 172465543Scg return NULL; 172565543Scg } 172665543Scg return buf; 172765543Scg} 172865543Scg 172965543Scgstatic void 1730137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf) 173165543Scg{ 1732137500Sjulian bus_dmamem_free(dmat, buf, NULL); 173365543Scg} 173465543Scg 173565543Scgstatic int 173665543Scgagg_probe(device_t dev) 173765543Scg{ 173865543Scg char *s = NULL; 173965543Scg 174065543Scg switch (pci_get_devid(dev)) { 174165543Scg case MAESTRO_1_PCI_ID: 174265543Scg s = "ESS Technology Maestro-1"; 174365543Scg break; 174465543Scg 174565543Scg case MAESTRO_2_PCI_ID: 174665543Scg s = "ESS Technology Maestro-2"; 174765543Scg break; 174865543Scg 174965543Scg case MAESTRO_2E_PCI_ID: 175065543Scg s = "ESS Technology Maestro-2E"; 175165543Scg break; 175265543Scg } 175365543Scg 175465543Scg if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { 175565543Scg device_set_desc(dev, s); 175665543Scg return 0; 175765543Scg } 175865543Scg return ENXIO; 175965543Scg} 176065543Scg 176165543Scgstatic int 176265543Scgagg_attach(device_t dev) 176365543Scg{ 176465543Scg struct agg_info *ess = NULL; 176565543Scg u_int32_t data; 1766119690Sjhb int regid = PCIR_BAR(0); 176765543Scg struct resource *reg = NULL; 176865543Scg struct ac97_info *codec = NULL; 176965543Scg int irqid = 0; 177065543Scg struct resource *irq = NULL; 177165543Scg void *ih = NULL; 177265543Scg char status[SND_STATUSLEN]; 1773137500Sjulian int ret = 0; 177465543Scg 177578564Sgreid if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 177665543Scg device_printf(dev, "cannot allocate softc\n"); 1777137500Sjulian ret = ENOMEM; 1778137500Sjulian goto bad; 177965543Scg } 178065543Scg ess->dev = dev; 178165543Scg 1782137500Sjulian#ifdef USING_MUTEX 1783137500Sjulian mtx_init(&ess->lock, device_get_desc(dev), "hardware status lock", 1784137500Sjulian MTX_DEF | MTX_RECURSE); 1785137500Sjulian if (!mtx_initialized(&ess->lock)) { 1786137500Sjulian device_printf(dev, "failed to create a mutex.\n"); 1787137500Sjulian ret = ENOMEM; 1788137500Sjulian goto bad; 1789137500Sjulian } 1790137500Sjulian#endif 1791137500Sjulian 179284658Scg ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); 1793137500Sjulian if (bus_dma_tag_create(/*parent*/ NULL, 1794137500Sjulian /*align */ 4, 1 << (16+1), 1795137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1796137500Sjulian /*filter*/ NULL, NULL, 1797137500Sjulian /*size */ ess->bufsz, 1, 0x3ffff, 1798137500Sjulian /*flags */ 0, 1799137500Sjulian#if __FreeBSD_version >= 501102 1800137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1801137500Sjulian#endif 1802137500Sjulian &ess->buf_dmat) != 0) { 1803137500Sjulian device_printf(dev, "unable to create dma tag\n"); 1804137500Sjulian ret = ENOMEM; 1805137500Sjulian goto bad; 1806137500Sjulian } 180784658Scg 180865543Scg if (bus_dma_tag_create(/*parent*/NULL, 1809137500Sjulian /*align */ 1 << WAVCACHE_BASEADDR_SHIFT, 1810137500Sjulian 1 << (16+1), 1811137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1812137500Sjulian /*filter*/ NULL, NULL, 1813137500Sjulian /*size */ 3*ess->bufsz, 1, 0x3ffff, 1814137500Sjulian /*flags */ 0, 1815137500Sjulian#if __FreeBSD_version >= 501102 1816137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1817137500Sjulian#endif 1818137500Sjulian &ess->stat_dmat) != 0) { 181965543Scg device_printf(dev, "unable to create dma tag\n"); 1820137500Sjulian ret = ENOMEM; 182165543Scg goto bad; 182265543Scg } 182365543Scg 1824137500Sjulian /* Allocate the room for brain-damaging status buffer. */ 1825137500Sjulian ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys); 182665543Scg if (ess->stat == NULL) { 1827137500Sjulian device_printf(dev, "cannot allocate status buffer\n"); 1828137500Sjulian ret = ENOMEM; 182965543Scg goto bad; 183065543Scg } 183165543Scg if (bootverbose) 1832137500Sjulian device_printf(dev, "Maestro status/record buffer: %#llx\n", 1833137500Sjulian (long long)ess->phys); 183465543Scg 1835137500Sjulian /* State D0-uninitialized. */ 1836137500Sjulian ess->curpwr = PCI_POWERSTATE_D3; 1837137500Sjulian pci_set_powerstate(dev, PCI_POWERSTATE_D0); 183865543Scg 183965543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 184065543Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); 184165543Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 184265543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 184365543Scg 1844137500Sjulian /* Allocate resources. */ 1845137500Sjulian if (data & PCIM_CMD_PORTEN) 1846141095Simp reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, 1847141095Simp RF_ACTIVE); 1848137500Sjulian if (reg != NULL) { 1849137500Sjulian ess->reg = reg; 1850137500Sjulian ess->regid = regid; 1851137500Sjulian ess->st = rman_get_bustag(reg); 1852137500Sjulian ess->sh = rman_get_bushandle(reg); 1853137500Sjulian } else { 185465543Scg device_printf(dev, "unable to map register space\n"); 1855137500Sjulian ret = ENXIO; 185665543Scg goto bad; 185765543Scg } 1858141095Simp irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, 1859141095Simp RF_ACTIVE | RF_SHAREABLE); 1860137500Sjulian if (irq != NULL) { 1861137500Sjulian ess->irq = irq; 1862137500Sjulian ess->irqid = irqid; 1863137500Sjulian } else { 1864137500Sjulian device_printf(dev, "unable to map interrupt\n"); 1865137500Sjulian ret = ENXIO; 1866137500Sjulian goto bad; 1867137500Sjulian } 186865543Scg 1869137500Sjulian /* Setup resources. */ 1870137500Sjulian if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) { 1871137500Sjulian device_printf(dev, "unable to setup interrupt\n"); 1872137500Sjulian ret = ENXIO; 1873137500Sjulian goto bad; 1874137500Sjulian } else 1875137500Sjulian ess->ih = ih; 1876137500Sjulian 1877137500Sjulian /* Transition from D0-uninitialized to D0. */ 1878137500Sjulian agg_lock(ess); 1879137500Sjulian agg_power(ess, PCI_POWERSTATE_D0); 1880137500Sjulian if (agg_rdcodec(ess, 0) == 0x80) { 1881137500Sjulian /* XXX - TODO: PT101 */ 188265543Scg device_printf(dev, "PT101 codec detected!\n"); 1883137500Sjulian ret = ENXIO; 188465543Scg goto bad; 188565543Scg } 188670134Scg codec = AC97_CREATE(dev, ess, agg_ac97); 1887137500Sjulian if (codec == NULL) { 1888137500Sjulian device_printf(dev, "failed to create AC97 codec softc!\n"); 1889137500Sjulian ret = ENOMEM; 189065543Scg goto bad; 1891137500Sjulian } 1892137500Sjulian if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { 1893137500Sjulian device_printf(dev, "mixer initialization failed!\n"); 1894137500Sjulian ret = ENXIO; 189565543Scg goto bad; 1896137500Sjulian } 189765543Scg ess->codec = codec; 189865543Scg 1899137500Sjulian ret = pcm_register(dev, ess, AGG_MAXPLAYCH, 1); 1900137500Sjulian if (ret) 190165543Scg goto bad; 190265543Scg 190370945Sjhb mixer_hwvol_init(dev); 1904137500Sjulian agg_power(ess, powerstate_init); 190565543Scg for (data = 0; data < AGG_MAXPLAYCH; data++) 1906137500Sjulian pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess); 190770134Scg pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); 1908137500Sjulian adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); 1909137500Sjulian 1910137500Sjulian agg_unlock(ess); 1911137500Sjulian 1912137500Sjulian snprintf(status, SND_STATUSLEN, 1913137500Sjulian "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d", 1914137500Sjulian rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), 1915137500Sjulian pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); 191665543Scg pcm_setstatus(dev, status); 191765543Scg 191865543Scg return 0; 191965543Scg 192065543Scg bad: 192165644Scg if (codec != NULL) 192265644Scg ac97_destroy(codec); 192365543Scg if (ih != NULL) 192465543Scg bus_teardown_intr(dev, irq, ih); 192565543Scg if (irq != NULL) 192665543Scg bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 192765543Scg if (reg != NULL) 192865543Scg bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); 1929137500Sjulian if (ess->stat != NULL) 1930137500Sjulian dma_free(ess->stat_dmat, ess->stat); 1931137500Sjulian if (ess->stat_dmat != NULL) 1932137500Sjulian bus_dma_tag_destroy(ess->stat_dmat); 1933137500Sjulian if (ess->buf_dmat != NULL) 1934137500Sjulian bus_dma_tag_destroy(ess->buf_dmat); 1935137500Sjulian#ifdef USING_MUTEX 1936137500Sjulian if (mtx_initialized(&ess->lock)) 1937137500Sjulian mtx_destroy(&ess->lock); 1938137500Sjulian#endif 1939137500Sjulian if (ess != NULL) 194065543Scg free(ess, M_DEVBUF); 194165543Scg 1942137500Sjulian return ret; 194365543Scg} 194465543Scg 194565543Scgstatic int 194665543Scgagg_detach(device_t dev) 194765543Scg{ 194865543Scg struct agg_info *ess = pcm_getdevinfo(dev); 194965543Scg int r; 1950137500Sjulian u_int16_t icr; 195165543Scg 1952137500Sjulian icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2); 1953137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 1954137500Sjulian 1955137500Sjulian agg_lock(ess); 1956137500Sjulian if (ess->active) { 1957137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 1958137500Sjulian agg_unlock(ess); 1959137500Sjulian return EBUSY; 1960137500Sjulian } 1961137500Sjulian agg_unlock(ess); 1962137500Sjulian 196365543Scg r = pcm_unregister(dev); 1964137500Sjulian if (r) { 1965137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 196665543Scg return r; 1967137500Sjulian } 196865543Scg 1969137500Sjulian agg_lock(ess); 1970137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 197165543Scg 197265543Scg bus_teardown_intr(dev, ess->irq, ess->ih); 197365543Scg bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); 197465543Scg bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); 1975137500Sjulian dma_free(ess->stat_dmat, ess->stat); 1976137500Sjulian bus_dma_tag_destroy(ess->stat_dmat); 1977137500Sjulian bus_dma_tag_destroy(ess->buf_dmat); 1978137500Sjulian#ifdef USING_MUTEX 1979137500Sjulian mtx_destroy(&ess->lock); 1980137500Sjulian#endif 198165543Scg free(ess, M_DEVBUF); 198265543Scg return 0; 198365543Scg} 198465543Scg 198565543Scgstatic int 198665543Scgagg_suspend(device_t dev) 198765543Scg{ 198865543Scg struct agg_info *ess = pcm_getdevinfo(dev); 1989137500Sjulian#ifndef USING_MUTEX 1990137500Sjulian int x; 199165543Scg 199265543Scg x = spltty(); 199365543Scg#endif 1994137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 1995137500Sjulian agg_lock(ess); 1996137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 1997137500Sjulian agg_unlock(ess); 1998137500Sjulian#ifndef USING_MUTEX 199965543Scg splx(x); 2000137500Sjulian#endif 200165543Scg 200265543Scg return 0; 200365543Scg} 200465543Scg 200565543Scgstatic int 200665543Scgagg_resume(device_t dev) 200765543Scg{ 2008137500Sjulian int i; 200965543Scg struct agg_info *ess = pcm_getdevinfo(dev); 2010137500Sjulian#ifndef USING_MUTEX 2011137500Sjulian int x; 201265543Scg 201365543Scg x = spltty(); 2014137500Sjulian#endif 201565543Scg for (i = 0; i < ess->playchns; i++) 201665543Scg if (ess->active & (1 << i)) 201765543Scg aggch_start_dac(ess->pch + i); 201865543Scg if (ess->active & (1 << i)) 201965543Scg aggch_start_adc(&ess->rch); 2020137500Sjulian 2021137500Sjulian agg_lock(ess); 2022137500Sjulian if (!ess->active) 2023137500Sjulian agg_power(ess, powerstate_init); 2024137500Sjulian agg_unlock(ess); 2025137500Sjulian#ifndef USING_MUTEX 2026137500Sjulian splx(x); 202765543Scg#endif 2028137500Sjulian 2029137500Sjulian if (mixer_reinit(dev)) { 2030137500Sjulian device_printf(dev, "unable to reinitialize the mixer\n"); 2031137500Sjulian return ENXIO; 203265543Scg } 2033137500Sjulian 203465543Scg return 0; 203565543Scg} 203665543Scg 203765543Scgstatic int 203865543Scgagg_shutdown(device_t dev) 203965543Scg{ 204065543Scg struct agg_info *ess = pcm_getdevinfo(dev); 204165543Scg 2042137500Sjulian agg_lock(ess); 2043137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 2044137500Sjulian agg_unlock(ess); 204565543Scg 204665543Scg return 0; 204765543Scg} 204865543Scg 204965543Scg 205065543Scgstatic device_method_t agg_methods[] = { 205165543Scg DEVMETHOD(device_probe, agg_probe), 205265543Scg DEVMETHOD(device_attach, agg_attach), 205365543Scg DEVMETHOD(device_detach, agg_detach), 205465543Scg DEVMETHOD(device_suspend, agg_suspend), 205565543Scg DEVMETHOD(device_resume, agg_resume), 205665543Scg DEVMETHOD(device_shutdown, agg_shutdown), 205765543Scg 205865543Scg { 0, 0 } 205965543Scg}; 206065543Scg 206165543Scgstatic driver_t agg_driver = { 206265543Scg "pcm", 206365543Scg agg_methods, 206482180Scg PCM_SOFTC_SIZE, 206565543Scg}; 206665543Scg 2067137500Sjulian/*static devclass_t pcm_devclass;*/ 2068137500Sjulian 206965543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); 2070132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 207165543ScgMODULE_VERSION(snd_maestro, 1); 2072