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, &regid, 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