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 50193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 51193640Sariff#include "opt_snd.h" 52193640Sariff#endif 53193640Sariff 5465543Scg#include <dev/sound/pcm/sound.h> 5565543Scg#include <dev/sound/pcm/ac97.h> 56119287Simp#include <dev/pci/pcireg.h> 57119287Simp#include <dev/pci/pcivar.h> 5865543Scg 5965543Scg#include <dev/sound/pci/maestro_reg.h> 6065543Scg 6182180ScgSND_DECLARE_FILE("$FreeBSD: releng/11.0/sys/dev/sound/pci/maestro.c 297000 2016-03-18 01:28:41Z jhibbits $"); 6282180Scg 6365543Scg/* 6465543Scg * PCI IDs of supported chips: 6565543Scg * 6665543Scg * MAESTRO-1 0x01001285 6765543Scg * MAESTRO-2 0x1968125d 6865543Scg * MAESTRO-2E 0x1978125d 6965543Scg */ 7065543Scg 7165543Scg#define MAESTRO_1_PCI_ID 0x01001285 7265543Scg#define MAESTRO_2_PCI_ID 0x1968125d 7365543Scg#define MAESTRO_2E_PCI_ID 0x1978125d 7465543Scg 7565543Scg#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ 7665543Scg#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ 7765543Scg 78137500Sjulian#ifdef AGG_MAXPLAYCH 79137500Sjulian# if AGG_MAXPLAYCH > 4 80137500Sjulian# undef AGG_MAXPLAYCH 81137500Sjulian# define AGG_MAXPLAYCH 4 82137500Sjulian# endif 83137500Sjulian#else 8465543Scg# define AGG_MAXPLAYCH 4 8565543Scg#endif 8665543Scg 8784658Scg#define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ 8865543Scg 8965543Scg 90137500Sjulian#ifndef PCIR_BAR 91137500Sjulian#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) 92137500Sjulian#endif 93137500Sjulian 94137500Sjulian 9565543Scg/* ----------------------------- 9665543Scg * Data structures. 9765543Scg */ 9865543Scgstruct agg_chinfo { 99137500Sjulian /* parent softc */ 10065543Scg struct agg_info *parent; 101137500Sjulian 102137500Sjulian /* FreeBSD newpcm related */ 10374763Scg struct pcm_channel *channel; 10474763Scg struct snd_dbuf *buffer; 105137500Sjulian 106137500Sjulian /* OS independent */ 107267581Sjhb bus_dmamap_t map; 108137500Sjulian bus_addr_t phys; /* channel buffer physical address */ 109137500Sjulian bus_addr_t base; /* channel buffer segment base */ 110137500Sjulian u_int32_t blklen; /* DMA block length in WORDs */ 111137500Sjulian u_int32_t buflen; /* channel buffer length in WORDs */ 11270291Scg u_int32_t speed; 113137500Sjulian unsigned num : 3; 114137500Sjulian unsigned stereo : 1; 115137500Sjulian unsigned qs16 : 1; /* quantum size is 16bit */ 116137500Sjulian unsigned us : 1; /* in unsigned format */ 11765543Scg}; 11865543Scg 119137500Sjulianstruct agg_rchinfo { 120137500Sjulian /* parent softc */ 121137500Sjulian struct agg_info *parent; 122137500Sjulian 123137500Sjulian /* FreeBSD newpcm related */ 124137500Sjulian struct pcm_channel *channel; 125137500Sjulian struct snd_dbuf *buffer; 126137500Sjulian 127137500Sjulian /* OS independent */ 128267581Sjhb bus_dmamap_t map; 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 struct mtx lock; /* mutual exclusion */ 161137500Sjulian /* FreeBSD newpcm related */ 16265543Scg struct ac97_info *codec; 16365543Scg 164137500Sjulian /* OS independent */ 165267581Sjhb bus_dmamap_t stat_map; 166137500Sjulian u_int8_t *stat; /* status buffer pointer */ 167137500Sjulian bus_addr_t phys; /* status buffer physical address */ 168137500Sjulian unsigned int bufsz; /* channel buffer size in bytes */ 169137500Sjulian u_int playchns; 170137500Sjulian volatile u_int active; 17165543Scg struct agg_chinfo pch[AGG_MAXPLAYCH]; 172137500Sjulian struct agg_rchinfo rch; 173137500Sjulian volatile u_int8_t curpwr; /* current power status: D[0-3] */ 17465543Scg}; 17565543Scg 176137500Sjulian 177137500Sjulian/* ----------------------------- 178137500Sjulian * Sysctls for debug. 179137500Sjulian */ 180137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1; 181137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE 182137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D2; 183137500Sjulian#else 184137500Sjulianstatic unsigned int powerstate_idle = PCI_POWERSTATE_D1; 185137500Sjulian#endif 186137500Sjulianstatic unsigned int powerstate_init = PCI_POWERSTATE_D2; 187137500Sjulian 188159732Snetchild/* XXX: this should move to a device specific sysctl dev.pcm.X.debug.Y via 189159732Snetchild device_get_sysctl_*() as discussed on multimedia@ in msg-id 190159732Snetchild <861wujij2q.fsf@xps.des.no> */ 191227309Sedstatic SYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, ""); 192137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW, 193137500Sjulian &powerstate_active, 0, "The Dx power state when active (0-1)"); 194137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW, 195137500Sjulian &powerstate_idle, 0, "The Dx power state when idle (0-2)"); 196137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW, 197166279Sariff &powerstate_init, 0, 198166279Sariff "The Dx power state prior to the first use (0-2)"); 199137500Sjulian 200137500Sjulian 201137500Sjulian/* ----------------------------- 202137500Sjulian * Prototypes 203137500Sjulian */ 204137500Sjulian 205166279Sariffstatic void agg_sleep(struct agg_info*, const char *wmesg, int msec); 206137500Sjulian 207260112Sdim#if 0 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); 211260112Sdim#endif 212166279Sariffstatic int agg_rdcodec(struct agg_info*, int); 213166279Sariffstatic int agg_wrcodec(struct agg_info*, int, u_int32_t); 214137500Sjulian 215166279Sariffstatic void ringbus_setdest(struct agg_info*, int, int); 216137500Sjulian 217166279Sariffstatic u_int16_t wp_rdreg(struct agg_info*, u_int16_t); 218166279Sariffstatic void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); 219166279Sariffstatic u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); 220166279Sariffstatic void wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t); 221166279Sariffstatic void wp_settimer(struct agg_info*, u_int); 222166279Sariffstatic void wp_starttimer(struct agg_info*); 223166279Sariffstatic void wp_stoptimer(struct agg_info*); 22465543Scg 225166279Sariff#if 0 226166279Sariffstatic u_int16_t wc_rdreg(struct agg_info*, u_int16_t); 227166279Sariff#endif 228166279Sariffstatic void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); 229166279Sariff#if 0 230166279Sariffstatic u_int16_t wc_rdchctl(struct agg_info*, int); 231166279Sariff#endif 232166279Sariffstatic void wc_wrchctl(struct agg_info*, int, u_int16_t); 23365543Scg 234166279Sariffstatic void agg_stopclock(struct agg_info*, int part, int st); 23565543Scg 236166279Sariffstatic void agg_initcodec(struct agg_info*); 237166279Sariffstatic void agg_init(struct agg_info*); 238166279Sariffstatic void agg_power(struct agg_info*, int); 23965543Scg 240166279Sariffstatic void aggch_start_dac(struct agg_chinfo*); 241166279Sariffstatic void aggch_stop_dac(struct agg_chinfo*); 242166279Sariffstatic void aggch_start_adc(struct agg_rchinfo*); 243166279Sariffstatic void aggch_stop_adc(struct agg_rchinfo*); 244166279Sariffstatic void aggch_feed_adc_stereo(struct agg_rchinfo*); 245166279Sariffstatic void aggch_feed_adc_mono(struct agg_rchinfo*); 24665543Scg 247166279Sariff#ifdef AGG_JITTER_CORRECTION 248166279Sariffstatic void suppress_jitter(struct agg_chinfo*); 249166279Sariffstatic void suppress_rec_jitter(struct agg_rchinfo*); 250166279Sariff#endif 25165543Scg 252166279Sariffstatic void set_timer(struct agg_info*); 25365543Scg 254166279Sariffstatic void agg_intr(void *); 255166279Sariffstatic int agg_probe(device_t); 256166279Sariffstatic int agg_attach(device_t); 257166279Sariffstatic int agg_detach(device_t); 258166279Sariffstatic int agg_suspend(device_t); 259166279Sariffstatic int agg_resume(device_t); 260166279Sariffstatic int agg_shutdown(device_t); 26165543Scg 262267581Sjhbstatic void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*, 263267581Sjhb bus_dmamap_t *); 264267581Sjhbstatic void dma_free(bus_dma_tag_t, void *, bus_dmamap_t); 26565543Scg 266137500Sjulian 26765543Scg/* ----------------------------- 26865543Scg * Subsystems. 26965543Scg */ 27065543Scg 271137500Sjulian/* locking */ 272166279Sariff#define agg_lock(sc) snd_mtxlock(&((sc)->lock)) 273166279Sariff#define agg_unlock(sc) snd_mtxunlock(&((sc)->lock)) 27465543Scg 275166279Sariffstatic void 276137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec) 277137500Sjulian{ 278137500Sjulian int timo; 279137500Sjulian 280137500Sjulian timo = msec * hz / 1000; 281137500Sjulian if (timo == 0) 282137500Sjulian timo = 1; 283137500Sjulian msleep(sc, &sc->lock, PWAIT, wmesg, timo); 284137500Sjulian} 285137500Sjulian 286137500Sjulian 287137500Sjulian/* I/O port */ 288137500Sjulian 289260112Sdim#if 0 290166279Sariffstatic __inline u_int32_t 291137500Sjulianagg_rd(struct agg_info *sc, int regno, int size) 292137500Sjulian{ 293137500Sjulian switch (size) { 294137500Sjulian case 1: 295137500Sjulian return bus_space_read_1(sc->st, sc->sh, regno); 296137500Sjulian case 2: 297137500Sjulian return bus_space_read_2(sc->st, sc->sh, regno); 298137500Sjulian case 4: 299137500Sjulian return bus_space_read_4(sc->st, sc->sh, regno); 300137500Sjulian default: 301137500Sjulian return ~(u_int32_t)0; 302137500Sjulian } 303137500Sjulian} 304260112Sdim#endif 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 311260112Sdim#if 0 312166279Sariffstatic __inline void 313137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size) 314137500Sjulian{ 315137500Sjulian switch (size) { 316137500Sjulian case 1: 317137500Sjulian bus_space_write_1(sc->st, sc->sh, regno, data); 318137500Sjulian break; 319137500Sjulian case 2: 320137500Sjulian bus_space_write_2(sc->st, sc->sh, regno, data); 321137500Sjulian break; 322137500Sjulian case 4: 323137500Sjulian bus_space_write_4(sc->st, sc->sh, regno, data); 324137500Sjulian break; 325137500Sjulian } 326137500Sjulian} 327260112Sdim#endif 328137500Sjulian 329137500Sjulian#define AGG_WR(sc, regno, data, size) \ 330137500Sjulian bus_space_write_##size( \ 331137500Sjulian ((struct agg_info*)(sc))->st, \ 332137500Sjulian ((struct agg_info*)(sc))->sh, (regno), (data)) 333137500Sjulian 33470134Scg/* -------------------------------------------------------------------- */ 33570134Scg 336137500Sjulian/* Codec/Ringbus */ 337137500Sjulian 338166279Sariffstatic int 339137500Sjulianagg_codec_wait4idle(struct agg_info *ess) 34065543Scg{ 341137500Sjulian unsigned t = 26; 34270134Scg 343137500Sjulian while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { 344137500Sjulian if (--t == 0) 345137500Sjulian return EBUSY; 346137500Sjulian DELAY(2); /* 20.8us / 13 */ 347137500Sjulian } 348137500Sjulian return 0; 34970134Scg} 35070134Scg 351137500Sjulian 352166279Sariffstatic int 353137500Sjulianagg_rdcodec(struct agg_info *ess, int regno) 35470134Scg{ 355137500Sjulian int ret; 35665543Scg 35765543Scg /* We have to wait for a SAFE time to write addr/data */ 358137500Sjulian if (agg_codec_wait4idle(ess)) { 359137500Sjulian /* Timed out. No read performed. */ 360137500Sjulian device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); 361137500Sjulian return -1; 36265543Scg } 36365543Scg 364137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); 365137500Sjulian /*DELAY(21); * AC97 cycle = 20.8usec */ 36665543Scg 36765543Scg /* Wait for data retrieve */ 368137500Sjulian if (!agg_codec_wait4idle(ess)) { 369137500Sjulian ret = AGG_RD(ess, PORT_CODEC_REG, 2); 370137500Sjulian } else { 371137500Sjulian /* Timed out. No read performed. */ 372137500Sjulian device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); 373137500Sjulian ret = -1; 37465543Scg } 37565543Scg 376137500Sjulian return ret; 37765543Scg} 37865543Scg 379166279Sariffstatic int 380137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data) 38165543Scg{ 38265543Scg /* We have to wait for a SAFE time to write addr/data */ 383137500Sjulian if (agg_codec_wait4idle(ess)) { 38465543Scg /* Timed out. Abort writing. */ 38565543Scg device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); 38670134Scg return -1; 38765543Scg } 38865543Scg 389137500Sjulian AGG_WR(ess, PORT_CODEC_REG, data, 2); 390137500Sjulian AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); 39170134Scg 392137500Sjulian /* Wait for write completion */ 393137500Sjulian if (agg_codec_wait4idle(ess)) { 394137500Sjulian /* Timed out. */ 395137500Sjulian device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); 396137500Sjulian return -1; 397137500Sjulian } 398137500Sjulian 39970134Scg return 0; 40065543Scg} 40165543Scg 402166279Sariffstatic void 40365543Scgringbus_setdest(struct agg_info *ess, int src, int dest) 40465543Scg{ 40565543Scg u_int32_t data; 40665543Scg 407137500Sjulian data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); 40865543Scg data &= ~(0xfU << src); 40965543Scg data |= (0xfU & dest) << src; 410137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); 41165543Scg} 41265543Scg 413137500Sjulian/* -------------------------------------------------------------------- */ 414137500Sjulian 41565543Scg/* Wave Processor */ 41665543Scg 417166279Sariffstatic u_int16_t 41865543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg) 41965543Scg{ 420137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 421137500Sjulian return AGG_RD(ess, PORT_DSP_DATA, 2); 42265543Scg} 42365543Scg 424166279Sariffstatic void 42565543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 42665543Scg{ 427137500Sjulian AGG_WR(ess, PORT_DSP_INDEX, reg, 2); 428137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 42965543Scg} 43065543Scg 431166279Sariffstatic int 432137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data) 43365543Scg{ 434137500Sjulian unsigned t = 0; 43565543Scg 436137500Sjulian while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { 437137500Sjulian if (++t == 1000) { 438137500Sjulian return EAGAIN; 439137500Sjulian } 440137500Sjulian AGG_WR(ess, PORT_DSP_DATA, data, 2); 44165543Scg } 442137500Sjulian 443137500Sjulian return 0; 44465543Scg} 44565543Scg 446166279Sariffstatic u_int16_t 447137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg) 44865543Scg{ 449137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 450137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) != 0) 451137500Sjulian device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); 452137500Sjulian return wp_rdreg(ess, WPREG_DATA_PORT); 45365543Scg} 45465543Scg 455166279Sariffstatic void 456137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data) 45765543Scg{ 458137500Sjulian wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); 459137500Sjulian if (wp_wait_data(ess, reg | (ch << 4)) == 0) { 460137500Sjulian wp_wrreg(ess, WPREG_DATA_PORT, data); 461137500Sjulian if (wp_wait_data(ess, data) != 0) 462166279Sariff device_printf(ess->dev, 463166279Sariff "wp_wrapu() write timed out.\n"); 464137500Sjulian } else { 465137500Sjulian device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); 46665543Scg } 46765543Scg} 46865543Scg 469137573Srustatic void 470137500Sjulianapu_setparam(struct agg_info *ess, int apuch, 471137500Sjulian u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) 47265543Scg{ 473137500Sjulian wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); 474137500Sjulian wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); 475137500Sjulian wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); 476137500Sjulian wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); 477137500Sjulian wp_wrapu(ess, apuch, APUREG_ROUTING, 0); 478137500Sjulian wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); 479137500Sjulian wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 480137500Sjulian | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) 481137500Sjulian | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); 482137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, 483137500Sjulian APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 484137500Sjulian wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); 485137500Sjulian} 48665543Scg 487166279Sariffstatic void 488137500Sjulianwp_settimer(struct agg_info *ess, u_int divide) 489137500Sjulian{ 490137500Sjulian u_int prescale = 0; 49165543Scg 492137500Sjulian RANGE(divide, 2, 32 << 7); 493137500Sjulian 494137500Sjulian for (; divide > 32; divide >>= 1) { 49565543Scg prescale++; 496137500Sjulian divide++; 497137500Sjulian } 49865543Scg 49965543Scg for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 50065543Scg prescale++; 50165543Scg 50265543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 503137500Sjulian wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | 50465543Scg (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 50565543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 50665543Scg} 50765543Scg 508166279Sariffstatic void 50965543Scgwp_starttimer(struct agg_info *ess) 51065543Scg{ 511137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 512137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED 513137500Sjulian | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 51465543Scg wp_wrreg(ess, WPREG_TIMER_START, 1); 51565543Scg} 51665543Scg 517166279Sariffstatic void 51865543Scgwp_stoptimer(struct agg_info *ess) 51965543Scg{ 520137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED 521137500Sjulian & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); 522137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 52365543Scg wp_wrreg(ess, WPREG_TIMER_START, 0); 52465543Scg} 52565543Scg 526137500Sjulian/* -------------------------------------------------------------------- */ 527137500Sjulian 52865543Scg/* WaveCache */ 52965543Scg 530166279Sariff#if 0 531166279Sariffstatic u_int16_t 53265543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg) 53365543Scg{ 534137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 535137500Sjulian return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); 53665543Scg} 537166279Sariff#endif 53865543Scg 539166279Sariffstatic void 54065543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 54165543Scg{ 542137500Sjulian AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); 543137500Sjulian AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); 54465543Scg} 54565543Scg 546166279Sariff#if 0 547166279Sariffstatic u_int16_t 54865543Scgwc_rdchctl(struct agg_info *ess, int ch) 54965543Scg{ 55065543Scg return wc_rdreg(ess, ch << 3); 55165543Scg} 552166279Sariff#endif 55365543Scg 554166279Sariffstatic void 55565543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) 55665543Scg{ 55765543Scg wc_wrreg(ess, ch << 3, data); 55865543Scg} 55965543Scg 560137500Sjulian/* -------------------------------------------------------------------- */ 561137500Sjulian 56265543Scg/* Power management */ 563166279Sariffstatic void 564137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st) 56565543Scg{ 566137500Sjulian u_int32_t data; 56765543Scg 568137500Sjulian data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); 569137500Sjulian if (part < 16) { 570137500Sjulian if (st == PCI_POWERSTATE_D1) 571137500Sjulian data &= ~(1 << part); 572137500Sjulian else 573137500Sjulian data |= (1 << part); 574137500Sjulian if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) 575137500Sjulian data |= (0x10000 << part); 576137500Sjulian else 577137500Sjulian data &= ~(0x10000 << part); 578137500Sjulian pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); 579137500Sjulian } 58065543Scg} 58165543Scg 58265543Scg 58365543Scg/* ----------------------------- 58465543Scg * Controller. 58565543Scg */ 58665543Scg 587166279Sariffstatic void 58865543Scgagg_initcodec(struct agg_info* ess) 58965543Scg{ 59065543Scg u_int16_t data; 59165543Scg 592137500Sjulian if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { 593137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 59465543Scg DELAY(104); /* 20.8us * (4 + 1) */ 59565543Scg } 59665543Scg /* XXX - 2nd codec should be looked at. */ 597137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); 59865543Scg DELAY(2); 599137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 600137500Sjulian DELAY(50); 60165543Scg 602137500Sjulian if (agg_rdcodec(ess, 0) < 0) { 603137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 60465543Scg DELAY(21); 60565543Scg 60665543Scg /* Try cold reset. */ 60765543Scg device_printf(ess->dev, "will perform cold reset.\n"); 608137500Sjulian data = AGG_RD(ess, PORT_GPIO_DIR, 2); 60965543Scg if (pci_read_config(ess->dev, 0x58, 2) & 1) 61065543Scg data |= 0x10; 611137500Sjulian data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); 612137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); 613137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); 614137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); 61565543Scg DELAY(2); 616137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); 61765543Scg DELAY(1); 618137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); 619137500Sjulian agg_sleep(ess, "agginicd", 500); 620137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, data, 2); 62165543Scg DELAY(84); /* 20.8us * 4 */ 622137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); 623137500Sjulian DELAY(50); 62465543Scg } 62565543Scg} 62665543Scg 62765543Scgstatic void 62865543Scgagg_init(struct agg_info* ess) 62965543Scg{ 63065543Scg u_int32_t data; 63165543Scg 63265543Scg /* Setup PCI config registers. */ 63365543Scg 63465543Scg /* Disable all legacy emulations. */ 63565543Scg data = pci_read_config(ess->dev, CONF_LEGACY, 2); 63665543Scg data |= LEGACY_DISABLED; 63765543Scg pci_write_config(ess->dev, CONF_LEGACY, data, 2); 63865543Scg 63965543Scg /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 64065543Scg * Enable posted write. 64165543Scg * Prefer PCI timing rather than that of ISA. 64265543Scg * Don't swap L/R. */ 64365543Scg data = pci_read_config(ess->dev, CONF_MAESTRO, 4); 644137500Sjulian data |= MAESTRO_PMC; 64565543Scg data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 64665543Scg data &= ~MAESTRO_SWAP_LR; 64765543Scg pci_write_config(ess->dev, CONF_MAESTRO, data, 4); 64865543Scg 649137500Sjulian /* Turn off unused parts if necessary. */ 650137500Sjulian /* consult CONF_MAESTRO. */ 651137500Sjulian if (data & MAESTRO_SPDIF) 652137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); 653137500Sjulian else 654137500Sjulian agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); 655137500Sjulian if (data & MAESTRO_HWVOL) 656137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); 657137500Sjulian else 658137500Sjulian agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); 659137500Sjulian 660137500Sjulian /* parts that never be used */ 661137500Sjulian agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); 662137500Sjulian agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); 663137500Sjulian agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); 664137500Sjulian agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); 665137500Sjulian agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); 666137500Sjulian agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); 667137500Sjulian agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); 668137500Sjulian 669137500Sjulian /* parts that will be used only when play/recording */ 670137500Sjulian agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); 671137500Sjulian 672137500Sjulian /* parts that should always be turned on */ 673137500Sjulian agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); 674137500Sjulian agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); 675137500Sjulian agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); 676137500Sjulian agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); 677137500Sjulian 67865543Scg /* Reset direct sound. */ 679137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); 680137500Sjulian DELAY(100); 681137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 682137500Sjulian DELAY(100); 683137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); 684137500Sjulian DELAY(100); 685137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 686137500Sjulian DELAY(100); 68765543Scg 688137500Sjulian /* Enable hardware volume control interruption. */ 689137500Sjulian if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ 690137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); 69165543Scg 69265543Scg /* Setup Wave Processor. */ 69365543Scg 69465543Scg /* Enable WaveCache, set DMA base address. */ 69565543Scg wp_wrreg(ess, WPREG_WAVE_ROMRAM, 69665543Scg WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 697137500Sjulian wp_wrreg(ess, WPREG_CRAM_DATA, 0); 69865543Scg 699137500Sjulian AGG_WR(ess, PORT_WAVCACHE_CTRL, 700137500Sjulian WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); 701137500Sjulian 70265543Scg for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) 703137500Sjulian wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT); 70465543Scg 70565543Scg /* Setup Codec/Ringbus. */ 70665543Scg agg_initcodec(ess); 707137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 708137500Sjulian RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); 70965543Scg 710137500Sjulian wp_wrreg(ess, 0x08, 0xB004); 711137500Sjulian wp_wrreg(ess, 0x09, 0x001B); 712137500Sjulian wp_wrreg(ess, 0x0A, 0x8000); 713137500Sjulian wp_wrreg(ess, 0x0B, 0x3F37); 714137500Sjulian wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ 715137500Sjulian wp_wrreg(ess, WPREG_BASE + 1, 0x7632); 71665543Scg ringbus_setdest(ess, RINGBUS_SRC_ADC, 71765543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 71865543Scg ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 71965543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 72065543Scg 721137500Sjulian /* Enable S/PDIF if necessary. */ 722137500Sjulian if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) 723137500Sjulian /* XXX - why not use device flags? */ 724137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | 725137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); 726137500Sjulian 72765543Scg /* Setup ASSP. Needed for Dell Inspiron 7500? */ 728137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); 729137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); 730137500Sjulian AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); 73165543Scg 73265543Scg /* 73365543Scg * Setup GPIO. 73465543Scg * There seems to be speciality with NEC systems. 73565543Scg */ 73665543Scg switch (pci_get_subvendor(ess->dev) 73765543Scg | (pci_get_subdevice(ess->dev) << 16)) { 73865543Scg case NEC_SUBID1: 73965543Scg case NEC_SUBID2: 74065543Scg /* Matthew Braithwaite <matt@braithwaite.net> reported that 74165543Scg * NEC Versa LX doesn't need GPIO operation. */ 742137500Sjulian AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); 743137500Sjulian AGG_WR(ess, PORT_GPIO_DIR, 744137500Sjulian AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); 745137500Sjulian AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); 74665543Scg break; 74765543Scg } 74865543Scg} 74965543Scg 750137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */ 751137500Sjulianstatic void 752137500Sjulianagg_power(struct agg_info *ess, int status) 753137500Sjulian{ 754137500Sjulian u_int8_t lastpwr; 755137500Sjulian 756137500Sjulian lastpwr = ess->curpwr; 757137500Sjulian if (lastpwr == status) 758137500Sjulian return; 759137500Sjulian 760137500Sjulian switch (status) { 761137500Sjulian case PCI_POWERSTATE_D0: 762137500Sjulian case PCI_POWERSTATE_D1: 763137500Sjulian switch (lastpwr) { 764137500Sjulian case PCI_POWERSTATE_D2: 765137500Sjulian pci_set_powerstate(ess->dev, status); 766137500Sjulian /* Turn on PCM-related parts. */ 767137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0); 768137500Sjulian DELAY(100); 769137500Sjulian#if 0 770137500Sjulian if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) 771166279Sariff device_printf(ess->dev, 772166279Sariff "warning: codec not ready.\n"); 773137500Sjulian#endif 774137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 775137500Sjulian (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 776137500Sjulian & ~RINGBUS_CTRL_ACLINK_ENABLED) 777137500Sjulian | RINGBUS_CTRL_RINGBUS_ENABLED, 4); 778137500Sjulian DELAY(50); 779137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 780137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 781137500Sjulian | RINGBUS_CTRL_ACLINK_ENABLED, 4); 782137500Sjulian break; 783137500Sjulian case PCI_POWERSTATE_D3: 784137500Sjulian /* Initialize. */ 785137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 786137500Sjulian DELAY(100); 787137500Sjulian agg_init(ess); 788137500Sjulian /* FALLTHROUGH */ 789137500Sjulian case PCI_POWERSTATE_D0: 790137500Sjulian case PCI_POWERSTATE_D1: 791137500Sjulian pci_set_powerstate(ess->dev, status); 792137500Sjulian break; 793137500Sjulian } 794137500Sjulian break; 795137500Sjulian case PCI_POWERSTATE_D2: 796137500Sjulian switch (lastpwr) { 797137500Sjulian case PCI_POWERSTATE_D3: 798137500Sjulian /* Initialize. */ 799137500Sjulian pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); 800137500Sjulian DELAY(100); 801137500Sjulian agg_init(ess); 802137500Sjulian /* FALLTHROUGH */ 803137500Sjulian case PCI_POWERSTATE_D0: 804137500Sjulian case PCI_POWERSTATE_D1: 805137500Sjulian /* Turn off PCM-related parts. */ 806137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 807137500Sjulian AGG_RD(ess, PORT_RINGBUS_CTRL, 4) 808137500Sjulian & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); 809137500Sjulian DELAY(100); 810137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0x300); 811137500Sjulian DELAY(100); 812137500Sjulian break; 813137500Sjulian } 814137500Sjulian pci_set_powerstate(ess->dev, status); 815137500Sjulian break; 816137500Sjulian case PCI_POWERSTATE_D3: 817137500Sjulian /* Entirely power down. */ 818137500Sjulian agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); 819137500Sjulian DELAY(100); 820137500Sjulian AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 821137500Sjulian /*DELAY(1);*/ 822137500Sjulian if (lastpwr != PCI_POWERSTATE_D2) 823137500Sjulian wp_stoptimer(ess); 824137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 825137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); 826137500Sjulian pci_set_powerstate(ess->dev, status); 827137500Sjulian break; 828137500Sjulian default: 829137500Sjulian /* Invalid power state; let it ignored. */ 830137500Sjulian status = lastpwr; 831137500Sjulian break; 832137500Sjulian } 833137500Sjulian 834137500Sjulian ess->curpwr = status; 835137500Sjulian} 836137500Sjulian 837137500Sjulian/* -------------------------------------------------------------------- */ 838137500Sjulian 83965543Scg/* Channel controller. */ 84065543Scg 84165543Scgstatic void 84265543Scgaggch_start_dac(struct agg_chinfo *ch) 84365543Scg{ 844137500Sjulian bus_addr_t wpwa; 845137500Sjulian u_int32_t speed; 846137500Sjulian u_int16_t size, apuch, wtbar, wcreg, aputype; 847137500Sjulian u_int dv; 848137500Sjulian int pan; 84965543Scg 850137500Sjulian speed = ch->speed; 851137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 852137500Sjulian wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); 853137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 854137500Sjulian size = ch->buflen; 855137500Sjulian apuch = (ch->num << 1) | 32; 856137500Sjulian pan = PAN_RIGHT - PAN_FRONT; 857137500Sjulian 858137500Sjulian if (ch->stereo) { 859137500Sjulian wcreg |= WAVCACHE_CHCTL_STEREO; 860137500Sjulian if (ch->qs16) { 861137500Sjulian aputype = APUTYPE_16BITSTEREO; 862137500Sjulian wpwa >>= 1; 863137500Sjulian size >>= 1; 864137500Sjulian pan = -pan; 865137500Sjulian } else 866137500Sjulian aputype = APUTYPE_8BITSTEREO; 867137500Sjulian } else { 868137500Sjulian pan = 0; 869137500Sjulian if (ch->qs16) 870137500Sjulian aputype = APUTYPE_16BITLINEAR; 871137500Sjulian else { 872137500Sjulian aputype = APUTYPE_8BITLINEAR; 873137500Sjulian speed >>= 1; 874137500Sjulian } 87565543Scg } 876137500Sjulian if (ch->us) 877137500Sjulian wcreg |= WAVCACHE_CHCTL_U8; 87865543Scg 879137500Sjulian if (wtbar > 8) 880137500Sjulian wtbar = (wtbar >> 1) + 4; 881137500Sjulian 88265543Scg dv = (((speed % 48000) << 16) + 24000) / 48000 88365543Scg + ((speed / 48000) << 16); 88465543Scg 885137500Sjulian agg_lock(ch->parent); 886137500Sjulian agg_power(ch->parent, powerstate_active); 88765543Scg 888137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar, 889137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 890137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1, 891137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 892137500Sjulian if (wtbar < 8) { 893137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2, 894137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 895137500Sjulian wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3, 896137500Sjulian ch->base >> WAVCACHE_BASEADDR_SHIFT); 897137500Sjulian } 898137500Sjulian wc_wrchctl(ch->parent, apuch, wcreg); 899137500Sjulian wc_wrchctl(ch->parent, apuch + 1, wcreg); 90065543Scg 901137500Sjulian apu_setparam(ch->parent, apuch, wpwa, size, pan, dv); 902137500Sjulian if (ch->stereo) { 903137500Sjulian if (ch->qs16) 904137500Sjulian wpwa |= (WPWA_STEREO >> 1); 905137500Sjulian apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv); 90665543Scg 907137500Sjulian critical_enter(); 908137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 909137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 91065543Scg wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, 911137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 912137500Sjulian critical_exit(); 913137500Sjulian } else { 914137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 915137500Sjulian (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 916137500Sjulian } 917137500Sjulian 918137500Sjulian /* to mark that this channel is ready for intr. */ 919137500Sjulian ch->parent->active |= (1 << ch->num); 920137500Sjulian 921137500Sjulian set_timer(ch->parent); 922137500Sjulian wp_starttimer(ch->parent); 923137500Sjulian agg_unlock(ch->parent); 92465543Scg} 92565543Scg 92665543Scgstatic void 92765543Scgaggch_stop_dac(struct agg_chinfo *ch) 92865543Scg{ 929137500Sjulian agg_lock(ch->parent); 930137500Sjulian 931137500Sjulian /* to mark that this channel no longer needs further intrs. */ 932137500Sjulian ch->parent->active &= ~(1 << ch->num); 933137500Sjulian 934137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE, 93565543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 936137500Sjulian wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE, 93765543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 938137500Sjulian 939137500Sjulian if (ch->parent->active) { 940137500Sjulian set_timer(ch->parent); 941137500Sjulian wp_starttimer(ch->parent); 942137500Sjulian } else { 943137500Sjulian wp_stoptimer(ch->parent); 944137500Sjulian agg_power(ch->parent, powerstate_idle); 945137500Sjulian } 946137500Sjulian agg_unlock(ch->parent); 94765543Scg} 94865543Scg 949137500Sjulianstatic void 950137500Sjulianaggch_start_adc(struct agg_rchinfo *ch) 951137500Sjulian{ 952137500Sjulian bus_addr_t wpwa, wpwa2; 953137500Sjulian u_int16_t wcreg, wcreg2; 954137500Sjulian u_int dv; 955137500Sjulian int pan; 956137500Sjulian 957137500Sjulian /* speed > 48000 not cared */ 958137500Sjulian dv = ((ch->speed << 16) + 24000) / 48000; 959137500Sjulian 960137500Sjulian /* RATECONV doesn't seem to like dv == 0x10000. */ 961137500Sjulian if (dv == 0x10000) 962137500Sjulian dv--; 963137500Sjulian 964137500Sjulian if (ch->stereo) { 965137500Sjulian wpwa = (ch->srcphys - ch->base) >> 1; 966137500Sjulian wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1; 967137500Sjulian wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 968137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 969137500Sjulian pan = PAN_LEFT - PAN_FRONT; 970137500Sjulian } else { 971137500Sjulian wpwa = (ch->phys - ch->base) >> 1; 972137500Sjulian wpwa2 = (ch->srcphys - ch->base) >> 1; 973137500Sjulian wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 974137500Sjulian wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 975137500Sjulian pan = 0; 976137500Sjulian } 977137500Sjulian 978137500Sjulian agg_lock(ch->parent); 979137500Sjulian 980137500Sjulian ch->hwptr = 0; 981137500Sjulian agg_power(ch->parent, powerstate_active); 982137500Sjulian 983137500Sjulian /* Invalidate WaveCache. */ 984137500Sjulian wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO); 985137500Sjulian wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO); 986137500Sjulian wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO); 987137500Sjulian wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO); 988137500Sjulian 989137500Sjulian /* Load APU registers. */ 990137500Sjulian /* APU #0 : Sample rate converter for left/center. */ 991137500Sjulian apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa, 992137500Sjulian ch->buflen >> ch->stereo, 0, dv); 993137500Sjulian wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0); 994137500Sjulian wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT); 995137500Sjulian 996137500Sjulian /* APU #1 : Sample rate converter for right. */ 997137500Sjulian apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2, 998137500Sjulian ch->buflen >> ch->stereo, 0, dv); 999137500Sjulian wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0); 1000137500Sjulian wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT); 1001137500Sjulian 1002137500Sjulian /* APU #2 : Input mixer for left. */ 1003137500Sjulian apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0, 1004137500Sjulian ch->parent->bufsz >> 2, pan, 0x10000); 1005137500Sjulian wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0); 1006137500Sjulian wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0); 1007137500Sjulian wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT); 1008137500Sjulian 1009137500Sjulian /* APU #3 : Input mixer for right. */ 1010137500Sjulian apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2), 1011137500Sjulian ch->parent->bufsz >> 2, -pan, 0x10000); 1012137500Sjulian wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0); 1013137500Sjulian wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0); 1014137500Sjulian wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT); 1015137500Sjulian 1016137500Sjulian /* to mark this channel ready for intr. */ 1017137500Sjulian ch->parent->active |= (1 << ch->parent->playchns); 1018137500Sjulian 1019137500Sjulian /* start adc */ 1020137500Sjulian critical_enter(); 1021137500Sjulian wp_wrapu(ch->parent, 0, APUREG_APUTYPE, 1022137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1023137500Sjulian wp_wrapu(ch->parent, 1, APUREG_APUTYPE, 1024137500Sjulian (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 1025137500Sjulian wp_wrapu(ch->parent, 2, APUREG_APUTYPE, 1026137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1027137500Sjulian wp_wrapu(ch->parent, 3, APUREG_APUTYPE, 1028137500Sjulian (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); 1029137500Sjulian critical_exit(); 1030137500Sjulian 1031137500Sjulian set_timer(ch->parent); 1032137500Sjulian wp_starttimer(ch->parent); 1033137500Sjulian agg_unlock(ch->parent); 1034137500Sjulian} 1035137500Sjulian 1036137500Sjulianstatic void 1037137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch) 1038137500Sjulian{ 1039137500Sjulian int apuch; 1040137500Sjulian 1041137500Sjulian agg_lock(ch->parent); 1042137500Sjulian 1043137500Sjulian /* to mark that this channel no longer needs further intrs. */ 1044137500Sjulian ch->parent->active &= ~(1 << ch->parent->playchns); 1045137500Sjulian 1046137500Sjulian for (apuch = 0; apuch < 4; apuch++) 1047137500Sjulian wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 1048137500Sjulian APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1049137500Sjulian 1050137500Sjulian if (ch->parent->active) { 1051137500Sjulian set_timer(ch->parent); 1052137500Sjulian wp_starttimer(ch->parent); 1053137500Sjulian } else { 1054137500Sjulian wp_stoptimer(ch->parent); 1055137500Sjulian agg_power(ch->parent, powerstate_idle); 1056137500Sjulian } 1057137500Sjulian agg_unlock(ch->parent); 1058137500Sjulian} 1059137500Sjulian 106065543Scg/* 1061137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving. 1062137500Sjulian * This function expects n not overwrapping the buffer boundary. 1063137500Sjulian * Note that n is measured in sample unit. 1064137500Sjulian * 1065137500Sjulian * XXX - this function works in 16bit stereo format only. 1066137500Sjulian */ 1067166279Sariffstatic void 1068137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n) 1069137500Sjulian{ 1070137500Sjulian int16_t *end; 1071137500Sjulian 1072137500Sjulian for (end = l + n; l < end; ) { 1073137500Sjulian *p++ = *l++; 1074137500Sjulian *p++ = *r++; 1075137500Sjulian } 1076137500Sjulian} 1077137500Sjulian 1078137500Sjulianstatic void 1079137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch) 1080137500Sjulian{ 1081137500Sjulian unsigned cur, last; 1082137500Sjulian int16_t *src2; 1083137500Sjulian 1084137500Sjulian agg_lock(ch->parent); 1085137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1086137500Sjulian agg_unlock(ch->parent); 1087137500Sjulian cur -= 0xffff & ((ch->srcphys - ch->base) >> 1); 1088137500Sjulian last = ch->hwptr; 1089137500Sjulian src2 = ch->src + ch->parent->bufsz/4; 1090137500Sjulian 1091137500Sjulian if (cur < last) { 1092137500Sjulian interleave(ch->src + last, src2 + last, 1093137500Sjulian ch->sink + 2*last, ch->buflen/2 - last); 1094137500Sjulian interleave(ch->src, src2, 1095137500Sjulian ch->sink, cur); 1096137500Sjulian } else if (cur > last) 1097137500Sjulian interleave(ch->src + last, src2 + last, 1098137500Sjulian ch->sink + 2*last, cur - last); 1099137500Sjulian ch->hwptr = cur; 1100137500Sjulian} 1101137500Sjulian 1102137500Sjulian/* 1103137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center. 1104137500Sjulian * This function expects n not overwrapping the buffer boundary. 1105137500Sjulian * Note that n is measured in sample unit. 1106137500Sjulian * 1107137500Sjulian * XXX - this function works in 16bit monoral format only. 1108137500Sjulian */ 1109166279Sariffstatic void 1110137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n) 1111137500Sjulian{ 1112137500Sjulian int16_t *end; 1113137500Sjulian 1114137500Sjulian for (end = dest + n; dest < end; dest++) 1115137500Sjulian *dest = (int16_t)(((int)*dest - (int)*src++) / 2); 1116137500Sjulian} 1117137500Sjulian 1118137500Sjulianstatic void 1119137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch) 1120137500Sjulian{ 1121137500Sjulian unsigned cur, last; 1122137500Sjulian 1123137500Sjulian agg_lock(ch->parent); 1124137500Sjulian cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); 1125137500Sjulian agg_unlock(ch->parent); 1126137500Sjulian cur -= 0xffff & ((ch->phys - ch->base) >> 1); 1127137500Sjulian last = ch->hwptr; 1128137500Sjulian 1129137500Sjulian if (cur < last) { 1130137500Sjulian mixdown(ch->src + last, ch->sink + last, ch->buflen - last); 1131137500Sjulian mixdown(ch->src, ch->sink, cur); 1132137500Sjulian } else if (cur > last) 1133137500Sjulian mixdown(ch->src + last, ch->sink + last, cur - last); 1134137500Sjulian ch->hwptr = cur; 1135137500Sjulian} 1136137500Sjulian 1137166279Sariff#ifdef AGG_JITTER_CORRECTION 1138137500Sjulian/* 113965543Scg * Stereo jitter suppressor. 114065543Scg * Sometimes playback pointers differ in stereo-paired channels. 114165543Scg * Calling this routine within intr fixes the problem. 114265543Scg */ 1143166279Sariffstatic 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 1160166279Sariffstatic 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} 1175166279Sariff#endif 1176137500Sjulian 1177166279Sariffstatic u_int 1178137500Sjuliancalc_timer_div(struct agg_chinfo *ch) 117965543Scg{ 1180137500Sjulian u_int speed; 118165543Scg 1182137500Sjulian speed = ch->speed; 1183137500Sjulian#ifdef INVARIANTS 1184137500Sjulian if (speed == 0) { 1185137500Sjulian printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n", 1186137500Sjulian ch->num); 1187137500Sjulian speed = 1; 1188137500Sjulian } 1189137500Sjulian#endif 1190137500Sjulian return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo)) 1191137500Sjulian + speed - 1) / speed; 1192137500Sjulian} 119365543Scg 1194166279Sariffstatic u_int 1195137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch) 1196137500Sjulian{ 1197137500Sjulian u_int speed; 1198137500Sjulian 1199137500Sjulian speed = ch->speed; 1200137500Sjulian#ifdef INVARIANTS 1201137500Sjulian if (speed == 0) { 1202137500Sjulian printf("snd_maestro: rch.speed == 0, which shouldn't\n"); 1203137500Sjulian speed = 1; 1204137500Sjulian } 1205137500Sjulian#endif 1206137500Sjulian return (48000 * (ch->blklen << (!ch->stereo)) 1207137500Sjulian + speed - 1) / speed; 120865543Scg} 120965543Scg 121065543Scgstatic void 121165543Scgset_timer(struct agg_info *ess) 121265543Scg{ 121365543Scg int i; 1214137500Sjulian u_int dv = 32 << 7, newdv; 121565543Scg 121665543Scg for (i = 0; i < ess->playchns; i++) 121765543Scg if ((ess->active & (1 << i)) && 1218137500Sjulian (dv > (newdv = calc_timer_div(ess->pch + i)))) 1219137500Sjulian dv = newdv; 1220137500Sjulian if ((ess->active & (1 << i)) && 1221137500Sjulian (dv > (newdv = calc_timer_div_rch(&ess->rch)))) 1222137500Sjulian dv = newdv; 122365543Scg 1224137500Sjulian wp_settimer(ess, dv); 122565543Scg} 122665543Scg 122765543Scg 122865543Scg/* ----------------------------- 122965543Scg * Newpcm glue. 123065543Scg */ 123165543Scg 1232137500Sjulian/* AC97 mixer interface. */ 1233137500Sjulian 1234137500Sjulianstatic u_int32_t 1235137500Sjulianagg_ac97_init(kobj_t obj, void *sc) 1236137500Sjulian{ 1237137500Sjulian struct agg_info *ess = sc; 1238137500Sjulian 1239137500Sjulian return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1; 1240137500Sjulian} 1241137500Sjulian 1242137500Sjulianstatic int 1243137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno) 1244137500Sjulian{ 1245137500Sjulian struct agg_info *ess = sc; 1246137500Sjulian int ret; 1247137500Sjulian 1248154067Sariff /* XXX sound locking violation: agg_lock(ess); */ 1249137500Sjulian ret = agg_rdcodec(ess, regno); 1250154067Sariff /* agg_unlock(ess); */ 1251137500Sjulian return ret; 1252137500Sjulian} 1253137500Sjulian 1254137500Sjulianstatic int 1255137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data) 1256137500Sjulian{ 1257137500Sjulian struct agg_info *ess = sc; 1258137500Sjulian int ret; 1259137500Sjulian 1260154067Sariff /* XXX sound locking violation: agg_lock(ess); */ 1261137500Sjulian ret = agg_wrcodec(ess, regno, data); 1262154067Sariff /* agg_unlock(ess); */ 1263137500Sjulian return ret; 1264137500Sjulian} 1265137500Sjulian 1266137500Sjulian 1267137500Sjulianstatic kobj_method_t agg_ac97_methods[] = { 1268137500Sjulian KOBJMETHOD(ac97_init, agg_ac97_init), 1269137500Sjulian KOBJMETHOD(ac97_read, agg_ac97_read), 1270137500Sjulian KOBJMETHOD(ac97_write, agg_ac97_write), 1271193640Sariff KOBJMETHOD_END 1272137500Sjulian}; 1273137500SjulianAC97_DECLARE(agg_ac97); 1274137500Sjulian 1275137500Sjulian 1276137500Sjulian/* -------------------------------------------------------------------- */ 1277137500Sjulian 1278137500Sjulian/* Playback channel. */ 1279137500Sjulian 128065543Scgstatic void * 1281166279Sariffaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 1282166279Sariff struct pcm_channel *c, int dir) 128365543Scg{ 128465543Scg struct agg_info *ess = devinfo; 128565543Scg struct agg_chinfo *ch; 128665543Scg bus_addr_t physaddr; 128770291Scg void *p; 128865543Scg 1289137500Sjulian KASSERT((dir == PCMDIR_PLAY), 1290137500Sjulian ("aggpch_init() called for RECORDING channel!")); 1291137500Sjulian ch = ess->pch + ess->playchns; 129265543Scg 129365543Scg ch->parent = ess; 129465543Scg ch->channel = c; 129565543Scg ch->buffer = b; 129665543Scg ch->num = ess->playchns; 129765543Scg 1298267581Sjhb p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr, &ch->map); 1299137500Sjulian if (p == NULL) 1300137500Sjulian return NULL; 1301137500Sjulian ch->phys = physaddr; 1302137500Sjulian ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT); 1303137500Sjulian 130484658Scg sndbuf_setup(b, p, ess->bufsz); 1305137500Sjulian ch->blklen = sndbuf_getblksz(b) / 2; 1306137500Sjulian ch->buflen = sndbuf_getsize(b) / 2; 1307137500Sjulian ess->playchns++; 130865543Scg 1309137500Sjulian return ch; 1310137500Sjulian} 131165543Scg 1312137500Sjulianstatic void 1313137500Sjulianadjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size) 1314137500Sjulian{ 1315137500Sjulian struct agg_chinfo *pchs[AGG_MAXPLAYCH]; 1316137500Sjulian u_int i, j, k; 1317137500Sjulian bus_addr_t base; 131865543Scg 1319137500Sjulian /* sort pchs by phys address */ 1320137500Sjulian for (i = 0; i < n; i++) { 1321137500Sjulian for (j = 0; j < i; j++) 1322137500Sjulian if (chans[i].phys < pchs[j]->phys) { 1323137500Sjulian for (k = i; k > j; k--) 1324137500Sjulian pchs[k] = pchs[k - 1]; 1325137500Sjulian break; 1326137500Sjulian } 1327137500Sjulian pchs[j] = chans + i; 1328137500Sjulian } 1329137500Sjulian 1330137500Sjulian /* use new base register if next buffer can not be addressed 1331137500Sjulian via current base. */ 1332137500Sjulian#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1) 1333137500Sjulian base = pchs[0]->base; 1334137500Sjulian for (k = 1, i = 1; i < n; i++) { 1335137500Sjulian if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT) 1336137500Sjulian /* not addressable: assign new base */ 1337137500Sjulian base = (pchs[i]->base -= k++ << BASE_SHIFT); 1338137500Sjulian else 1339137500Sjulian pchs[i]->base = base; 1340137500Sjulian } 1341137500Sjulian#undef BASE_SHIFT 1342137500Sjulian 1343137500Sjulian if (bootverbose) { 1344137500Sjulian printf("Total of %d bases are assigned.\n", k); 1345137500Sjulian for (i = 0; i < n; i++) { 1346137500Sjulian printf("ch.%d: phys 0x%llx, wpwa 0x%llx\n", 1347137500Sjulian i, (long long)chans[i].phys, 1348137500Sjulian (long long)(chans[i].phys - 1349137500Sjulian chans[i].base) >> 1); 1350137500Sjulian } 1351137500Sjulian } 135265543Scg} 135365543Scg 135465543Scgstatic int 1355137500Sjulianaggpch_free(kobj_t obj, void *data) 135665543Scg{ 1357137500Sjulian struct agg_chinfo *ch = data; 1358137500Sjulian struct agg_info *ess = ch->parent; 1359137500Sjulian 1360137500Sjulian /* free up buffer - called after channel stopped */ 1361267581Sjhb dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer), ch->map); 1362137500Sjulian 136365543Scg /* return 0 if ok */ 136465543Scg return 0; 136565543Scg} 136665543Scg 136765543Scgstatic int 1368137500Sjulianaggpch_setformat(kobj_t obj, void *data, u_int32_t format) 136965543Scg{ 137065543Scg struct agg_chinfo *ch = data; 137165543Scg 1372137500Sjulian if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) 1373137500Sjulian return EINVAL; 1374137500Sjulian ch->stereo = ch->qs16 = ch->us = 0; 1375193640Sariff if (AFMT_CHANNEL(format) > 1) 1376137500Sjulian ch->stereo = 1; 137765543Scg 137865543Scg if (format & AFMT_U8 || format & AFMT_S8) { 137965543Scg if (format & AFMT_U8) 1380137500Sjulian ch->us = 1; 1381137500Sjulian } else 1382137500Sjulian ch->qs16 = 1; 138389887Sscottl return 0; 138465543Scg} 138565543Scg 1386193640Sariffstatic u_int32_t 1387137500Sjulianaggpch_setspeed(kobj_t obj, void *data, u_int32_t speed) 138865543Scg{ 1389193640Sariff 1390193640Sariff ((struct agg_chinfo*)data)->speed = speed; 1391193640Sariff 1392193640Sariff return (speed); 139365543Scg} 139465543Scg 1395193640Sariffstatic u_int32_t 1396137500Sjulianaggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 139765543Scg{ 1398137500Sjulian struct agg_chinfo *ch = data; 1399137500Sjulian int blkcnt; 1400137500Sjulian 1401137500Sjulian /* try to keep at least 20msec DMA space */ 1402137500Sjulian blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize); 1403137500Sjulian RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); 1404137500Sjulian 1405137500Sjulian if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { 1406137500Sjulian sndbuf_resize(ch->buffer, blkcnt, blocksize); 1407137500Sjulian blkcnt = sndbuf_getblkcnt(ch->buffer); 1408137500Sjulian blocksize = sndbuf_getblksz(ch->buffer); 1409137500Sjulian } else { 1410137500Sjulian sndbuf_setblkcnt(ch->buffer, blkcnt); 1411137500Sjulian sndbuf_setblksz(ch->buffer, blocksize); 1412137500Sjulian } 1413137500Sjulian 1414137500Sjulian ch->blklen = blocksize / 2; 1415137500Sjulian ch->buflen = blkcnt * blocksize / 2; 1416137500Sjulian return blocksize; 141765543Scg} 141865543Scg 141965543Scgstatic int 1420137500Sjulianaggpch_trigger(kobj_t obj, void *data, int go) 142165543Scg{ 142265543Scg struct agg_chinfo *ch = data; 142365543Scg 142465543Scg switch (go) { 142565543Scg case PCMTRIG_EMLDMAWR: 1426137500Sjulian break; 142765543Scg case PCMTRIG_START: 1428137500Sjulian aggch_start_dac(ch); 142965543Scg break; 143065543Scg case PCMTRIG_ABORT: 143165543Scg case PCMTRIG_STOP: 1432137500Sjulian aggch_stop_dac(ch); 143365543Scg break; 143465543Scg } 143565543Scg return 0; 143665543Scg} 143765543Scg 1438193640Sariffstatic u_int32_t 1439137500Sjulianaggpch_getptr(kobj_t obj, void *data) 144065543Scg{ 144165543Scg struct agg_chinfo *ch = data; 1442193640Sariff u_int32_t cp; 144365543Scg 1444137500Sjulian agg_lock(ch->parent); 1445137500Sjulian cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); 1446137500Sjulian agg_unlock(ch->parent); 144765543Scg 1448137500Sjulian return ch->qs16 && ch->stereo 1449137500Sjulian ? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base)) 1450137500Sjulian : (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base)); 145165543Scg} 145265543Scg 145374763Scgstatic struct pcmchan_caps * 1454137500Sjulianaggpch_getcaps(kobj_t obj, void *data) 145565543Scg{ 145665543Scg static u_int32_t playfmt[] = { 1457193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 1458193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 1459193640Sariff SND_FORMAT(AFMT_S8, 1, 0), 1460193640Sariff SND_FORMAT(AFMT_S8, 2, 0), 1461193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 1462193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 146365543Scg 0 146465543Scg }; 1465154241Sariff static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; 146665543Scg 1467137500Sjulian return &playcaps; 1468137500Sjulian} 1469137500Sjulian 1470137500Sjulian 1471137500Sjulianstatic kobj_method_t aggpch_methods[] = { 1472137500Sjulian KOBJMETHOD(channel_init, aggpch_init), 1473137500Sjulian KOBJMETHOD(channel_free, aggpch_free), 1474137500Sjulian KOBJMETHOD(channel_setformat, aggpch_setformat), 1475137500Sjulian KOBJMETHOD(channel_setspeed, aggpch_setspeed), 1476137500Sjulian KOBJMETHOD(channel_setblocksize, aggpch_setblocksize), 1477137500Sjulian KOBJMETHOD(channel_trigger, aggpch_trigger), 1478137500Sjulian KOBJMETHOD(channel_getptr, aggpch_getptr), 1479137500Sjulian KOBJMETHOD(channel_getcaps, aggpch_getcaps), 1480193640Sariff KOBJMETHOD_END 1481137500Sjulian}; 1482137500SjulianCHANNEL_DECLARE(aggpch); 1483137500Sjulian 1484137500Sjulian 1485137500Sjulian/* -------------------------------------------------------------------- */ 1486137500Sjulian 1487137500Sjulian/* Recording channel. */ 1488137500Sjulian 1489137500Sjulianstatic void * 1490166279Sariffaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 1491166279Sariff struct pcm_channel *c, int dir) 1492137500Sjulian{ 1493137500Sjulian struct agg_info *ess = devinfo; 1494137500Sjulian struct agg_rchinfo *ch; 1495137500Sjulian u_int8_t *p; 1496137500Sjulian 1497137500Sjulian KASSERT((dir == PCMDIR_REC), 1498137500Sjulian ("aggrch_init() called for PLAYBACK channel!")); 1499137500Sjulian ch = &ess->rch; 1500137500Sjulian 1501137500Sjulian ch->parent = ess; 1502137500Sjulian ch->channel = c; 1503137500Sjulian ch->buffer = b; 1504137500Sjulian 1505137500Sjulian /* Uses the bottom-half of the status buffer. */ 1506137500Sjulian p = ess->stat + ess->bufsz; 1507137500Sjulian ch->phys = ess->phys + ess->bufsz; 1508137500Sjulian ch->base = ess->phys; 1509137500Sjulian ch->src = (int16_t *)(p + ess->bufsz); 1510137500Sjulian ch->srcphys = ch->phys + ess->bufsz; 1511137500Sjulian ch->sink = (int16_t *)p; 1512137500Sjulian 1513137500Sjulian sndbuf_setup(b, p, ess->bufsz); 1514137500Sjulian ch->blklen = sndbuf_getblksz(b) / 2; 1515137500Sjulian ch->buflen = sndbuf_getsize(b) / 2; 1516137500Sjulian 1517137500Sjulian return ch; 1518137500Sjulian} 1519137500Sjulian 1520137500Sjulianstatic int 1521137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format) 1522137500Sjulian{ 1523137500Sjulian struct agg_rchinfo *ch = data; 1524137500Sjulian 1525137500Sjulian if (!(format & AFMT_S16_LE)) 1526137500Sjulian return EINVAL; 1527193640Sariff if (AFMT_CHANNEL(format) > 1) 1528137500Sjulian ch->stereo = 1; 1529137500Sjulian else 1530137500Sjulian ch->stereo = 0; 1531137500Sjulian return 0; 1532137500Sjulian} 1533137500Sjulian 1534193640Sariffstatic u_int32_t 1535137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) 1536137500Sjulian{ 1537193640Sariff 1538193640Sariff ((struct agg_rchinfo*)data)->speed = speed; 1539193640Sariff 1540193640Sariff return (speed); 1541137500Sjulian} 1542137500Sjulian 1543193640Sariffstatic u_int32_t 1544137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 1545137500Sjulian{ 1546137500Sjulian struct agg_rchinfo *ch = data; 1547137500Sjulian int blkcnt; 1548137500Sjulian 1549137500Sjulian /* try to keep at least 20msec DMA space */ 1550137500Sjulian blkcnt = (ch->speed << ch->stereo) / (25 * blocksize); 1551137500Sjulian RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); 1552137500Sjulian 1553137500Sjulian if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { 1554137500Sjulian sndbuf_resize(ch->buffer, blkcnt, blocksize); 1555137500Sjulian blkcnt = sndbuf_getblkcnt(ch->buffer); 1556137500Sjulian blocksize = sndbuf_getblksz(ch->buffer); 1557137500Sjulian } else { 1558137500Sjulian sndbuf_setblkcnt(ch->buffer, blkcnt); 1559137500Sjulian sndbuf_setblksz(ch->buffer, blocksize); 1560137500Sjulian } 1561137500Sjulian 1562137500Sjulian ch->blklen = blocksize / 2; 1563137500Sjulian ch->buflen = blkcnt * blocksize / 2; 1564137500Sjulian return blocksize; 1565137500Sjulian} 1566137500Sjulian 1567137500Sjulianstatic int 1568137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go) 1569137500Sjulian{ 1570137500Sjulian struct agg_rchinfo *ch = sc; 1571137500Sjulian 1572137500Sjulian switch (go) { 1573137500Sjulian case PCMTRIG_EMLDMARD: 1574137500Sjulian if (ch->stereo) 1575137500Sjulian aggch_feed_adc_stereo(ch); 1576137500Sjulian else 1577137500Sjulian aggch_feed_adc_mono(ch); 1578137500Sjulian break; 1579137500Sjulian case PCMTRIG_START: 1580137500Sjulian aggch_start_adc(ch); 1581137500Sjulian break; 1582137500Sjulian case PCMTRIG_ABORT: 1583137500Sjulian case PCMTRIG_STOP: 1584137500Sjulian aggch_stop_adc(ch); 1585137500Sjulian break; 1586137500Sjulian } 1587137500Sjulian return 0; 1588137500Sjulian} 1589137500Sjulian 1590193640Sariffstatic u_int32_t 1591137500Sjulianaggrch_getptr(kobj_t obj, void *sc) 1592137500Sjulian{ 1593137500Sjulian struct agg_rchinfo *ch = sc; 1594137500Sjulian 1595137500Sjulian return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1; 1596137500Sjulian} 1597137500Sjulian 1598137500Sjulianstatic struct pcmchan_caps * 1599137500Sjulianaggrch_getcaps(kobj_t obj, void *sc) 1600137500Sjulian{ 160165543Scg static u_int32_t recfmt[] = { 1602193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 1603193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 160465543Scg 0 160565543Scg }; 1606137500Sjulian static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; 160765543Scg 1608137500Sjulian return &reccaps; 160965543Scg} 161065543Scg 1611137500Sjulianstatic kobj_method_t aggrch_methods[] = { 1612137500Sjulian KOBJMETHOD(channel_init, aggrch_init), 1613137500Sjulian /* channel_free: no-op */ 1614137500Sjulian KOBJMETHOD(channel_setformat, aggrch_setformat), 1615137500Sjulian KOBJMETHOD(channel_setspeed, aggrch_setspeed), 1616137500Sjulian KOBJMETHOD(channel_setblocksize, aggrch_setblocksize), 1617137500Sjulian KOBJMETHOD(channel_trigger, aggrch_trigger), 1618137500Sjulian KOBJMETHOD(channel_getptr, aggrch_getptr), 1619137500Sjulian KOBJMETHOD(channel_getcaps, aggrch_getcaps), 1620193640Sariff KOBJMETHOD_END 162170134Scg}; 1622137500SjulianCHANNEL_DECLARE(aggrch); 162365543Scg 1624137500Sjulian 162565543Scg/* ----------------------------- 162665543Scg * Bus space. 162765543Scg */ 162865543Scg 162965543Scgstatic void 163065543Scgagg_intr(void *sc) 163165543Scg{ 163265543Scg struct agg_info* ess = sc; 1633137500Sjulian register u_int8_t status; 163465543Scg int i; 1635137500Sjulian u_int m; 163665543Scg 1637137500Sjulian status = AGG_RD(ess, PORT_HOSTINT_STAT, 1); 163865543Scg if (!status) 163965543Scg return; 164065543Scg 1641137500Sjulian /* Acknowledge intr. */ 1642137500Sjulian AGG_WR(ess, PORT_HOSTINT_STAT, status, 1); 164370619Sjhb 1644137500Sjulian if (status & HOSTINT_STAT_DSOUND) { 1645137500Sjulian#ifdef AGG_JITTER_CORRECTION 1646137500Sjulian agg_lock(ess); 1647137500Sjulian#endif 1648137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) { 1649137500Sjulian AGG_WR(ess, PORT_INT_STAT, 1, 2); 1650137500Sjulian#ifdef AGG_JITTER_CORRECTION 1651137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1652137500Sjulian if (ess->active & m) 1653137500Sjulian suppress_jitter(ess->pch + i); 1654137500Sjulian } 1655137500Sjulian if (ess->active & m) 1656137500Sjulian suppress_rec_jitter(&ess->rch); 1657137500Sjulian agg_unlock(ess); 1658137500Sjulian#endif 1659137500Sjulian for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { 1660137500Sjulian if (ess->active & m) { 1661137500Sjulian if (ess->curpwr <= PCI_POWERSTATE_D1) 1662137500Sjulian chn_intr(ess->pch[i].channel); 1663137500Sjulian else { 1664137500Sjulian m = 0; 1665137500Sjulian break; 1666137500Sjulian } 1667137500Sjulian } 1668137500Sjulian } 1669137500Sjulian if ((ess->active & m) 1670137500Sjulian && ess->curpwr <= PCI_POWERSTATE_D1) 1671137500Sjulian chn_intr(ess->rch.channel); 1672137500Sjulian } 1673137500Sjulian#ifdef AGG_JITTER_CORRECTION 1674137500Sjulian else 1675137500Sjulian agg_unlock(ess); 1676137500Sjulian#endif 1677137500Sjulian } 1678137500Sjulian 167965543Scg if (status & HOSTINT_STAT_HWVOL) { 1680137500Sjulian register u_int8_t event; 168170619Sjhb 1682137500Sjulian agg_lock(ess); 1683137500Sjulian event = AGG_RD(ess, PORT_HWVOL_MASTER, 1); 1684137500Sjulian AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1); 1685137500Sjulian agg_unlock(ess); 1686137500Sjulian 168770619Sjhb switch (event) { 168870619Sjhb case HWVOL_UP: 168970945Sjhb mixer_hwvol_step(ess->dev, 1, 1); 169070619Sjhb break; 169170619Sjhb case HWVOL_DOWN: 169270945Sjhb mixer_hwvol_step(ess->dev, -1, -1); 169370619Sjhb break; 169470619Sjhb case HWVOL_NOP: 169570619Sjhb break; 169670619Sjhb default: 1697137500Sjulian if (event & HWVOL_MUTE) { 1698137500Sjulian mixer_hwvol_mute(ess->dev); 1699137500Sjulian break; 1700137500Sjulian } 1701137500Sjulian device_printf(ess->dev, 1702137500Sjulian "%s: unknown HWVOL event 0x%x\n", 1703137500Sjulian device_get_nameunit(ess->dev), event); 170465543Scg } 170565543Scg } 170665543Scg} 170765543Scg 170865543Scgstatic void 170965543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 171065543Scg{ 171165543Scg bus_addr_t *phys = arg; 171265543Scg 171365543Scg *phys = error? 0 : segs->ds_addr; 171465543Scg 171565543Scg if (bootverbose) { 171665543Scg printf("setmap (%lx, %lx), nseg=%d, error=%d\n", 171765543Scg (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 171865543Scg nseg, error); 171965543Scg } 172065543Scg} 172165543Scg 172265543Scgstatic void * 1723267581Sjhbdma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys, 1724267581Sjhb bus_dmamap_t *map) 172565543Scg{ 172665543Scg void *buf; 172765543Scg 1728267581Sjhb if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, map)) 172965543Scg return NULL; 1730267581Sjhb if (bus_dmamap_load(dmat, *map, buf, sz, setmap, phys, 0) != 0 || 1731267581Sjhb *phys == 0) { 1732267581Sjhb bus_dmamem_free(dmat, buf, *map); 173365543Scg return NULL; 173465543Scg } 173565543Scg return buf; 173665543Scg} 173765543Scg 173865543Scgstatic void 1739267581Sjhbdma_free(bus_dma_tag_t dmat, void *buf, bus_dmamap_t map) 174065543Scg{ 1741267581Sjhb bus_dmamap_unload(dmat, map); 1742267581Sjhb bus_dmamem_free(dmat, buf, map); 174365543Scg} 174465543Scg 174565543Scgstatic int 174665543Scgagg_probe(device_t dev) 174765543Scg{ 174865543Scg char *s = NULL; 174965543Scg 175065543Scg switch (pci_get_devid(dev)) { 175165543Scg case MAESTRO_1_PCI_ID: 175265543Scg s = "ESS Technology Maestro-1"; 175365543Scg break; 175465543Scg 175565543Scg case MAESTRO_2_PCI_ID: 175665543Scg s = "ESS Technology Maestro-2"; 175765543Scg break; 175865543Scg 175965543Scg case MAESTRO_2E_PCI_ID: 176065543Scg s = "ESS Technology Maestro-2E"; 176165543Scg break; 176265543Scg } 176365543Scg 176465543Scg if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { 176565543Scg device_set_desc(dev, s); 1766142890Simp return BUS_PROBE_DEFAULT; 176765543Scg } 176865543Scg return ENXIO; 176965543Scg} 177065543Scg 177165543Scgstatic int 177265543Scgagg_attach(device_t dev) 177365543Scg{ 177465543Scg struct agg_info *ess = NULL; 177565543Scg u_int32_t data; 1776119690Sjhb int regid = PCIR_BAR(0); 177765543Scg struct resource *reg = NULL; 177865543Scg struct ac97_info *codec = NULL; 177965543Scg int irqid = 0; 178065543Scg struct resource *irq = NULL; 178165543Scg void *ih = NULL; 178265543Scg char status[SND_STATUSLEN]; 1783174582Sariff int dacn, ret = 0; 178465543Scg 1785170873Sariff ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO); 178665543Scg ess->dev = dev; 178765543Scg 1788167608Sariff mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc", 1789137500Sjulian MTX_DEF | MTX_RECURSE); 1790137500Sjulian if (!mtx_initialized(&ess->lock)) { 1791137500Sjulian device_printf(dev, "failed to create a mutex.\n"); 1792137500Sjulian ret = ENOMEM; 1793137500Sjulian goto bad; 1794137500Sjulian } 1795137500Sjulian 1796174582Sariff if (resource_int_value(device_get_name(dev), device_get_unit(dev), 1797174582Sariff "dac", &dacn) == 0) { 1798174582Sariff if (dacn < 1) 1799174582Sariff dacn = 1; 1800174582Sariff else if (dacn > AGG_MAXPLAYCH) 1801174582Sariff dacn = AGG_MAXPLAYCH; 1802174582Sariff } else 1803174582Sariff dacn = AGG_MAXPLAYCH; 1804174582Sariff 180584658Scg ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); 1806166904Snetchild if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev), 1807137500Sjulian /*align */ 4, 1 << (16+1), 1808137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1809137500Sjulian /*filter*/ NULL, NULL, 1810137500Sjulian /*size */ ess->bufsz, 1, 0x3ffff, 1811137500Sjulian /*flags */ 0, 1812137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1813137500Sjulian &ess->buf_dmat) != 0) { 1814137500Sjulian device_printf(dev, "unable to create dma tag\n"); 1815137500Sjulian ret = ENOMEM; 1816137500Sjulian goto bad; 1817137500Sjulian } 181884658Scg 1819166904Snetchild if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev), 1820137500Sjulian /*align */ 1 << WAVCACHE_BASEADDR_SHIFT, 1821137500Sjulian 1 << (16+1), 1822137500Sjulian /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, 1823137500Sjulian /*filter*/ NULL, NULL, 1824137500Sjulian /*size */ 3*ess->bufsz, 1, 0x3ffff, 1825137500Sjulian /*flags */ 0, 1826137500Sjulian /*lock */ busdma_lock_mutex, &Giant, 1827137500Sjulian &ess->stat_dmat) != 0) { 182865543Scg device_printf(dev, "unable to create dma tag\n"); 1829137500Sjulian ret = ENOMEM; 183065543Scg goto bad; 183165543Scg } 183265543Scg 1833137500Sjulian /* Allocate the room for brain-damaging status buffer. */ 1834267581Sjhb ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys, 1835267581Sjhb &ess->stat_map); 183665543Scg if (ess->stat == NULL) { 1837137500Sjulian device_printf(dev, "cannot allocate status buffer\n"); 1838137500Sjulian ret = ENOMEM; 183965543Scg goto bad; 184065543Scg } 184165543Scg if (bootverbose) 1842137500Sjulian device_printf(dev, "Maestro status/record buffer: %#llx\n", 1843137500Sjulian (long long)ess->phys); 184465543Scg 1845137500Sjulian /* State D0-uninitialized. */ 1846137500Sjulian ess->curpwr = PCI_POWERSTATE_D3; 1847137500Sjulian pci_set_powerstate(dev, PCI_POWERSTATE_D0); 184865543Scg 1849254263Sscottl pci_enable_busmaster(dev); 185065543Scg 1851137500Sjulian /* Allocate resources. */ 1852254263Sscottl reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, RF_ACTIVE); 1853137500Sjulian if (reg != NULL) { 1854137500Sjulian ess->reg = reg; 1855137500Sjulian ess->regid = regid; 1856137500Sjulian ess->st = rman_get_bustag(reg); 1857137500Sjulian ess->sh = rman_get_bushandle(reg); 1858137500Sjulian } else { 185965543Scg device_printf(dev, "unable to map register space\n"); 1860137500Sjulian ret = ENXIO; 186165543Scg goto bad; 186265543Scg } 1863141095Simp irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, 1864141095Simp RF_ACTIVE | RF_SHAREABLE); 1865137500Sjulian if (irq != NULL) { 1866137500Sjulian ess->irq = irq; 1867137500Sjulian ess->irqid = irqid; 1868137500Sjulian } else { 1869137500Sjulian device_printf(dev, "unable to map interrupt\n"); 1870137500Sjulian ret = ENXIO; 1871137500Sjulian goto bad; 1872137500Sjulian } 187365543Scg 1874137500Sjulian /* Setup resources. */ 1875137500Sjulian if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) { 1876137500Sjulian device_printf(dev, "unable to setup interrupt\n"); 1877137500Sjulian ret = ENXIO; 1878137500Sjulian goto bad; 1879137500Sjulian } else 1880137500Sjulian ess->ih = ih; 1881137500Sjulian 1882137500Sjulian /* Transition from D0-uninitialized to D0. */ 1883137500Sjulian agg_lock(ess); 1884137500Sjulian agg_power(ess, PCI_POWERSTATE_D0); 1885137500Sjulian if (agg_rdcodec(ess, 0) == 0x80) { 1886137500Sjulian /* XXX - TODO: PT101 */ 1887154067Sariff agg_unlock(ess); 188865543Scg device_printf(dev, "PT101 codec detected!\n"); 1889137500Sjulian ret = ENXIO; 189065543Scg goto bad; 189165543Scg } 1892154067Sariff agg_unlock(ess); 189370134Scg codec = AC97_CREATE(dev, ess, agg_ac97); 1894137500Sjulian if (codec == NULL) { 1895137500Sjulian device_printf(dev, "failed to create AC97 codec softc!\n"); 1896137500Sjulian ret = ENOMEM; 189765543Scg goto bad; 1898137500Sjulian } 1899137500Sjulian if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { 1900137500Sjulian device_printf(dev, "mixer initialization failed!\n"); 1901137500Sjulian ret = ENXIO; 190265543Scg goto bad; 1903137500Sjulian } 190465543Scg ess->codec = codec; 190565543Scg 1906174582Sariff ret = pcm_register(dev, ess, dacn, 1); 1907137500Sjulian if (ret) 190865543Scg goto bad; 190965543Scg 191070945Sjhb mixer_hwvol_init(dev); 1911154067Sariff agg_lock(ess); 1912137500Sjulian agg_power(ess, powerstate_init); 1913154067Sariff agg_unlock(ess); 1914174582Sariff for (data = 0; data < dacn; data++) 1915137500Sjulian pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess); 191670134Scg pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); 1917137500Sjulian adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); 1918137500Sjulian 1919137500Sjulian snprintf(status, SND_STATUSLEN, 1920297000Sjhibbits "port 0x%jx-0x%jx irq %jd at device %d.%d on pci%d", 1921137500Sjulian rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), 1922137500Sjulian pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); 192365543Scg pcm_setstatus(dev, status); 192465543Scg 192565543Scg return 0; 192665543Scg 192765543Scg bad: 192865644Scg if (codec != NULL) 192965644Scg ac97_destroy(codec); 193065543Scg if (ih != NULL) 193165543Scg bus_teardown_intr(dev, irq, ih); 193265543Scg if (irq != NULL) 193365543Scg bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 193465543Scg if (reg != NULL) 193565543Scg bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); 1936154627Snetchild if (ess != NULL) { 1937154627Snetchild if (ess->stat != NULL) 1938267581Sjhb dma_free(ess->stat_dmat, ess->stat, ess->stat_map); 1939154627Snetchild if (ess->stat_dmat != NULL) 1940154627Snetchild bus_dma_tag_destroy(ess->stat_dmat); 1941154627Snetchild if (ess->buf_dmat != NULL) 1942154627Snetchild bus_dma_tag_destroy(ess->buf_dmat); 1943154627Snetchild if (mtx_initialized(&ess->lock)) 1944154627Snetchild mtx_destroy(&ess->lock); 194565543Scg free(ess, M_DEVBUF); 1946154627Snetchild } 194765543Scg 1948137500Sjulian return ret; 194965543Scg} 195065543Scg 195165543Scgstatic int 195265543Scgagg_detach(device_t dev) 195365543Scg{ 195465543Scg struct agg_info *ess = pcm_getdevinfo(dev); 195565543Scg int r; 1956137500Sjulian u_int16_t icr; 195765543Scg 1958137500Sjulian icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2); 1959137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 1960137500Sjulian 1961137500Sjulian agg_lock(ess); 1962137500Sjulian if (ess->active) { 1963137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 1964137500Sjulian agg_unlock(ess); 1965137500Sjulian return EBUSY; 1966137500Sjulian } 1967137500Sjulian agg_unlock(ess); 1968137500Sjulian 196965543Scg r = pcm_unregister(dev); 1970137500Sjulian if (r) { 1971137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); 197265543Scg return r; 1973137500Sjulian } 197465543Scg 1975137500Sjulian agg_lock(ess); 1976137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 1977154067Sariff agg_unlock(ess); 197865543Scg 197965543Scg bus_teardown_intr(dev, ess->irq, ess->ih); 198065543Scg bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); 198165543Scg bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); 1982267581Sjhb dma_free(ess->stat_dmat, ess->stat, ess->stat_map); 1983137500Sjulian bus_dma_tag_destroy(ess->stat_dmat); 1984137500Sjulian bus_dma_tag_destroy(ess->buf_dmat); 1985137500Sjulian mtx_destroy(&ess->lock); 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); 199465543Scg 1995137500Sjulian AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); 1996137500Sjulian agg_lock(ess); 1997137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 1998137500Sjulian agg_unlock(ess); 199965543Scg 200065543Scg return 0; 200165543Scg} 200265543Scg 200365543Scgstatic int 200465543Scgagg_resume(device_t dev) 200565543Scg{ 2006137500Sjulian int i; 200765543Scg struct agg_info *ess = pcm_getdevinfo(dev); 200865543Scg 200965543Scg for (i = 0; i < ess->playchns; i++) 201065543Scg if (ess->active & (1 << i)) 201165543Scg aggch_start_dac(ess->pch + i); 201265543Scg if (ess->active & (1 << i)) 201365543Scg aggch_start_adc(&ess->rch); 2014137500Sjulian 2015137500Sjulian agg_lock(ess); 2016137500Sjulian if (!ess->active) 2017137500Sjulian agg_power(ess, powerstate_init); 2018137500Sjulian agg_unlock(ess); 2019137500Sjulian 2020137500Sjulian if (mixer_reinit(dev)) { 2021137500Sjulian device_printf(dev, "unable to reinitialize the mixer\n"); 2022137500Sjulian return ENXIO; 202365543Scg } 2024137500Sjulian 202565543Scg return 0; 202665543Scg} 202765543Scg 202865543Scgstatic int 202965543Scgagg_shutdown(device_t dev) 203065543Scg{ 203165543Scg struct agg_info *ess = pcm_getdevinfo(dev); 203265543Scg 2033137500Sjulian agg_lock(ess); 2034137500Sjulian agg_power(ess, PCI_POWERSTATE_D3); 2035137500Sjulian agg_unlock(ess); 203665543Scg 203765543Scg return 0; 203865543Scg} 203965543Scg 204065543Scg 204165543Scgstatic device_method_t agg_methods[] = { 204265543Scg DEVMETHOD(device_probe, agg_probe), 204365543Scg DEVMETHOD(device_attach, agg_attach), 204465543Scg DEVMETHOD(device_detach, agg_detach), 204565543Scg DEVMETHOD(device_suspend, agg_suspend), 204665543Scg DEVMETHOD(device_resume, agg_resume), 204765543Scg DEVMETHOD(device_shutdown, agg_shutdown), 204865543Scg 204965543Scg { 0, 0 } 205065543Scg}; 205165543Scg 205265543Scgstatic driver_t agg_driver = { 205365543Scg "pcm", 205465543Scg agg_methods, 205582180Scg PCM_SOFTC_SIZE, 205665543Scg}; 205765543Scg 2058137500Sjulian/*static devclass_t pcm_devclass;*/ 2059137500Sjulian 206065543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); 2061132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 206265543ScgMODULE_VERSION(snd_maestro, 1); 2063