maestro.c revision 84658
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 */ 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 3865543Scg * Cameron Grant <gandalf@vilnya.demon.co.uk>. 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. 4565543Scg */ 4665543Scg 4765543Scg#include <dev/sound/pcm/sound.h> 4865543Scg#include <dev/sound/pcm/ac97.h> 4965543Scg#include <pci/pcireg.h> 5065543Scg#include <pci/pcivar.h> 5165543Scg 5265543Scg#include <dev/sound/pci/maestro_reg.h> 5365543Scg 5482180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/maestro.c 84658 2001-10-08 05:59:54Z cg $"); 5582180Scg 5665543Scg#define inline __inline 5765543Scg 5865543Scg/* 5965543Scg * PCI IDs of supported chips: 6065543Scg * 6165543Scg * MAESTRO-1 0x01001285 6265543Scg * MAESTRO-2 0x1968125d 6365543Scg * MAESTRO-2E 0x1978125d 6465543Scg */ 6565543Scg 6665543Scg#define MAESTRO_1_PCI_ID 0x01001285 6765543Scg#define MAESTRO_2_PCI_ID 0x1968125d 6865543Scg#define MAESTRO_2E_PCI_ID 0x1978125d 6965543Scg 7065543Scg#define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ 7165543Scg#define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ 7265543Scg 7365543Scg#ifndef AGG_MAXPLAYCH 7465543Scg# define AGG_MAXPLAYCH 4 7565543Scg#endif 7665543Scg 7784658Scg#define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ 7865543Scg 7965543Scg 8065543Scg/* ----------------------------- 8165543Scg * Data structures. 8265543Scg */ 8365543Scgstruct agg_chinfo { 8465543Scg struct agg_info *parent; 8574763Scg struct pcm_channel *channel; 8674763Scg struct snd_dbuf *buffer; 8765543Scg bus_addr_t offset; 8865543Scg u_int32_t blocksize; 8970291Scg u_int32_t speed; 9065543Scg int dir; 9165543Scg u_int num; 9265543Scg u_int16_t aputype; 9365543Scg u_int16_t wcreg_tpl; 9465543Scg}; 9565543Scg 9665543Scgstruct agg_info { 9765543Scg device_t dev; 9865543Scg struct resource *reg; 9965543Scg int regid; 10065543Scg 10165543Scg bus_space_tag_t st; 10265543Scg bus_space_handle_t sh; 10365543Scg bus_dma_tag_t parent_dmat; 10465543Scg 10565543Scg struct resource *irq; 10665543Scg int irqid; 10765543Scg void *ih; 10865543Scg 10965543Scg u_int8_t *stat; 11065543Scg bus_addr_t baseaddr; 11165543Scg 11265543Scg struct ac97_info *codec; 11374763Scg void *lock; 11465543Scg 11584658Scg unsigned int bufsz; 11665543Scg u_int playchns, active; 11765543Scg struct agg_chinfo pch[AGG_MAXPLAYCH]; 11865543Scg struct agg_chinfo rch; 11965543Scg}; 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*); 13965543Scg 14065543Scgstatic void aggch_start_dac(struct agg_chinfo*); 14165543Scgstatic void aggch_stop_dac(struct agg_chinfo*); 14265543Scg 14365543Scgstatic inline void suppress_jitter(struct agg_chinfo*); 14465543Scg 14565543Scgstatic inline u_int calc_timer_freq(struct agg_chinfo*); 14665543Scgstatic void set_timer(struct agg_info*); 14765543Scg 14865543Scgstatic void agg_intr(void *); 14965543Scgstatic int agg_probe(device_t); 15065543Scgstatic int agg_attach(device_t); 15165543Scgstatic int agg_detach(device_t); 15265543Scgstatic int agg_suspend(device_t); 15365543Scgstatic int agg_resume(device_t); 15465543Scgstatic int agg_shutdown(device_t); 15565543Scg 15665543Scgstatic void *dma_malloc(struct agg_info*, u_int32_t, bus_addr_t*); 15765543Scgstatic void dma_free(struct agg_info*, void *); 15865543Scg 15965543Scg/* ----------------------------- 16065543Scg * Subsystems. 16165543Scg */ 16265543Scg 16365543Scg/* Codec/Ringbus */ 16465543Scg 16570134Scg/* -------------------------------------------------------------------- */ 16670134Scg 16765543Scgstatic u_int32_t 16870134Scgagg_ac97_init(kobj_t obj, void *sc) 16965543Scg{ 17065543Scg struct agg_info *ess = sc; 17170134Scg 17270134Scg return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; 17370134Scg} 17470134Scg 17570134Scgstatic int 17670134Scgagg_rdcodec(kobj_t obj, void *sc, int regno) 17770134Scg{ 17870134Scg 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 20970134Scgstatic int 21070134Scgagg_wrcodec(kobj_t obj, 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"); 22570134Scg return -1; 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); 23170134Scg 23270134Scg return 0; 23365543Scg} 23465543Scg 23570134Scgstatic kobj_method_t agg_ac97_methods[] = { 23670134Scg KOBJMETHOD(ac97_init, agg_ac97_init), 23770134Scg KOBJMETHOD(ac97_read, agg_rdcodec), 23870134Scg KOBJMETHOD(ac97_write, agg_wrcodec), 23970134Scg { 0, 0 } 24070134Scg}; 24170134ScgAC97_DECLARE(agg_ac97); 24270134Scg 24370134Scg/* -------------------------------------------------------------------- */ 24470134Scg 24565543Scgstatic inline void 24665543Scgringbus_setdest(struct agg_info *ess, int src, int dest) 24765543Scg{ 24865543Scg u_int32_t data; 24965543Scg 25065543Scg data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL); 25165543Scg data &= ~(0xfU << src); 25265543Scg data |= (0xfU & dest) << src; 25365543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); 25465543Scg} 25565543Scg 25665543Scg/* Wave Processor */ 25765543Scg 25865543Scgstatic inline u_int16_t 25965543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg) 26065543Scg{ 26165543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 26265543Scg return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA); 26365543Scg} 26465543Scg 26565543Scgstatic inline void 26665543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 26765543Scg{ 26865543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 26965543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 27065543Scg} 27165543Scg 27265543Scgstatic inline void 27365543Scgapu_setindex(struct agg_info *ess, u_int16_t reg) 27465543Scg{ 27565543Scg int t; 27665543Scg 27765543Scg wp_wrreg(ess, WPREG_CRAM_PTR, reg); 27865543Scg /* Sometimes WP fails to set apu register index. */ 27965543Scg for (t = 0; t < 1000; t++) { 28065543Scg if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg) 28165543Scg break; 28265543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg); 28365543Scg } 28465543Scg if (t == 1000) 28565543Scg device_printf(ess->dev, "apu_setindex() timed out.\n"); 28665543Scg} 28765543Scg 28865543Scgstatic inline u_int16_t 28965543Scgwp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) 29065543Scg{ 29165543Scg u_int16_t ret; 29265543Scg 29365543Scg apu_setindex(ess, ((unsigned)ch << 4) + reg); 29465543Scg ret = wp_rdreg(ess, WPREG_DATA_PORT); 29565543Scg return ret; 29665543Scg} 29765543Scg 29865543Scgstatic inline void 29965543Scgwp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) 30065543Scg{ 30165543Scg int t; 30265543Scg 30365543Scg apu_setindex(ess, ((unsigned)ch << 4) + reg); 30465543Scg wp_wrreg(ess, WPREG_DATA_PORT, data); 30565543Scg for (t = 0; t < 1000; t++) { 30665543Scg if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data) 30765543Scg break; 30865543Scg bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 30965543Scg } 31065543Scg if (t == 1000) 31165543Scg device_printf(ess->dev, "wp_wrapu() timed out.\n"); 31265543Scg} 31365543Scg 31465543Scgstatic inline void 31565543Scgwp_settimer(struct agg_info *ess, u_int freq) 31665543Scg{ 31765543Scg u_int clock = 48000 << 2; 31865543Scg u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; 31965543Scg 32065543Scg RANGE(divide, 4, 32 << 8); 32165543Scg 32265543Scg for (; divide > 32 << 1; divide >>= 1) 32365543Scg prescale++; 32465543Scg divide = (divide + 1) >> 1; 32565543Scg 32665543Scg for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 32765543Scg prescale++; 32865543Scg 32965543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 33065543Scg wp_wrreg(ess, WPREG_TIMER_FREQ, 33165543Scg (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 33265543Scg wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 33365543Scg} 33465543Scg 33565543Scgstatic inline void 33665543Scgwp_starttimer(struct agg_info *ess) 33765543Scg{ 33865543Scg wp_wrreg(ess, WPREG_TIMER_START, 1); 33965543Scg} 34065543Scg 34165543Scgstatic inline void 34265543Scgwp_stoptimer(struct agg_info *ess) 34365543Scg{ 34465543Scg wp_wrreg(ess, WPREG_TIMER_START, 0); 34565543Scg bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 34665543Scg} 34765543Scg 34865543Scg/* WaveCache */ 34965543Scg 35065543Scgstatic inline u_int16_t 35165543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg) 35265543Scg{ 35365543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 35465543Scg return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA); 35565543Scg} 35665543Scg 35765543Scgstatic inline void 35865543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) 35965543Scg{ 36065543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 36165543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data); 36265543Scg} 36365543Scg 36465543Scgstatic inline u_int16_t 36565543Scgwc_rdchctl(struct agg_info *ess, int ch) 36665543Scg{ 36765543Scg return wc_rdreg(ess, ch << 3); 36865543Scg} 36965543Scg 37065543Scgstatic inline void 37165543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) 37265543Scg{ 37365543Scg wc_wrreg(ess, ch << 3, data); 37465543Scg} 37565543Scg 37665543Scg/* Power management */ 37765543Scg 37865543Scgstatic inline void 37965543Scgagg_power(struct agg_info *ess, int status) 38065543Scg{ 38165543Scg u_int8_t data; 38265543Scg 38365543Scg data = pci_read_config(ess->dev, CONF_PM_PTR, 1); 38465543Scg if (pci_read_config(ess->dev, data, 1) == PPMI_CID) 38565543Scg pci_write_config(ess->dev, data + PM_CTRL, status, 1); 38665543Scg} 38765543Scg 38865543Scg 38965543Scg/* ----------------------------- 39065543Scg * Controller. 39165543Scg */ 39265543Scg 39365543Scgstatic inline void 39465543Scgagg_initcodec(struct agg_info* ess) 39565543Scg{ 39665543Scg u_int16_t data; 39765543Scg 39865543Scg if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL) 39965543Scg & RINGBUS_CTRL_ACLINK_ENABLED) { 40065543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 40165543Scg DELAY(104); /* 20.8us * (4 + 1) */ 40265543Scg } 40365543Scg /* XXX - 2nd codec should be looked at. */ 40465543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 40565543Scg RINGBUS_CTRL_AC97_SWRESET); 40665543Scg DELAY(2); 40765543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 40865543Scg RINGBUS_CTRL_ACLINK_ENABLED); 40965543Scg DELAY(21); 41065543Scg 41170134Scg agg_rdcodec(NULL, ess, 0); 41265543Scg if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 41365543Scg & CODEC_STAT_MASK) { 41465543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 41565543Scg DELAY(21); 41665543Scg 41765543Scg /* Try cold reset. */ 41865543Scg device_printf(ess->dev, "will perform cold reset.\n"); 41965543Scg data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR); 42065543Scg if (pci_read_config(ess->dev, 0x58, 2) & 1) 42165543Scg data |= 0x10; 42265543Scg data |= 0x009 & 42365543Scg ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA); 42465543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6); 42565543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 42665543Scg data | 0x009); 42765543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000); 42865543Scg DELAY(2); 42965543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); 43065543Scg DELAY(1); 43165543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009); 43265543Scg DELAY(500000); 43365543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data); 43465543Scg DELAY(84); /* 20.8us * 4 */ 43565543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 43665543Scg RINGBUS_CTRL_ACLINK_ENABLED); 43765543Scg DELAY(21); 43865543Scg } 43965543Scg} 44065543Scg 44165543Scgstatic void 44265543Scgagg_init(struct agg_info* ess) 44365543Scg{ 44465543Scg u_int32_t data; 44565543Scg 44665543Scg /* Setup PCI config registers. */ 44765543Scg 44865543Scg /* Disable all legacy emulations. */ 44965543Scg data = pci_read_config(ess->dev, CONF_LEGACY, 2); 45065543Scg data |= LEGACY_DISABLED; 45165543Scg pci_write_config(ess->dev, CONF_LEGACY, data, 2); 45265543Scg 45365543Scg /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 45465543Scg * Enable posted write. 45565543Scg * Prefer PCI timing rather than that of ISA. 45665543Scg * Don't swap L/R. */ 45765543Scg data = pci_read_config(ess->dev, CONF_MAESTRO, 4); 45865543Scg data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 45965543Scg data &= ~MAESTRO_SWAP_LR; 46065543Scg pci_write_config(ess->dev, CONF_MAESTRO, data, 4); 46165543Scg 46265543Scg /* Reset direct sound. */ 46365543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 46465543Scg HOSTINT_CTRL_DSOUND_RESET); 46565543Scg DELAY(10000); /* XXX - too long? */ 46665543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 46765543Scg DELAY(10000); 46865543Scg 46970619Sjhb /* Enable direct sound interruption and hardware volume control. */ 47065543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 47170619Sjhb HOSTINT_CTRL_DSOUND_INT_ENABLED | HOSTINT_CTRL_HWVOL_ENABLED); 47265543Scg 47365543Scg /* Setup Wave Processor. */ 47465543Scg 47565543Scg /* Enable WaveCache, set DMA base address. */ 47665543Scg wp_wrreg(ess, WPREG_WAVE_ROMRAM, 47765543Scg WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 47865543Scg bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL, 47965543Scg WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB); 48065543Scg 48165543Scg for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) 48265543Scg wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); 48365543Scg 48465543Scg /* Setup Codec/Ringbus. */ 48565543Scg agg_initcodec(ess); 48665543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 48765543Scg RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED); 48865543Scg 48965543Scg wp_wrreg(ess, WPREG_BASE, 0x8500); /* Parallel I/O */ 49065543Scg ringbus_setdest(ess, RINGBUS_SRC_ADC, 49165543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 49265543Scg ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 49365543Scg RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 49465543Scg 49565543Scg /* Setup ASSP. Needed for Dell Inspiron 7500? */ 49665543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00); 49765543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03); 49865543Scg bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00); 49965543Scg 50065543Scg /* 50165543Scg * Setup GPIO. 50265543Scg * There seems to be speciality with NEC systems. 50365543Scg */ 50465543Scg switch (pci_get_subvendor(ess->dev) 50565543Scg | (pci_get_subdevice(ess->dev) << 16)) { 50665543Scg case NEC_SUBID1: 50765543Scg case NEC_SUBID2: 50865543Scg /* Matthew Braithwaite <matt@braithwaite.net> reported that 50965543Scg * NEC Versa LX doesn't need GPIO operation. */ 51065543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0x9ff); 51165543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 51265543Scg bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 0x600); 51365543Scg bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x200); 51465543Scg break; 51565543Scg } 51665543Scg} 51765543Scg 51865543Scg/* Channel controller. */ 51965543Scg 52065543Scgstatic void 52165543Scgaggch_start_dac(struct agg_chinfo *ch) 52265543Scg{ 52365543Scg u_int wpwa = APU_USE_SYSMEM | (ch->offset >> 9); 52484658Scg u_int size = ch->parent->bufsz >> 1; 52570291Scg u_int speed = ch->speed; 52665543Scg u_int offset = ch->offset >> 1; 52770291Scg u_int cp = 0; 52865543Scg u_int16_t apuch = ch->num << 1; 52965543Scg u_int dv; 53065543Scg int pan = 0; 53165543Scg 53265543Scg switch (ch->aputype) { 53365543Scg case APUTYPE_16BITSTEREO: 53465543Scg wpwa >>= 1; 53565543Scg size >>= 1; 53665543Scg offset >>= 1; 53765543Scg cp >>= 1; 53865543Scg /* FALLTHROUGH */ 53965543Scg case APUTYPE_8BITSTEREO: 54065543Scg pan = 8; 54165543Scg apuch++; 54265543Scg break; 54365543Scg case APUTYPE_8BITLINEAR: 54465543Scg speed >>= 1; 54565543Scg break; 54665543Scg } 54765543Scg 54865543Scg dv = (((speed % 48000) << 16) + 24000) / 48000 54965543Scg + ((speed / 48000) << 16); 55065543Scg 55165543Scg do { 55265543Scg wp_wrapu(ch->parent, apuch, APUREG_WAVESPACE, wpwa & 0xff00); 55365543Scg wp_wrapu(ch->parent, apuch, APUREG_CURPTR, offset + cp); 55465543Scg wp_wrapu(ch->parent, apuch, APUREG_ENDPTR, offset + size); 55565543Scg wp_wrapu(ch->parent, apuch, APUREG_LOOPLEN, size); 55665543Scg wp_wrapu(ch->parent, apuch, APUREG_AMPLITUDE, 0xe800); 55765543Scg wp_wrapu(ch->parent, apuch, APUREG_POSITION, 0x8f00 55865543Scg | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) 55965543Scg | ((PAN_FRONT + pan) << APU_PAN_SHIFT)); 56065543Scg wp_wrapu(ch->parent, apuch, APUREG_FREQ_LOBYTE, APU_plus6dB 56165543Scg | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 56265543Scg wp_wrapu(ch->parent, apuch, APUREG_FREQ_HIWORD, dv >> 8); 56365543Scg 56465543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 56565543Scg wpwa |= APU_STEREO >> 1; 56665543Scg pan = -pan; 56765543Scg } while (pan < 0 && apuch--); 56865543Scg 56965543Scg wc_wrchctl(ch->parent, apuch, ch->wcreg_tpl); 57065543Scg wc_wrchctl(ch->parent, apuch + 1, ch->wcreg_tpl); 57165543Scg 57265543Scg wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, 57365543Scg (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 57465543Scg if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) 57565543Scg wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, 57665543Scg (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 57765543Scg} 57865543Scg 57965543Scgstatic void 58065543Scgaggch_stop_dac(struct agg_chinfo *ch) 58165543Scg{ 58265543Scg wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE, 58365543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 58465543Scg wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE, 58565543Scg APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 58665543Scg} 58765543Scg 58865543Scg/* 58965543Scg * Stereo jitter suppressor. 59065543Scg * Sometimes playback pointers differ in stereo-paired channels. 59165543Scg * Calling this routine within intr fixes the problem. 59265543Scg */ 59365543Scgstatic inline void 59465543Scgsuppress_jitter(struct agg_chinfo *ch) 59565543Scg{ 59665543Scg if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) { 59784658Scg int cp, diff, halfsize = ch->parent->bufsz >> 2; 59865543Scg 59965543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 60065543Scg halfsize >>= 1; 60165543Scg cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); 60265543Scg diff = wp_rdapu(ch->parent, (ch->num << 1) + 1, APUREG_CURPTR); 60365543Scg diff -= cp; 60465543Scg if (diff >> 1 && diff > -halfsize && diff < halfsize) 60565543Scg bus_space_write_2(ch->parent->st, ch->parent->sh, 60665543Scg PORT_DSP_DATA, cp); 60765543Scg } 60865543Scg} 60965543Scg 61065543Scgstatic inline u_int 61165543Scgcalc_timer_freq(struct agg_chinfo *ch) 61265543Scg{ 61365543Scg u_int ss = 2; 61465543Scg 61565543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 61665543Scg ss <<= 1; 61765543Scg if (ch->aputype == APUTYPE_8BITLINEAR) 61865543Scg ss >>= 1; 61965543Scg 62070291Scg return (ch->speed * ss) / ch->blocksize; 62165543Scg} 62265543Scg 62365543Scgstatic void 62465543Scgset_timer(struct agg_info *ess) 62565543Scg{ 62665543Scg int i; 62765543Scg u_int freq = 0; 62865543Scg 62965543Scg for (i = 0; i < ess->playchns; i++) 63065543Scg if ((ess->active & (1 << i)) && 63165543Scg (freq < calc_timer_freq(ess->pch + i))) 63265543Scg freq = calc_timer_freq(ess->pch + i); 63365543Scg 63465543Scg wp_settimer(ess, freq); 63565543Scg} 63665543Scg 63765543Scg 63865543Scg/* ----------------------------- 63965543Scg * Newpcm glue. 64065543Scg */ 64165543Scg 64265543Scgstatic void * 64374763Scgaggch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 64465543Scg{ 64565543Scg struct agg_info *ess = devinfo; 64665543Scg struct agg_chinfo *ch; 64765543Scg bus_addr_t physaddr; 64870291Scg void *p; 64965543Scg 65065543Scg ch = (dir == PCMDIR_PLAY)? ess->pch + ess->playchns : &ess->rch; 65165543Scg 65265543Scg ch->parent = ess; 65365543Scg ch->channel = c; 65465543Scg ch->buffer = b; 65565543Scg ch->num = ess->playchns; 65665543Scg ch->dir = dir; 65765543Scg 65884658Scg p = dma_malloc(ess, ess->bufsz, &physaddr); 65970291Scg if (p == NULL) 66065543Scg return NULL; 66184658Scg sndbuf_setup(b, p, ess->bufsz); 66265543Scg 66365543Scg ch->offset = physaddr - ess->baseaddr; 66465543Scg if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) { 66565543Scg device_printf(ess->dev, 66665543Scg "offset %#x exceeds limit. ", ch->offset); 66774763Scg dma_free(ess, sndbuf_getbuf(b)); 66865543Scg return NULL; 66965543Scg } 67065543Scg 67165543Scg ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 67265543Scg 67365543Scg if (dir == PCMDIR_PLAY) { 67465543Scg ess->playchns++; 67565543Scg if (bootverbose) 67665543Scg device_printf(ess->dev, "pch[%d].offset = %#x\n", ch->num, ch->offset); 67765543Scg } else if (bootverbose) 67865543Scg device_printf(ess->dev, "rch.offset = %#x\n", ch->offset); 67965543Scg 68065543Scg return ch; 68165543Scg} 68265543Scg 68365543Scgstatic int 68470134Scgaggch_free(kobj_t obj, void *data) 68565543Scg{ 68665543Scg struct agg_chinfo *ch = data; 68765543Scg struct agg_info *ess = ch->parent; 68865543Scg 68965543Scg /* free up buffer - called after channel stopped */ 69070291Scg dma_free(ess, sndbuf_getbuf(ch->buffer)); 69165543Scg 69265543Scg /* return 0 if ok */ 69365543Scg return 0; 69465543Scg} 69565543Scg 69665543Scgstatic int 69770134Scgaggch_setplayformat(kobj_t obj, void *data, u_int32_t format) 69865543Scg{ 69965543Scg struct agg_chinfo *ch = data; 70065543Scg u_int16_t wcreg_tpl; 70165543Scg u_int16_t aputype = APUTYPE_16BITLINEAR; 70265543Scg 70365543Scg wcreg_tpl = ch->wcreg_tpl & WAVCACHE_CHCTL_ADDRTAG_MASK; 70465543Scg 70565543Scg if (format & AFMT_STEREO) { 70665543Scg wcreg_tpl |= WAVCACHE_CHCTL_STEREO; 70765543Scg aputype += 1; 70865543Scg } 70965543Scg if (format & AFMT_U8 || format & AFMT_S8) { 71065543Scg aputype += 2; 71165543Scg if (format & AFMT_U8) 71265543Scg wcreg_tpl |= WAVCACHE_CHCTL_U8; 71365543Scg } 71465543Scg if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) { 71565543Scg format &= ~AFMT_BIGENDIAN & ~AFMT_U16_LE; 71665543Scg format |= AFMT_S16_LE; 71765543Scg } 71865543Scg ch->wcreg_tpl = wcreg_tpl; 71965543Scg ch->aputype = aputype; 72065543Scg return format; 72165543Scg} 72265543Scg 72365543Scgstatic int 72470134Scgaggch_setspeed(kobj_t obj, void *data, u_int32_t speed) 72565543Scg{ 72670291Scg struct agg_chinfo *ch = data; 72770291Scg 72870291Scg ch->speed = speed; 72970291Scg return ch->speed; 73065543Scg} 73165543Scg 73265543Scgstatic int 73370134Scgaggch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 73465543Scg{ 73565543Scg return ((struct agg_chinfo*)data)->blocksize = blocksize; 73665543Scg} 73765543Scg 73865543Scgstatic int 73970134Scgaggch_trigger(kobj_t obj, void *data, int go) 74065543Scg{ 74165543Scg struct agg_chinfo *ch = data; 74265543Scg 74365543Scg switch (go) { 74465543Scg case PCMTRIG_EMLDMAWR: 74565543Scg return 0; 74665543Scg case PCMTRIG_START: 74765543Scg ch->parent->active |= (1 << ch->num); 74865543Scg if (ch->dir == PCMDIR_PLAY) 74965543Scg aggch_start_dac(ch); 75065543Scg#if 0 /* XXX - RECORDING */ 75165543Scg else 75265543Scg aggch_start_adc(ch); 75365543Scg#endif 75465543Scg break; 75565543Scg case PCMTRIG_ABORT: 75665543Scg case PCMTRIG_STOP: 75765543Scg ch->parent->active &= ~(1 << ch->num); 75865543Scg if (ch->dir == PCMDIR_PLAY) 75965543Scg aggch_stop_dac(ch); 76065543Scg#if 0 /* XXX - RECORDING */ 76165543Scg else 76265543Scg aggch_stop_adc(ch); 76365543Scg#endif 76465543Scg break; 76565543Scg } 76665543Scg 76765543Scg if (ch->parent->active) { 76865543Scg set_timer(ch->parent); 76965543Scg wp_starttimer(ch->parent); 77065543Scg } else 77165543Scg wp_stoptimer(ch->parent); 77265543Scg 77365543Scg return 0; 77465543Scg} 77565543Scg 77665543Scgstatic int 77770134Scgaggch_getplayptr(kobj_t obj, void *data) 77865543Scg{ 77965543Scg struct agg_chinfo *ch = data; 78065543Scg u_int cp; 78165543Scg 78265543Scg cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); 78365543Scg if (ch->aputype == APUTYPE_16BITSTEREO) 78465543Scg cp = (0xffff << 2) & ((cp << 2) - ch->offset); 78565543Scg else 78665543Scg cp = (0xffff << 1) & ((cp << 1) - ch->offset); 78765543Scg 78865543Scg return cp; 78965543Scg} 79065543Scg 79174763Scgstatic struct pcmchan_caps * 79270134Scgaggch_getcaps(kobj_t obj, void *data) 79365543Scg{ 79465543Scg static u_int32_t playfmt[] = { 79565543Scg AFMT_U8, 79665543Scg AFMT_STEREO | AFMT_U8, 79765543Scg AFMT_S8, 79865543Scg AFMT_STEREO | AFMT_S8, 79965543Scg AFMT_S16_LE, 80065543Scg AFMT_STEREO | AFMT_S16_LE, 80165543Scg 0 80265543Scg }; 80374763Scg static struct pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; 80465543Scg 80565543Scg static u_int32_t recfmt[] = { 80665543Scg AFMT_S8, 80765543Scg AFMT_STEREO | AFMT_S8, 80865543Scg AFMT_S16_LE, 80965543Scg AFMT_STEREO | AFMT_S16_LE, 81065543Scg 0 81165543Scg }; 81274763Scg static struct pcmchan_caps reccaps = {4000, 48000, recfmt, 0}; 81365543Scg 81465543Scg return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)? 81565543Scg &playcaps : &reccaps; 81665543Scg} 81765543Scg 81870134Scgstatic kobj_method_t aggch_methods[] = { 81970134Scg KOBJMETHOD(channel_init, aggch_init), 82070134Scg KOBJMETHOD(channel_free, aggch_free), 82170134Scg KOBJMETHOD(channel_setformat, aggch_setplayformat), 82270134Scg KOBJMETHOD(channel_setspeed, aggch_setspeed), 82370134Scg KOBJMETHOD(channel_setblocksize, aggch_setblocksize), 82470134Scg KOBJMETHOD(channel_trigger, aggch_trigger), 82570134Scg KOBJMETHOD(channel_getptr, aggch_getplayptr), 82670134Scg KOBJMETHOD(channel_getcaps, aggch_getcaps), 82770134Scg { 0, 0 } 82870134Scg}; 82970134ScgCHANNEL_DECLARE(aggch); 83065543Scg 83165543Scg/* ----------------------------- 83265543Scg * Bus space. 83365543Scg */ 83465543Scg 83565543Scgstatic void 83665543Scgagg_intr(void *sc) 83765543Scg{ 83865543Scg struct agg_info* ess = sc; 83965543Scg u_int16_t status; 84065543Scg int i; 84165543Scg 84265543Scg status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); 84365543Scg if (!status) 84465543Scg return; 84565543Scg 84665543Scg /* Acknowledge all. */ 84765543Scg bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 84870619Sjhb bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0xff); 84970619Sjhb 85065543Scg if (status & HOSTINT_STAT_HWVOL) { 85170619Sjhb u_int event; 85270619Sjhb 85370619Sjhb event = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER); 85470619Sjhb switch (event) { 85570619Sjhb case HWVOL_MUTE: 85670945Sjhb mixer_hwvol_mute(ess->dev); 85770619Sjhb break; 85870619Sjhb case HWVOL_UP: 85970945Sjhb mixer_hwvol_step(ess->dev, 1, 1); 86070619Sjhb break; 86170619Sjhb case HWVOL_DOWN: 86270945Sjhb mixer_hwvol_step(ess->dev, -1, -1); 86370619Sjhb break; 86470619Sjhb case HWVOL_NOP: 86570619Sjhb break; 86670619Sjhb default: 86770619Sjhb device_printf(ess->dev, "%s: unknown HWVOL event 0x%x\n", 86870619Sjhb device_get_nameunit(ess->dev), event); 86965543Scg } 87070619Sjhb bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 87170619Sjhb HWVOL_NOP); 87265543Scg } 87365543Scg 87465543Scg for (i = 0; i < ess->playchns; i++) 87565543Scg if (ess->active & (1 << i)) { 87665543Scg suppress_jitter(ess->pch + i); 87765543Scg chn_intr(ess->pch[i].channel); 87865543Scg } 87965543Scg#if 0 /* XXX - RECORDING */ 88065543Scg if (ess->active & (1 << i)) 88165543Scg chn_intr(ess->rch.channel); 88265543Scg#endif 88365543Scg} 88465543Scg 88565543Scgstatic void 88665543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 88765543Scg{ 88865543Scg bus_addr_t *phys = arg; 88965543Scg 89065543Scg *phys = error? 0 : segs->ds_addr; 89165543Scg 89265543Scg if (bootverbose) { 89365543Scg printf("setmap (%lx, %lx), nseg=%d, error=%d\n", 89465543Scg (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 89565543Scg nseg, error); 89665543Scg } 89765543Scg} 89865543Scg 89965543Scgstatic void * 90065543Scgdma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys) 90165543Scg{ 90265543Scg void *buf; 90365543Scg bus_dmamap_t map; 90465543Scg 90565543Scg if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) 90665543Scg return NULL; 90765543Scg if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, setmap, phys, 0) 90865543Scg || !*phys) { 90965543Scg bus_dmamem_free(sc->parent_dmat, buf, map); 91065543Scg return NULL; 91165543Scg } 91265543Scg return buf; 91365543Scg} 91465543Scg 91565543Scgstatic void 91665543Scgdma_free(struct agg_info *sc, void *buf) 91765543Scg{ 91865543Scg bus_dmamem_free(sc->parent_dmat, buf, NULL); 91965543Scg} 92065543Scg 92165543Scgstatic int 92265543Scgagg_probe(device_t dev) 92365543Scg{ 92465543Scg char *s = NULL; 92565543Scg 92665543Scg switch (pci_get_devid(dev)) { 92765543Scg case MAESTRO_1_PCI_ID: 92865543Scg s = "ESS Technology Maestro-1"; 92965543Scg break; 93065543Scg 93165543Scg case MAESTRO_2_PCI_ID: 93265543Scg s = "ESS Technology Maestro-2"; 93365543Scg break; 93465543Scg 93565543Scg case MAESTRO_2E_PCI_ID: 93665543Scg s = "ESS Technology Maestro-2E"; 93765543Scg break; 93865543Scg } 93965543Scg 94065543Scg if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { 94165543Scg device_set_desc(dev, s); 94265543Scg return 0; 94365543Scg } 94465543Scg return ENXIO; 94565543Scg} 94665543Scg 94765543Scgstatic int 94865543Scgagg_attach(device_t dev) 94965543Scg{ 95065543Scg struct agg_info *ess = NULL; 95165543Scg u_int32_t data; 95265543Scg int mapped = 0; 95365543Scg int regid = PCIR_MAPS; 95465543Scg struct resource *reg = NULL; 95565543Scg struct ac97_info *codec = NULL; 95665543Scg int irqid = 0; 95765543Scg struct resource *irq = NULL; 95865543Scg void *ih = NULL; 95965543Scg char status[SND_STATUSLEN]; 96065543Scg 96178564Sgreid if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 96265543Scg device_printf(dev, "cannot allocate softc\n"); 96365543Scg return ENXIO; 96465543Scg } 96565543Scg ess->dev = dev; 96665543Scg 96784658Scg ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); 96884658Scg 96965543Scg if (bus_dma_tag_create(/*parent*/NULL, 97065543Scg /*alignment*/1 << WAVCACHE_BASEADDR_SHIFT, 97165543Scg /*boundary*/WPWA_MAXADDR + 1, 97265543Scg /*lowaddr*/MAESTRO_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, 97365543Scg /*filter*/NULL, /*filterarg*/NULL, 97484658Scg /*maxsize*/ess->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, 97565543Scg /*flags*/0, &ess->parent_dmat) != 0) { 97665543Scg device_printf(dev, "unable to create dma tag\n"); 97765543Scg goto bad; 97865543Scg } 97965543Scg 98084658Scg ess->stat = dma_malloc(ess, ess->bufsz, &ess->baseaddr); 98165543Scg if (ess->stat == NULL) { 98265543Scg device_printf(dev, "cannot allocate status buffer\n"); 98365543Scg goto bad; 98465543Scg } 98565543Scg if (bootverbose) 98665543Scg device_printf(dev, "Maestro DMA base: %#x\n", ess->baseaddr); 98765543Scg 98865543Scg agg_power(ess, PPMI_D0); 98965543Scg DELAY(100000); 99065543Scg 99165543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 99265543Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); 99365543Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 99465543Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 99565543Scg 99665543Scg if (data & PCIM_CMD_PORTEN) { 99765543Scg reg = bus_alloc_resource(dev, SYS_RES_IOPORT, ®id, 99865543Scg 0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE); 99965543Scg if (reg != NULL) { 100065543Scg ess->reg = reg; 100165543Scg ess->regid = regid; 100265543Scg ess->st = rman_get_bustag(reg); 100365543Scg ess->sh = rman_get_bushandle(reg); 100465543Scg mapped++; 100565543Scg } 100665543Scg } 100765543Scg if (mapped == 0) { 100865543Scg device_printf(dev, "unable to map register space\n"); 100965543Scg goto bad; 101065543Scg } 101165543Scg 101265543Scg agg_init(ess); 101370134Scg if (agg_rdcodec(NULL, ess, 0) == 0x80) { 101465543Scg device_printf(dev, "PT101 codec detected!\n"); 101565543Scg goto bad; 101665543Scg } 101770134Scg codec = AC97_CREATE(dev, ess, agg_ac97); 101865543Scg if (codec == NULL) 101965543Scg goto bad; 102070134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) 102165543Scg goto bad; 102265543Scg ess->codec = codec; 102365543Scg 102465543Scg irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 102565543Scg 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE); 102674763Scg if (irq == NULL || snd_setup_intr(dev, irq, 0, agg_intr, ess, &ih)) { 102765543Scg device_printf(dev, "unable to map interrupt\n"); 102865543Scg goto bad; 102965543Scg } 103065543Scg ess->irq = irq; 103165543Scg ess->irqid = irqid; 103265543Scg ess->ih = ih; 103365543Scg 103465543Scg snprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld", 103565543Scg rman_get_start(reg), rman_get_start(irq)); 103665543Scg 103765543Scg if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1)) 103865543Scg goto bad; 103965543Scg 104070945Sjhb mixer_hwvol_init(dev); 104165543Scg for (data = 0; data < AGG_MAXPLAYCH; data++) 104270134Scg pcm_addchan(dev, PCMDIR_PLAY, &aggch_class, ess); 104365543Scg#if 0 /* XXX - RECORDING */ 104470134Scg pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); 104565543Scg#endif 104665543Scg pcm_setstatus(dev, status); 104765543Scg 104865543Scg return 0; 104965543Scg 105065543Scg bad: 105165644Scg if (codec != NULL) 105265644Scg ac97_destroy(codec); 105365543Scg if (ih != NULL) 105465543Scg bus_teardown_intr(dev, irq, ih); 105565543Scg if (irq != NULL) 105665543Scg bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 105765543Scg if (reg != NULL) 105865543Scg bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); 105965543Scg if (ess != NULL) { 106065543Scg agg_power(ess, PPMI_D3); 106165543Scg if (ess->stat != NULL) 106265543Scg dma_free(ess, ess->stat); 106365543Scg if (ess->parent_dmat != NULL) 106465543Scg bus_dma_tag_destroy(ess->parent_dmat); 106565543Scg free(ess, M_DEVBUF); 106665543Scg } 106765543Scg 106865543Scg return ENXIO; 106965543Scg} 107065543Scg 107165543Scgstatic int 107265543Scgagg_detach(device_t dev) 107365543Scg{ 107465543Scg struct agg_info *ess = pcm_getdevinfo(dev); 107565543Scg int r; 107665543Scg 107765543Scg r = pcm_unregister(dev); 107865543Scg if (r) 107965543Scg return r; 108065543Scg 108165543Scg ess = pcm_getdevinfo(dev); 108265543Scg dma_free(ess, ess->stat); 108365543Scg 108465543Scg /* Power down everything except clock and vref. */ 108570134Scg agg_wrcodec(NULL, ess, AC97_REG_POWER, 0xd700); 108665543Scg DELAY(20); 108765543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 108865543Scg agg_power(ess, PPMI_D3); 108965543Scg 109065543Scg bus_teardown_intr(dev, ess->irq, ess->ih); 109165543Scg bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); 109265543Scg bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); 109365543Scg bus_dma_tag_destroy(ess->parent_dmat); 109465543Scg free(ess, M_DEVBUF); 109565543Scg return 0; 109665543Scg} 109765543Scg 109865543Scgstatic int 109965543Scgagg_suspend(device_t dev) 110065543Scg{ 110165543Scg struct agg_info *ess = pcm_getdevinfo(dev); 110265543Scg int i, x; 110365543Scg 110465543Scg x = spltty(); 110565543Scg wp_stoptimer(ess); 110665543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 110765543Scg 110865543Scg for (i = 0; i < ess->playchns; i++) 110965543Scg aggch_stop_dac(ess->pch + i); 111065543Scg 111165543Scg#if 0 /* XXX - RECORDING */ 111265543Scg aggch_stop_adc(&ess->rch); 111365543Scg#endif 111465543Scg splx(x); 111565543Scg /* Power down everything except clock. */ 111670134Scg agg_wrcodec(NULL, ess, AC97_REG_POWER, 0xdf00); 111765543Scg DELAY(20); 111865543Scg bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 111965543Scg DELAY(1); 112065543Scg agg_power(ess, PPMI_D3); 112165543Scg 112265543Scg return 0; 112365543Scg} 112465543Scg 112565543Scgstatic int 112665543Scgagg_resume(device_t dev) 112765543Scg{ 112865543Scg int i, x; 112965543Scg struct agg_info *ess = pcm_getdevinfo(dev); 113065543Scg 113165543Scg agg_power(ess, PPMI_D0); 113265543Scg DELAY(100000); 113365543Scg agg_init(ess); 113465543Scg if (mixer_reinit(dev)) { 113565543Scg device_printf(dev, "unable to reinitialize the mixer\n"); 113665543Scg return ENXIO; 113765543Scg } 113865543Scg 113965543Scg x = spltty(); 114065543Scg for (i = 0; i < ess->playchns; i++) 114165543Scg if (ess->active & (1 << i)) 114265543Scg aggch_start_dac(ess->pch + i); 114365543Scg#if 0 /* XXX - RECORDING */ 114465543Scg if (ess->active & (1 << i)) 114565543Scg aggch_start_adc(&ess->rch); 114665543Scg#endif 114765543Scg if (ess->active) { 114865543Scg set_timer(ess); 114965543Scg wp_starttimer(ess); 115065543Scg } 115165543Scg splx(x); 115265543Scg return 0; 115365543Scg} 115465543Scg 115565543Scgstatic int 115665543Scgagg_shutdown(device_t dev) 115765543Scg{ 115865543Scg struct agg_info *ess = pcm_getdevinfo(dev); 115965543Scg int i; 116065543Scg 116165543Scg wp_stoptimer(ess); 116265543Scg bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 116365543Scg 116465543Scg for (i = 0; i < ess->playchns; i++) 116565543Scg aggch_stop_dac(ess->pch + i); 116665543Scg 116765543Scg#if 0 /* XXX - RECORDING */ 116865543Scg aggch_stop_adc(&ess->rch); 116965543Scg#endif 117065543Scg return 0; 117165543Scg} 117265543Scg 117365543Scg 117465543Scgstatic device_method_t agg_methods[] = { 117565543Scg DEVMETHOD(device_probe, agg_probe), 117665543Scg DEVMETHOD(device_attach, agg_attach), 117765543Scg DEVMETHOD(device_detach, agg_detach), 117865543Scg DEVMETHOD(device_suspend, agg_suspend), 117965543Scg DEVMETHOD(device_resume, agg_resume), 118065543Scg DEVMETHOD(device_shutdown, agg_shutdown), 118165543Scg 118265543Scg { 0, 0 } 118365543Scg}; 118465543Scg 118565543Scgstatic driver_t agg_driver = { 118665543Scg "pcm", 118765543Scg agg_methods, 118882180Scg PCM_SOFTC_SIZE, 118965543Scg}; 119065543Scg 119165543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); 119265543ScgMODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 119365543ScgMODULE_VERSION(snd_maestro, 1); 1194