maestro.c revision 137573
165543Scg/*-
2137573Sru * Copyright (c) 2000-2004 Taku YAMAMOTO <taku@tackymt.homeip.net>
365543Scg * All rights reserved.
465543Scg *
565543Scg * Redistribution and use in source and binary forms, with or without
665543Scg * modification, are permitted provided that the following conditions
765543Scg * are met:
865543Scg * 1. Redistributions of source code must retain the above copyright
965543Scg *    notice, this list of conditions and the following disclaimer.
1065543Scg * 2. Redistributions in binary form must reproduce the above copyright
1165543Scg *    notice, this list of conditions and the following disclaimer in the
1265543Scg *    documentation and/or other materials provided with the distribution.
1365543Scg *
1465543Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1565543Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1665543Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1765543Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1865543Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1965543Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2065543Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2165543Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2265543Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2365543Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2465543Scg * SUCH DAMAGE.
2565543Scg *
26137500Sjulian *	maestro.c,v 1.23.2.1 2003/10/03 18:21:38 taku Exp
2765543Scg */
2865543Scg
2965543Scg/*
3065543Scg * Credits:
3165543Scg *
3265543Scg * Part of this code (especially in many magic numbers) was heavily inspired
3365543Scg * by the Linux driver originally written by
3465543Scg * Alan Cox <alan.cox@linux.org>, modified heavily by
3565543Scg * Zach Brown <zab@zabbo.net>.
3665543Scg *
3765543Scg * busdma()-ize and buffer size reduction were suggested by
38119853Scg * Cameron Grant <cg@freebsd.org>.
3965543Scg * Also he showed me the way to use busdma() suite.
4065543Scg *
4165543Scg * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500
4265543Scg * were looked at by
4365543Scg * Munehiro Matsuda <haro@tk.kubota.co.jp>,
4465543Scg * who brought patches based on the Linux driver with some simplification.
45137500Sjulian *
46137500Sjulian * Hardware volume controller was implemented by
47137500Sjulian * John Baldwin <jhb@freebsd.org>.
4865543Scg */
4965543Scg
5065543Scg#include <dev/sound/pcm/sound.h>
5165543Scg#include <dev/sound/pcm/ac97.h>
52119287Simp#include <dev/pci/pcireg.h>
53119287Simp#include <dev/pci/pcivar.h>
5465543Scg
5565543Scg#include <dev/sound/pci/maestro_reg.h>
5665543Scg
5782180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/maestro.c 137573 2004-11-11 13:01:13Z ru $");
5882180Scg
59137500Sjulian
6065543Scg#define inline __inline
6165543Scg
6265543Scg/*
6365543Scg * PCI IDs of supported chips:
6465543Scg *
6565543Scg * MAESTRO-1	0x01001285
6665543Scg * MAESTRO-2	0x1968125d
6765543Scg * MAESTRO-2E	0x1978125d
6865543Scg */
6965543Scg
7065543Scg#define MAESTRO_1_PCI_ID	0x01001285
7165543Scg#define MAESTRO_2_PCI_ID	0x1968125d
7265543Scg#define MAESTRO_2E_PCI_ID	0x1978125d
7365543Scg
7465543Scg#define NEC_SUBID1	0x80581033	/* Taken from Linux driver */
7565543Scg#define NEC_SUBID2	0x803c1033	/* NEC VersaProNX VA26D    */
7665543Scg
77137500Sjulian#ifdef AGG_MAXPLAYCH
78137500Sjulian# if AGG_MAXPLAYCH > 4
79137500Sjulian#  undef AGG_MAXPLAYCH
80137500Sjulian#  define AGG_MAXPLAYCH 4
81137500Sjulian# endif
82137500Sjulian#else
8365543Scg# define AGG_MAXPLAYCH	4
8465543Scg#endif
8565543Scg
8684658Scg#define AGG_DEFAULT_BUFSZ	0x4000 /* 0x1000, but gets underflows */
8765543Scg
8865543Scg
89137500Sjulian/* compatibility */
90137500Sjulian#if __FreeBSD_version < 500000
91137500Sjulian# define critical_enter()	disable_intr()
92137500Sjulian# define critical_exit()	enable_intr()
93137500Sjulian#endif
94137500Sjulian
95137500Sjulian#ifndef PCIR_BAR
96137500Sjulian#define PCIR_BAR(x)	(PCIR_MAPS + (x) * 4)
97137500Sjulian#endif
98137500Sjulian
99137500Sjulian
10065543Scg/* -----------------------------
10165543Scg * Data structures.
10265543Scg */
10365543Scgstruct agg_chinfo {
104137500Sjulian	/* parent softc */
10565543Scg	struct agg_info		*parent;
106137500Sjulian
107137500Sjulian	/* FreeBSD newpcm related */
10874763Scg	struct pcm_channel	*channel;
10974763Scg	struct snd_dbuf		*buffer;
110137500Sjulian
111137500Sjulian	/* OS independent */
112137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
113137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
114137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
115137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
11670291Scg	u_int32_t		speed;
117137500Sjulian	unsigned		num	: 3;
118137500Sjulian	unsigned		stereo	: 1;
119137500Sjulian	unsigned		qs16	: 1;	/* quantum size is 16bit */
120137500Sjulian	unsigned		us	: 1;	/* in unsigned format */
12165543Scg};
12265543Scg
123137500Sjulianstruct agg_rchinfo {
124137500Sjulian	/* parent softc */
125137500Sjulian	struct agg_info		*parent;
126137500Sjulian
127137500Sjulian	/* FreeBSD newpcm related */
128137500Sjulian	struct pcm_channel	*channel;
129137500Sjulian	struct snd_dbuf		*buffer;
130137500Sjulian
131137500Sjulian	/* OS independent */
132137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
133137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
134137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
135137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
136137500Sjulian	u_int32_t		speed;
137137500Sjulian	unsigned			: 3;
138137500Sjulian	unsigned		stereo	: 1;
139137500Sjulian	bus_addr_t		srcphys;
140137500Sjulian	int16_t			*src;	/* stereo peer buffer */
141137500Sjulian	int16_t			*sink;	/* channel buffer pointer */
142137500Sjulian	volatile u_int32_t	hwptr;	/* ready point in 16bit sample */
143137500Sjulian};
144137500Sjulian
14565543Scgstruct agg_info {
146137500Sjulian	/* FreeBSD newbus related */
14765543Scg	device_t		dev;
148137500Sjulian
149137500Sjulian	/* I wonder whether bus_space_* are in common in *BSD... */
15065543Scg	struct resource		*reg;
15165543Scg	int			regid;
15265543Scg	bus_space_tag_t		st;
15365543Scg	bus_space_handle_t	sh;
15465543Scg
15565543Scg	struct resource		*irq;
15665543Scg	int			irqid;
15765543Scg	void			*ih;
15865543Scg
159137500Sjulian	bus_dma_tag_t		buf_dmat;
160137500Sjulian	bus_dma_tag_t		stat_dmat;
16165543Scg
162137500Sjulian	/* FreeBSD SMPng related */
163137500Sjulian#ifdef USING_MUTEX
164137500Sjulian	struct mtx		lock;	/* mutual exclusion */
165137500Sjulian#endif
166137500Sjulian	/* FreeBSD newpcm related */
16765543Scg	struct ac97_info	*codec;
16865543Scg
169137500Sjulian	/* OS independent */
170137500Sjulian	u_int8_t		*stat;	/* status buffer pointer */
171137500Sjulian	bus_addr_t		phys;	/* status buffer physical address */
172137500Sjulian	unsigned int		bufsz;	/* channel buffer size in bytes */
173137500Sjulian	u_int			playchns;
174137500Sjulian	volatile u_int		active;
17565543Scg	struct agg_chinfo	pch[AGG_MAXPLAYCH];
176137500Sjulian	struct agg_rchinfo	rch;
177137500Sjulian	volatile u_int8_t	curpwr;	/* current power status: D[0-3] */
17865543Scg};
17965543Scg
180137500Sjulian
181137500Sjulian/* -----------------------------
182137500Sjulian * Sysctls for debug.
183137500Sjulian */
184137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1;
185137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE
186137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D2;
187137500Sjulian#else
188137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D1;
189137500Sjulian#endif
190137500Sjulianstatic unsigned int powerstate_init   = PCI_POWERSTATE_D2;
191137500Sjulian
192137500SjulianSYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, "");
193137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW,
194137500Sjulian	    &powerstate_active, 0, "The Dx power state when active (0-1)");
195137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW,
196137500Sjulian	    &powerstate_idle, 0, "The Dx power state when idle (0-2)");
197137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW,
198137500Sjulian	    &powerstate_init, 0, "The Dx power state prior to the first use (0-2)");
199137500Sjulian
200137500Sjulian
201137500Sjulian/* -----------------------------
202137500Sjulian * Prototypes
203137500Sjulian */
204137500Sjulian
205137500Sjulianstatic inline void	 agg_lock(struct agg_info*);
206137500Sjulianstatic inline void	 agg_unlock(struct agg_info*);
207137500Sjulianstatic inline void	 agg_sleep(struct agg_info*, const char *wmesg, int msec);
208137500Sjulian
209137500Sjulianstatic inline u_int32_t	 agg_rd(struct agg_info*, int, int size);
210137500Sjulianstatic inline void	 agg_wr(struct agg_info*, int, u_int32_t data, int size);
211137500Sjulian
212137500Sjulianstatic inline int	 agg_rdcodec(struct agg_info*, int);
213137500Sjulianstatic inline int	 agg_wrcodec(struct agg_info*, int, u_int32_t);
214137500Sjulian
21565543Scgstatic inline void	 ringbus_setdest(struct agg_info*, int, int);
21665543Scg
21765543Scgstatic inline u_int16_t	 wp_rdreg(struct agg_info*, u_int16_t);
21865543Scgstatic inline void	 wp_wrreg(struct agg_info*, u_int16_t, u_int16_t);
219137500Sjulianstatic inline u_int16_t	 wp_rdapu(struct agg_info*, unsigned, u_int16_t);
220137500Sjulianstatic inline void	 wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t);
22165543Scgstatic inline void	 wp_settimer(struct agg_info*, u_int);
22265543Scgstatic inline void	 wp_starttimer(struct agg_info*);
22365543Scgstatic inline void	 wp_stoptimer(struct agg_info*);
22465543Scg
22565543Scgstatic inline u_int16_t	 wc_rdreg(struct agg_info*, u_int16_t);
22665543Scgstatic inline void	 wc_wrreg(struct agg_info*, u_int16_t, u_int16_t);
22765543Scgstatic inline u_int16_t	 wc_rdchctl(struct agg_info*, int);
22865543Scgstatic inline void	 wc_wrchctl(struct agg_info*, int, u_int16_t);
22965543Scg
230137500Sjulianstatic inline void	 agg_stopclock(struct agg_info*, int part, int st);
23165543Scg
232137500Sjulianstatic inline void	 agg_initcodec(struct agg_info*);
23365543Scgstatic void		 agg_init(struct agg_info*);
234137500Sjulianstatic void		 agg_power(struct agg_info*, int);
23565543Scg
23665543Scgstatic void		 aggch_start_dac(struct agg_chinfo*);
23765543Scgstatic void		 aggch_stop_dac(struct agg_chinfo*);
238137500Sjulianstatic void		 aggch_start_adc(struct agg_rchinfo*);
239137500Sjulianstatic void		 aggch_stop_adc(struct agg_rchinfo*);
240137500Sjulianstatic void		 aggch_feed_adc_stereo(struct agg_rchinfo*);
241137500Sjulianstatic void		 aggch_feed_adc_mono(struct agg_rchinfo*);
24265543Scg
24365543Scgstatic inline void	 suppress_jitter(struct agg_chinfo*);
244137500Sjulianstatic inline void	 suppress_rec_jitter(struct agg_rchinfo*);
24565543Scg
24665543Scgstatic void		 set_timer(struct agg_info*);
24765543Scg
24865543Scgstatic void		 agg_intr(void *);
24965543Scgstatic int		 agg_probe(device_t);
25065543Scgstatic int		 agg_attach(device_t);
25165543Scgstatic int		 agg_detach(device_t);
25265543Scgstatic int		 agg_suspend(device_t);
25365543Scgstatic int		 agg_resume(device_t);
25465543Scgstatic int		 agg_shutdown(device_t);
25565543Scg
256137500Sjulianstatic void	*dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*);
257137500Sjulianstatic void	 dma_free(bus_dma_tag_t, void *);
25865543Scg
259137500Sjulian
26065543Scg/* -----------------------------
26165543Scg * Subsystems.
26265543Scg */
26365543Scg
264137500Sjulian/* locking */
26565543Scg
266137500Sjulianstatic inline void
267137500Sjulianagg_lock(struct agg_info *sc)
268137500Sjulian{
269137500Sjulian#ifdef USING_MUTEX
270137500Sjulian	mtx_lock(&sc->lock);
271137500Sjulian#endif
272137500Sjulian}
273137500Sjulian
274137500Sjulianstatic inline void
275137500Sjulianagg_unlock(struct agg_info *sc)
276137500Sjulian{
277137500Sjulian#ifdef USING_MUTEX
278137500Sjulian	mtx_unlock(&sc->lock);
279137500Sjulian#endif
280137500Sjulian}
281137500Sjulian
282137500Sjulianstatic inline void
283137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec)
284137500Sjulian{
285137500Sjulian	int timo;
286137500Sjulian
287137500Sjulian	timo = msec * hz / 1000;
288137500Sjulian	if (timo == 0)
289137500Sjulian		timo = 1;
290137500Sjulian#ifdef USING_MUTEX
291137500Sjulian	msleep(sc, &sc->lock, PWAIT, wmesg, timo);
292137500Sjulian#else
293137500Sjulian	tsleep(sc, PWAIT, wmesg, timo);
294137500Sjulian#endif
295137500Sjulian}
296137500Sjulian
297137500Sjulian
298137500Sjulian/* I/O port */
299137500Sjulian
300137500Sjulianstatic inline u_int32_t
301137500Sjulianagg_rd(struct agg_info *sc, int regno, int size)
302137500Sjulian{
303137500Sjulian	switch (size) {
304137500Sjulian	case 1:
305137500Sjulian		return bus_space_read_1(sc->st, sc->sh, regno);
306137500Sjulian	case 2:
307137500Sjulian		return bus_space_read_2(sc->st, sc->sh, regno);
308137500Sjulian	case 4:
309137500Sjulian		return bus_space_read_4(sc->st, sc->sh, regno);
310137500Sjulian	default:
311137500Sjulian		return ~(u_int32_t)0;
312137500Sjulian	}
313137500Sjulian}
314137500Sjulian
315137500Sjulian#define AGG_RD(sc, regno, size)           \
316137500Sjulian	bus_space_read_##size(            \
317137500Sjulian	    ((struct agg_info*)(sc))->st, \
318137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno))
319137500Sjulian
320137500Sjulianstatic inline void
321137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size)
322137500Sjulian{
323137500Sjulian	switch (size) {
324137500Sjulian	case 1:
325137500Sjulian		bus_space_write_1(sc->st, sc->sh, regno, data);
326137500Sjulian		break;
327137500Sjulian	case 2:
328137500Sjulian		bus_space_write_2(sc->st, sc->sh, regno, data);
329137500Sjulian		break;
330137500Sjulian	case 4:
331137500Sjulian		bus_space_write_4(sc->st, sc->sh, regno, data);
332137500Sjulian		break;
333137500Sjulian	}
334137500Sjulian}
335137500Sjulian
336137500Sjulian#define AGG_WR(sc, regno, data, size)     \
337137500Sjulian	bus_space_write_##size(           \
338137500Sjulian	    ((struct agg_info*)(sc))->st, \
339137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno), (data))
340137500Sjulian
34170134Scg/* -------------------------------------------------------------------- */
34270134Scg
343137500Sjulian/* Codec/Ringbus */
344137500Sjulian
345137500Sjulianstatic inline int
346137500Sjulianagg_codec_wait4idle(struct agg_info *ess)
34765543Scg{
348137500Sjulian	unsigned t = 26;
34970134Scg
350137500Sjulian	while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) {
351137500Sjulian		if (--t == 0)
352137500Sjulian			return EBUSY;
353137500Sjulian		DELAY(2);	/* 20.8us / 13 */
354137500Sjulian	}
355137500Sjulian	return 0;
35670134Scg}
35770134Scg
358137500Sjulian
359137500Sjulianstatic inline int
360137500Sjulianagg_rdcodec(struct agg_info *ess, int regno)
36170134Scg{
362137500Sjulian	int ret;
36365543Scg
36465543Scg	/* We have to wait for a SAFE time to write addr/data */
365137500Sjulian	if (agg_codec_wait4idle(ess)) {
366137500Sjulian		/* Timed out. No read performed. */
367137500Sjulian		device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n");
368137500Sjulian		return -1;
36965543Scg	}
37065543Scg
371137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1);
372137500Sjulian	/*DELAY(21);	* AC97 cycle = 20.8usec */
37365543Scg
37465543Scg	/* Wait for data retrieve */
375137500Sjulian	if (!agg_codec_wait4idle(ess)) {
376137500Sjulian		ret = AGG_RD(ess, PORT_CODEC_REG, 2);
377137500Sjulian	} else {
378137500Sjulian		/* Timed out. No read performed. */
379137500Sjulian		device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n");
380137500Sjulian		ret = -1;
38165543Scg	}
38265543Scg
383137500Sjulian	return ret;
38465543Scg}
38565543Scg
386137500Sjulianstatic inline int
387137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data)
38865543Scg{
38965543Scg	/* We have to wait for a SAFE time to write addr/data */
390137500Sjulian	if (agg_codec_wait4idle(ess)) {
39165543Scg		/* Timed out. Abort writing. */
39265543Scg		device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n");
39370134Scg		return -1;
39465543Scg	}
39565543Scg
396137500Sjulian	AGG_WR(ess, PORT_CODEC_REG, data, 2);
397137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1);
39870134Scg
399137500Sjulian	/* Wait for write completion */
400137500Sjulian	if (agg_codec_wait4idle(ess)) {
401137500Sjulian		/* Timed out. */
402137500Sjulian		device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n");
403137500Sjulian		return -1;
404137500Sjulian	}
405137500Sjulian
40670134Scg	return 0;
40765543Scg}
40865543Scg
40965543Scgstatic inline void
41065543Scgringbus_setdest(struct agg_info *ess, int src, int dest)
41165543Scg{
41265543Scg	u_int32_t	data;
41365543Scg
414137500Sjulian	data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4);
41565543Scg	data &= ~(0xfU << src);
41665543Scg	data |= (0xfU & dest) << src;
417137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4);
41865543Scg}
41965543Scg
420137500Sjulian/* -------------------------------------------------------------------- */
421137500Sjulian
42265543Scg/* Wave Processor */
42365543Scg
42465543Scgstatic inline u_int16_t
42565543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg)
42665543Scg{
427137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
428137500Sjulian	return AGG_RD(ess, PORT_DSP_DATA, 2);
42965543Scg}
43065543Scg
43165543Scgstatic inline void
43265543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
43365543Scg{
434137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
435137500Sjulian	AGG_WR(ess, PORT_DSP_DATA, data, 2);
43665543Scg}
43765543Scg
438137500Sjulianstatic inline int
439137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data)
44065543Scg{
441137500Sjulian	unsigned t = 0;
44265543Scg
443137500Sjulian	while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) {
444137500Sjulian		if (++t == 1000) {
445137500Sjulian			return EAGAIN;
446137500Sjulian		}
447137500Sjulian		AGG_WR(ess, PORT_DSP_DATA, data, 2);
44865543Scg	}
449137500Sjulian
450137500Sjulian	return 0;
45165543Scg}
45265543Scg
45365543Scgstatic inline u_int16_t
454137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg)
45565543Scg{
456137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
457137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) != 0)
458137500Sjulian		device_printf(ess->dev, "wp_rdapu() indexing timed out.\n");
459137500Sjulian	return wp_rdreg(ess, WPREG_DATA_PORT);
46065543Scg}
46165543Scg
46265543Scgstatic inline void
463137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data)
46465543Scg{
465137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
466137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) == 0) {
467137500Sjulian		wp_wrreg(ess, WPREG_DATA_PORT, data);
468137500Sjulian		if (wp_wait_data(ess, data) != 0)
469137500Sjulian			device_printf(ess->dev, "wp_wrapu() write timed out.\n");
470137500Sjulian	} else {
471137500Sjulian		device_printf(ess->dev, "wp_wrapu() indexing timed out.\n");
47265543Scg	}
47365543Scg}
47465543Scg
475137573Srustatic void
476137500Sjulianapu_setparam(struct agg_info *ess, int apuch,
477137500Sjulian    u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv)
47865543Scg{
479137500Sjulian	wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK);
480137500Sjulian	wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa);
481137500Sjulian	wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size);
482137500Sjulian	wp_wrapu(ess, apuch, APUREG_LOOPLEN, size);
483137500Sjulian	wp_wrapu(ess, apuch, APUREG_ROUTING, 0);
484137500Sjulian	wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000);
485137500Sjulian	wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00
486137500Sjulian	    | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT))
487137500Sjulian	    | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT)));
488137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE,
489137500Sjulian	    APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT));
490137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8);
491137500Sjulian}
49265543Scg
493137500Sjulianstatic inline void
494137500Sjulianwp_settimer(struct agg_info *ess, u_int divide)
495137500Sjulian{
496137500Sjulian	u_int prescale = 0;
49765543Scg
498137500Sjulian	RANGE(divide, 2, 32 << 7);
499137500Sjulian
500137500Sjulian	for (; divide > 32; divide >>= 1) {
50165543Scg		prescale++;
502137500Sjulian		divide++;
503137500Sjulian	}
50465543Scg
50565543Scg	for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1)
50665543Scg		prescale++;
50765543Scg
50865543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 0);
509137500Sjulian	wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 |
51065543Scg	    (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1));
51165543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 1);
51265543Scg}
51365543Scg
51465543Scgstatic inline void
51565543Scgwp_starttimer(struct agg_info *ess)
51665543Scg{
517137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
518137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED
519137500Sjulian	       | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
52065543Scg	wp_wrreg(ess, WPREG_TIMER_START, 1);
52165543Scg}
52265543Scg
52365543Scgstatic inline void
52465543Scgwp_stoptimer(struct agg_info *ess)
52565543Scg{
526137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED
527137500Sjulian	       & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
528137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
52965543Scg	wp_wrreg(ess, WPREG_TIMER_START, 0);
53065543Scg}
53165543Scg
532137500Sjulian/* -------------------------------------------------------------------- */
533137500Sjulian
53465543Scg/* WaveCache */
53565543Scg
53665543Scgstatic inline u_int16_t
53765543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg)
53865543Scg{
539137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
540137500Sjulian	return AGG_RD(ess, PORT_WAVCACHE_DATA, 2);
54165543Scg}
54265543Scg
54365543Scgstatic inline void
54465543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
54565543Scg{
546137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
547137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2);
54865543Scg}
54965543Scg
55065543Scgstatic inline u_int16_t
55165543Scgwc_rdchctl(struct agg_info *ess, int ch)
55265543Scg{
55365543Scg	return wc_rdreg(ess, ch << 3);
55465543Scg}
55565543Scg
55665543Scgstatic inline void
55765543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data)
55865543Scg{
55965543Scg	wc_wrreg(ess, ch << 3, data);
56065543Scg}
56165543Scg
562137500Sjulian/* -------------------------------------------------------------------- */
563137500Sjulian
56465543Scg/* Power management */
56565543Scgstatic inline void
566137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st)
56765543Scg{
568137500Sjulian	u_int32_t data;
56965543Scg
570137500Sjulian	data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4);
571137500Sjulian	if (part < 16) {
572137500Sjulian		if (st == PCI_POWERSTATE_D1)
573137500Sjulian			data &= ~(1 << part);
574137500Sjulian		else
575137500Sjulian			data |= (1 << part);
576137500Sjulian		if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2)
577137500Sjulian			data |= (0x10000 << part);
578137500Sjulian		else
579137500Sjulian			data &= ~(0x10000 << part);
580137500Sjulian		pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4);
581137500Sjulian	}
58265543Scg}
58365543Scg
58465543Scg
58565543Scg/* -----------------------------
58665543Scg * Controller.
58765543Scg */
58865543Scg
58965543Scgstatic inline void
59065543Scgagg_initcodec(struct agg_info* ess)
59165543Scg{
59265543Scg	u_int16_t data;
59365543Scg
594137500Sjulian	if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) {
595137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
59665543Scg		DELAY(104);	/* 20.8us * (4 + 1) */
59765543Scg	}
59865543Scg	/* XXX - 2nd codec should be looked at. */
599137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4);
60065543Scg	DELAY(2);
601137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
602137500Sjulian	DELAY(50);
60365543Scg
604137500Sjulian	if (agg_rdcodec(ess, 0) < 0) {
605137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
60665543Scg		DELAY(21);
60765543Scg
60865543Scg		/* Try cold reset. */
60965543Scg		device_printf(ess->dev, "will perform cold reset.\n");
610137500Sjulian		data = AGG_RD(ess, PORT_GPIO_DIR, 2);
61165543Scg		if (pci_read_config(ess->dev, 0x58, 2) & 1)
61265543Scg			data |= 0x10;
613137500Sjulian		data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2);
614137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2);
615137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2);
616137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2);
61765543Scg		DELAY(2);
618137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2);
61965543Scg		DELAY(1);
620137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2);
621137500Sjulian		agg_sleep(ess, "agginicd", 500);
622137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data, 2);
62365543Scg		DELAY(84);	/* 20.8us * 4 */
624137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
625137500Sjulian		DELAY(50);
62665543Scg	}
62765543Scg}
62865543Scg
62965543Scgstatic void
63065543Scgagg_init(struct agg_info* ess)
63165543Scg{
63265543Scg	u_int32_t data;
63365543Scg
63465543Scg	/* Setup PCI config registers. */
63565543Scg
63665543Scg	/* Disable all legacy emulations. */
63765543Scg	data = pci_read_config(ess->dev, CONF_LEGACY, 2);
63865543Scg	data |= LEGACY_DISABLED;
63965543Scg	pci_write_config(ess->dev, CONF_LEGACY, data, 2);
64065543Scg
64165543Scg	/* Disconnect from CHI. (Makes Dell inspiron 7500 work?)
64265543Scg	 * Enable posted write.
64365543Scg	 * Prefer PCI timing rather than that of ISA.
64465543Scg	 * Don't swap L/R. */
64565543Scg	data = pci_read_config(ess->dev, CONF_MAESTRO, 4);
646137500Sjulian	data |= MAESTRO_PMC;
64765543Scg	data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING;
64865543Scg	data &= ~MAESTRO_SWAP_LR;
64965543Scg	pci_write_config(ess->dev, CONF_MAESTRO, data, 4);
65065543Scg
651137500Sjulian	/* Turn off unused parts if necessary. */
652137500Sjulian	/* consult CONF_MAESTRO. */
653137500Sjulian	if (data & MAESTRO_SPDIF)
654137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D2);
655137500Sjulian	else
656137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D1);
657137500Sjulian	if (data & MAESTRO_HWVOL)
658137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D3);
659137500Sjulian	else
660137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D1);
661137500Sjulian
662137500Sjulian	/* parts that never be used */
663137500Sjulian	agg_stopclock(ess, ACPI_PART_978,	PCI_POWERSTATE_D1);
664137500Sjulian	agg_stopclock(ess, ACPI_PART_DAA,	PCI_POWERSTATE_D1);
665137500Sjulian	agg_stopclock(ess, ACPI_PART_GPIO,	PCI_POWERSTATE_D1);
666137500Sjulian	agg_stopclock(ess, ACPI_PART_SB,	PCI_POWERSTATE_D1);
667137500Sjulian	agg_stopclock(ess, ACPI_PART_FM,	PCI_POWERSTATE_D1);
668137500Sjulian	agg_stopclock(ess, ACPI_PART_MIDI,	PCI_POWERSTATE_D1);
669137500Sjulian	agg_stopclock(ess, ACPI_PART_GAME_PORT,	PCI_POWERSTATE_D1);
670137500Sjulian
671137500Sjulian	/* parts that will be used only when play/recording */
672137500Sjulian	agg_stopclock(ess, ACPI_PART_WP,	PCI_POWERSTATE_D2);
673137500Sjulian
674137500Sjulian	/* parts that should always be turned on */
675137500Sjulian	agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3);
676137500Sjulian	agg_stopclock(ess, ACPI_PART_GLUE,	PCI_POWERSTATE_D3);
677137500Sjulian	agg_stopclock(ess, ACPI_PART_PCI_IF,	PCI_POWERSTATE_D3);
678137500Sjulian	agg_stopclock(ess, ACPI_PART_RINGBUS,	PCI_POWERSTATE_D3);
679137500Sjulian
68065543Scg	/* Reset direct sound. */
681137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2);
682137500Sjulian	DELAY(100);
683137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
684137500Sjulian	DELAY(100);
685137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2);
686137500Sjulian	DELAY(100);
687137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
688137500Sjulian	DELAY(100);
68965543Scg
690137500Sjulian	/* Enable hardware volume control interruption. */
691137500Sjulian	if (data & MAESTRO_HWVOL)	/* XXX - why not use device flags? */
692137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2);
69365543Scg
69465543Scg	/* Setup Wave Processor. */
69565543Scg
69665543Scg	/* Enable WaveCache, set DMA base address. */
69765543Scg	wp_wrreg(ess, WPREG_WAVE_ROMRAM,
69865543Scg	    WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED);
699137500Sjulian	wp_wrreg(ess, WPREG_CRAM_DATA, 0);
70065543Scg
701137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_CTRL,
702137500Sjulian	       WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2);
703137500Sjulian
70465543Scg	for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++)
705137500Sjulian		wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT);
70665543Scg
70765543Scg	/* Setup Codec/Ringbus. */
70865543Scg	agg_initcodec(ess);
709137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL,
710137500Sjulian	       RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4);
71165543Scg
712137500Sjulian	wp_wrreg(ess, 0x08, 0xB004);
713137500Sjulian	wp_wrreg(ess, 0x09, 0x001B);
714137500Sjulian	wp_wrreg(ess, 0x0A, 0x8000);
715137500Sjulian	wp_wrreg(ess, 0x0B, 0x3F37);
716137500Sjulian	wp_wrreg(ess, WPREG_BASE, 0x8598);	/* Parallel I/O */
717137500Sjulian	wp_wrreg(ess, WPREG_BASE + 1, 0x7632);
71865543Scg	ringbus_setdest(ess, RINGBUS_SRC_ADC,
71965543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN);
72065543Scg	ringbus_setdest(ess, RINGBUS_SRC_DSOUND,
72165543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC);
72265543Scg
723137500Sjulian	/* Enable S/PDIF if necessary. */
724137500Sjulian	if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF)
725137500Sjulian		/* XXX - why not use device flags? */
726137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF |
727137500Sjulian		       AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1);
728137500Sjulian
72965543Scg	/* Setup ASSP. Needed for Dell Inspiron 7500? */
730137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1);
731137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1);
732137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1);
73365543Scg
73465543Scg	/*
73565543Scg	 * Setup GPIO.
73665543Scg	 * There seems to be speciality with NEC systems.
73765543Scg	 */
73865543Scg	switch (pci_get_subvendor(ess->dev)
73965543Scg	    | (pci_get_subdevice(ess->dev) << 16)) {
74065543Scg	case NEC_SUBID1:
74165543Scg	case NEC_SUBID2:
74265543Scg		/* Matthew Braithwaite <matt@braithwaite.net> reported that
74365543Scg		 * NEC Versa LX doesn't need GPIO operation. */
744137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2);
745137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR,
746137500Sjulian		       AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2);
747137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2);
74865543Scg		break;
74965543Scg	}
75065543Scg}
75165543Scg
752137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */
753137500Sjulianstatic void
754137500Sjulianagg_power(struct agg_info *ess, int status)
755137500Sjulian{
756137500Sjulian	u_int8_t lastpwr;
757137500Sjulian
758137500Sjulian	lastpwr = ess->curpwr;
759137500Sjulian	if (lastpwr == status)
760137500Sjulian		return;
761137500Sjulian
762137500Sjulian	switch (status) {
763137500Sjulian	case PCI_POWERSTATE_D0:
764137500Sjulian	case PCI_POWERSTATE_D1:
765137500Sjulian		switch (lastpwr) {
766137500Sjulian		case PCI_POWERSTATE_D2:
767137500Sjulian			pci_set_powerstate(ess->dev, status);
768137500Sjulian			/* Turn on PCM-related parts. */
769137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0);
770137500Sjulian			DELAY(100);
771137500Sjulian#if 0
772137500Sjulian			if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3)
773137500Sjulian				device_printf(ess->dev, "warning: codec not ready.\n");
774137500Sjulian#endif
775137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
776137500Sjulian			       (AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
777137500Sjulian				& ~RINGBUS_CTRL_ACLINK_ENABLED)
778137500Sjulian			       | RINGBUS_CTRL_RINGBUS_ENABLED, 4);
779137500Sjulian			DELAY(50);
780137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
781137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
782137500Sjulian			       | RINGBUS_CTRL_ACLINK_ENABLED, 4);
783137500Sjulian			break;
784137500Sjulian		case PCI_POWERSTATE_D3:
785137500Sjulian			/* Initialize. */
786137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
787137500Sjulian			DELAY(100);
788137500Sjulian			agg_init(ess);
789137500Sjulian			/* FALLTHROUGH */
790137500Sjulian		case PCI_POWERSTATE_D0:
791137500Sjulian		case PCI_POWERSTATE_D1:
792137500Sjulian			pci_set_powerstate(ess->dev, status);
793137500Sjulian			break;
794137500Sjulian		}
795137500Sjulian		break;
796137500Sjulian	case PCI_POWERSTATE_D2:
797137500Sjulian		switch (lastpwr) {
798137500Sjulian		case PCI_POWERSTATE_D3:
799137500Sjulian			/* Initialize. */
800137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
801137500Sjulian			DELAY(100);
802137500Sjulian			agg_init(ess);
803137500Sjulian			/* FALLTHROUGH */
804137500Sjulian		case PCI_POWERSTATE_D0:
805137500Sjulian		case PCI_POWERSTATE_D1:
806137500Sjulian			/* Turn off PCM-related parts. */
807137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
808137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
809137500Sjulian			       & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4);
810137500Sjulian			DELAY(100);
811137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0x300);
812137500Sjulian			DELAY(100);
813137500Sjulian			break;
814137500Sjulian		}
815137500Sjulian		pci_set_powerstate(ess->dev, status);
816137500Sjulian		break;
817137500Sjulian	case PCI_POWERSTATE_D3:
818137500Sjulian		/* Entirely power down. */
819137500Sjulian		agg_wrcodec(ess, AC97_REG_POWER, 0xdf00);
820137500Sjulian		DELAY(100);
821137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
822137500Sjulian		/*DELAY(1);*/
823137500Sjulian		if (lastpwr != PCI_POWERSTATE_D2)
824137500Sjulian			wp_stoptimer(ess);
825137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
826137500Sjulian		AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1);
827137500Sjulian		pci_set_powerstate(ess->dev, status);
828137500Sjulian		break;
829137500Sjulian	default:
830137500Sjulian		/* Invalid power state; let it ignored. */
831137500Sjulian		status = lastpwr;
832137500Sjulian		break;
833137500Sjulian	}
834137500Sjulian
835137500Sjulian	ess->curpwr = status;
836137500Sjulian}
837137500Sjulian
838137500Sjulian/* -------------------------------------------------------------------- */
839137500Sjulian
84065543Scg/* Channel controller. */
84165543Scg
84265543Scgstatic void
84365543Scgaggch_start_dac(struct agg_chinfo *ch)
84465543Scg{
845137500Sjulian	bus_addr_t	wpwa;
846137500Sjulian	u_int32_t	speed;
847137500Sjulian	u_int16_t	size, apuch, wtbar, wcreg, aputype;
848137500Sjulian	u_int		dv;
849137500Sjulian	int		pan;
85065543Scg
851137500Sjulian	speed = ch->speed;
852137500Sjulian	wpwa = (ch->phys - ch->base) >> 1;
853137500Sjulian	wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2));
854137500Sjulian	wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
855137500Sjulian	size  = ch->buflen;
856137500Sjulian	apuch = (ch->num << 1) | 32;
857137500Sjulian	pan = PAN_RIGHT - PAN_FRONT;
858137500Sjulian
859137500Sjulian	if (ch->stereo) {
860137500Sjulian		wcreg |= WAVCACHE_CHCTL_STEREO;
861137500Sjulian		if (ch->qs16) {
862137500Sjulian			aputype = APUTYPE_16BITSTEREO;
863137500Sjulian			wpwa >>= 1;
864137500Sjulian			size >>= 1;
865137500Sjulian			pan = -pan;
866137500Sjulian		} else
867137500Sjulian			aputype = APUTYPE_8BITSTEREO;
868137500Sjulian	} else {
869137500Sjulian		pan = 0;
870137500Sjulian		if (ch->qs16)
871137500Sjulian			aputype = APUTYPE_16BITLINEAR;
872137500Sjulian		else {
873137500Sjulian			aputype = APUTYPE_8BITLINEAR;
874137500Sjulian			speed >>= 1;
875137500Sjulian		}
87665543Scg	}
877137500Sjulian	if (ch->us)
878137500Sjulian		wcreg |= WAVCACHE_CHCTL_U8;
87965543Scg
880137500Sjulian	if (wtbar > 8)
881137500Sjulian		wtbar = (wtbar >> 1) + 4;
882137500Sjulian
88365543Scg	dv = (((speed % 48000) << 16) + 24000) / 48000
88465543Scg	    + ((speed / 48000) << 16);
88565543Scg
886137500Sjulian	agg_lock(ch->parent);
887137500Sjulian	agg_power(ch->parent, powerstate_active);
88865543Scg
889137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar,
890137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
891137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1,
892137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
893137500Sjulian	if (wtbar < 8) {
894137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2,
895137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
896137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3,
897137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
898137500Sjulian	}
899137500Sjulian	wc_wrchctl(ch->parent, apuch, wcreg);
900137500Sjulian	wc_wrchctl(ch->parent, apuch + 1, wcreg);
90165543Scg
902137500Sjulian	apu_setparam(ch->parent, apuch, wpwa, size, pan, dv);
903137500Sjulian	if (ch->stereo) {
904137500Sjulian		if (ch->qs16)
905137500Sjulian			wpwa |= (WPWA_STEREO >> 1);
906137500Sjulian		apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv);
90765543Scg
908137500Sjulian		critical_enter();
909137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
910137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
91165543Scg		wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE,
912137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
913137500Sjulian		critical_exit();
914137500Sjulian	} else {
915137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
916137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
917137500Sjulian	}
918137500Sjulian
919137500Sjulian	/* to mark that this channel is ready for intr. */
920137500Sjulian	ch->parent->active |= (1 << ch->num);
921137500Sjulian
922137500Sjulian	set_timer(ch->parent);
923137500Sjulian	wp_starttimer(ch->parent);
924137500Sjulian	agg_unlock(ch->parent);
92565543Scg}
92665543Scg
92765543Scgstatic void
92865543Scgaggch_stop_dac(struct agg_chinfo *ch)
92965543Scg{
930137500Sjulian	agg_lock(ch->parent);
931137500Sjulian
932137500Sjulian	/* to mark that this channel no longer needs further intrs. */
933137500Sjulian	ch->parent->active &= ~(1 << ch->num);
934137500Sjulian
935137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE,
93665543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
937137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE,
93865543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
939137500Sjulian
940137500Sjulian	if (ch->parent->active) {
941137500Sjulian		set_timer(ch->parent);
942137500Sjulian		wp_starttimer(ch->parent);
943137500Sjulian	} else {
944137500Sjulian		wp_stoptimer(ch->parent);
945137500Sjulian		agg_power(ch->parent, powerstate_idle);
946137500Sjulian	}
947137500Sjulian	agg_unlock(ch->parent);
94865543Scg}
94965543Scg
950137500Sjulianstatic void
951137500Sjulianaggch_start_adc(struct agg_rchinfo *ch)
952137500Sjulian{
953137500Sjulian	bus_addr_t	wpwa, wpwa2;
954137500Sjulian	u_int16_t	wcreg, wcreg2;
955137500Sjulian	u_int	dv;
956137500Sjulian	int	pan;
957137500Sjulian
958137500Sjulian	/* speed > 48000 not cared */
959137500Sjulian	dv = ((ch->speed << 16) + 24000) / 48000;
960137500Sjulian
961137500Sjulian	/* RATECONV doesn't seem to like dv == 0x10000. */
962137500Sjulian	if (dv == 0x10000)
963137500Sjulian		dv--;
964137500Sjulian
965137500Sjulian	if (ch->stereo) {
966137500Sjulian		wpwa = (ch->srcphys - ch->base) >> 1;
967137500Sjulian		wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1;
968137500Sjulian		wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
969137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
970137500Sjulian		pan = PAN_LEFT - PAN_FRONT;
971137500Sjulian	} else {
972137500Sjulian		wpwa = (ch->phys - ch->base) >> 1;
973137500Sjulian		wpwa2 = (ch->srcphys - ch->base) >> 1;
974137500Sjulian		wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
975137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
976137500Sjulian		pan = 0;
977137500Sjulian	}
978137500Sjulian
979137500Sjulian	agg_lock(ch->parent);
980137500Sjulian
981137500Sjulian	ch->hwptr = 0;
982137500Sjulian	agg_power(ch->parent, powerstate_active);
983137500Sjulian
984137500Sjulian	/* Invalidate WaveCache. */
985137500Sjulian	wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO);
986137500Sjulian	wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO);
987137500Sjulian	wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO);
988137500Sjulian	wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO);
989137500Sjulian
990137500Sjulian	/* Load APU registers. */
991137500Sjulian	/* APU #0 : Sample rate converter for left/center. */
992137500Sjulian	apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa,
993137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
994137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0);
995137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT);
996137500Sjulian
997137500Sjulian	/* APU #1 : Sample rate converter for right. */
998137500Sjulian	apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2,
999137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
1000137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0);
1001137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT);
1002137500Sjulian
1003137500Sjulian	/* APU #2 : Input mixer for left. */
1004137500Sjulian	apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0,
1005137500Sjulian		     ch->parent->bufsz >> 2, pan, 0x10000);
1006137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0);
1007137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0);
1008137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT);
1009137500Sjulian
1010137500Sjulian	/* APU #3 : Input mixer for right. */
1011137500Sjulian	apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2),
1012137500Sjulian		     ch->parent->bufsz >> 2, -pan, 0x10000);
1013137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0);
1014137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0);
1015137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT);
1016137500Sjulian
1017137500Sjulian	/* to mark this channel ready for intr. */
1018137500Sjulian	ch->parent->active |= (1 << ch->parent->playchns);
1019137500Sjulian
1020137500Sjulian	/* start adc */
1021137500Sjulian	critical_enter();
1022137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_APUTYPE,
1023137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1024137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_APUTYPE,
1025137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1026137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_APUTYPE,
1027137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1028137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_APUTYPE,
1029137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1030137500Sjulian	critical_exit();
1031137500Sjulian
1032137500Sjulian	set_timer(ch->parent);
1033137500Sjulian	wp_starttimer(ch->parent);
1034137500Sjulian	agg_unlock(ch->parent);
1035137500Sjulian}
1036137500Sjulian
1037137500Sjulianstatic void
1038137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch)
1039137500Sjulian{
1040137500Sjulian	int apuch;
1041137500Sjulian
1042137500Sjulian	agg_lock(ch->parent);
1043137500Sjulian
1044137500Sjulian	/* to mark that this channel no longer needs further intrs. */
1045137500Sjulian	ch->parent->active &= ~(1 << ch->parent->playchns);
1046137500Sjulian
1047137500Sjulian	for (apuch = 0; apuch < 4; apuch++)
1048137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
1049137500Sjulian		    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
1050137500Sjulian
1051137500Sjulian	if (ch->parent->active) {
1052137500Sjulian		set_timer(ch->parent);
1053137500Sjulian		wp_starttimer(ch->parent);
1054137500Sjulian	} else {
1055137500Sjulian		wp_stoptimer(ch->parent);
1056137500Sjulian		agg_power(ch->parent, powerstate_idle);
1057137500Sjulian	}
1058137500Sjulian	agg_unlock(ch->parent);
1059137500Sjulian}
1060137500Sjulian
106165543Scg/*
1062137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving.
1063137500Sjulian * This function expects n not overwrapping the buffer boundary.
1064137500Sjulian * Note that n is measured in sample unit.
1065137500Sjulian *
1066137500Sjulian * XXX - this function works in 16bit stereo format only.
1067137500Sjulian */
1068137500Sjulianstatic inline void
1069137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n)
1070137500Sjulian{
1071137500Sjulian	int16_t *end;
1072137500Sjulian
1073137500Sjulian	for (end = l + n; l < end; ) {
1074137500Sjulian		*p++ = *l++;
1075137500Sjulian		*p++ = *r++;
1076137500Sjulian	}
1077137500Sjulian}
1078137500Sjulian
1079137500Sjulianstatic void
1080137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch)
1081137500Sjulian{
1082137500Sjulian	unsigned cur, last;
1083137500Sjulian	int16_t *src2;
1084137500Sjulian
1085137500Sjulian	agg_lock(ch->parent);
1086137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1087137500Sjulian	agg_unlock(ch->parent);
1088137500Sjulian	cur -= 0xffff & ((ch->srcphys - ch->base) >> 1);
1089137500Sjulian	last = ch->hwptr;
1090137500Sjulian	src2 = ch->src + ch->parent->bufsz/4;
1091137500Sjulian
1092137500Sjulian	if (cur < last) {
1093137500Sjulian		interleave(ch->src + last, src2 + last,
1094137500Sjulian			   ch->sink + 2*last, ch->buflen/2 - last);
1095137500Sjulian		interleave(ch->src, src2,
1096137500Sjulian			   ch->sink, cur);
1097137500Sjulian	} else if (cur > last)
1098137500Sjulian		interleave(ch->src + last, src2 + last,
1099137500Sjulian			   ch->sink + 2*last, cur - last);
1100137500Sjulian	ch->hwptr = cur;
1101137500Sjulian}
1102137500Sjulian
1103137500Sjulian/*
1104137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center.
1105137500Sjulian * This function expects n not overwrapping the buffer boundary.
1106137500Sjulian * Note that n is measured in sample unit.
1107137500Sjulian *
1108137500Sjulian * XXX - this function works in 16bit monoral format only.
1109137500Sjulian */
1110137500Sjulianstatic inline void
1111137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n)
1112137500Sjulian{
1113137500Sjulian	int16_t *end;
1114137500Sjulian
1115137500Sjulian	for (end = dest + n; dest < end; dest++)
1116137500Sjulian		*dest = (int16_t)(((int)*dest - (int)*src++) / 2);
1117137500Sjulian}
1118137500Sjulian
1119137500Sjulianstatic void
1120137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch)
1121137500Sjulian{
1122137500Sjulian	unsigned cur, last;
1123137500Sjulian
1124137500Sjulian	agg_lock(ch->parent);
1125137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1126137500Sjulian	agg_unlock(ch->parent);
1127137500Sjulian	cur -= 0xffff & ((ch->phys - ch->base) >> 1);
1128137500Sjulian	last = ch->hwptr;
1129137500Sjulian
1130137500Sjulian	if (cur < last) {
1131137500Sjulian		mixdown(ch->src + last, ch->sink + last, ch->buflen - last);
1132137500Sjulian		mixdown(ch->src, ch->sink, cur);
1133137500Sjulian	} else if (cur > last)
1134137500Sjulian		mixdown(ch->src + last, ch->sink + last, cur - last);
1135137500Sjulian	ch->hwptr = cur;
1136137500Sjulian}
1137137500Sjulian
1138137500Sjulian/*
113965543Scg * Stereo jitter suppressor.
114065543Scg * Sometimes playback pointers differ in stereo-paired channels.
114165543Scg * Calling this routine within intr fixes the problem.
114265543Scg */
114365543Scgstatic inline 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
1160137500Sjulianstatic inline 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}
1175137500Sjulian
117665543Scgstatic inline u_int
1177137500Sjuliancalc_timer_div(struct agg_chinfo *ch)
117865543Scg{
1179137500Sjulian	u_int speed;
118065543Scg
1181137500Sjulian	speed = ch->speed;
1182137500Sjulian#ifdef INVARIANTS
1183137500Sjulian	if (speed == 0) {
1184137500Sjulian		printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n",
1185137500Sjulian		       ch->num);
1186137500Sjulian		speed = 1;
1187137500Sjulian	}
1188137500Sjulian#endif
1189137500Sjulian	return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo))
1190137500Sjulian		+ speed - 1) / speed;
1191137500Sjulian}
119265543Scg
1193137500Sjulianstatic inline u_int
1194137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch)
1195137500Sjulian{
1196137500Sjulian	u_int speed;
1197137500Sjulian
1198137500Sjulian	speed = ch->speed;
1199137500Sjulian#ifdef INVARIANTS
1200137500Sjulian	if (speed == 0) {
1201137500Sjulian		printf("snd_maestro: rch.speed == 0, which shouldn't\n");
1202137500Sjulian		speed = 1;
1203137500Sjulian	}
1204137500Sjulian#endif
1205137500Sjulian	return (48000 * (ch->blklen << (!ch->stereo))
1206137500Sjulian		+ speed - 1) / speed;
120765543Scg}
120865543Scg
120965543Scgstatic void
121065543Scgset_timer(struct agg_info *ess)
121165543Scg{
121265543Scg	int i;
1213137500Sjulian	u_int	dv = 32 << 7, newdv;
121465543Scg
121565543Scg	for (i = 0; i < ess->playchns; i++)
121665543Scg		if ((ess->active & (1 << i)) &&
1217137500Sjulian		    (dv > (newdv = calc_timer_div(ess->pch + i))))
1218137500Sjulian			dv = newdv;
1219137500Sjulian	if ((ess->active & (1 << i)) &&
1220137500Sjulian	    (dv > (newdv = calc_timer_div_rch(&ess->rch))))
1221137500Sjulian		dv = newdv;
122265543Scg
1223137500Sjulian	wp_settimer(ess, dv);
122465543Scg}
122565543Scg
122665543Scg
122765543Scg/* -----------------------------
122865543Scg * Newpcm glue.
122965543Scg */
123065543Scg
1231137500Sjulian/* AC97 mixer interface. */
1232137500Sjulian
1233137500Sjulianstatic u_int32_t
1234137500Sjulianagg_ac97_init(kobj_t obj, void *sc)
1235137500Sjulian{
1236137500Sjulian	struct agg_info *ess = sc;
1237137500Sjulian
1238137500Sjulian	return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1;
1239137500Sjulian}
1240137500Sjulian
1241137500Sjulianstatic int
1242137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno)
1243137500Sjulian{
1244137500Sjulian	struct agg_info *ess = sc;
1245137500Sjulian	int ret;
1246137500Sjulian
1247137500Sjulian	agg_lock(ess);
1248137500Sjulian	ret = agg_rdcodec(ess, regno);
1249137500Sjulian	agg_unlock(ess);
1250137500Sjulian	return ret;
1251137500Sjulian}
1252137500Sjulian
1253137500Sjulianstatic int
1254137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data)
1255137500Sjulian{
1256137500Sjulian	struct agg_info *ess = sc;
1257137500Sjulian	int ret;
1258137500Sjulian
1259137500Sjulian	agg_lock(ess);
1260137500Sjulian	ret = agg_wrcodec(ess, regno, data);
1261137500Sjulian	agg_unlock(ess);
1262137500Sjulian	return ret;
1263137500Sjulian}
1264137500Sjulian
1265137500Sjulian
1266137500Sjulianstatic kobj_method_t agg_ac97_methods[] = {
1267137500Sjulian    	KOBJMETHOD(ac97_init,		agg_ac97_init),
1268137500Sjulian    	KOBJMETHOD(ac97_read,		agg_ac97_read),
1269137500Sjulian    	KOBJMETHOD(ac97_write,		agg_ac97_write),
1270137500Sjulian	{ 0, 0 }
1271137500Sjulian};
1272137500SjulianAC97_DECLARE(agg_ac97);
1273137500Sjulian
1274137500Sjulian
1275137500Sjulian/* -------------------------------------------------------------------- */
1276137500Sjulian
1277137500Sjulian/* Playback channel. */
1278137500Sjulian
127965543Scgstatic void *
1280137500Sjulianaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
128165543Scg{
128265543Scg	struct agg_info *ess = devinfo;
128365543Scg	struct agg_chinfo *ch;
128465543Scg	bus_addr_t physaddr;
128570291Scg	void *p;
128665543Scg
1287137500Sjulian	KASSERT((dir == PCMDIR_PLAY),
1288137500Sjulian	    ("aggpch_init() called for RECORDING channel!"));
1289137500Sjulian	ch = ess->pch + ess->playchns;
129065543Scg
129165543Scg	ch->parent = ess;
129265543Scg	ch->channel = c;
129365543Scg	ch->buffer = b;
129465543Scg	ch->num = ess->playchns;
129565543Scg
1296137500Sjulian	p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr);
1297137500Sjulian	if (p == NULL)
1298137500Sjulian		return NULL;
1299137500Sjulian	ch->phys = physaddr;
1300137500Sjulian	ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT);
1301137500Sjulian
130284658Scg	sndbuf_setup(b, p, ess->bufsz);
1303137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1304137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1305137500Sjulian	ess->playchns++;
130665543Scg
1307137500Sjulian	return ch;
1308137500Sjulian}
130965543Scg
1310137500Sjulianstatic void
1311137500Sjulianadjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size)
1312137500Sjulian{
1313137500Sjulian	struct agg_chinfo *pchs[AGG_MAXPLAYCH];
1314137500Sjulian	u_int i, j, k;
1315137500Sjulian	bus_addr_t base;
131665543Scg
1317137500Sjulian	/* sort pchs by phys address */
1318137500Sjulian	for (i = 0; i < n; i++) {
1319137500Sjulian		for (j = 0; j < i; j++)
1320137500Sjulian			if (chans[i].phys < pchs[j]->phys) {
1321137500Sjulian				for (k = i; k > j; k--)
1322137500Sjulian					pchs[k] = pchs[k - 1];
1323137500Sjulian				break;
1324137500Sjulian			}
1325137500Sjulian		pchs[j] = chans + i;
1326137500Sjulian	}
1327137500Sjulian
1328137500Sjulian	/* use new base register if next buffer can not be addressed
1329137500Sjulian	   via current base. */
1330137500Sjulian#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1)
1331137500Sjulian	base = pchs[0]->base;
1332137500Sjulian	for (k = 1, i = 1; i < n; i++) {
1333137500Sjulian		if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT)
1334137500Sjulian			/* not addressable: assign new base */
1335137500Sjulian			base = (pchs[i]->base -= k++ << BASE_SHIFT);
1336137500Sjulian		else
1337137500Sjulian			pchs[i]->base = base;
1338137500Sjulian	}
1339137500Sjulian#undef BASE_SHIFT
1340137500Sjulian
1341137500Sjulian	if (bootverbose) {
1342137500Sjulian		printf("Total of %d bases are assigned.\n", k);
1343137500Sjulian		for (i = 0; i < n; i++) {
1344137500Sjulian			printf("ch.%d: phys 0x%llx, wpwa 0x%llx\n",
1345137500Sjulian			       i, (long long)chans[i].phys,
1346137500Sjulian			       (long long)(chans[i].phys -
1347137500Sjulian					   chans[i].base) >> 1);
1348137500Sjulian		}
1349137500Sjulian	}
135065543Scg}
135165543Scg
135265543Scgstatic int
1353137500Sjulianaggpch_free(kobj_t obj, void *data)
135465543Scg{
1355137500Sjulian	struct agg_chinfo *ch = data;
1356137500Sjulian	struct agg_info *ess = ch->parent;
1357137500Sjulian
1358137500Sjulian	/* free up buffer - called after channel stopped */
1359137500Sjulian	dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer));
1360137500Sjulian
136165543Scg	/* return 0 if ok */
136265543Scg	return 0;
136365543Scg}
136465543Scg
136565543Scgstatic int
1366137500Sjulianaggpch_setformat(kobj_t obj, void *data, u_int32_t format)
136765543Scg{
136865543Scg	struct agg_chinfo *ch = data;
136965543Scg
1370137500Sjulian	if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE)
1371137500Sjulian		return EINVAL;
1372137500Sjulian	ch->stereo = ch->qs16 = ch->us = 0;
1373137500Sjulian	if (format & AFMT_STEREO)
1374137500Sjulian		ch->stereo = 1;
137565543Scg
137665543Scg	if (format & AFMT_U8 || format & AFMT_S8) {
137765543Scg		if (format & AFMT_U8)
1378137500Sjulian			ch->us = 1;
1379137500Sjulian	} else
1380137500Sjulian		ch->qs16 = 1;
138189887Sscottl	return 0;
138265543Scg}
138365543Scg
138465543Scgstatic int
1385137500Sjulianaggpch_setspeed(kobj_t obj, void *data, u_int32_t speed)
138665543Scg{
1387137500Sjulian	return ((struct agg_chinfo*)data)->speed = speed;
138865543Scg}
138965543Scg
139065543Scgstatic int
1391137500Sjulianaggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
139265543Scg{
1393137500Sjulian	struct agg_chinfo *ch = data;
1394137500Sjulian	int blkcnt;
1395137500Sjulian
1396137500Sjulian	/* try to keep at least 20msec DMA space */
1397137500Sjulian	blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize);
1398137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1399137500Sjulian
1400137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1401137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1402137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1403137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1404137500Sjulian	} else {
1405137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1406137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1407137500Sjulian	}
1408137500Sjulian
1409137500Sjulian	ch->blklen = blocksize / 2;
1410137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1411137500Sjulian	return blocksize;
141265543Scg}
141365543Scg
141465543Scgstatic int
1415137500Sjulianaggpch_trigger(kobj_t obj, void *data, int go)
141665543Scg{
141765543Scg	struct agg_chinfo *ch = data;
141865543Scg
141965543Scg	switch (go) {
142065543Scg	case PCMTRIG_EMLDMAWR:
1421137500Sjulian		break;
142265543Scg	case PCMTRIG_START:
1423137500Sjulian		aggch_start_dac(ch);
142465543Scg		break;
142565543Scg	case PCMTRIG_ABORT:
142665543Scg	case PCMTRIG_STOP:
1427137500Sjulian		aggch_stop_dac(ch);
142865543Scg		break;
142965543Scg	}
143065543Scg	return 0;
143165543Scg}
143265543Scg
143365543Scgstatic int
1434137500Sjulianaggpch_getptr(kobj_t obj, void *data)
143565543Scg{
143665543Scg	struct agg_chinfo *ch = data;
143765543Scg	u_int cp;
143865543Scg
1439137500Sjulian	agg_lock(ch->parent);
1440137500Sjulian	cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1441137500Sjulian	agg_unlock(ch->parent);
144265543Scg
1443137500Sjulian	return ch->qs16 && ch->stereo
1444137500Sjulian		? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base))
1445137500Sjulian		: (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base));
144665543Scg}
144765543Scg
144874763Scgstatic struct pcmchan_caps *
1449137500Sjulianaggpch_getcaps(kobj_t obj, void *data)
145065543Scg{
145165543Scg	static u_int32_t playfmt[] = {
145265543Scg		AFMT_U8,
145365543Scg		AFMT_STEREO | AFMT_U8,
145465543Scg		AFMT_S8,
145565543Scg		AFMT_STEREO | AFMT_S8,
145665543Scg		AFMT_S16_LE,
145765543Scg		AFMT_STEREO | AFMT_S16_LE,
145865543Scg		0
145965543Scg	};
1460137500Sjulian	static struct pcmchan_caps playcaps = {2000, 767999, playfmt, 0};
146165543Scg
1462137500Sjulian	return &playcaps;
1463137500Sjulian}
1464137500Sjulian
1465137500Sjulian
1466137500Sjulianstatic kobj_method_t aggpch_methods[] = {
1467137500Sjulian    	KOBJMETHOD(channel_init,		aggpch_init),
1468137500Sjulian    	KOBJMETHOD(channel_free,		aggpch_free),
1469137500Sjulian    	KOBJMETHOD(channel_setformat,		aggpch_setformat),
1470137500Sjulian    	KOBJMETHOD(channel_setspeed,		aggpch_setspeed),
1471137500Sjulian    	KOBJMETHOD(channel_setblocksize,	aggpch_setblocksize),
1472137500Sjulian    	KOBJMETHOD(channel_trigger,		aggpch_trigger),
1473137500Sjulian    	KOBJMETHOD(channel_getptr,		aggpch_getptr),
1474137500Sjulian    	KOBJMETHOD(channel_getcaps,		aggpch_getcaps),
1475137500Sjulian	{ 0, 0 }
1476137500Sjulian};
1477137500SjulianCHANNEL_DECLARE(aggpch);
1478137500Sjulian
1479137500Sjulian
1480137500Sjulian/* -------------------------------------------------------------------- */
1481137500Sjulian
1482137500Sjulian/* Recording channel. */
1483137500Sjulian
1484137500Sjulianstatic void *
1485137500Sjulianaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
1486137500Sjulian{
1487137500Sjulian	struct agg_info *ess = devinfo;
1488137500Sjulian	struct agg_rchinfo *ch;
1489137500Sjulian	u_int8_t *p;
1490137500Sjulian
1491137500Sjulian	KASSERT((dir == PCMDIR_REC),
1492137500Sjulian	    ("aggrch_init() called for PLAYBACK channel!"));
1493137500Sjulian	ch = &ess->rch;
1494137500Sjulian
1495137500Sjulian	ch->parent = ess;
1496137500Sjulian	ch->channel = c;
1497137500Sjulian	ch->buffer = b;
1498137500Sjulian
1499137500Sjulian	/* Uses the bottom-half of the status buffer. */
1500137500Sjulian	p        = ess->stat + ess->bufsz;
1501137500Sjulian	ch->phys = ess->phys + ess->bufsz;
1502137500Sjulian	ch->base = ess->phys;
1503137500Sjulian	ch->src  = (int16_t *)(p + ess->bufsz);
1504137500Sjulian	ch->srcphys = ch->phys + ess->bufsz;
1505137500Sjulian	ch->sink = (int16_t *)p;
1506137500Sjulian
1507137500Sjulian	sndbuf_setup(b, p, ess->bufsz);
1508137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1509137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1510137500Sjulian
1511137500Sjulian	return ch;
1512137500Sjulian}
1513137500Sjulian
1514137500Sjulianstatic int
1515137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format)
1516137500Sjulian{
1517137500Sjulian	struct agg_rchinfo *ch = data;
1518137500Sjulian
1519137500Sjulian	if (!(format & AFMT_S16_LE))
1520137500Sjulian		return EINVAL;
1521137500Sjulian	if (format & AFMT_STEREO)
1522137500Sjulian		ch->stereo = 1;
1523137500Sjulian	else
1524137500Sjulian		ch->stereo = 0;
1525137500Sjulian	return 0;
1526137500Sjulian}
1527137500Sjulian
1528137500Sjulianstatic int
1529137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed)
1530137500Sjulian{
1531137500Sjulian	return ((struct agg_rchinfo*)data)->speed = speed;
1532137500Sjulian}
1533137500Sjulian
1534137500Sjulianstatic int
1535137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1536137500Sjulian{
1537137500Sjulian	struct agg_rchinfo *ch = data;
1538137500Sjulian	int blkcnt;
1539137500Sjulian
1540137500Sjulian	/* try to keep at least 20msec DMA space */
1541137500Sjulian	blkcnt = (ch->speed << ch->stereo) / (25 * blocksize);
1542137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1543137500Sjulian
1544137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1545137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1546137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1547137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1548137500Sjulian	} else {
1549137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1550137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1551137500Sjulian	}
1552137500Sjulian
1553137500Sjulian	ch->blklen = blocksize / 2;
1554137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1555137500Sjulian	return blocksize;
1556137500Sjulian}
1557137500Sjulian
1558137500Sjulianstatic int
1559137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go)
1560137500Sjulian{
1561137500Sjulian	struct agg_rchinfo *ch = sc;
1562137500Sjulian
1563137500Sjulian	switch (go) {
1564137500Sjulian	case PCMTRIG_EMLDMARD:
1565137500Sjulian		if (ch->stereo)
1566137500Sjulian			aggch_feed_adc_stereo(ch);
1567137500Sjulian		else
1568137500Sjulian			aggch_feed_adc_mono(ch);
1569137500Sjulian		break;
1570137500Sjulian	case PCMTRIG_START:
1571137500Sjulian		aggch_start_adc(ch);
1572137500Sjulian		break;
1573137500Sjulian	case PCMTRIG_ABORT:
1574137500Sjulian	case PCMTRIG_STOP:
1575137500Sjulian		aggch_stop_adc(ch);
1576137500Sjulian		break;
1577137500Sjulian	}
1578137500Sjulian	return 0;
1579137500Sjulian}
1580137500Sjulian
1581137500Sjulianstatic int
1582137500Sjulianaggrch_getptr(kobj_t obj, void *sc)
1583137500Sjulian{
1584137500Sjulian	struct agg_rchinfo *ch = sc;
1585137500Sjulian
1586137500Sjulian	return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1;
1587137500Sjulian}
1588137500Sjulian
1589137500Sjulianstatic struct pcmchan_caps *
1590137500Sjulianaggrch_getcaps(kobj_t obj, void *sc)
1591137500Sjulian{
159265543Scg	static u_int32_t recfmt[] = {
159365543Scg		AFMT_S16_LE,
159465543Scg		AFMT_STEREO | AFMT_S16_LE,
159565543Scg		0
159665543Scg	};
1597137500Sjulian	static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0};
159865543Scg
1599137500Sjulian	return &reccaps;
160065543Scg}
160165543Scg
1602137500Sjulianstatic kobj_method_t aggrch_methods[] = {
1603137500Sjulian	KOBJMETHOD(channel_init,		aggrch_init),
1604137500Sjulian	/* channel_free: no-op */
1605137500Sjulian	KOBJMETHOD(channel_setformat,		aggrch_setformat),
1606137500Sjulian	KOBJMETHOD(channel_setspeed,		aggrch_setspeed),
1607137500Sjulian	KOBJMETHOD(channel_setblocksize,	aggrch_setblocksize),
1608137500Sjulian	KOBJMETHOD(channel_trigger,		aggrch_trigger),
1609137500Sjulian	KOBJMETHOD(channel_getptr,		aggrch_getptr),
1610137500Sjulian	KOBJMETHOD(channel_getcaps,		aggrch_getcaps),
161170134Scg	{ 0, 0 }
161270134Scg};
1613137500SjulianCHANNEL_DECLARE(aggrch);
161465543Scg
1615137500Sjulian
161665543Scg/* -----------------------------
161765543Scg * Bus space.
161865543Scg */
161965543Scg
162065543Scgstatic void
162165543Scgagg_intr(void *sc)
162265543Scg{
162365543Scg	struct agg_info* ess = sc;
1624137500Sjulian	register u_int8_t status;
162565543Scg	int i;
1626137500Sjulian	u_int m;
162765543Scg
1628137500Sjulian	status = AGG_RD(ess, PORT_HOSTINT_STAT, 1);
162965543Scg	if (!status)
163065543Scg		return;
163165543Scg
1632137500Sjulian	/* Acknowledge intr. */
1633137500Sjulian	AGG_WR(ess, PORT_HOSTINT_STAT, status, 1);
163470619Sjhb
1635137500Sjulian	if (status & HOSTINT_STAT_DSOUND) {
1636137500Sjulian#ifdef AGG_JITTER_CORRECTION
1637137500Sjulian		agg_lock(ess);
1638137500Sjulian#endif
1639137500Sjulian		if (ess->curpwr <= PCI_POWERSTATE_D1) {
1640137500Sjulian			AGG_WR(ess, PORT_INT_STAT, 1, 2);
1641137500Sjulian#ifdef AGG_JITTER_CORRECTION
1642137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1643137500Sjulian				if (ess->active & m)
1644137500Sjulian					suppress_jitter(ess->pch + i);
1645137500Sjulian			}
1646137500Sjulian			if (ess->active & m)
1647137500Sjulian				suppress_rec_jitter(&ess->rch);
1648137500Sjulian			agg_unlock(ess);
1649137500Sjulian#endif
1650137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1651137500Sjulian				if (ess->active & m) {
1652137500Sjulian					if (ess->curpwr <= PCI_POWERSTATE_D1)
1653137500Sjulian						chn_intr(ess->pch[i].channel);
1654137500Sjulian					else {
1655137500Sjulian						m = 0;
1656137500Sjulian						break;
1657137500Sjulian					}
1658137500Sjulian				}
1659137500Sjulian			}
1660137500Sjulian			if ((ess->active & m)
1661137500Sjulian			    && ess->curpwr <= PCI_POWERSTATE_D1)
1662137500Sjulian				chn_intr(ess->rch.channel);
1663137500Sjulian		}
1664137500Sjulian#ifdef AGG_JITTER_CORRECTION
1665137500Sjulian		else
1666137500Sjulian			agg_unlock(ess);
1667137500Sjulian#endif
1668137500Sjulian	}
1669137500Sjulian
167065543Scg	if (status & HOSTINT_STAT_HWVOL) {
1671137500Sjulian		register u_int8_t event;
167270619Sjhb
1673137500Sjulian		agg_lock(ess);
1674137500Sjulian		event = AGG_RD(ess, PORT_HWVOL_MASTER, 1);
1675137500Sjulian		AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1);
1676137500Sjulian		agg_unlock(ess);
1677137500Sjulian
167870619Sjhb		switch (event) {
167970619Sjhb		case HWVOL_UP:
168070945Sjhb			mixer_hwvol_step(ess->dev, 1, 1);
168170619Sjhb			break;
168270619Sjhb		case HWVOL_DOWN:
168370945Sjhb			mixer_hwvol_step(ess->dev, -1, -1);
168470619Sjhb			break;
168570619Sjhb		case HWVOL_NOP:
168670619Sjhb			break;
168770619Sjhb		default:
1688137500Sjulian			if (event & HWVOL_MUTE) {
1689137500Sjulian				mixer_hwvol_mute(ess->dev);
1690137500Sjulian				break;
1691137500Sjulian			}
1692137500Sjulian			device_printf(ess->dev,
1693137500Sjulian				      "%s: unknown HWVOL event 0x%x\n",
1694137500Sjulian				      device_get_nameunit(ess->dev), event);
169565543Scg		}
169665543Scg	}
169765543Scg}
169865543Scg
169965543Scgstatic void
170065543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
170165543Scg{
170265543Scg	bus_addr_t *phys = arg;
170365543Scg
170465543Scg	*phys = error? 0 : segs->ds_addr;
170565543Scg
170665543Scg	if (bootverbose) {
170765543Scg		printf("setmap (%lx, %lx), nseg=%d, error=%d\n",
170865543Scg		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
170965543Scg		    nseg, error);
171065543Scg	}
171165543Scg}
171265543Scg
171365543Scgstatic void *
1714137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys)
171565543Scg{
171665543Scg	void *buf;
171765543Scg	bus_dmamap_t map;
171865543Scg
1719137500Sjulian	if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map))
172065543Scg		return NULL;
1721137500Sjulian	if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0)
1722137500Sjulian	    || !*phys || map) {
1723137500Sjulian		bus_dmamem_free(dmat, buf, map);
172465543Scg		return NULL;
172565543Scg	}
172665543Scg	return buf;
172765543Scg}
172865543Scg
172965543Scgstatic void
1730137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf)
173165543Scg{
1732137500Sjulian	bus_dmamem_free(dmat, buf, NULL);
173365543Scg}
173465543Scg
173565543Scgstatic int
173665543Scgagg_probe(device_t dev)
173765543Scg{
173865543Scg	char *s = NULL;
173965543Scg
174065543Scg	switch (pci_get_devid(dev)) {
174165543Scg	case MAESTRO_1_PCI_ID:
174265543Scg		s = "ESS Technology Maestro-1";
174365543Scg		break;
174465543Scg
174565543Scg	case MAESTRO_2_PCI_ID:
174665543Scg		s = "ESS Technology Maestro-2";
174765543Scg		break;
174865543Scg
174965543Scg	case MAESTRO_2E_PCI_ID:
175065543Scg		s = "ESS Technology Maestro-2E";
175165543Scg		break;
175265543Scg	}
175365543Scg
175465543Scg	if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) {
175565543Scg		device_set_desc(dev, s);
175665543Scg		return 0;
175765543Scg	}
175865543Scg	return ENXIO;
175965543Scg}
176065543Scg
176165543Scgstatic int
176265543Scgagg_attach(device_t dev)
176365543Scg{
176465543Scg	struct agg_info	*ess = NULL;
176565543Scg	u_int32_t	data;
1766119690Sjhb	int	regid = PCIR_BAR(0);
176765543Scg	struct resource	*reg = NULL;
176865543Scg	struct ac97_info	*codec = NULL;
176965543Scg	int	irqid = 0;
177065543Scg	struct resource	*irq = NULL;
177165543Scg	void	*ih = NULL;
177265543Scg	char	status[SND_STATUSLEN];
1773137500Sjulian	int	ret = 0;
177465543Scg
177578564Sgreid	if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
177665543Scg		device_printf(dev, "cannot allocate softc\n");
1777137500Sjulian		ret = ENOMEM;
1778137500Sjulian		goto bad;
177965543Scg	}
178065543Scg	ess->dev = dev;
178165543Scg
1782137500Sjulian#ifdef USING_MUTEX
1783137500Sjulian	mtx_init(&ess->lock, device_get_desc(dev), "hardware status lock",
1784137500Sjulian		 MTX_DEF | MTX_RECURSE);
1785137500Sjulian	if (!mtx_initialized(&ess->lock)) {
1786137500Sjulian		device_printf(dev, "failed to create a mutex.\n");
1787137500Sjulian		ret = ENOMEM;
1788137500Sjulian		goto bad;
1789137500Sjulian	}
1790137500Sjulian#endif
1791137500Sjulian
179284658Scg	ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536);
1793137500Sjulian	if (bus_dma_tag_create(/*parent*/ NULL,
1794137500Sjulian			       /*align */ 4, 1 << (16+1),
1795137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1796137500Sjulian			       /*filter*/ NULL, NULL,
1797137500Sjulian			       /*size  */ ess->bufsz, 1, 0x3ffff,
1798137500Sjulian			       /*flags */ 0,
1799137500Sjulian#if __FreeBSD_version >= 501102
1800137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1801137500Sjulian#endif
1802137500Sjulian			       &ess->buf_dmat) != 0) {
1803137500Sjulian		device_printf(dev, "unable to create dma tag\n");
1804137500Sjulian		ret = ENOMEM;
1805137500Sjulian		goto bad;
1806137500Sjulian	}
180784658Scg
180865543Scg	if (bus_dma_tag_create(/*parent*/NULL,
1809137500Sjulian			       /*align */ 1 << WAVCACHE_BASEADDR_SHIFT,
1810137500Sjulian			                  1 << (16+1),
1811137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1812137500Sjulian			       /*filter*/ NULL, NULL,
1813137500Sjulian			       /*size  */ 3*ess->bufsz, 1, 0x3ffff,
1814137500Sjulian			       /*flags */ 0,
1815137500Sjulian#if __FreeBSD_version >= 501102
1816137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1817137500Sjulian#endif
1818137500Sjulian			       &ess->stat_dmat) != 0) {
181965543Scg		device_printf(dev, "unable to create dma tag\n");
1820137500Sjulian		ret = ENOMEM;
182165543Scg		goto bad;
182265543Scg	}
182365543Scg
1824137500Sjulian	/* Allocate the room for brain-damaging status buffer. */
1825137500Sjulian	ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys);
182665543Scg	if (ess->stat == NULL) {
1827137500Sjulian		device_printf(dev, "cannot allocate status buffer\n");
1828137500Sjulian		ret = ENOMEM;
182965543Scg		goto bad;
183065543Scg	}
183165543Scg	if (bootverbose)
1832137500Sjulian		device_printf(dev, "Maestro status/record buffer: %#llx\n",
1833137500Sjulian		    (long long)ess->phys);
183465543Scg
1835137500Sjulian	/* State D0-uninitialized. */
1836137500Sjulian	ess->curpwr = PCI_POWERSTATE_D3;
1837137500Sjulian	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
183865543Scg
183965543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
184065543Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN);
184165543Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
184265543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
184365543Scg
1844137500Sjulian	/* Allocate resources. */
1845137500Sjulian	if (data & PCIM_CMD_PORTEN)
184665543Scg		reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &regid,
184765543Scg		    0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE);
1848137500Sjulian	if (reg != NULL) {
1849137500Sjulian		ess->reg = reg;
1850137500Sjulian		ess->regid = regid;
1851137500Sjulian		ess->st = rman_get_bustag(reg);
1852137500Sjulian		ess->sh = rman_get_bushandle(reg);
1853137500Sjulian	} else {
185465543Scg		device_printf(dev, "unable to map register space\n");
1855137500Sjulian		ret = ENXIO;
185665543Scg		goto bad;
185765543Scg	}
1858137500Sjulian	irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
1859137500Sjulian	    0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE);
1860137500Sjulian	if (irq != NULL) {
1861137500Sjulian		ess->irq = irq;
1862137500Sjulian		ess->irqid = irqid;
1863137500Sjulian	} else {
1864137500Sjulian		device_printf(dev, "unable to map interrupt\n");
1865137500Sjulian		ret = ENXIO;
1866137500Sjulian		goto bad;
1867137500Sjulian	}
186865543Scg
1869137500Sjulian	/* Setup resources. */
1870137500Sjulian	if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) {
1871137500Sjulian		device_printf(dev, "unable to setup interrupt\n");
1872137500Sjulian		ret = ENXIO;
1873137500Sjulian		goto bad;
1874137500Sjulian	} else
1875137500Sjulian		ess->ih = ih;
1876137500Sjulian
1877137500Sjulian	/* Transition from D0-uninitialized to D0. */
1878137500Sjulian	agg_lock(ess);
1879137500Sjulian	agg_power(ess, PCI_POWERSTATE_D0);
1880137500Sjulian	if (agg_rdcodec(ess, 0) == 0x80) {
1881137500Sjulian		/* XXX - TODO: PT101 */
188265543Scg		device_printf(dev, "PT101 codec detected!\n");
1883137500Sjulian		ret = ENXIO;
188465543Scg		goto bad;
188565543Scg	}
188670134Scg	codec = AC97_CREATE(dev, ess, agg_ac97);
1887137500Sjulian	if (codec == NULL) {
1888137500Sjulian		device_printf(dev, "failed to create AC97 codec softc!\n");
1889137500Sjulian		ret = ENOMEM;
189065543Scg		goto bad;
1891137500Sjulian	}
1892137500Sjulian	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
1893137500Sjulian		device_printf(dev, "mixer initialization failed!\n");
1894137500Sjulian		ret = ENXIO;
189565543Scg		goto bad;
1896137500Sjulian	}
189765543Scg	ess->codec = codec;
189865543Scg
1899137500Sjulian	ret = pcm_register(dev, ess, AGG_MAXPLAYCH, 1);
1900137500Sjulian	if (ret)
190165543Scg		goto bad;
190265543Scg
190370945Sjhb	mixer_hwvol_init(dev);
1904137500Sjulian	agg_power(ess, powerstate_init);
190565543Scg	for (data = 0; data < AGG_MAXPLAYCH; data++)
1906137500Sjulian		pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess);
190770134Scg	pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess);
1908137500Sjulian	adjust_pchbase(ess->pch, ess->playchns, ess->bufsz);
1909137500Sjulian
1910137500Sjulian	agg_unlock(ess);
1911137500Sjulian
1912137500Sjulian	snprintf(status, SND_STATUSLEN,
1913137500Sjulian	    "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d",
1914137500Sjulian	    rman_get_start(reg), rman_get_end(reg), rman_get_start(irq),
1915137500Sjulian	    pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev));
191665543Scg	pcm_setstatus(dev, status);
191765543Scg
191865543Scg	return 0;
191965543Scg
192065543Scg bad:
192165644Scg	if (codec != NULL)
192265644Scg		ac97_destroy(codec);
192365543Scg	if (ih != NULL)
192465543Scg		bus_teardown_intr(dev, irq, ih);
192565543Scg	if (irq != NULL)
192665543Scg		bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
192765543Scg	if (reg != NULL)
192865543Scg		bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
1929137500Sjulian	if (ess->stat != NULL)
1930137500Sjulian		dma_free(ess->stat_dmat, ess->stat);
1931137500Sjulian	if (ess->stat_dmat != NULL)
1932137500Sjulian		bus_dma_tag_destroy(ess->stat_dmat);
1933137500Sjulian	if (ess->buf_dmat != NULL)
1934137500Sjulian		bus_dma_tag_destroy(ess->buf_dmat);
1935137500Sjulian#ifdef USING_MUTEX
1936137500Sjulian	if (mtx_initialized(&ess->lock))
1937137500Sjulian		mtx_destroy(&ess->lock);
1938137500Sjulian#endif
1939137500Sjulian	if (ess != NULL)
194065543Scg		free(ess, M_DEVBUF);
194165543Scg
1942137500Sjulian	return ret;
194365543Scg}
194465543Scg
194565543Scgstatic int
194665543Scgagg_detach(device_t dev)
194765543Scg{
194865543Scg	struct agg_info	*ess = pcm_getdevinfo(dev);
194965543Scg	int r;
1950137500Sjulian	u_int16_t icr;
195165543Scg
1952137500Sjulian	icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2);
1953137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
1954137500Sjulian
1955137500Sjulian	agg_lock(ess);
1956137500Sjulian	if (ess->active) {
1957137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
1958137500Sjulian		agg_unlock(ess);
1959137500Sjulian		return EBUSY;
1960137500Sjulian	}
1961137500Sjulian	agg_unlock(ess);
1962137500Sjulian
196365543Scg	r = pcm_unregister(dev);
1964137500Sjulian	if (r) {
1965137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
196665543Scg		return r;
1967137500Sjulian	}
196865543Scg
1969137500Sjulian	agg_lock(ess);
1970137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
197165543Scg
197265543Scg	bus_teardown_intr(dev, ess->irq, ess->ih);
197365543Scg	bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq);
197465543Scg	bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg);
1975137500Sjulian	dma_free(ess->stat_dmat, ess->stat);
1976137500Sjulian	bus_dma_tag_destroy(ess->stat_dmat);
1977137500Sjulian	bus_dma_tag_destroy(ess->buf_dmat);
1978137500Sjulian#ifdef USING_MUTEX
1979137500Sjulian	mtx_destroy(&ess->lock);
1980137500Sjulian#endif
198165543Scg	free(ess, M_DEVBUF);
198265543Scg	return 0;
198365543Scg}
198465543Scg
198565543Scgstatic int
198665543Scgagg_suspend(device_t dev)
198765543Scg{
198865543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
1989137500Sjulian#ifndef USING_MUTEX
1990137500Sjulian	int x;
199165543Scg
199265543Scg	x = spltty();
199365543Scg#endif
1994137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
1995137500Sjulian	agg_lock(ess);
1996137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
1997137500Sjulian	agg_unlock(ess);
1998137500Sjulian#ifndef USING_MUTEX
199965543Scg	splx(x);
2000137500Sjulian#endif
200165543Scg
200265543Scg	return 0;
200365543Scg}
200465543Scg
200565543Scgstatic int
200665543Scgagg_resume(device_t dev)
200765543Scg{
2008137500Sjulian	int i;
200965543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
2010137500Sjulian#ifndef USING_MUTEX
2011137500Sjulian	int x;
201265543Scg
201365543Scg	x = spltty();
2014137500Sjulian#endif
201565543Scg	for (i = 0; i < ess->playchns; i++)
201665543Scg		if (ess->active & (1 << i))
201765543Scg			aggch_start_dac(ess->pch + i);
201865543Scg	if (ess->active & (1 << i))
201965543Scg		aggch_start_adc(&ess->rch);
2020137500Sjulian
2021137500Sjulian	agg_lock(ess);
2022137500Sjulian	if (!ess->active)
2023137500Sjulian		agg_power(ess, powerstate_init);
2024137500Sjulian	agg_unlock(ess);
2025137500Sjulian#ifndef USING_MUTEX
2026137500Sjulian	splx(x);
202765543Scg#endif
2028137500Sjulian
2029137500Sjulian	if (mixer_reinit(dev)) {
2030137500Sjulian		device_printf(dev, "unable to reinitialize the mixer\n");
2031137500Sjulian		return ENXIO;
203265543Scg	}
2033137500Sjulian
203465543Scg	return 0;
203565543Scg}
203665543Scg
203765543Scgstatic int
203865543Scgagg_shutdown(device_t dev)
203965543Scg{
204065543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
204165543Scg
2042137500Sjulian	agg_lock(ess);
2043137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2044137500Sjulian	agg_unlock(ess);
204565543Scg
204665543Scg	return 0;
204765543Scg}
204865543Scg
204965543Scg
205065543Scgstatic device_method_t agg_methods[] = {
205165543Scg    DEVMETHOD(device_probe,	agg_probe),
205265543Scg    DEVMETHOD(device_attach,	agg_attach),
205365543Scg    DEVMETHOD(device_detach,	agg_detach),
205465543Scg    DEVMETHOD(device_suspend,	agg_suspend),
205565543Scg    DEVMETHOD(device_resume,	agg_resume),
205665543Scg    DEVMETHOD(device_shutdown,	agg_shutdown),
205765543Scg
205865543Scg    { 0, 0 }
205965543Scg};
206065543Scg
206165543Scgstatic driver_t agg_driver = {
206265543Scg    "pcm",
206365543Scg    agg_methods,
206482180Scg    PCM_SOFTC_SIZE,
206565543Scg};
206665543Scg
2067137500Sjulian/*static devclass_t pcm_devclass;*/
2068137500Sjulian
206965543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
2070132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
207165543ScgMODULE_VERSION(snd_maestro, 1);
2072