maestro.c revision 166279
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 166279 2007-01-27 13:30:19Z ariff $"); 5882180Scg 5965543Scg/* 6065543Scg * PCI IDs of supported chips: 6165543Scg * 6265543Scg * MAESTRO-1 0x01001285 6365543Scg * MAESTRO-2 0x1968125d 6465543Scg * MAESTRO-2E 0x1978125d 6565543Scg */ 6665543Scg 6765543Scg#define MAESTRO_1_PCI_ID 0x01001285 6865543Scg#define MAESTRO_2_PCI_ID 0x1968125d 6965543Scg#define MAESTRO_2E_PCI_ID 0x1978125d 7065543Scg 7165543Scg#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ 7265543Scg#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ 7365543Scg 74137500Sjulian#ifdef AGG_MAXPLAYCH 75137500Sjulian# if AGG_MAXPLAYCH > 4 76137500Sjulian# undef AGG_MAXPLAYCH 77137500Sjulian# define AGG_MAXPLAYCH 4 78137500Sjulian# endif 79137500Sjulian#else 8065543Scg# define AGG_MAXPLAYCH 4 8165543Scg#endif 8265543Scg 8384658Scg#define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ 8465543Scg 8565543Scg 86137500Sjulian/* compatibility */ 87137500Sjulian#if __FreeBSD_version < 500000 88137500Sjulian# define critical_enter() disable_intr() 89137500Sjulian# define critical_exit() enable_intr() 90137500Sjulian#endif 91137500Sjulian 92137500Sjulian#ifndef PCIR_BAR 93137500Sjulian#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) 94137500Sjulian#endif 95137500Sjulian 96137500Sjulian 9765543Scg/* ----------------------------- 9865543Scg * Data structures. 9965543Scg */ 10065543Scgstruct agg_chinfo { 101137500Sjulian /* parent softc */ 10265543Scg struct agg_info *parent; 103137500Sjulian 104137500Sjulian /* FreeBSD newpcm related */ 10574763Scg struct pcm_channel *channel; 10674763Scg struct snd_dbuf *buffer; 107137500Sjulian 108137500Sjulian /* OS independent */ 109137500Sjulian bus_addr_t phys; /* channel buffer physical address */ 110137500Sjulian bus_addr_t base; /* channel buffer segment base */ 111137500Sjulian u_int32_t blklen; /* DMA block length in WORDs */ 112137500Sjulian u_int32_t buflen; /* channel buffer length in WORDs */ 11370291Scg u_int32_t speed; 114137500Sjulian unsigned num : 3; 115137500Sjulian unsigned stereo : 1; 116137500Sjulian unsigned qs16 : 1; /* quantum size is 16bit */ 117137500Sjulian unsigned us : 1; /* in unsigned format */ 11865543Scg}; 11965543Scg 120137500Sjulianstruct agg_rchinfo { 121137500Sjulian /* parent softc */ 122137500Sjulian struct agg_info *parent; 123137500Sjulian 124137500Sjulian /* FreeBSD newpcm related */ 125137500Sjulian struct pcm_channel *channel; 126137500Sjulian struct snd_dbuf *buffer; 127137500Sjulian 128137500Sjulian /* OS independent */ 129137500Sjulian bus_addr_t phys; /* channel buffer physical address */ 130137500Sjulian bus_addr_t base; /* channel buffer segment base */ 131137500Sjulian u_int32_t blklen; /* DMA block length in WORDs */ 132137500Sjulian u_int32_t buflen; /* channel buffer length in WORDs */ 133137500Sjulian u_int32_t speed; 134137500Sjulian unsigned : 3; 135137500Sjulian unsigned stereo : 1; 136137500Sjulian bus_addr_t srcphys; 137137500Sjulian int16_t *src; /* stereo peer buffer */ 138137500Sjulian int16_t *sink; /* channel buffer pointer */ 139137500Sjulian volatile u_int32_t hwptr; /* ready point in 16bit sample */ 140137500Sjulian}; 141137500Sjulian 14265543Scgstruct agg_info { 143137500Sjulian /* FreeBSD newbus related */ 14465543Scg device_t dev; 145137500Sjulian 146137500Sjulian /* I wonder whether bus_space_* are in common in *BSD... */ 14765543Scg struct resource *reg; 14865543Scg int regid; 14965543Scg bus_space_tag_t st; 15065543Scg bus_space_handle_t sh; 15165543Scg 15265543Scg struct resource *irq; 15365543Scg int irqid; 15465543Scg void *ih; 15565543Scg 156137500Sjulian bus_dma_tag_t buf_dmat; 157137500Sjulian bus_dma_tag_t stat_dmat; 15865543Scg 159137500Sjulian /* FreeBSD SMPng related */ 160137500Sjulian#ifdef USING_MUTEX 161137500Sjulian struct mtx lock; /* mutual exclusion */ 162137500Sjulian#endif 163137500Sjulian /* FreeBSD newpcm related */ 16465543Scg struct ac97_info *codec; 16565543Scg 166137500Sjulian /* OS independent */ 167137500Sjulian u_int8_t *stat; /* status buffer pointer */ 168137500Sjulian bus_addr_t phys; /* status buffer physical address */ 169137500Sjulian unsigned int bufsz; /* channel buffer size in bytes */ 170137500Sjulian u_int playchns; 171137500Sjulian volatile u_int active; 17265543Scg struct agg_chinfo pch[AGG_MAXPLAYCH]; 173137500Sjulian struct agg_rchinfo rch; 174137500Sjulian volatile u_int8_t curpwr; /* current power status: D[0-3] */ 17565543Scg}; 17665543Scg 177137500Sjulian 178137500Sjulian/* ----------------------------- 179137500Sjulian * Sysctls for debug. 180137500Sjulian */ 181137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1; 182137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE 183137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D2; 184137500Sjulian#else 185137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D1; 186137500Sjulian#endif 187137500Sjulianstatic unsigned int powerstate_init = PCI_POWERSTATE_D2; 188137500Sjulian 189159732Snetchild/* XXX: this should move to a device specific sysctl dev.pcm.X.debug.Y via 190159732Snetchild device_get_sysctl_*() as discussed on multimedia@ in msg-id 191159732Snetchild <861wujij2q.fsf@xps.des.no> */ 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, 198166279Sariff &powerstate_init, 0, 199166279Sariff "The Dx power state prior to the first use (0-2)"); 200137500Sjulian 201137500Sjulian 202137500Sjulian/* ----------------------------- 203137500Sjulian * Prototypes 204137500Sjulian */ 205137500Sjulian 206166279Sariffstatic void agg_sleep(struct agg_info*, const char *wmesg, int msec); 207137500Sjulian 208166279Sariffstatic __inline u_int32_t agg_rd(struct agg_info*, int, int size); 209166279Sariffstatic __inline void agg_wr(struct agg_info*, int, u_int32_t data, 210166279Sariff int size); 211166279Sariffstatic int agg_rdcodec(struct agg_info*, int); 212166279Sariffstatic int agg_wrcodec(struct agg_info*, int, u_int32_t); 213137500Sjulian 214166279Sariffstatic void ringbus_setdest(struct agg_info*, int, int); 215137500Sjulian 216166279Sariffstatic u_int16_t wp_rdreg(struct agg_info*, u_int16_t); 217166279Sariffstatic void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); 218166279Sariffstatic u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); 219166279Sariffstatic void wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t); 220166279Sariffstatic void wp_settimer(struct agg_info*, u_int); 221166279Sariffstatic void wp_starttimer(struct agg_info*); 222166279Sariffstatic void wp_stoptimer(struct agg_info*); 22365543Scg 224166279Sariff#if 0 225166279Sariffstatic u_int16_t wc_rdreg(struct agg_info*, u_int16_t); 226166279Sariff#endif 227166279Sariffstatic void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); 228166279Sariff#if 0 229166279Sariffstatic u_int16_t wc_rdchctl(struct agg_info*, int); 230166279Sariff#endif 231166279Sariffstatic void wc_wrchctl(struct agg_info*, int, u_int16_t); 23265543Scg 233166279Sariffstatic void agg_stopclock(struct agg_info*, int part, int st); 23465543Scg 235166279Sariffstatic void agg_initcodec(struct agg_info*); 236166279Sariffstatic void agg_init(struct agg_info*); 237166279Sariffstatic void agg_power(struct agg_info*, int); 23865543Scg 239166279Sariffstatic void aggch_start_dac(struct agg_chinfo*); 240166279Sariffstatic void aggch_stop_dac(struct agg_chinfo*); 241166279Sariffstatic void aggch_start_adc(struct agg_rchinfo*); 242166279Sariffstatic void aggch_stop_adc(struct agg_rchinfo*); 243166279Sariffstatic void aggch_feed_adc_stereo(struct agg_rchinfo*); 244166279Sariffstatic void aggch_feed_adc_mono(struct agg_rchinfo*); 24565543Scg 246166279Sariff#ifdef AGG_JITTER_CORRECTION 247166279Sariffstatic void suppress_jitter(struct agg_chinfo*); 248166279Sariffstatic void suppress_rec_jitter(struct agg_rchinfo*); 249166279Sariff#endif 25065543Scg 251166279Sariffstatic void set_timer(struct agg_info*); 25265543Scg 253166279Sariffstatic void agg_intr(void *); 254166279Sariffstatic int agg_probe(device_t); 255166279Sariffstatic int agg_attach(device_t); 256166279Sariffstatic int agg_detach(device_t); 257166279Sariffstatic int agg_suspend(device_t); 258166279Sariffstatic int agg_resume(device_t); 259166279Sariffstatic int agg_shutdown(device_t); 26065543Scg 261137500Sjulianstatic void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*); 262166279Sariffstatic void dma_free(bus_dma_tag_t, void *); 26365543Scg 264137500Sjulian 26565543Scg/* ----------------------------- 26665543Scg * Subsystems. 26765543Scg */ 26865543Scg 269137500Sjulian/* locking */ 270166279Sariff#define agg_lock(sc) snd_mtxlock(&((sc)->lock)) 271166279Sariff#define agg_unlock(sc) snd_mtxunlock(&((sc)->lock)) 27265543Scg 273166279Sariffstatic void 274137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec) 275137500Sjulian{ 276137500Sjulian int timo; 277137500Sjulian 278137500Sjulian timo = msec * hz / 1000; 279137500Sjulian if (timo == 0) 280137500Sjulian timo = 1; 281137500Sjulian#ifdef USING_MUTEX 282137500Sjulian msleep(sc, &sc->lock, PWAIT, wmesg, timo); 283137500Sjulian#else 284137500Sjulian tsleep(sc, PWAIT, wmesg, timo); 285137500Sjulian#endif 286137500Sjulian} 287137500Sjulian 288137500Sjulian 289137500Sjulian/* I/O port */ 290137500Sjulian 291166279Sariffstatic __inline u_int32_t 292137500Sjulianagg_rd(struct agg_info *sc, int regno, int size) 293137500Sjulian{ 294137500Sjulian switch (size) { 295137500Sjulian case 1: 296137500Sjulian return bus_space_read_1(sc->st, sc->sh, regno); 297137500Sjulian case 2: 298137500Sjulian return bus_space_read_2(sc->st, sc->sh, regno); 299137500Sjulian case 4: 300137500Sjulian return bus_space_read_4(sc->st, sc->sh, regno); 301137500Sjulian default: 302137500Sjulian return ~(u_int32_t)0; 303137500Sjulian } 304137500Sjulian} 305137500Sjulian 306137500Sjulian#define AGG_RD(sc, regno, size) \ 307137500Sjulian bus_space_read_##size( \ 308137500Sjulian ((struct agg_info*)(sc))->st, \ 309137500Sjulian ((struct agg_info*)(sc))->sh, (regno)) 310137500Sjulian 311166279Sariffstatic __inline void 312137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size) 313137500Sjulian{ 314137500Sjulian switch (size) { 315137500Sjulian case 1: 316137500Sjulian bus_space_write_1(sc->st, sc->sh, regno, data); 317137500Sjulian break; 318137500Sjulian case 2: 319137500Sjulian bus_space_write_2(sc->st, sc->sh, regno, data); 320137500Sjulian break; 321137500Sjulian case 4: 322137500Sjulian bus_space_write_4(sc->st, sc->sh, regno, data); 323137500Sjulian break; 324137500Sjulian } 325137500Sjulian} 326137500Sjulian 327137500Sjulian#define AGG_WR(sc, regno, data, size) \ 328137500Sjulian bus_space_write_##size( \ 329137500Sjulian ((struct agg_info*)(sc))->st, \ 330137500Sjulian ((struct agg_info*)(sc))->sh, (regno), (data)) 331137500Sjulian 33270134Scg/* -------------------------------------------------------------------- */ 33370134Scg 334137500Sjulian/* Codec/Ringbus */ 335137500Sjulian 336166279Sariffstatic int 337137500Sjulianagg_codec_wait4idle(struct agg_info *ess) 33865543Scg{ 339137500Sjulian unsigned t = 26; 34070134Scg 341137500Sjulian while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { 342137500Sjulian if (--t == 0) 343137500Sjulian return EBUSY; 344137500Sjulian DELAY(2); /* 20.8us / 13 */ 345137500Sjulian } 346137500Sjulian return 0; 34770134Scg} 34870134Scg 349137500Sjulian 350166279Sariffstatic int 351137500Sjulianagg_rdcodec(struct agg_info *ess, int regno) 35270134Scg{ 353137500Sjulian int ret; 35465543Scg 35565543Scg /* We have to wait for a SAFE time to write addr/data */ 356137500Sjulian if (agg_codec_wait4idle(ess)) { 357137500Sjulian /* Timed out. No read performed. */ 358137500Sjulian device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); 359137500Sjulian return -1; 36065543Scg } 36165543Scg 362137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); 363137500Sjulian /*DELAY(21); * AC97 cycle = 20.8usec */ 36465543Scg 36565543Scg /* Wait for data retrieve */ 366137500Sjulian if (!agg_codec_wait4idle(ess)) { 367137500Sjulian ret = AGG_RD(ess, PORT_CODEC_REG, 2); 368137500Sjulian } else { 369137500Sjulian /* Timed out. No read performed. */ 370137500Sjulian device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); 371137500Sjulian ret = -1; 37265543Scg } 37365543Scg 374137500Sjulian return ret; 37565543Scg} 37665543Scg 377166279Sariffstatic int 378137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data) 37965543Scg{ 38065543Scg /* We have to wait for a SAFE time to write addr/data */ 381137500Sjulian if (agg_codec_wait4idle(ess)) { 38265543Scg /* Timed out. Abort writing. */ 38365543Scg device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); 38470134Scg return -1; 38565543Scg } 38665543Scg 387137500Sjulian AGG_WR(ess, PORT_CODEC_REG, data, 2); 388137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); 38970134Scg 390137500Sjulian /* Wait for write completion */ 391137500Sjulian if (agg_codec_wait4idle(ess)) { 392137500Sjulian /* Timed out. */ 393137500Sjulian device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); 394137500Sjulian return -1; 395137500Sjulian } 396137500Sjulian 39770134Scg return 0; 39865543Scg} 39965543Scg 400166279Sariffstatic void 40165543Scgringbus_setdest(struct agg_info *ess, int src, int dest) 40265543Scg{ 40365543Scg u_int32_t data; 40465543Scg 405137500Sjulian data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); 40665543Scg data &= ~(0xfU << src); 40765543Scg data |= (0xfU & dest) << src; 408137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); 40965543Scg} 41065543Scg 411137500Sjulian/* -------------------------------------------------------------------- */ 412137500Sjulian 41365543Scg/* Wave Processor */ 41465543Scg 415166279Sariffstatic u_int16_t 41665543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg) 41765543Scg{ 418137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 419137500Sjulian return AGG_RD(ess, PORT_DSP_DATA, 2); 42065543Scg} 42165543Scg 422166279Sariffstatic void 42365543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 42465543Scg{ 425137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 426137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 42765543Scg} 42865543Scg 429166279Sariffstatic int 430137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data) 43165543Scg{ 432137500Sjulian unsigned t = 0; 43365543Scg 434137500Sjulian while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { 435137500Sjulian if (++t == 1000) { 436137500Sjulian return EAGAIN; 437137500Sjulian } 438137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 43965543Scg } 440137500Sjulian 441137500Sjulian return 0; 44265543Scg} 44365543Scg 444166279Sariffstatic u_int16_t 445137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg) 44665543Scg{ 447137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 448137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) != 0) 449137500Sjulian device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); 450137500Sjulian return wp_rdreg(ess, WPREG_DATA_PORT); 45165543Scg} 45265543Scg 453166279Sariffstatic void 454137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data) 45565543Scg{ 456137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 457137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) == 0) { 458137500Sjulian wp_wrreg(ess, WPREG_DATA_PORT, data); 459137500Sjulian if (wp_wait_data(ess, data) != 0) 460166279Sariff device_printf(ess->dev, 461166279Sariff "wp_wrapu() write timed out.\n"); 462137500Sjulian } else { 463137500Sjulian device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); 46465543Scg } 46565543Scg} 46665543Scg 467137573Srustatic void 468137500Sjulianapu_setparam(struct agg_info *ess, int apuch, 469137500Sjulian u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) 47065543Scg{ 471137500Sjulian wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); 472137500Sjulian wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); 473137500Sjulian wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); 474137500Sjulian wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); 475137500Sjulian wp_wrapu(ess, apuch, APUREG_ROUTING, 0); 476137500Sjulian wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); 477137500Sjulian wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 478137500Sjulian | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) 479137500Sjulian | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); 480137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, 481137500Sjulian APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 482137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); 483137500Sjulian} 48465543Scg 485166279Sariffstatic void 486137500Sjulianwp_settimer(struct agg_info *ess, u_int divide) 487137500Sjulian{ 488137500Sjulian u_int prescale = 0; 48965543Scg 490137500Sjulian RANGE(divide, 2, 32 << 7); 491137500Sjulian 492137500Sjulian for (; divide > 32; divide >>= 1) { 49365543Scg prescale++; 494137500Sjulian divide++; 495137500Sjulian } 49665543Scg 49765543Scg for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 49865543Scg prescale++; 49965543Scg 50065543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 501137500Sjulian wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | 50265543Scg (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 50365543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 50465543Scg} 50565543Scg 506166279Sariffstatic void 50765543Scgwp_starttimer(struct agg_info *ess) 50865543Scg{ 509137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 510137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED 511137500Sjulian | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 51265543Scg wp_wrreg(ess, WPREG_TIMER_START, 1); 51365543Scg} 51465543Scg 515166279Sariffstatic void 51665543Scgwp_stoptimer(struct agg_info *ess) 51765543Scg{ 518137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED 519137500Sjulian & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 520137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 52165543Scg wp_wrreg(ess, WPREG_TIMER_START, 0); 52265543Scg} 52365543Scg 524137500Sjulian/* -------------------------------------------------------------------- */ 525137500Sjulian 52665543Scg/* WaveCache */ 52765543Scg 528166279Sariff#if 0 529166279Sariffstatic u_int16_t 53065543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg) 53165543Scg{ 532137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 533137500Sjulian return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); 53465543Scg} 535166279Sariff#endif 53665543Scg 537166279Sariffstatic void 53865543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 53965543Scg{ 540137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 541137500Sjulian AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); 54265543Scg} 54365543Scg 544166279Sariff#if 0 545166279Sariffstatic u_int16_t 54665543Scgwc_rdchctl(struct agg_info *ess, int ch) 54765543Scg{ 54865543Scg return wc_rdreg(ess, ch << 3); 54965543Scg} 550166279Sariff#endif 55165543Scg 552166279Sariffstatic void 55365543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) 55465543Scg{ 55565543Scg wc_wrreg(ess, ch << 3, data); 55665543Scg} 55765543Scg 558137500Sjulian/* -------------------------------------------------------------------- */ 559137500Sjulian 56065543Scg/* Power management */ 561166279Sariffstatic void 562137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st) 56365543Scg{ 564137500Sjulian u_int32_t data; 56565543Scg 566137500Sjulian data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); 567137500Sjulian if (part < 16) { 568137500Sjulian if (st == PCI_POWERSTATE_D1) 569137500Sjulian data &= ~(1 << part); 570137500Sjulian else 571137500Sjulian data |= (1 << part); 572137500Sjulian if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) 573137500Sjulian data |= (0x10000 << part); 574137500Sjulian else 575137500Sjulian data &= ~(0x10000 << part); 576137500Sjulian pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); 577137500Sjulian } 57865543Scg} 57965543Scg 58065543Scg 58165543Scg/* ----------------------------- 58265543Scg * Controller. 58365543Scg */ 58465543Scg 585166279Sariffstatic void 58665543Scgagg_initcodec(struct agg_info* ess) 58765543Scg{ 58865543Scg u_int16_t data; 58965543Scg 590137500Sjulian if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { 591137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 59265543Scg DELAY(104); /* 20.8us * (4 + 1) */ 59365543Scg } 59465543Scg /* XXX - 2nd codec should be looked at. */ 595137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); 59665543Scg DELAY(2); 597137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 598137500Sjulian DELAY(50); 59965543Scg 600137500Sjulian if (agg_rdcodec(ess, 0) < 0) { 601137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 60265543Scg DELAY(21); 60365543Scg 60465543Scg /* Try cold reset. */ 60565543Scg device_printf(ess->dev, "will perform cold reset.\n"); 606137500Sjulian data = AGG_RD(ess, PORT_GPIO_DIR, 2); 60765543Scg if (pci_read_config(ess->dev, 0x58, 2) & 1) 60865543Scg data |= 0x10; 609137500Sjulian data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); 610137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); 611137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); 612137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); 61365543Scg DELAY(2); 614137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); 61565543Scg DELAY(1); 616137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); 617137500Sjulian agg_sleep(ess, "agginicd", 500); 618137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data, 2); 61965543Scg DELAY(84); /* 20.8us * 4 */ 620137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 621137500Sjulian DELAY(50); 62265543Scg } 62365543Scg} 62465543Scg 62565543Scgstatic void 62665543Scgagg_init(struct agg_info* ess) 62765543Scg{ 62865543Scg u_int32_t data; 62965543Scg 63065543Scg /* Setup PCI config registers. */ 63165543Scg 63265543Scg /* Disable all legacy emulations. */ 63365543Scg data = pci_read_config(ess->dev, CONF_LEGACY, 2); 63465543Scg data |= LEGACY_DISABLED; 63565543Scg pci_write_config(ess->dev, CONF_LEGACY, data, 2); 63665543Scg 63765543Scg /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 63865543Scg * Enable posted write. 63965543Scg * Prefer PCI timing rather than that of ISA. 64065543Scg * Don't swap L/R. */ 64165543Scg data = pci_read_config(ess->dev, CONF_MAESTRO, 4); 642137500Sjulian data |= MAESTRO_PMC; 64365543Scg data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 64465543Scg data &= ~MAESTRO_SWAP_LR; 64565543Scg pci_write_config(ess->dev, CONF_MAESTRO, data, 4); 64665543Scg 647137500Sjulian /* Turn off unused parts if necessary. */ 648137500Sjulian /* consult CONF_MAESTRO. */ 649137500Sjulian if (data & MAESTRO_SPDIF) 650137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); 651137500Sjulian else 652137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); 653137500Sjulian if (data & MAESTRO_HWVOL) 654137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); 655137500Sjulian else 656137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); 657137500Sjulian 658137500Sjulian /* parts that never be used */ 659137500Sjulian agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); 660137500Sjulian agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); 661137500Sjulian agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); 662137500Sjulian agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); 663137500Sjulian agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); 664137500Sjulian agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); 665137500Sjulian agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); 666137500Sjulian 667137500Sjulian /* parts that will be used only when play/recording */ 668137500Sjulian agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); 669137500Sjulian 670137500Sjulian /* parts that should always be turned on */ 671137500Sjulian agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); 672137500Sjulian agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); 673137500Sjulian agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); 674137500Sjulian agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); 675137500Sjulian 67665543Scg /* Reset direct sound. */ 677137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); 678137500Sjulian DELAY(100); 679137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 680137500Sjulian DELAY(100); 681137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); 682137500Sjulian DELAY(100); 683137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 684137500Sjulian DELAY(100); 68565543Scg 686137500Sjulian /* Enable hardware volume control interruption. */ 687137500Sjulian if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ 688137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); 68965543Scg 69065543Scg /* Setup Wave Processor. */ 69165543Scg 69265543Scg /* Enable WaveCache, set DMA base address. */ 69365543Scg wp_wrreg(ess, WPREG_WAVE_ROMRAM, 69465543Scg WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 695137500Sjulian wp_wrreg(ess, WPREG_CRAM_DATA, 0); 69665543Scg 697137500Sjulian AGG_WR(ess, PORT_WAVCACHE_CTRL, 698137500Sjulian WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); 699137500Sjulian 70065543Scg for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) 701137500Sjulian wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT); 70265543Scg 70365543Scg /* Setup Codec/Ringbus. */ 70465543Scg agg_initcodec(ess); 705137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 706137500Sjulian RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); 70765543Scg 708137500Sjulian wp_wrreg(ess, 0x08, 0xB004); 709137500Sjulian wp_wrreg(ess, 0x09, 0x001B); 710137500Sjulian wp_wrreg(ess, 0x0A, 0x8000); 711137500Sjulian wp_wrreg(ess, 0x0B, 0x3F37); 712137500Sjulian wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ 713137500Sjulian wp_wrreg(ess, WPREG_BASE + 1, 0x7632); 71465543Scg ringbus_setdest(ess, RINGBUS_SRC_ADC, 71565543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 71665543Scg ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 71765543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 71865543Scg 719137500Sjulian /* Enable S/PDIF if necessary. */ 720137500Sjulian if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) 721137500Sjulian /* XXX - why not use device flags? */ 722137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | 723137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); 724137500Sjulian 72565543Scg /* Setup ASSP. Needed for Dell Inspiron 7500? */ 726137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); 727137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); 728137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); 72965543Scg 73065543Scg /* 73165543Scg * Setup GPIO. 73265543Scg * There seems to be speciality with NEC systems. 73365543Scg */ 73465543Scg switch (pci_get_subvendor(ess->dev) 73565543Scg | (pci_get_subdevice(ess->dev) << 16)) { 73665543Scg case NEC_SUBID1: 73765543Scg case NEC_SUBID2: 73865543Scg /* Matthew Braithwaite <matt@braithwaite.net> reported that 73965543Scg * NEC Versa LX doesn't need GPIO operation. */ 740137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); 741137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, 742137500Sjulian AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); 743137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); 74465543Scg break; 74565543Scg } 74665543Scg} 74765543Scg 748137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */ 749137500Sjulianstatic void 750137500Sjulianagg_power(struct agg_info *ess, int status) 751137500Sjulian{ 752137500Sjulian u_int8_t lastpwr; 753137500Sjulian 754137500Sjulian lastpwr = ess->curpwr; 755137500Sjulian if (lastpwr == status) 756137500Sjulian return; 757137500Sjulian 758137500Sjulian switch (status) { 759137500Sjulian case PCI_POWERSTATE_D0: 760137500Sjulian case PCI_POWERSTATE_D1: 761137500Sjulian switch (lastpwr) { 762137500Sjulian case PCI_POWERSTATE_D2: 763137500Sjulian pci_set_powerstate(ess->dev, status); 764137500Sjulian /* Turn on PCM-related parts. */ 765137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0); 766137500Sjulian DELAY(100); 767137500Sjulian#if 0 768137500Sjulian if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) 769166279Sariff device_printf(ess->dev, 770166279Sariff "warning: codec not ready.\n"); 771137500Sjulian#endif 772137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 773137500Sjulian (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 774137500Sjulian & ~RINGBUS_CTRL_ACLINK_ENABLED) 775137500Sjulian | RINGBUS_CTRL_RINGBUS_ENABLED, 4); 776137500Sjulian DELAY(50); 777137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 778137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 779137500Sjulian | RINGBUS_CTRL_ACLINK_ENABLED, 4); 780137500Sjulian break; 781137500Sjulian case PCI_POWERSTATE_D3: 782137500Sjulian /* Initialize. */ 783137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 784137500Sjulian DELAY(100); 785137500Sjulian agg_init(ess); 786137500Sjulian /* FALLTHROUGH */ 787137500Sjulian case PCI_POWERSTATE_D0: 788137500Sjulian case PCI_POWERSTATE_D1: 789137500Sjulian pci_set_powerstate(ess->dev, status); 790137500Sjulian break; 791137500Sjulian } 792137500Sjulian break; 793137500Sjulian case PCI_POWERSTATE_D2: 794137500Sjulian switch (lastpwr) { 795137500Sjulian case PCI_POWERSTATE_D3: 796137500Sjulian /* Initialize. */ 797137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 798137500Sjulian DELAY(100); 799137500Sjulian agg_init(ess); 800137500Sjulian /* FALLTHROUGH */ 801137500Sjulian case PCI_POWERSTATE_D0: 802137500Sjulian case PCI_POWERSTATE_D1: 803137500Sjulian /* Turn off PCM-related parts. */ 804137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 805137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 806137500Sjulian & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); 807137500Sjulian DELAY(100); 808137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0x300); 809137500Sjulian DELAY(100); 810137500Sjulian break; 811137500Sjulian } 812137500Sjulian pci_set_powerstate(ess->dev, status); 813137500Sjulian break; 814137500Sjulian case PCI_POWERSTATE_D3: 815137500Sjulian /* Entirely power down. */ 816137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); 817137500Sjulian DELAY(100); 818137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 819137500Sjulian /*DELAY(1);*/ 820137500Sjulian if (lastpwr != PCI_POWERSTATE_D2) 821137500Sjulian wp_stoptimer(ess); 822137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 823137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); 824137500Sjulian pci_set_powerstate(ess->dev, status); 825137500Sjulian break; 826137500Sjulian default: 827137500Sjulian /* Invalid power state; let it ignored. */ 828137500Sjulian status = lastpwr; 829137500Sjulian break; 830137500Sjulian } 831137500Sjulian 832137500Sjulian ess->curpwr = status; 833137500Sjulian} 834137500Sjulian 835137500Sjulian/* -------------------------------------------------------------------- */ 836137500Sjulian 83765543Scg/* Channel controller. */ 83865543Scg 83965543Scgstatic void 84065543Scgaggch_start_dac(struct agg_chinfo *ch) 84165543Scg{ 842137500Sjulian bus_addr_t wpwa; 843137500Sjulian u_int32_t speed; 844137500Sjulian u_int16_t size, apuch, wtbar, wcreg, aputype; 845137500Sjulian u_int dv; 846137500Sjulian int pan; 84765543Scg 848137500Sjulian speed = ch->speed; 849137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 850137500Sjulian wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); 851137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 852137500Sjulian size = ch->buflen; 853137500Sjulian apuch = (ch->num << 1) | 32; 854137500Sjulian pan = PAN_RIGHT - PAN_FRONT; 855137500Sjulian 856137500Sjulian if (ch->stereo) { 857137500Sjulian wcreg |= WAVCACHE_CHCTL_STEREO; 858137500Sjulian if (ch->qs16) { 859137500Sjulian aputype = APUTYPE_16BITSTEREO; 860137500Sjulian wpwa >>= 1; 861137500Sjulian size >>= 1; 862137500Sjulian pan = -pan; 863137500Sjulian } else 864137500Sjulian aputype = APUTYPE_8BITSTEREO; 865137500Sjulian } else { 866137500Sjulian pan = 0; 867137500Sjulian if (ch->qs16) 868137500Sjulian aputype = APUTYPE_16BITLINEAR; 869137500Sjulian else { 870137500Sjulian aputype = APUTYPE_8BITLINEAR; 871137500Sjulian speed >>= 1; 872137500Sjulian } 87365543Scg } 874137500Sjulian if (ch->us) 875137500Sjulian wcreg |= WAVCACHE_CHCTL_U8; 87665543Scg 877137500Sjulian if (wtbar > 8) 878137500Sjulian wtbar = (wtbar >> 1) + 4; 879137500Sjulian 88065543Scg dv = (((speed % 48000) << 16) + 24000) / 48000 88165543Scg + ((speed / 48000) << 16); 88265543Scg 883137500Sjulian agg_lock(ch->parent); 884137500Sjulian agg_power(ch->parent, powerstate_active); 88565543Scg 886137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar, 887137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 888137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1, 889137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 890137500Sjulian if (wtbar < 8) { 891137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2, 892137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 893137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3, 894137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 895137500Sjulian } 896137500Sjulian wc_wrchctl(ch->parent, apuch, wcreg); 897137500Sjulian wc_wrchctl(ch->parent, apuch + 1, wcreg); 89865543Scg 899137500Sjulian apu_setparam(ch->parent, apuch, wpwa, size, pan, dv); 900137500Sjulian if (ch->stereo) { 901137500Sjulian if (ch->qs16) 902137500Sjulian wpwa |= (WPWA_STEREO >> 1); 903137500Sjulian apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv); 90465543Scg 905137500Sjulian critical_enter(); 906137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 907137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 90865543Scg wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, 909137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 910137500Sjulian critical_exit(); 911137500Sjulian } else { 912137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 913137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 914137500Sjulian } 915137500Sjulian 916137500Sjulian /* to mark that this channel is ready for intr. */ 917137500Sjulian ch->parent->active |= (1 << ch->num); 918137500Sjulian 919137500Sjulian set_timer(ch->parent); 920137500Sjulian wp_starttimer(ch->parent); 921137500Sjulian agg_unlock(ch->parent); 92265543Scg} 92365543Scg 92465543Scgstatic void 92565543Scgaggch_stop_dac(struct agg_chinfo *ch) 92665543Scg{ 927137500Sjulian agg_lock(ch->parent); 928137500Sjulian 929137500Sjulian /* to mark that this channel no longer needs further intrs. */ 930137500Sjulian ch->parent->active &= ~(1 << ch->num); 931137500Sjulian 932137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE, 93365543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 934137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE, 93565543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 936137500Sjulian 937137500Sjulian if (ch->parent->active) { 938137500Sjulian set_timer(ch->parent); 939137500Sjulian wp_starttimer(ch->parent); 940137500Sjulian } else { 941137500Sjulian wp_stoptimer(ch->parent); 942137500Sjulian agg_power(ch->parent, powerstate_idle); 943137500Sjulian } 944137500Sjulian agg_unlock(ch->parent); 94565543Scg} 94665543Scg 947137500Sjulianstatic void 948137500Sjulianaggch_start_adc(struct agg_rchinfo *ch) 949137500Sjulian{ 950137500Sjulian bus_addr_t wpwa, wpwa2; 951137500Sjulian u_int16_t wcreg, wcreg2; 952137500Sjulian u_int dv; 953137500Sjulian int pan; 954137500Sjulian 955137500Sjulian /* speed > 48000 not cared */ 956137500Sjulian dv = ((ch->speed << 16) + 24000) / 48000; 957137500Sjulian 958137500Sjulian /* RATECONV doesn't seem to like dv == 0x10000. */ 959137500Sjulian if (dv == 0x10000) 960137500Sjulian dv--; 961137500Sjulian 962137500Sjulian if (ch->stereo) { 963137500Sjulian wpwa = (ch->srcphys - ch->base) >> 1; 964137500Sjulian wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1; 965137500Sjulian wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 966137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 967137500Sjulian pan = PAN_LEFT - PAN_FRONT; 968137500Sjulian } else { 969137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 970137500Sjulian wpwa2 = (ch->srcphys - ch->base) >> 1; 971137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 972137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 973137500Sjulian pan = 0; 974137500Sjulian } 975137500Sjulian 976137500Sjulian agg_lock(ch->parent); 977137500Sjulian 978137500Sjulian ch->hwptr = 0; 979137500Sjulian agg_power(ch->parent, powerstate_active); 980137500Sjulian 981137500Sjulian /* Invalidate WaveCache. */ 982137500Sjulian wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO); 983137500Sjulian wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO); 984137500Sjulian wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO); 985137500Sjulian wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO); 986137500Sjulian 987137500Sjulian /* Load APU registers. */ 988137500Sjulian /* APU #0 : Sample rate converter for left/center. */ 989137500Sjulian apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa, 990137500Sjulian ch->buflen >> ch->stereo, 0, dv); 991137500Sjulian wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0); 992137500Sjulian wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT); 993137500Sjulian 994137500Sjulian /* APU #1 : Sample rate converter for right. */ 995137500Sjulian apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2, 996137500Sjulian ch->buflen >> ch->stereo, 0, dv); 997137500Sjulian wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0); 998137500Sjulian wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT); 999137500Sjulian 1000137500Sjulian /* APU #2 : Input mixer for left. */ 1001137500Sjulian apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0, 1002137500Sjulian ch->parent->bufsz >> 2, pan, 0x10000); 1003137500Sjulian wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0); 1004137500Sjulian wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0); 1005137500Sjulian wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT); 1006137500Sjulian 1007137500Sjulian /* APU #3 : Input mixer for right. */ 1008137500Sjulian apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2), 1009137500Sjulian ch->parent->bufsz >> 2, -pan, 0x10000); 1010137500Sjulian wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0); 1011137500Sjulian wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0); 1012137500Sjulian wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT); 1013137500Sjulian 1014137500Sjulian /* to mark this channel ready for intr. */ 1015137500Sjulian ch->parent->active |= (1 << ch->parent->playchns); 1016137500Sjulian 1017137500Sjulian /* start adc */ 1018137500Sjulian critical_enter(); 1019137500Sjulian wp_wrapu(ch->parent, 0, APUREG_APUTYPE, 1020137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1021137500Sjulian wp_wrapu(ch->parent, 1, APUREG_APUTYPE, 1022137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1023137500Sjulian wp_wrapu(ch->parent, 2, APUREG_APUTYPE, 1024137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1025137500Sjulian wp_wrapu(ch->parent, 3, APUREG_APUTYPE, 1026137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1027137500Sjulian critical_exit(); 1028137500Sjulian 1029137500Sjulian set_timer(ch->parent); 1030137500Sjulian wp_starttimer(ch->parent); 1031137500Sjulian agg_unlock(ch->parent); 1032137500Sjulian} 1033137500Sjulian 1034137500Sjulianstatic void 1035137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch) 1036137500Sjulian{ 1037137500Sjulian int apuch; 1038137500Sjulian 1039137500Sjulian agg_lock(ch->parent); 1040137500Sjulian 1041137500Sjulian /* to mark that this channel no longer needs further intrs. */ 1042137500Sjulian ch->parent->active &= ~(1 << ch->parent->playchns); 1043137500Sjulian 1044137500Sjulian for (apuch = 0; apuch < 4; apuch++) 1045137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 1046137500Sjulian APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1047137500Sjulian 1048137500Sjulian if (ch->parent->active) { 1049137500Sjulian set_timer(ch->parent); 1050137500Sjulian wp_starttimer(ch->parent); 1051137500Sjulian } else { 1052137500Sjulian wp_stoptimer(ch->parent); 1053137500Sjulian agg_power(ch->parent, powerstate_idle); 1054137500Sjulian } 1055137500Sjulian agg_unlock(ch->parent); 1056137500Sjulian} 1057137500Sjulian 105865543Scg/* 1059137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving. 1060137500Sjulian * This function expects n not overwrapping the buffer boundary. 1061137500Sjulian * Note that n is measured in sample unit. 1062137500Sjulian * 1063137500Sjulian * XXX - this function works in 16bit stereo format only. 1064137500Sjulian */ 1065166279Sariffstatic void 1066137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n) 1067137500Sjulian{ 1068137500Sjulian int16_t *end; 1069137500Sjulian 1070137500Sjulian for (end = l + n; l < end; ) { 1071137500Sjulian *p++ = *l++; 1072137500Sjulian *p++ = *r++; 1073137500Sjulian } 1074137500Sjulian} 1075137500Sjulian 1076137500Sjulianstatic void 1077137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch) 1078137500Sjulian{ 1079137500Sjulian unsigned cur, last; 1080137500Sjulian int16_t *src2; 1081137500Sjulian 1082137500Sjulian agg_lock(ch->parent); 1083137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1084137500Sjulian agg_unlock(ch->parent); 1085137500Sjulian cur -= 0xffff & ((ch->srcphys - ch->base) >> 1); 1086137500Sjulian last = ch->hwptr; 1087137500Sjulian src2 = ch->src + ch->parent->bufsz/4; 1088137500Sjulian 1089137500Sjulian if (cur < last) { 1090137500Sjulian interleave(ch->src + last, src2 + last, 1091137500Sjulian ch->sink + 2*last, ch->buflen/2 - last); 1092137500Sjulian interleave(ch->src, src2, 1093137500Sjulian ch->sink, cur); 1094137500Sjulian } else if (cur > last) 1095137500Sjulian interleave(ch->src + last, src2 + last, 1096137500Sjulian ch->sink + 2*last, cur - last); 1097137500Sjulian ch->hwptr = cur; 1098137500Sjulian} 1099137500Sjulian 1100137500Sjulian/* 1101137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center. 1102137500Sjulian * This function expects n not overwrapping the buffer boundary. 1103137500Sjulian * Note that n is measured in sample unit. 1104137500Sjulian * 1105137500Sjulian * XXX - this function works in 16bit monoral format only. 1106137500Sjulian */ 1107166279Sariffstatic void 1108137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n) 1109137500Sjulian{ 1110137500Sjulian int16_t *end; 1111137500Sjulian 1112137500Sjulian for (end = dest + n; dest < end; dest++) 1113137500Sjulian *dest = (int16_t)(((int)*dest - (int)*src++) / 2); 1114137500Sjulian} 1115137500Sjulian 1116137500Sjulianstatic void 1117137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch) 1118137500Sjulian{ 1119137500Sjulian unsigned cur, last; 1120137500Sjulian 1121137500Sjulian agg_lock(ch->parent); 1122137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1123137500Sjulian agg_unlock(ch->parent); 1124137500Sjulian cur -= 0xffff & ((ch->phys - ch->base) >> 1); 1125137500Sjulian last = ch->hwptr; 1126137500Sjulian 1127137500Sjulian if (cur < last) { 1128137500Sjulian mixdown(ch->src + last, ch->sink + last, ch->buflen - last); 1129137500Sjulian mixdown(ch->src, ch->sink, cur); 1130137500Sjulian } else if (cur > last) 1131137500Sjulian mixdown(ch->src + last, ch->sink + last, cur - last); 1132137500Sjulian ch->hwptr = cur; 1133137500Sjulian} 1134137500Sjulian 1135166279Sariff#ifdef AGG_JITTER_CORRECTION 1136137500Sjulian/* 113765543Scg * Stereo jitter suppressor. 113865543Scg * Sometimes playback pointers differ in stereo-paired channels. 113965543Scg * Calling this routine within intr fixes the problem. 114065543Scg */ 1141166279Sariffstatic void 114265543Scgsuppress_jitter(struct agg_chinfo *ch) 114365543Scg{ 1144137500Sjulian if (ch->stereo) { 1145137500Sjulian int cp1, cp2, diff /*, halfsize*/ ; 114665543Scg 1147137500Sjulian /*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/ 1148137500Sjulian cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); 1149137500Sjulian cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR); 1150137500Sjulian if (cp1 != cp2) { 1151137500Sjulian diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); 1152137500Sjulian if (diff > 1 /* && diff < halfsize*/ ) 1153137500Sjulian AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); 1154137500Sjulian } 115565543Scg } 115665543Scg} 115765543Scg 1158166279Sariffstatic void 1159137500Sjuliansuppress_rec_jitter(struct agg_rchinfo *ch) 1160137500Sjulian{ 1161137500Sjulian int cp1, cp2, diff /*, halfsize*/ ; 1162137500Sjulian 1163137500Sjulian /*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/ 1164137500Sjulian cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1) 1165137500Sjulian + wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1166137500Sjulian cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR); 1167137500Sjulian if (cp1 != cp2) { 1168137500Sjulian diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); 1169137500Sjulian if (diff > 1 /* && diff < halfsize*/ ) 1170137500Sjulian AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); 1171137500Sjulian } 1172137500Sjulian} 1173166279Sariff#endif 1174137500Sjulian 1175166279Sariffstatic u_int 1176137500Sjuliancalc_timer_div(struct agg_chinfo *ch) 117765543Scg{ 1178137500Sjulian u_int speed; 117965543Scg 1180137500Sjulian speed = ch->speed; 1181137500Sjulian#ifdef INVARIANTS 1182137500Sjulian if (speed == 0) { 1183137500Sjulian printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n", 1184137500Sjulian ch->num); 1185137500Sjulian speed = 1; 1186137500Sjulian } 1187137500Sjulian#endif 1188137500Sjulian return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo)) 1189137500Sjulian + speed - 1) / speed; 1190137500Sjulian} 119165543Scg 1192166279Sariffstatic u_int 1193137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch) 1194137500Sjulian{ 1195137500Sjulian u_int speed; 1196137500Sjulian 1197137500Sjulian speed = ch->speed; 1198137500Sjulian#ifdef INVARIANTS 1199137500Sjulian if (speed == 0) { 1200137500Sjulian printf("snd_maestro: rch.speed == 0, which shouldn't\n"); 1201137500Sjulian speed = 1; 1202137500Sjulian } 1203137500Sjulian#endif 1204137500Sjulian return (48000 * (ch->blklen << (!ch->stereo)) 1205137500Sjulian + speed - 1) / speed; 120665543Scg} 120765543Scg 120865543Scgstatic void 120965543Scgset_timer(struct agg_info *ess) 121065543Scg{ 121165543Scg int i; 1212137500Sjulian u_int dv = 32 << 7, newdv; 121365543Scg 121465543Scg for (i = 0; i < ess->playchns; i++) 121565543Scg if ((ess->active & (1 << i)) && 1216137500Sjulian (dv > (newdv = calc_timer_div(ess->pch + i)))) 1217137500Sjulian dv = newdv; 1218137500Sjulian if ((ess->active & (1 << i)) && 1219137500Sjulian (dv > (newdv = calc_timer_div_rch(&ess->rch)))) 1220137500Sjulian dv = newdv; 122165543Scg 1222137500Sjulian wp_settimer(ess, dv); 122365543Scg} 122465543Scg 122565543Scg 122665543Scg/* ----------------------------- 122765543Scg * Newpcm glue. 122865543Scg */ 122965543Scg 1230137500Sjulian/* AC97 mixer interface. */ 1231137500Sjulian 1232137500Sjulianstatic u_int32_t 1233137500Sjulianagg_ac97_init(kobj_t obj, void *sc) 1234137500Sjulian{ 1235137500Sjulian struct agg_info *ess = sc; 1236137500Sjulian 1237137500Sjulian return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1; 1238137500Sjulian} 1239137500Sjulian 1240137500Sjulianstatic int 1241137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno) 1242137500Sjulian{ 1243137500Sjulian struct agg_info *ess = sc; 1244137500Sjulian int ret; 1245137500Sjulian 1246154067Sariff /* XXX sound locking violation: agg_lock(ess); */ 1247137500Sjulian ret = agg_rdcodec(ess, regno); 1248154067Sariff /* agg_unlock(ess); */ 1249137500Sjulian return ret; 1250137500Sjulian} 1251137500Sjulian 1252137500Sjulianstatic int 1253137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data) 1254137500Sjulian{ 1255137500Sjulian struct agg_info *ess = sc; 1256137500Sjulian int ret; 1257137500Sjulian 1258154067Sariff /* XXX sound locking violation: agg_lock(ess); */ 1259137500Sjulian ret = agg_wrcodec(ess, regno, data); 1260154067Sariff /* agg_unlock(ess); */ 1261137500Sjulian return ret; 1262137500Sjulian} 1263137500Sjulian 1264137500Sjulian 1265137500Sjulianstatic kobj_method_t agg_ac97_methods[] = { 1266137500Sjulian KOBJMETHOD(ac97_init, agg_ac97_init), 1267137500Sjulian KOBJMETHOD(ac97_read, agg_ac97_read), 1268137500Sjulian KOBJMETHOD(ac97_write, agg_ac97_write), 1269137500Sjulian { 0, 0 } 1270137500Sjulian}; 1271137500SjulianAC97_DECLARE(agg_ac97); 1272137500Sjulian 1273137500Sjulian 1274137500Sjulian/* -------------------------------------------------------------------- */ 1275137500Sjulian 1276137500Sjulian/* Playback channel. */ 1277137500Sjulian 127865543Scgstatic void * 1279166279Sariffaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 1280166279Sariff 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 }; 1460154241Sariff static struct pcmchan_caps playcaps = {8000, 48000, 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 * 1485166279Sariffaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 1486166279Sariff struct pcm_channel *c, int dir) 1487137500Sjulian{ 1488137500Sjulian struct agg_info *ess = devinfo; 1489137500Sjulian struct agg_rchinfo *ch; 1490137500Sjulian u_int8_t *p; 1491137500Sjulian 1492137500Sjulian KASSERT((dir == PCMDIR_REC), 1493137500Sjulian ("aggrch_init() called for PLAYBACK channel!")); 1494137500Sjulian ch = &ess->rch; 1495137500Sjulian 1496137500Sjulian ch->parent = ess; 1497137500Sjulian ch->channel = c; 1498137500Sjulian ch->buffer = b; 1499137500Sjulian 1500137500Sjulian /* Uses the bottom-half of the status buffer. */ 1501137500Sjulian p = ess->stat + ess->bufsz; 1502137500Sjulian ch->phys = ess->phys + ess->bufsz; 1503137500Sjulian ch->base = ess->phys; 1504137500Sjulian ch->src = (int16_t *)(p + ess->bufsz); 1505137500Sjulian ch->srcphys = ch->phys + ess->bufsz; 1506137500Sjulian ch->sink = (int16_t *)p; 1507137500Sjulian 1508137500Sjulian sndbuf_setup(b, p, ess->bufsz); 1509137500Sjulian ch->blklen = sndbuf_getblksz(b) / 2; 1510137500Sjulian ch->buflen = sndbuf_getsize(b) / 2; 1511137500Sjulian 1512137500Sjulian return ch; 1513137500Sjulian} 1514137500Sjulian 1515137500Sjulianstatic int 1516137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format) 1517137500Sjulian{ 1518137500Sjulian struct agg_rchinfo *ch = data; 1519137500Sjulian 1520137500Sjulian if (!(format & AFMT_S16_LE)) 1521137500Sjulian return EINVAL; 1522137500Sjulian if (format & AFMT_STEREO) 1523137500Sjulian ch->stereo = 1; 1524137500Sjulian else 1525137500Sjulian ch->stereo = 0; 1526137500Sjulian return 0; 1527137500Sjulian} 1528137500Sjulian 1529137500Sjulianstatic int 1530137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) 1531137500Sjulian{ 1532137500Sjulian return ((struct agg_rchinfo*)data)->speed = speed; 1533137500Sjulian} 1534137500Sjulian 1535137500Sjulianstatic int 1536137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 1537137500Sjulian{ 1538137500Sjulian struct agg_rchinfo *ch = data; 1539137500Sjulian int blkcnt; 1540137500Sjulian 1541137500Sjulian /* try to keep at least 20msec DMA space */ 1542137500Sjulian blkcnt = (ch->speed << ch->stereo) / (25 * blocksize); 1543137500Sjulian RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); 1544137500Sjulian 1545137500Sjulian if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { 1546137500Sjulian sndbuf_resize(ch->buffer, blkcnt, blocksize); 1547137500Sjulian blkcnt = sndbuf_getblkcnt(ch->buffer); 1548137500Sjulian blocksize = sndbuf_getblksz(ch->buffer); 1549137500Sjulian } else { 1550137500Sjulian sndbuf_setblkcnt(ch->buffer, blkcnt); 1551137500Sjulian sndbuf_setblksz(ch->buffer, blocksize); 1552137500Sjulian } 1553137500Sjulian 1554137500Sjulian ch->blklen = blocksize / 2; 1555137500Sjulian ch->buflen = blkcnt * blocksize / 2; 1556137500Sjulian return blocksize; 1557137500Sjulian} 1558137500Sjulian 1559137500Sjulianstatic int 1560137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go) 1561137500Sjulian{ 1562137500Sjulian struct agg_rchinfo *ch = sc; 1563137500Sjulian 1564137500Sjulian switch (go) { 1565137500Sjulian case PCMTRIG_EMLDMARD: 1566137500Sjulian if (ch->stereo) 1567137500Sjulian aggch_feed_adc_stereo(ch); 1568137500Sjulian else 1569137500Sjulian aggch_feed_adc_mono(ch); 1570137500Sjulian break; 1571137500Sjulian case PCMTRIG_START: 1572137500Sjulian aggch_start_adc(ch); 1573137500Sjulian break; 1574137500Sjulian case PCMTRIG_ABORT: 1575137500Sjulian case PCMTRIG_STOP: 1576137500Sjulian aggch_stop_adc(ch); 1577137500Sjulian break; 1578137500Sjulian } 1579137500Sjulian return 0; 1580137500Sjulian} 1581137500Sjulian 1582137500Sjulianstatic int 1583137500Sjulianaggrch_getptr(kobj_t obj, void *sc) 1584137500Sjulian{ 1585137500Sjulian struct agg_rchinfo *ch = sc; 1586137500Sjulian 1587137500Sjulian return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1; 1588137500Sjulian} 1589137500Sjulian 1590137500Sjulianstatic struct pcmchan_caps * 1591137500Sjulianaggrch_getcaps(kobj_t obj, void *sc) 1592137500Sjulian{ 159365543Scg static u_int32_t recfmt[] = { 159465543Scg AFMT_S16_LE, 159565543Scg AFMT_STEREO | AFMT_S16_LE, 159665543Scg 0 159765543Scg }; 1598137500Sjulian static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; 159965543Scg 1600137500Sjulian return &reccaps; 160165543Scg} 160265543Scg 1603137500Sjulianstatic kobj_method_t aggrch_methods[] = { 1604137500Sjulian KOBJMETHOD(channel_init, aggrch_init), 1605137500Sjulian /* channel_free: no-op */ 1606137500Sjulian KOBJMETHOD(channel_setformat, aggrch_setformat), 1607137500Sjulian KOBJMETHOD(channel_setspeed, aggrch_setspeed), 1608137500Sjulian KOBJMETHOD(channel_setblocksize, aggrch_setblocksize), 1609137500Sjulian KOBJMETHOD(channel_trigger, aggrch_trigger), 1610137500Sjulian KOBJMETHOD(channel_getptr, aggrch_getptr), 1611137500Sjulian KOBJMETHOD(channel_getcaps, aggrch_getcaps), 161270134Scg { 0, 0 } 161370134Scg}; 1614137500SjulianCHANNEL_DECLARE(aggrch); 161565543Scg 1616137500Sjulian 161765543Scg/* ----------------------------- 161865543Scg * Bus space. 161965543Scg */ 162065543Scg 162165543Scgstatic void 162265543Scgagg_intr(void *sc) 162365543Scg{ 162465543Scg struct agg_info* ess = sc; 1625137500Sjulian register u_int8_t status; 162665543Scg int i; 1627137500Sjulian u_int m; 162865543Scg 1629137500Sjulian status = AGG_RD(ess, PORT_HOSTINT_STAT, 1); 163065543Scg if (!status) 163165543Scg return; 163265543Scg 1633137500Sjulian /* Acknowledge intr. */ 1634137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, status, 1); 163570619Sjhb 1636137500Sjulian if (status & HOSTINT_STAT_DSOUND) { 1637137500Sjulian#ifdef AGG_JITTER_CORRECTION 1638137500Sjulian agg_lock(ess); 1639137500Sjulian#endif 1640137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) { 1641137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 1642137500Sjulian#ifdef AGG_JITTER_CORRECTION 1643137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1644137500Sjulian if (ess->active & m) 1645137500Sjulian suppress_jitter(ess->pch + i); 1646137500Sjulian } 1647137500Sjulian if (ess->active & m) 1648137500Sjulian suppress_rec_jitter(&ess->rch); 1649137500Sjulian agg_unlock(ess); 1650137500Sjulian#endif 1651137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1652137500Sjulian if (ess->active & m) { 1653137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) 1654137500Sjulian chn_intr(ess->pch[i].channel); 1655137500Sjulian else { 1656137500Sjulian m = 0; 1657137500Sjulian break; 1658137500Sjulian } 1659137500Sjulian } 1660137500Sjulian } 1661137500Sjulian if ((ess->active & m) 1662137500Sjulian && ess->curpwr <= PCI_POWERSTATE_D1) 1663137500Sjulian chn_intr(ess->rch.channel); 1664137500Sjulian } 1665137500Sjulian#ifdef AGG_JITTER_CORRECTION 1666137500Sjulian else 1667137500Sjulian agg_unlock(ess); 1668137500Sjulian#endif 1669137500Sjulian } 1670137500Sjulian 167165543Scg if (status & HOSTINT_STAT_HWVOL) { 1672137500Sjulian register u_int8_t event; 167370619Sjhb 1674137500Sjulian agg_lock(ess); 1675137500Sjulian event = AGG_RD(ess, PORT_HWVOL_MASTER, 1); 1676137500Sjulian AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1); 1677137500Sjulian agg_unlock(ess); 1678137500Sjulian 167970619Sjhb switch (event) { 168070619Sjhb case HWVOL_UP: 168170945Sjhb mixer_hwvol_step(ess->dev, 1, 1); 168270619Sjhb break; 168370619Sjhb case HWVOL_DOWN: 168470945Sjhb mixer_hwvol_step(ess->dev, -1, -1); 168570619Sjhb break; 168670619Sjhb case HWVOL_NOP: 168770619Sjhb break; 168870619Sjhb default: 1689137500Sjulian if (event & HWVOL_MUTE) { 1690137500Sjulian mixer_hwvol_mute(ess->dev); 1691137500Sjulian break; 1692137500Sjulian } 1693137500Sjulian device_printf(ess->dev, 1694137500Sjulian "%s: unknown HWVOL event 0x%x\n", 1695137500Sjulian device_get_nameunit(ess->dev), event); 169665543Scg } 169765543Scg } 169865543Scg} 169965543Scg 170065543Scgstatic void 170165543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 170265543Scg{ 170365543Scg bus_addr_t *phys = arg; 170465543Scg 170565543Scg *phys = error? 0 : segs->ds_addr; 170665543Scg 170765543Scg if (bootverbose) { 170865543Scg printf("setmap (%lx, %lx), nseg=%d, error=%d\n", 170965543Scg (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 171065543Scg nseg, error); 171165543Scg } 171265543Scg} 171365543Scg 171465543Scgstatic void * 1715137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys) 171665543Scg{ 171765543Scg void *buf; 171865543Scg bus_dmamap_t map; 171965543Scg 1720137500Sjulian if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map)) 172165543Scg return NULL; 1722137500Sjulian if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0) 1723137500Sjulian || !*phys || map) { 1724137500Sjulian bus_dmamem_free(dmat, buf, map); 172565543Scg return NULL; 172665543Scg } 172765543Scg return buf; 172865543Scg} 172965543Scg 173065543Scgstatic void 1731137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf) 173265543Scg{ 1733137500Sjulian bus_dmamem_free(dmat, buf, NULL); 173465543Scg} 173565543Scg 173665543Scgstatic int 173765543Scgagg_probe(device_t dev) 173865543Scg{ 173965543Scg char *s = NULL; 174065543Scg 174165543Scg switch (pci_get_devid(dev)) { 174265543Scg case MAESTRO_1_PCI_ID: 174365543Scg s = "ESS Technology Maestro-1"; 174465543Scg break; 174565543Scg 174665543Scg case MAESTRO_2_PCI_ID: 174765543Scg s = "ESS Technology Maestro-2"; 174865543Scg break; 174965543Scg 175065543Scg case MAESTRO_2E_PCI_ID: 175165543Scg s = "ESS Technology Maestro-2E"; 175265543Scg break; 175365543Scg } 175465543Scg 175565543Scg if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { 175665543Scg device_set_desc(dev, s); 1757142890Simp return BUS_PROBE_DEFAULT; 175865543Scg } 175965543Scg return ENXIO; 176065543Scg} 176165543Scg 176265543Scgstatic int 176365543Scgagg_attach(device_t dev) 176465543Scg{ 176565543Scg struct agg_info *ess = NULL; 176665543Scg u_int32_t data; 1767119690Sjhb int regid = PCIR_BAR(0); 176865543Scg struct resource *reg = NULL; 176965543Scg struct ac97_info *codec = NULL; 177065543Scg int irqid = 0; 177165543Scg struct resource *irq = NULL; 177265543Scg void *ih = NULL; 177365543Scg char status[SND_STATUSLEN]; 1774137500Sjulian int ret = 0; 177565543Scg 177678564Sgreid if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 177765543Scg device_printf(dev, "cannot allocate softc\n"); 1778137500Sjulian ret = ENOMEM; 1779137500Sjulian goto bad; 178065543Scg } 178165543Scg ess->dev = dev; 178265543Scg 1783137500Sjulian#ifdef USING_MUTEX 1784137500Sjulian mtx_init(&ess->lock, device_get_desc(dev), "hardware status lock", 1785137500Sjulian MTX_DEF | MTX_RECURSE); 1786137500Sjulian if (!mtx_initialized(&ess->lock)) { 1787137500Sjulian device_printf(dev, "failed to create a mutex.\n"); 1788137500Sjulian ret = ENOMEM; 1789137500Sjulian goto bad; 1790137500Sjulian } 1791137500Sjulian#endif 1792137500Sjulian 179384658Scg ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); 1794137500Sjulian if (bus_dma_tag_create(/*parent*/ NULL, 1795137500Sjulian /*align */ 4, 1 << (16+1), 1796137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1797137500Sjulian /*filter*/ NULL, NULL, 1798137500Sjulian /*size */ ess->bufsz, 1, 0x3ffff, 1799137500Sjulian /*flags */ 0, 1800137500Sjulian#if __FreeBSD_version >= 501102 1801137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1802137500Sjulian#endif 1803137500Sjulian &ess->buf_dmat) != 0) { 1804137500Sjulian device_printf(dev, "unable to create dma tag\n"); 1805137500Sjulian ret = ENOMEM; 1806137500Sjulian goto bad; 1807137500Sjulian } 180884658Scg 180965543Scg if (bus_dma_tag_create(/*parent*/NULL, 1810137500Sjulian /*align */ 1 << WAVCACHE_BASEADDR_SHIFT, 1811137500Sjulian 1 << (16+1), 1812137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1813137500Sjulian /*filter*/ NULL, NULL, 1814137500Sjulian /*size */ 3*ess->bufsz, 1, 0x3ffff, 1815137500Sjulian /*flags */ 0, 1816137500Sjulian#if __FreeBSD_version >= 501102 1817137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1818137500Sjulian#endif 1819137500Sjulian &ess->stat_dmat) != 0) { 182065543Scg device_printf(dev, "unable to create dma tag\n"); 1821137500Sjulian ret = ENOMEM; 182265543Scg goto bad; 182365543Scg } 182465543Scg 1825137500Sjulian /* Allocate the room for brain-damaging status buffer. */ 1826137500Sjulian ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys); 182765543Scg if (ess->stat == NULL) { 1828137500Sjulian device_printf(dev, "cannot allocate status buffer\n"); 1829137500Sjulian ret = ENOMEM; 183065543Scg goto bad; 183165543Scg } 183265543Scg if (bootverbose) 1833137500Sjulian device_printf(dev, "Maestro status/record buffer: %#llx\n", 1834137500Sjulian (long long)ess->phys); 183565543Scg 1836137500Sjulian /* State D0-uninitialized. */ 1837137500Sjulian ess->curpwr = PCI_POWERSTATE_D3; 1838137500Sjulian pci_set_powerstate(dev, PCI_POWERSTATE_D0); 183965543Scg 184065543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 184165543Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); 184265543Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 184365543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 184465543Scg 1845137500Sjulian /* Allocate resources. */ 1846137500Sjulian if (data & PCIM_CMD_PORTEN) 1847141095Simp reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, 1848141095Simp RF_ACTIVE); 1849137500Sjulian if (reg != NULL) { 1850137500Sjulian ess->reg = reg; 1851137500Sjulian ess->regid = regid; 1852137500Sjulian ess->st = rman_get_bustag(reg); 1853137500Sjulian ess->sh = rman_get_bushandle(reg); 1854137500Sjulian } else { 185565543Scg device_printf(dev, "unable to map register space\n"); 1856137500Sjulian ret = ENXIO; 185765543Scg goto bad; 185865543Scg } 1859141095Simp irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, 1860141095Simp RF_ACTIVE | RF_SHAREABLE); 1861137500Sjulian if (irq != NULL) { 1862137500Sjulian ess->irq = irq; 1863137500Sjulian ess->irqid = irqid; 1864137500Sjulian } else { 1865137500Sjulian device_printf(dev, "unable to map interrupt\n"); 1866137500Sjulian ret = ENXIO; 1867137500Sjulian goto bad; 1868137500Sjulian } 186965543Scg 1870137500Sjulian /* Setup resources. */ 1871137500Sjulian if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) { 1872137500Sjulian device_printf(dev, "unable to setup interrupt\n"); 1873137500Sjulian ret = ENXIO; 1874137500Sjulian goto bad; 1875137500Sjulian } else 1876137500Sjulian ess->ih = ih; 1877137500Sjulian 1878137500Sjulian /* Transition from D0-uninitialized to D0. */ 1879137500Sjulian agg_lock(ess); 1880137500Sjulian agg_power(ess, PCI_POWERSTATE_D0); 1881137500Sjulian if (agg_rdcodec(ess, 0) == 0x80) { 1882137500Sjulian /* XXX - TODO: PT101 */ 1883154067Sariff agg_unlock(ess); 188465543Scg device_printf(dev, "PT101 codec detected!\n"); 1885137500Sjulian ret = ENXIO; 188665543Scg goto bad; 188765543Scg } 1888154067Sariff agg_unlock(ess); 188970134Scg codec = AC97_CREATE(dev, ess, agg_ac97); 1890137500Sjulian if (codec == NULL) { 1891137500Sjulian device_printf(dev, "failed to create AC97 codec softc!\n"); 1892137500Sjulian ret = ENOMEM; 189365543Scg goto bad; 1894137500Sjulian } 1895137500Sjulian if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { 1896137500Sjulian device_printf(dev, "mixer initialization failed!\n"); 1897137500Sjulian ret = ENXIO; 189865543Scg goto bad; 1899137500Sjulian } 190065543Scg ess->codec = codec; 190165543Scg 1902137500Sjulian ret = pcm_register(dev, ess, AGG_MAXPLAYCH, 1); 1903137500Sjulian if (ret) 190465543Scg goto bad; 190565543Scg 190670945Sjhb mixer_hwvol_init(dev); 1907154067Sariff agg_lock(ess); 1908137500Sjulian agg_power(ess, powerstate_init); 1909154067Sariff agg_unlock(ess); 191065543Scg for (data = 0; data < AGG_MAXPLAYCH; data++) 1911137500Sjulian pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess); 191270134Scg pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); 1913137500Sjulian adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); 1914137500Sjulian 1915137500Sjulian snprintf(status, SND_STATUSLEN, 1916137500Sjulian "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d", 1917137500Sjulian rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), 1918137500Sjulian pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); 191965543Scg pcm_setstatus(dev, status); 192065543Scg 192165543Scg return 0; 192265543Scg 192365543Scg bad: 192465644Scg if (codec != NULL) 192565644Scg ac97_destroy(codec); 192665543Scg if (ih != NULL) 192765543Scg bus_teardown_intr(dev, irq, ih); 192865543Scg if (irq != NULL) 192965543Scg bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 193065543Scg if (reg != NULL) 193165543Scg bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); 1932154627Snetchild if (ess != NULL) { 1933154627Snetchild if (ess->stat != NULL) 1934154627Snetchild dma_free(ess->stat_dmat, ess->stat); 1935154627Snetchild if (ess->stat_dmat != NULL) 1936154627Snetchild bus_dma_tag_destroy(ess->stat_dmat); 1937154627Snetchild if (ess->buf_dmat != NULL) 1938154627Snetchild bus_dma_tag_destroy(ess->buf_dmat); 1939137500Sjulian#ifdef USING_MUTEX 1940154627Snetchild if (mtx_initialized(&ess->lock)) 1941154627Snetchild mtx_destroy(&ess->lock); 1942137500Sjulian#endif 194365543Scg free(ess, M_DEVBUF); 1944154627Snetchild } 194565543Scg 1946137500Sjulian return ret; 194765543Scg} 194865543Scg 194965543Scgstatic int 195065543Scgagg_detach(device_t dev) 195165543Scg{ 195265543Scg struct agg_info *ess = pcm_getdevinfo(dev); 195365543Scg int r; 1954137500Sjulian u_int16_t icr; 195565543Scg 1956137500Sjulian icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2); 1957137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 1958137500Sjulian 1959137500Sjulian agg_lock(ess); 1960137500Sjulian if (ess->active) { 1961137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 1962137500Sjulian agg_unlock(ess); 1963137500Sjulian return EBUSY; 1964137500Sjulian } 1965137500Sjulian agg_unlock(ess); 1966137500Sjulian 196765543Scg r = pcm_unregister(dev); 1968137500Sjulian if (r) { 1969137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 197065543Scg return r; 1971137500Sjulian } 197265543Scg 1973137500Sjulian agg_lock(ess); 1974137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 1975154067Sariff agg_unlock(ess); 197665543Scg 197765543Scg bus_teardown_intr(dev, ess->irq, ess->ih); 197865543Scg bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); 197965543Scg bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); 1980137500Sjulian dma_free(ess->stat_dmat, ess->stat); 1981137500Sjulian bus_dma_tag_destroy(ess->stat_dmat); 1982137500Sjulian bus_dma_tag_destroy(ess->buf_dmat); 1983137500Sjulian#ifdef USING_MUTEX 1984137500Sjulian mtx_destroy(&ess->lock); 1985137500Sjulian#endif 198665543Scg free(ess, M_DEVBUF); 198765543Scg return 0; 198865543Scg} 198965543Scg 199065543Scgstatic int 199165543Scgagg_suspend(device_t dev) 199265543Scg{ 199365543Scg struct agg_info *ess = pcm_getdevinfo(dev); 1994137500Sjulian#ifndef USING_MUTEX 1995137500Sjulian int x; 199665543Scg 199765543Scg x = spltty(); 199865543Scg#endif 1999137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 2000137500Sjulian agg_lock(ess); 2001137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 2002137500Sjulian agg_unlock(ess); 2003137500Sjulian#ifndef USING_MUTEX 200465543Scg splx(x); 2005137500Sjulian#endif 200665543Scg 200765543Scg return 0; 200865543Scg} 200965543Scg 201065543Scgstatic int 201165543Scgagg_resume(device_t dev) 201265543Scg{ 2013137500Sjulian int i; 201465543Scg struct agg_info *ess = pcm_getdevinfo(dev); 2015137500Sjulian#ifndef USING_MUTEX 2016137500Sjulian int x; 201765543Scg 201865543Scg x = spltty(); 2019137500Sjulian#endif 202065543Scg for (i = 0; i < ess->playchns; i++) 202165543Scg if (ess->active & (1 << i)) 202265543Scg aggch_start_dac(ess->pch + i); 202365543Scg if (ess->active & (1 << i)) 202465543Scg aggch_start_adc(&ess->rch); 2025137500Sjulian 2026137500Sjulian agg_lock(ess); 2027137500Sjulian if (!ess->active) 2028137500Sjulian agg_power(ess, powerstate_init); 2029137500Sjulian agg_unlock(ess); 2030137500Sjulian#ifndef USING_MUTEX 2031137500Sjulian splx(x); 203265543Scg#endif 2033137500Sjulian 2034137500Sjulian if (mixer_reinit(dev)) { 2035137500Sjulian device_printf(dev, "unable to reinitialize the mixer\n"); 2036137500Sjulian return ENXIO; 203765543Scg } 2038137500Sjulian 203965543Scg return 0; 204065543Scg} 204165543Scg 204265543Scgstatic int 204365543Scgagg_shutdown(device_t dev) 204465543Scg{ 204565543Scg struct agg_info *ess = pcm_getdevinfo(dev); 204665543Scg 2047137500Sjulian agg_lock(ess); 2048137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 2049137500Sjulian agg_unlock(ess); 205065543Scg 205165543Scg return 0; 205265543Scg} 205365543Scg 205465543Scg 205565543Scgstatic device_method_t agg_methods[] = { 205665543Scg DEVMETHOD(device_probe, agg_probe), 205765543Scg DEVMETHOD(device_attach, agg_attach), 205865543Scg DEVMETHOD(device_detach, agg_detach), 205965543Scg DEVMETHOD(device_suspend, agg_suspend), 206065543Scg DEVMETHOD(device_resume, agg_resume), 206165543Scg DEVMETHOD(device_shutdown, agg_shutdown), 206265543Scg 206365543Scg { 0, 0 } 206465543Scg}; 206565543Scg 206665543Scgstatic driver_t agg_driver = { 206765543Scg "pcm", 206865543Scg agg_methods, 206982180Scg PCM_SOFTC_SIZE, 207065543Scg}; 207165543Scg 2072137500Sjulian/*static devclass_t pcm_devclass;*/ 2073137500Sjulian 207465543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); 2075132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 207665543ScgMODULE_VERSION(snd_maestro, 1); 2077