maestro.c revision 65644
165543Scg/*- 265543Scg * Copyright (c) 2000 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp> 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 * 2665543Scg * $Id: maestro.c,v 1.12 2000/09/06 03:32:34 taku Exp $ 2765543Scg * $FreeBSD: head/sys/dev/sound/pci/maestro.c 65644 2000-09-09 19:21:04Z cg $ 2865543Scg */ 2965543Scg 3065543Scg/* 3165543Scg * Credits: 3265543Scg * 3365543Scg * Part of this code (especially in many magic numbers) was heavily inspired 3465543Scg * by the Linux driver originally written by 3565543Scg * Alan Cox <alan.cox@linux.org>, modified heavily by 3665543Scg * Zach Brown <zab@zabbo.net>. 3765543Scg * 3865543Scg * busdma()-ize and buffer size reduction were suggested by 3965543Scg * Cameron Grant <gandalf@vilnya.demon.co.uk>. 4065543Scg * Also he showed me the way to use busdma() suite. 4165543Scg * 4265543Scg * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 4365543Scg * were looked at by 4465543Scg * Munehiro Matsuda <haro@tk.kubota.co.jp>, 4565543Scg * who brought patches based on the Linux driver with some simplification. 4665543Scg */ 4765543Scg 4865543Scg#include <dev/sound/pcm/sound.h> 4965543Scg#include <dev/sound/pcm/ac97.h> 5065543Scg#include <pci/pcireg.h> 5165543Scg#include <pci/pcivar.h> 5265543Scg 5365543Scg#include <dev/sound/pci/maestro_reg.h> 5465543Scg 5565543Scg#define inline __inline 5665543Scg 5765543Scg/* 5865543Scg * PCI IDs of supported chips: 5965543Scg * 6065543Scg * MAESTRO-1 0x01001285 6165543Scg * MAESTRO-2 0x1968125d 6265543Scg * MAESTRO-2E 0x1978125d 6365543Scg */ 6465543Scg 6565543Scg#define MAESTRO_1_PCI_ID 0x01001285 6665543Scg#define MAESTRO_2_PCI_ID 0x1968125d 6765543Scg#define MAESTRO_2E_PCI_ID 0x1978125d 6865543Scg 6965543Scg#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ 7065543Scg#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ 7165543Scg 7265543Scg#ifndef AGG_MAXPLAYCH 7365543Scg# define AGG_MAXPLAYCH 4 7465543Scg#endif 7565543Scg 7665543Scg#define AGG_BUFSIZ 4096 7765543Scg 7865543Scg 7965543Scg/* ----------------------------- 8065543Scg * Data structures. 8165543Scg */ 8265543Scgstruct agg_chinfo { 8365543Scg struct agg_info *parent; 8465543Scg pcm_channel *channel; 8565543Scg snd_dbuf *buffer; 8665543Scg bus_addr_t offset; 8765543Scg u_int32_t blocksize; 8865543Scg int dir; 8965543Scg u_int num; 9065543Scg u_int16_t aputype; 9165543Scg u_int16_t wcreg_tpl; 9265543Scg}; 9365543Scg 9465543Scgstruct agg_info { 9565543Scg device_t dev; 9665543Scg struct resource *reg; 9765543Scg int regid; 9865543Scg 9965543Scg bus_space_tag_t st; 10065543Scg bus_space_handle_t sh; 10165543Scg bus_dma_tag_t parent_dmat; 10265543Scg 10365543Scg struct resource *irq; 10465543Scg int irqid; 10565543Scg void *ih; 10665543Scg 10765543Scg u_int8_t *stat; 10865543Scg bus_addr_t baseaddr; 10965543Scg 11065543Scg struct ac97_info *codec; 11165543Scg 11265543Scg u_int playchns, active; 11365543Scg struct agg_chinfo pch[AGG_MAXPLAYCH]; 11465543Scg struct agg_chinfo rch; 11565543Scg}; 11665543Scg 11765543Scg 11865543Scgstatic u_int32_t agg_rdcodec(void *, int); 11965543Scgstatic void agg_wrcodec(void *, int, u_int32_t); 12065543Scg 12165543Scgstatic inline void ringbus_setdest(struct agg_info*, int, int); 12265543Scg 12365543Scgstatic inline u_int16_t wp_rdreg(struct agg_info*, u_int16_t); 12465543Scgstatic inline void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); 12565543Scgstatic inline u_int16_t wp_rdapu(struct agg_info*, int, u_int16_t); 12665543Scgstatic inline void wp_wrapu(struct agg_info*, int, u_int16_t, u_int16_t); 12765543Scgstatic inline void wp_settimer(struct agg_info*, u_int); 12865543Scgstatic inline void wp_starttimer(struct agg_info*); 12965543Scgstatic inline void wp_stoptimer(struct agg_info*); 13065543Scg 13165543Scgstatic inline u_int16_t wc_rdreg(struct agg_info*, u_int16_t); 13265543Scgstatic inline void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); 13365543Scgstatic inline u_int16_t wc_rdchctl(struct agg_info*, int); 13465543Scgstatic inline void wc_wrchctl(struct agg_info*, int, u_int16_t); 13565543Scg 13665543Scgstatic inline void agg_power(struct agg_info*, int); 13765543Scg 13865543Scgstatic void agg_init(struct agg_info*); 13965543Scgstatic u_int32_t agg_ac97_init(void *); 14065543Scg 14165543Scgstatic void aggch_start_dac(struct agg_chinfo*); 14265543Scgstatic void aggch_stop_dac(struct agg_chinfo*); 14365543Scg 14465543Scgstatic inline void suppress_jitter(struct agg_chinfo*); 14565543Scg 14665543Scgstatic inline u_int calc_timer_freq(struct agg_chinfo*); 14765543Scgstatic void set_timer(struct agg_info*); 14865543Scg 14965543Scgstatic pcmchan_init_t aggch_init; 15065543Scgstatic pcmchan_free_t aggch_free; 15165543Scgstatic pcmchan_setformat_t aggch_setplayformat; 15265543Scgstatic pcmchan_setspeed_t aggch_setspeed; 15365543Scgstatic pcmchan_setblocksize_t aggch_setblocksize; 15465543Scgstatic pcmchan_trigger_t aggch_trigger; 15565543Scgstatic pcmchan_getptr_t aggch_getplayptr; 15665543Scgstatic pcmchan_getcaps_t aggch_getcaps; 15765543Scg 15865543Scgstatic void agg_intr(void *); 15965543Scgstatic int agg_probe(device_t); 16065543Scgstatic int agg_attach(device_t); 16165543Scgstatic int agg_detach(device_t); 16265543Scgstatic int agg_suspend(device_t); 16365543Scgstatic int agg_resume(device_t); 16465543Scgstatic int agg_shutdown(device_t); 16565543Scg 16665543Scgstatic void *dma_malloc(struct agg_info*, u_int32_t, bus_addr_t*); 16765543Scgstatic void dma_free(struct agg_info*, void *); 16865543Scg 16965543Scg/* ----------------------------- 17065543Scg * Subsystems. 17165543Scg */ 17265543Scg 17365543Scg/* Codec/Ringbus */ 17465543Scg 17565543Scgstatic u_int32_t 17665543Scgagg_rdcodec(void *sc, int regno) 17765543Scg{ 17865543Scg struct agg_info *ess = sc; 17965543Scg unsigned t; 18065543Scg 18165543Scg /* We have to wait for a SAFE time to write addr/data */ 18265543Scg for (t = 0; t < 20; t++) { 18365543Scg if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 18465543Scg & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) 18565543Scg break; 18665543Scg DELAY(2); /* 20.8us / 13 */ 18765543Scg } 18865543Scg if (t == 20) 18965543Scg device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); 19065543Scg 19165543Scg bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, 19265543Scg CODEC_CMD_READ | regno); 19365543Scg DELAY(21); /* AC97 cycle = 20.8usec */ 19465543Scg 19565543Scg /* Wait for data retrieve */ 19665543Scg for (t = 0; t < 20; t++) { 19765543Scg if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 19865543Scg & CODEC_STAT_MASK) == CODEC_STAT_RW_DONE) 19965543Scg break; 20065543Scg DELAY(2); /* 20.8us / 13 */ 20165543Scg } 20265543Scg if (t == 20) 20365543Scg /* Timed out, but perform dummy read. */ 20465543Scg device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); 20565543Scg 20665543Scg return bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG); 20765543Scg} 20865543Scg 20965543Scgstatic void 21065543Scgagg_wrcodec(void *sc, int regno, u_int32_t data) 21165543Scg{ 21265543Scg unsigned t; 21365543Scg struct agg_info *ess = sc; 21465543Scg 21565543Scg /* We have to wait for a SAFE time to write addr/data */ 21665543Scg for (t = 0; t < 20; t++) { 21765543Scg if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 21865543Scg & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) 21965543Scg break; 22065543Scg DELAY(2); /* 20.8us / 13 */ 22165543Scg } 22265543Scg if (t == 20) { 22365543Scg /* Timed out. Abort writing. */ 22465543Scg device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); 22565543Scg return; 22665543Scg } 22765543Scg 22865543Scg bus_space_write_2(ess->st, ess->sh, PORT_CODEC_REG, data); 22965543Scg bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, 23065543Scg CODEC_CMD_WRITE | regno); 23165543Scg} 23265543Scg 23365543Scgstatic inline void 23465543Scgringbus_setdest(struct agg_info *ess, int src, int dest) 23565543Scg{ 23665543Scg u_int32_t data; 23765543Scg 23865543Scg data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL); 23965543Scg data &= ~(0xfU << src); 24065543Scg data |= (0xfU & dest) << src; 24165543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); 24265543Scg} 24365543Scg 24465543Scg/* Wave Processor */ 24565543Scg 24665543Scgstatic inline u_int16_t 24765543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg) 24865543Scg{ 24965543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 25065543Scg return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA); 25165543Scg} 25265543Scg 25365543Scgstatic inline void 25465543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 25565543Scg{ 25665543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 25765543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 25865543Scg} 25965543Scg 26065543Scgstatic inline void 26165543Scgapu_setindex(struct agg_info *ess, u_int16_t reg) 26265543Scg{ 26365543Scg int t; 26465543Scg 26565543Scg wp_wrreg(ess, WPREG_CRAM_PTR, reg); 26665543Scg /* Sometimes WP fails to set apu register index. */ 26765543Scg for (t = 0; t < 1000; t++) { 26865543Scg if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg) 26965543Scg break; 27065543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg); 27165543Scg } 27265543Scg if (t == 1000) 27365543Scg device_printf(ess->dev, "apu_setindex() timed out.\n"); 27465543Scg} 27565543Scg 27665543Scgstatic inline u_int16_t 27765543Scgwp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) 27865543Scg{ 27965543Scg u_int16_t ret; 28065543Scg 28165543Scg apu_setindex(ess, ((unsigned)ch << 4) + reg); 28265543Scg ret = wp_rdreg(ess, WPREG_DATA_PORT); 28365543Scg return ret; 28465543Scg} 28565543Scg 28665543Scgstatic inline void 28765543Scgwp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) 28865543Scg{ 28965543Scg int t; 29065543Scg 29165543Scg apu_setindex(ess, ((unsigned)ch << 4) + reg); 29265543Scg wp_wrreg(ess, WPREG_DATA_PORT, data); 29365543Scg for (t = 0; t < 1000; t++) { 29465543Scg if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data) 29565543Scg break; 29665543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 29765543Scg } 29865543Scg if (t == 1000) 29965543Scg device_printf(ess->dev, "wp_wrapu() timed out.\n"); 30065543Scg} 30165543Scg 30265543Scgstatic inline void 30365543Scgwp_settimer(struct agg_info *ess, u_int freq) 30465543Scg{ 30565543Scg u_int clock = 48000 << 2; 30665543Scg u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; 30765543Scg 30865543Scg RANGE(divide, 4, 32 << 8); 30965543Scg 31065543Scg for (; divide > 32 << 1; divide >>= 1) 31165543Scg prescale++; 31265543Scg divide = (divide + 1) >> 1; 31365543Scg 31465543Scg for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 31565543Scg prescale++; 31665543Scg 31765543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 31865543Scg wp_wrreg(ess, WPREG_TIMER_FREQ, 31965543Scg (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 32065543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 32165543Scg} 32265543Scg 32365543Scgstatic inline void 32465543Scgwp_starttimer(struct agg_info *ess) 32565543Scg{ 32665543Scg wp_wrreg(ess, WPREG_TIMER_START, 1); 32765543Scg} 32865543Scg 32965543Scgstatic inline void 33065543Scgwp_stoptimer(struct agg_info *ess) 33165543Scg{ 33265543Scg wp_wrreg(ess, WPREG_TIMER_START, 0); 33365543Scg bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 33465543Scg} 33565543Scg 33665543Scg/* WaveCache */ 33765543Scg 33865543Scgstatic inline u_int16_t 33965543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg) 34065543Scg{ 34165543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 34265543Scg return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA); 34365543Scg} 34465543Scg 34565543Scgstatic inline void 34665543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 34765543Scg{ 34865543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 34965543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data); 35065543Scg} 35165543Scg 35265543Scgstatic inline u_int16_t 35365543Scgwc_rdchctl(struct agg_info *ess, int ch) 35465543Scg{ 35565543Scg return wc_rdreg(ess, ch << 3); 35665543Scg} 35765543Scg 35865543Scgstatic inline void 35965543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) 36065543Scg{ 36165543Scg wc_wrreg(ess, ch << 3, data); 36265543Scg} 36365543Scg 36465543Scg/* Power management */ 36565543Scg 36665543Scgstatic inline void 36765543Scgagg_power(struct agg_info *ess, int status) 36865543Scg{ 36965543Scg u_int8_t data; 37065543Scg 37165543Scg data = pci_read_config(ess->dev, CONF_PM_PTR, 1); 37265543Scg if (pci_read_config(ess->dev, data, 1) == PPMI_CID) 37365543Scg pci_write_config(ess->dev, data + PM_CTRL, status, 1); 37465543Scg} 37565543Scg 37665543Scg 37765543Scg/* ----------------------------- 37865543Scg * Controller. 37965543Scg */ 38065543Scg 38165543Scgstatic inline void 38265543Scgagg_initcodec(struct agg_info* ess) 38365543Scg{ 38465543Scg u_int16_t data; 38565543Scg 38665543Scg if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL) 38765543Scg & RINGBUS_CTRL_ACLINK_ENABLED) { 38865543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 38965543Scg DELAY(104); /* 20.8us * (4 + 1) */ 39065543Scg } 39165543Scg /* XXX - 2nd codec should be looked at. */ 39265543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 39365543Scg RINGBUS_CTRL_AC97_SWRESET); 39465543Scg DELAY(2); 39565543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 39665543Scg RINGBUS_CTRL_ACLINK_ENABLED); 39765543Scg DELAY(21); 39865543Scg 39965543Scg agg_rdcodec(ess, 0); 40065543Scg if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 40165543Scg & CODEC_STAT_MASK) { 40265543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 40365543Scg DELAY(21); 40465543Scg 40565543Scg /* Try cold reset. */ 40665543Scg device_printf(ess->dev, "will perform cold reset.\n"); 40765543Scg data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR); 40865543Scg if (pci_read_config(ess->dev, 0x58, 2) & 1) 40965543Scg data |= 0x10; 41065543Scg data |= 0x009 & 41165543Scg ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA); 41265543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6); 41365543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 41465543Scg data | 0x009); 41565543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000); 41665543Scg DELAY(2); 41765543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); 41865543Scg DELAY(1); 41965543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009); 42065543Scg DELAY(500000); 42165543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data); 42265543Scg DELAY(84); /* 20.8us * 4 */ 42365543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 42465543Scg RINGBUS_CTRL_ACLINK_ENABLED); 42565543Scg DELAY(21); 42665543Scg } 42765543Scg} 42865543Scg 42965543Scgstatic void 43065543Scgagg_init(struct agg_info* ess) 43165543Scg{ 43265543Scg u_int32_t data; 43365543Scg 43465543Scg /* Setup PCI config registers. */ 43565543Scg 43665543Scg /* Disable all legacy emulations. */ 43765543Scg data = pci_read_config(ess->dev, CONF_LEGACY, 2); 43865543Scg data |= LEGACY_DISABLED; 43965543Scg pci_write_config(ess->dev, CONF_LEGACY, data, 2); 44065543Scg 44165543Scg /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 44265543Scg * Enable posted write. 44365543Scg * Prefer PCI timing rather than that of ISA. 44465543Scg * Don't swap L/R. */ 44565543Scg data = pci_read_config(ess->dev, CONF_MAESTRO, 4); 44665543Scg data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 44765543Scg data &= ~MAESTRO_SWAP_LR; 44865543Scg pci_write_config(ess->dev, CONF_MAESTRO, data, 4); 44965543Scg 45065543Scg /* Reset direct sound. */ 45165543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 45265543Scg HOSTINT_CTRL_DSOUND_RESET); 45365543Scg DELAY(10000); /* XXX - too long? */ 45465543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 45565543Scg DELAY(10000); 45665543Scg 45765543Scg /* Enable direct sound interruption. */ 45865543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 45965543Scg HOSTINT_CTRL_DSOUND_INT_ENABLED); 46065543Scg 46165543Scg /* Setup Wave Processor. */ 46265543Scg 46365543Scg /* Enable WaveCache, set DMA base address. */ 46465543Scg wp_wrreg(ess, WPREG_WAVE_ROMRAM, 46565543Scg WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 46665543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL, 46765543Scg WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB); 46865543Scg 46965543Scg for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) 47065543Scg wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); 47165543Scg 47265543Scg /* Setup Codec/Ringbus. */ 47365543Scg agg_initcodec(ess); 47465543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 47565543Scg RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED); 47665543Scg 47765543Scg wp_wrreg(ess, WPREG_BASE, 0x8500); /* Parallel I/O */ 47865543Scg ringbus_setdest(ess, RINGBUS_SRC_ADC, 47965543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 48065543Scg ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 48165543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 48265543Scg 48365543Scg /* Setup ASSP. Needed for Dell Inspiron 7500? */ 48465543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00); 48565543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03); 48665543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00); 48765543Scg 48865543Scg /* 48965543Scg * Setup GPIO. 49065543Scg * There seems to be speciality with NEC systems. 49165543Scg */ 49265543Scg switch (pci_get_subvendor(ess->dev) 49365543Scg | (pci_get_subdevice(ess->dev) << 16)) { 49465543Scg case NEC_SUBID1: 49565543Scg case NEC_SUBID2: 49665543Scg /* Matthew Braithwaite <matt@braithwaite.net> reported that 49765543Scg * NEC Versa LX doesn't need GPIO operation. */ 49865543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0x9ff); 49965543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 50065543Scg bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 0x600); 50165543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x200); 50265543Scg break; 50365543Scg } 50465543Scg} 50565543Scg 50665543Scg/* Channel controller. */ 50765543Scg 50865543Scgstatic void 50965543Scgaggch_start_dac(struct agg_chinfo *ch) 51065543Scg{ 51165543Scg u_int wpwa = APU_USE_SYSMEM | (ch->offset >> 9); 51265543Scg u_int size = AGG_BUFSIZ >> 1; 51365543Scg u_int speed = ch->channel->speed; 51465543Scg u_int offset = ch->offset >> 1; 51565543Scg u_int cp = ch->buffer->rp >> 1; 51665543Scg u_int16_t apuch = ch->num << 1; 51765543Scg u_int dv; 51865543Scg int pan = 0; 51965543Scg 52065543Scg switch (ch->aputype) { 52165543Scg case APUTYPE_16BITSTEREO: 52265543Scg wpwa >>= 1; 52365543Scg size >>= 1; 52465543Scg offset >>= 1; 52565543Scg cp >>= 1; 52665543Scg /* FALLTHROUGH */ 52765543Scg case APUTYPE_8BITSTEREO: 52865543Scg pan = 8; 52965543Scg apuch++; 53065543Scg break; 53165543Scg case APUTYPE_8BITLINEAR: 53265543Scg speed >>= 1; 53365543Scg break; 53465543Scg } 53565543Scg 53665543Scg dv = (((speed % 48000) << 16) + 24000) / 48000 53765543Scg + ((speed / 48000) << 16); 53865543Scg 53965543Scg do { 54065543Scg wp_wrapu(ch->parent, apuch, APUREG_WAVESPACE, wpwa & 0xff00); 54165543Scg wp_wrapu(ch->parent, apuch, APUREG_CURPTR, offset + cp); 54265543Scg wp_wrapu(ch->parent, apuch, APUREG_ENDPTR, offset + size); 54365543Scg wp_wrapu(ch->parent, apuch, APUREG_LOOPLEN, size); 54465543Scg wp_wrapu(ch->parent, apuch, APUREG_AMPLITUDE, 0xe800); 54565543Scg wp_wrapu(ch->parent, apuch, APUREG_POSITION, 0x8f00 54665543Scg | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) 54765543Scg | ((PAN_FRONT + pan) << APU_PAN_SHIFT)); 54865543Scg wp_wrapu(ch->parent, apuch, APUREG_FREQ_LOBYTE, APU_plus6dB 54965543Scg | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 55065543Scg wp_wrapu(ch->parent, apuch, APUREG_FREQ_HIWORD, dv >> 8); 55165543Scg 55265543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 55365543Scg wpwa |= APU_STEREO >> 1; 55465543Scg pan = -pan; 55565543Scg } while (pan < 0 && apuch--); 55665543Scg 55765543Scg wc_wrchctl(ch->parent, apuch, ch->wcreg_tpl); 55865543Scg wc_wrchctl(ch->parent, apuch + 1, ch->wcreg_tpl); 55965543Scg 56065543Scg wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 56165543Scg (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 56265543Scg if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) 56365543Scg wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, 56465543Scg (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 56565543Scg} 56665543Scg 56765543Scgstatic void 56865543Scgaggch_stop_dac(struct agg_chinfo *ch) 56965543Scg{ 57065543Scg wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE, 57165543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 57265543Scg wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE, 57365543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 57465543Scg} 57565543Scg 57665543Scg/* 57765543Scg * Stereo jitter suppressor. 57865543Scg * Sometimes playback pointers differ in stereo-paired channels. 57965543Scg * Calling this routine within intr fixes the problem. 58065543Scg */ 58165543Scgstatic inline void 58265543Scgsuppress_jitter(struct agg_chinfo *ch) 58365543Scg{ 58465543Scg if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) { 58565543Scg int cp, diff, halfsize = AGG_BUFSIZ >> 2; 58665543Scg 58765543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 58865543Scg halfsize >>= 1; 58965543Scg cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); 59065543Scg diff = wp_rdapu(ch->parent, (ch->num << 1) + 1, APUREG_CURPTR); 59165543Scg diff -= cp; 59265543Scg if (diff >> 1 && diff > -halfsize && diff < halfsize) 59365543Scg bus_space_write_2(ch->parent->st, ch->parent->sh, 59465543Scg PORT_DSP_DATA, cp); 59565543Scg } 59665543Scg} 59765543Scg 59865543Scgstatic inline u_int 59965543Scgcalc_timer_freq(struct agg_chinfo *ch) 60065543Scg{ 60165543Scg u_int ss = 2; 60265543Scg 60365543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 60465543Scg ss <<= 1; 60565543Scg if (ch->aputype == APUTYPE_8BITLINEAR) 60665543Scg ss >>= 1; 60765543Scg 60865543Scg return (ch->channel->speed * ss + ch->blocksize - 1) / ch->blocksize; 60965543Scg} 61065543Scg 61165543Scgstatic void 61265543Scgset_timer(struct agg_info *ess) 61365543Scg{ 61465543Scg int i; 61565543Scg u_int freq = 0; 61665543Scg 61765543Scg for (i = 0; i < ess->playchns; i++) 61865543Scg if ((ess->active & (1 << i)) && 61965543Scg (freq < calc_timer_freq(ess->pch + i))) 62065543Scg freq = calc_timer_freq(ess->pch + i); 62165543Scg 62265543Scg wp_settimer(ess, freq); 62365543Scg} 62465543Scg 62565543Scg 62665543Scg/* ----------------------------- 62765543Scg * Newpcm glue. 62865543Scg */ 62965543Scg 63065543Scgstatic u_int32_t 63165543Scgagg_ac97_init(void *sc) 63265543Scg{ 63365543Scg struct agg_info *ess = sc; 63465543Scg 63565543Scg return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; 63665543Scg} 63765543Scg 63865543Scgstatic void * 63965543Scgaggch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 64065543Scg{ 64165543Scg struct agg_info *ess = devinfo; 64265543Scg struct agg_chinfo *ch; 64365543Scg bus_addr_t physaddr; 64465543Scg 64565543Scg ch = (dir == PCMDIR_PLAY)? ess->pch + ess->playchns : &ess->rch; 64665543Scg 64765543Scg ch->parent = ess; 64865543Scg ch->channel = c; 64965543Scg ch->buffer = b; 65065543Scg ch->num = ess->playchns; 65165543Scg ch->dir = dir; 65265543Scg 65365543Scg b->buf = dma_malloc(ess, AGG_BUFSIZ, &physaddr); 65465543Scg if (b->buf == NULL) 65565543Scg return NULL; 65665543Scg 65765543Scg ch->offset = physaddr - ess->baseaddr; 65865543Scg if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) { 65965543Scg device_printf(ess->dev, 66065543Scg "offset %#x exceeds limit. ", ch->offset); 66165543Scg dma_free(ess, b->buf); 66265543Scg b->buf = NULL; 66365543Scg return NULL; 66465543Scg } 66565543Scg 66665543Scg b->bufsize = AGG_BUFSIZ; 66765543Scg ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 66865543Scg 66965543Scg if (dir == PCMDIR_PLAY) { 67065543Scg ess->playchns++; 67165543Scg if (bootverbose) 67265543Scg device_printf(ess->dev, "pch[%d].offset = %#x\n", ch->num, ch->offset); 67365543Scg } else if (bootverbose) 67465543Scg device_printf(ess->dev, "rch.offset = %#x\n", ch->offset); 67565543Scg 67665543Scg return ch; 67765543Scg} 67865543Scg 67965543Scgstatic int 68065543Scgaggch_free(void *data) 68165543Scg{ 68265543Scg struct agg_chinfo *ch = data; 68365543Scg struct agg_info *ess = ch->parent; 68465543Scg 68565543Scg /* free up buffer - called after channel stopped */ 68665543Scg dma_free(ess, ch->buffer->buf); 68765543Scg 68865543Scg /* return 0 if ok */ 68965543Scg return 0; 69065543Scg} 69165543Scg 69265543Scgstatic int 69365543Scgaggch_setplayformat(void *data, u_int32_t format) 69465543Scg{ 69565543Scg struct agg_chinfo *ch = data; 69665543Scg u_int16_t wcreg_tpl; 69765543Scg u_int16_t aputype = APUTYPE_16BITLINEAR; 69865543Scg 69965543Scg wcreg_tpl = ch->wcreg_tpl & WAVCACHE_CHCTL_ADDRTAG_MASK; 70065543Scg 70165543Scg if (format & AFMT_STEREO) { 70265543Scg wcreg_tpl |= WAVCACHE_CHCTL_STEREO; 70365543Scg aputype += 1; 70465543Scg } 70565543Scg if (format & AFMT_U8 || format & AFMT_S8) { 70665543Scg aputype += 2; 70765543Scg if (format & AFMT_U8) 70865543Scg wcreg_tpl |= WAVCACHE_CHCTL_U8; 70965543Scg } 71065543Scg if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) { 71165543Scg format &= ~AFMT_BIGENDIAN & ~AFMT_U16_LE; 71265543Scg format |= AFMT_S16_LE; 71365543Scg } 71465543Scg ch->wcreg_tpl = wcreg_tpl; 71565543Scg ch->aputype = aputype; 71665543Scg return format; 71765543Scg} 71865543Scg 71965543Scgstatic int 72065543Scgaggch_setspeed(void *data, u_int32_t speed) 72165543Scg{ 72265543Scg return speed; 72365543Scg} 72465543Scg 72565543Scgstatic int 72665543Scgaggch_setblocksize(void *data, u_int32_t blocksize) 72765543Scg{ 72865543Scg return ((struct agg_chinfo*)data)->blocksize = blocksize; 72965543Scg} 73065543Scg 73165543Scgstatic int 73265543Scgaggch_trigger(void *data, int go) 73365543Scg{ 73465543Scg struct agg_chinfo *ch = data; 73565543Scg 73665543Scg switch (go) { 73765543Scg case PCMTRIG_EMLDMAWR: 73865543Scg return 0; 73965543Scg case PCMTRIG_START: 74065543Scg ch->parent->active |= (1 << ch->num); 74165543Scg if (ch->dir == PCMDIR_PLAY) 74265543Scg aggch_start_dac(ch); 74365543Scg#if 0 /* XXX - RECORDING */ 74465543Scg else 74565543Scg aggch_start_adc(ch); 74665543Scg#endif 74765543Scg break; 74865543Scg case PCMTRIG_ABORT: 74965543Scg case PCMTRIG_STOP: 75065543Scg ch->parent->active &= ~(1 << ch->num); 75165543Scg if (ch->dir == PCMDIR_PLAY) 75265543Scg aggch_stop_dac(ch); 75365543Scg#if 0 /* XXX - RECORDING */ 75465543Scg else 75565543Scg aggch_stop_adc(ch); 75665543Scg#endif 75765543Scg break; 75865543Scg } 75965543Scg 76065543Scg if (ch->parent->active) { 76165543Scg set_timer(ch->parent); 76265543Scg wp_starttimer(ch->parent); 76365543Scg } else 76465543Scg wp_stoptimer(ch->parent); 76565543Scg 76665543Scg return 0; 76765543Scg} 76865543Scg 76965543Scgstatic int 77065543Scgaggch_getplayptr(void *data) 77165543Scg{ 77265543Scg struct agg_chinfo *ch = data; 77365543Scg u_int cp; 77465543Scg 77565543Scg cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); 77665543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 77765543Scg cp = (0xffff << 2) & ((cp << 2) - ch->offset); 77865543Scg else 77965543Scg cp = (0xffff << 1) & ((cp << 1) - ch->offset); 78065543Scg 78165543Scg return cp; 78265543Scg} 78365543Scg 78465543Scgstatic pcmchan_caps * 78565543Scgaggch_getcaps(void *data) 78665543Scg{ 78765543Scg static u_int32_t playfmt[] = { 78865543Scg AFMT_U8, 78965543Scg AFMT_STEREO | AFMT_U8, 79065543Scg AFMT_S8, 79165543Scg AFMT_STEREO | AFMT_S8, 79265543Scg AFMT_S16_LE, 79365543Scg AFMT_STEREO | AFMT_S16_LE, 79465543Scg 0 79565543Scg }; 79665543Scg static pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; 79765543Scg 79865543Scg static u_int32_t recfmt[] = { 79965543Scg AFMT_S8, 80065543Scg AFMT_STEREO | AFMT_S8, 80165543Scg AFMT_S16_LE, 80265543Scg AFMT_STEREO | AFMT_S16_LE, 80365543Scg 0 80465543Scg }; 80565543Scg static pcmchan_caps reccaps = {4000, 48000, recfmt, 0}; 80665543Scg 80765543Scg return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)? 80865543Scg &playcaps : &reccaps; 80965543Scg} 81065543Scg 81165543Scg 81265543Scg/* ----------------------------- 81365543Scg * Bus space. 81465543Scg */ 81565543Scg 81665543Scgstatic void 81765543Scgagg_intr(void *sc) 81865543Scg{ 81965543Scg struct agg_info* ess = sc; 82065543Scg u_int16_t status; 82165543Scg int i; 82265543Scg 82365543Scg status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); 82465543Scg if (!status) 82565543Scg return; 82665543Scg 82765543Scg /* Acknowledge all. */ 82865543Scg bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 82965543Scg bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0); 83065543Scg#if 0 /* XXX - HWVOL */ 83165543Scg if (status & HOSTINT_STAT_HWVOL) { 83265543Scg u_int delta; 83365543Scg delta = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER) 83465543Scg - 0x88; 83565543Scg if (delta & 0x11) 83665543Scg mixer_set(device_get_softc(ess->dev), 83765543Scg SOUND_MIXER_VOLUME, 0); 83865543Scg else { 83965543Scg mixer_set(device_get_softc(ess->dev), 84065543Scg SOUND_MIXER_VOLUME, 84165543Scg mixer_get(device_get_softc(ess->dev), 84265543Scg SOUND_MIXER_VOLUME) 84365543Scg + ((delta >> 5) & 0x7) - 4 84465543Scg + ((delta << 7) & 0x700) - 0x400); 84565543Scg } 84665543Scg bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 0x88); 84765543Scg } 84865543Scg#endif /* XXX - HWVOL */ 84965543Scg 85065543Scg for (i = 0; i < ess->playchns; i++) 85165543Scg if (ess->active & (1 << i)) { 85265543Scg suppress_jitter(ess->pch + i); 85365543Scg chn_intr(ess->pch[i].channel); 85465543Scg } 85565543Scg#if 0 /* XXX - RECORDING */ 85665543Scg if (ess->active & (1 << i)) 85765543Scg chn_intr(ess->rch.channel); 85865543Scg#endif 85965543Scg} 86065543Scg 86165543Scgstatic void 86265543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 86365543Scg{ 86465543Scg bus_addr_t *phys = arg; 86565543Scg 86665543Scg *phys = error? 0 : segs->ds_addr; 86765543Scg 86865543Scg if (bootverbose) { 86965543Scg printf("setmap (%lx, %lx), nseg=%d, error=%d\n", 87065543Scg (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 87165543Scg nseg, error); 87265543Scg } 87365543Scg} 87465543Scg 87565543Scgstatic void * 87665543Scgdma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys) 87765543Scg{ 87865543Scg void *buf; 87965543Scg bus_dmamap_t map; 88065543Scg 88165543Scg if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) 88265543Scg return NULL; 88365543Scg if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, setmap, phys, 0) 88465543Scg || !*phys) { 88565543Scg bus_dmamem_free(sc->parent_dmat, buf, map); 88665543Scg return NULL; 88765543Scg } 88865543Scg return buf; 88965543Scg} 89065543Scg 89165543Scgstatic void 89265543Scgdma_free(struct agg_info *sc, void *buf) 89365543Scg{ 89465543Scg bus_dmamem_free(sc->parent_dmat, buf, NULL); 89565543Scg} 89665543Scg 89765543Scgstatic int 89865543Scgagg_probe(device_t dev) 89965543Scg{ 90065543Scg char *s = NULL; 90165543Scg 90265543Scg switch (pci_get_devid(dev)) { 90365543Scg case MAESTRO_1_PCI_ID: 90465543Scg s = "ESS Technology Maestro-1"; 90565543Scg break; 90665543Scg 90765543Scg case MAESTRO_2_PCI_ID: 90865543Scg s = "ESS Technology Maestro-2"; 90965543Scg break; 91065543Scg 91165543Scg case MAESTRO_2E_PCI_ID: 91265543Scg s = "ESS Technology Maestro-2E"; 91365543Scg break; 91465543Scg } 91565543Scg 91665543Scg if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { 91765543Scg device_set_desc(dev, s); 91865543Scg return 0; 91965543Scg } 92065543Scg return ENXIO; 92165543Scg} 92265543Scg 92365543Scgstatic int 92465543Scgagg_attach(device_t dev) 92565543Scg{ 92665543Scg struct agg_info *ess = NULL; 92765543Scg u_int32_t data; 92865543Scg int mapped = 0; 92965543Scg int regid = PCIR_MAPS; 93065543Scg struct resource *reg = NULL; 93165543Scg struct ac97_info *codec = NULL; 93265543Scg int irqid = 0; 93365543Scg struct resource *irq = NULL; 93465543Scg void *ih = NULL; 93565543Scg char status[SND_STATUSLEN]; 93665543Scg static pcm_channel agg_pchtpl = { 93765543Scg aggch_init, 93865543Scg NULL, /* setdir */ 93965543Scg aggch_setplayformat, 94065543Scg aggch_setspeed, 94165543Scg aggch_setblocksize, 94265543Scg aggch_trigger, 94365543Scg aggch_getplayptr, 94465543Scg aggch_getcaps, 94565543Scg aggch_free, /* free */ 94665543Scg NULL, /* nop1 */ 94765543Scg NULL, /* nop2 */ 94865543Scg NULL, /* nop3 */ 94965543Scg NULL, /* nop4 */ 95065543Scg NULL, /* nop5 */ 95165543Scg NULL, /* nop6 */ 95265543Scg NULL, /* nop7 */ 95365543Scg }; 95465543Scg 95565543Scg if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT)) == NULL) { 95665543Scg device_printf(dev, "cannot allocate softc\n"); 95765543Scg return ENXIO; 95865543Scg } 95965543Scg bzero(ess, sizeof *ess); 96065543Scg ess->dev = dev; 96165543Scg 96265543Scg if (bus_dma_tag_create(/*parent*/NULL, 96365543Scg /*alignment*/1 << WAVCACHE_BASEADDR_SHIFT, 96465543Scg /*boundary*/WPWA_MAXADDR + 1, 96565543Scg /*lowaddr*/MAESTRO_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, 96665543Scg /*filter*/NULL, /*filterarg*/NULL, 96765543Scg /*maxsize*/AGG_BUFSIZ * 2, /*nsegments*/1, /*maxsegz*/0x3ffff, 96865543Scg /*flags*/0, &ess->parent_dmat) != 0) { 96965543Scg device_printf(dev, "unable to create dma tag\n"); 97065543Scg goto bad; 97165543Scg } 97265543Scg 97365543Scg ess->stat = dma_malloc(ess, AGG_BUFSIZ, &ess->baseaddr); 97465543Scg if (ess->stat == NULL) { 97565543Scg device_printf(dev, "cannot allocate status buffer\n"); 97665543Scg goto bad; 97765543Scg } 97865543Scg if (bootverbose) 97965543Scg device_printf(dev, "Maestro DMA base: %#x\n", ess->baseaddr); 98065543Scg 98165543Scg agg_power(ess, PPMI_D0); 98265543Scg DELAY(100000); 98365543Scg 98465543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 98565543Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); 98665543Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 98765543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 98865543Scg 98965543Scg if (data & PCIM_CMD_PORTEN) { 99065543Scg reg = bus_alloc_resource(dev, SYS_RES_IOPORT, ®id, 99165543Scg 0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE); 99265543Scg if (reg != NULL) { 99365543Scg ess->reg = reg; 99465543Scg ess->regid = regid; 99565543Scg ess->st = rman_get_bustag(reg); 99665543Scg ess->sh = rman_get_bushandle(reg); 99765543Scg mapped++; 99865543Scg } 99965543Scg } 100065543Scg if (mapped == 0) { 100165543Scg device_printf(dev, "unable to map register space\n"); 100265543Scg goto bad; 100365543Scg } 100465543Scg 100565543Scg agg_init(ess); 100665543Scg if (agg_rdcodec(ess, 0) == 0x80) { 100765543Scg device_printf(dev, "PT101 codec detected!\n"); 100865543Scg goto bad; 100965543Scg } 101065543Scg codec = ac97_create(dev, ess, agg_ac97_init, agg_rdcodec, agg_wrcodec); 101165543Scg if (codec == NULL) 101265543Scg goto bad; 101365543Scg if (mixer_init(dev, &ac97_mixer, codec) == -1) 101465543Scg goto bad; 101565543Scg ess->codec = codec; 101665543Scg 101765543Scg irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 101865543Scg 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE); 101965543Scg if (irq == NULL 102065543Scg || bus_setup_intr(dev, irq, INTR_TYPE_TTY, agg_intr, ess, &ih)) { 102165543Scg device_printf(dev, "unable to map interrupt\n"); 102265543Scg goto bad; 102365543Scg } 102465543Scg ess->irq = irq; 102565543Scg ess->irqid = irqid; 102665543Scg ess->ih = ih; 102765543Scg 102865543Scg snprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld", 102965543Scg rman_get_start(reg), rman_get_start(irq)); 103065543Scg 103165543Scg if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1)) 103265543Scg goto bad; 103365543Scg 103465543Scg for (data = 0; data < AGG_MAXPLAYCH; data++) 103565543Scg pcm_addchan(dev, PCMDIR_PLAY, &agg_pchtpl, ess); 103665543Scg#if 0 /* XXX - RECORDING */ 103765543Scg pcm_addchan(dev, PCMDIR_REC, &agg_rchtpl, ess); 103865543Scg#endif 103965543Scg pcm_setstatus(dev, status); 104065543Scg 104165543Scg return 0; 104265543Scg 104365543Scg bad: 104465644Scg if (codec != NULL) 104565644Scg ac97_destroy(codec); 104665543Scg if (ih != NULL) 104765543Scg bus_teardown_intr(dev, irq, ih); 104865543Scg if (irq != NULL) 104965543Scg bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 105065543Scg if (reg != NULL) 105165543Scg bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); 105265543Scg if (ess != NULL) { 105365543Scg agg_power(ess, PPMI_D3); 105465543Scg if (ess->stat != NULL) 105565543Scg dma_free(ess, ess->stat); 105665543Scg if (ess->parent_dmat != NULL) 105765543Scg bus_dma_tag_destroy(ess->parent_dmat); 105865543Scg free(ess, M_DEVBUF); 105965543Scg } 106065543Scg 106165543Scg return ENXIO; 106265543Scg} 106365543Scg 106465543Scgstatic int 106565543Scgagg_detach(device_t dev) 106665543Scg{ 106765543Scg struct agg_info *ess = pcm_getdevinfo(dev); 106865543Scg int r; 106965543Scg 107065543Scg r = pcm_unregister(dev); 107165543Scg if (r) 107265543Scg return r; 107365543Scg 107465543Scg ess = pcm_getdevinfo(dev); 107565543Scg dma_free(ess, ess->stat); 107665543Scg 107765543Scg /* Power down everything except clock and vref. */ 107865543Scg agg_wrcodec(ess, AC97_REG_POWER, 0xd700); 107965543Scg DELAY(20); 108065543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 108165543Scg agg_power(ess, PPMI_D3); 108265543Scg 108365543Scg bus_teardown_intr(dev, ess->irq, ess->ih); 108465543Scg bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); 108565543Scg bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); 108665543Scg bus_dma_tag_destroy(ess->parent_dmat); 108765543Scg free(ess, M_DEVBUF); 108865543Scg return 0; 108965543Scg} 109065543Scg 109165543Scgstatic int 109265543Scgagg_suspend(device_t dev) 109365543Scg{ 109465543Scg struct agg_info *ess = pcm_getdevinfo(dev); 109565543Scg int i, x; 109665543Scg 109765543Scg x = spltty(); 109865543Scg wp_stoptimer(ess); 109965543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 110065543Scg 110165543Scg for (i = 0; i < ess->playchns; i++) 110265543Scg aggch_stop_dac(ess->pch + i); 110365543Scg 110465543Scg#if 0 /* XXX - RECORDING */ 110565543Scg aggch_stop_adc(&ess->rch); 110665543Scg#endif 110765543Scg splx(x); 110865543Scg /* Power down everything except clock. */ 110965543Scg agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); 111065543Scg DELAY(20); 111165543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 111265543Scg DELAY(1); 111365543Scg agg_power(ess, PPMI_D3); 111465543Scg 111565543Scg return 0; 111665543Scg} 111765543Scg 111865543Scgstatic int 111965543Scgagg_resume(device_t dev) 112065543Scg{ 112165543Scg int i, x; 112265543Scg struct agg_info *ess = pcm_getdevinfo(dev); 112365543Scg 112465543Scg agg_power(ess, PPMI_D0); 112565543Scg DELAY(100000); 112665543Scg agg_init(ess); 112765543Scg if (mixer_reinit(dev)) { 112865543Scg device_printf(dev, "unable to reinitialize the mixer\n"); 112965543Scg return ENXIO; 113065543Scg } 113165543Scg 113265543Scg x = spltty(); 113365543Scg for (i = 0; i < ess->playchns; i++) 113465543Scg if (ess->active & (1 << i)) 113565543Scg aggch_start_dac(ess->pch + i); 113665543Scg#if 0 /* XXX - RECORDING */ 113765543Scg if (ess->active & (1 << i)) 113865543Scg aggch_start_adc(&ess->rch); 113965543Scg#endif 114065543Scg if (ess->active) { 114165543Scg set_timer(ess); 114265543Scg wp_starttimer(ess); 114365543Scg } 114465543Scg splx(x); 114565543Scg return 0; 114665543Scg} 114765543Scg 114865543Scgstatic int 114965543Scgagg_shutdown(device_t dev) 115065543Scg{ 115165543Scg struct agg_info *ess = pcm_getdevinfo(dev); 115265543Scg int i; 115365543Scg 115465543Scg wp_stoptimer(ess); 115565543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 115665543Scg 115765543Scg for (i = 0; i < ess->playchns; i++) 115865543Scg aggch_stop_dac(ess->pch + i); 115965543Scg 116065543Scg#if 0 /* XXX - RECORDING */ 116165543Scg aggch_stop_adc(&ess->rch); 116265543Scg#endif 116365543Scg return 0; 116465543Scg} 116565543Scg 116665543Scg 116765543Scgstatic device_method_t agg_methods[] = { 116865543Scg DEVMETHOD(device_probe, agg_probe), 116965543Scg DEVMETHOD(device_attach, agg_attach), 117065543Scg DEVMETHOD(device_detach, agg_detach), 117165543Scg DEVMETHOD(device_suspend, agg_suspend), 117265543Scg DEVMETHOD(device_resume, agg_resume), 117365543Scg DEVMETHOD(device_shutdown, agg_shutdown), 117465543Scg 117565543Scg { 0, 0 } 117665543Scg}; 117765543Scg 117865543Scgstatic driver_t agg_driver = { 117965543Scg "pcm", 118065543Scg agg_methods, 118165543Scg sizeof(snddev_info), 118265543Scg}; 118365543Scg 118465543Scgstatic devclass_t pcm_devclass; 118565543Scg 118665543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); 118765543ScgMODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 118865543ScgMODULE_VERSION(snd_maestro, 1); 1189