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$");
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/* compatibility */
91137500Sjulian#if __FreeBSD_version < 500000
92137500Sjulian# define critical_enter()	disable_intr()
93137500Sjulian# define critical_exit()	enable_intr()
94137500Sjulian#endif
95137500Sjulian
96137500Sjulian#ifndef PCIR_BAR
97137500Sjulian#define PCIR_BAR(x)	(PCIR_MAPS + (x) * 4)
98137500Sjulian#endif
99137500Sjulian
100137500Sjulian
10165543Scg/* -----------------------------
10265543Scg * Data structures.
10365543Scg */
10465543Scgstruct agg_chinfo {
105137500Sjulian	/* parent softc */
10665543Scg	struct agg_info		*parent;
107137500Sjulian
108137500Sjulian	/* FreeBSD newpcm related */
10974763Scg	struct pcm_channel	*channel;
11074763Scg	struct snd_dbuf		*buffer;
111137500Sjulian
112137500Sjulian	/* OS independent */
113137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
114137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
115137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
116137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
11770291Scg	u_int32_t		speed;
118137500Sjulian	unsigned		num	: 3;
119137500Sjulian	unsigned		stereo	: 1;
120137500Sjulian	unsigned		qs16	: 1;	/* quantum size is 16bit */
121137500Sjulian	unsigned		us	: 1;	/* in unsigned format */
12265543Scg};
12365543Scg
124137500Sjulianstruct agg_rchinfo {
125137500Sjulian	/* parent softc */
126137500Sjulian	struct agg_info		*parent;
127137500Sjulian
128137500Sjulian	/* FreeBSD newpcm related */
129137500Sjulian	struct pcm_channel	*channel;
130137500Sjulian	struct snd_dbuf		*buffer;
131137500Sjulian
132137500Sjulian	/* OS independent */
133137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
134137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
135137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
136137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
137137500Sjulian	u_int32_t		speed;
138137500Sjulian	unsigned			: 3;
139137500Sjulian	unsigned		stereo	: 1;
140137500Sjulian	bus_addr_t		srcphys;
141137500Sjulian	int16_t			*src;	/* stereo peer buffer */
142137500Sjulian	int16_t			*sink;	/* channel buffer pointer */
143137500Sjulian	volatile u_int32_t	hwptr;	/* ready point in 16bit sample */
144137500Sjulian};
145137500Sjulian
14665543Scgstruct agg_info {
147137500Sjulian	/* FreeBSD newbus related */
14865543Scg	device_t		dev;
149137500Sjulian
150137500Sjulian	/* I wonder whether bus_space_* are in common in *BSD... */
15165543Scg	struct resource		*reg;
15265543Scg	int			regid;
15365543Scg	bus_space_tag_t		st;
15465543Scg	bus_space_handle_t	sh;
15565543Scg
15665543Scg	struct resource		*irq;
15765543Scg	int			irqid;
15865543Scg	void			*ih;
15965543Scg
160137500Sjulian	bus_dma_tag_t		buf_dmat;
161137500Sjulian	bus_dma_tag_t		stat_dmat;
16265543Scg
163137500Sjulian	/* FreeBSD SMPng related */
164137500Sjulian	struct mtx		lock;	/* mutual exclusion */
165137500Sjulian	/* FreeBSD newpcm related */
16665543Scg	struct ac97_info	*codec;
16765543Scg
168137500Sjulian	/* OS independent */
169137500Sjulian	u_int8_t		*stat;	/* status buffer pointer */
170137500Sjulian	bus_addr_t		phys;	/* status buffer physical address */
171137500Sjulian	unsigned int		bufsz;	/* channel buffer size in bytes */
172137500Sjulian	u_int			playchns;
173137500Sjulian	volatile u_int		active;
17465543Scg	struct agg_chinfo	pch[AGG_MAXPLAYCH];
175137500Sjulian	struct agg_rchinfo	rch;
176137500Sjulian	volatile u_int8_t	curpwr;	/* current power status: D[0-3] */
17765543Scg};
17865543Scg
179137500Sjulian
180137500Sjulian/* -----------------------------
181137500Sjulian * Sysctls for debug.
182137500Sjulian */
183137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1;
184137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE
185137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D2;
186137500Sjulian#else
187137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D1;
188137500Sjulian#endif
189137500Sjulianstatic unsigned int powerstate_init   = PCI_POWERSTATE_D2;
190137500Sjulian
191159732Snetchild/* XXX: this should move to a device specific sysctl dev.pcm.X.debug.Y via
192159732Snetchild   device_get_sysctl_*() as discussed on multimedia@ in msg-id
193159732Snetchild   <861wujij2q.fsf@xps.des.no> */
194227309Sedstatic SYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, "");
195137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW,
196137500Sjulian	    &powerstate_active, 0, "The Dx power state when active (0-1)");
197137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW,
198137500Sjulian	    &powerstate_idle, 0, "The Dx power state when idle (0-2)");
199137500SjulianSYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW,
200166279Sariff	    &powerstate_init, 0,
201166279Sariff	    "The Dx power state prior to the first use (0-2)");
202137500Sjulian
203137500Sjulian
204137500Sjulian/* -----------------------------
205137500Sjulian * Prototypes
206137500Sjulian */
207137500Sjulian
208166279Sariffstatic void	agg_sleep(struct agg_info*, const char *wmesg, int msec);
209137500Sjulian
210166279Sariffstatic __inline u_int32_t	agg_rd(struct agg_info*, int, int size);
211166279Sariffstatic __inline void		agg_wr(struct agg_info*, int, u_int32_t data,
212166279Sariff								int size);
213166279Sariffstatic int	agg_rdcodec(struct agg_info*, int);
214166279Sariffstatic int	agg_wrcodec(struct agg_info*, int, u_int32_t);
215137500Sjulian
216166279Sariffstatic void	ringbus_setdest(struct agg_info*, int, int);
217137500Sjulian
218166279Sariffstatic u_int16_t	wp_rdreg(struct agg_info*, u_int16_t);
219166279Sariffstatic void		wp_wrreg(struct agg_info*, u_int16_t, u_int16_t);
220166279Sariffstatic u_int16_t	wp_rdapu(struct agg_info*, unsigned, u_int16_t);
221166279Sariffstatic void	wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t);
222166279Sariffstatic void	wp_settimer(struct agg_info*, u_int);
223166279Sariffstatic void	wp_starttimer(struct agg_info*);
224166279Sariffstatic void	wp_stoptimer(struct agg_info*);
22565543Scg
226166279Sariff#if 0
227166279Sariffstatic u_int16_t	wc_rdreg(struct agg_info*, u_int16_t);
228166279Sariff#endif
229166279Sariffstatic void		wc_wrreg(struct agg_info*, u_int16_t, u_int16_t);
230166279Sariff#if 0
231166279Sariffstatic u_int16_t	wc_rdchctl(struct agg_info*, int);
232166279Sariff#endif
233166279Sariffstatic void		wc_wrchctl(struct agg_info*, int, u_int16_t);
23465543Scg
235166279Sariffstatic void	agg_stopclock(struct agg_info*, int part, int st);
23665543Scg
237166279Sariffstatic void	agg_initcodec(struct agg_info*);
238166279Sariffstatic void	agg_init(struct agg_info*);
239166279Sariffstatic void	agg_power(struct agg_info*, int);
24065543Scg
241166279Sariffstatic void	aggch_start_dac(struct agg_chinfo*);
242166279Sariffstatic void	aggch_stop_dac(struct agg_chinfo*);
243166279Sariffstatic void	aggch_start_adc(struct agg_rchinfo*);
244166279Sariffstatic void	aggch_stop_adc(struct agg_rchinfo*);
245166279Sariffstatic void	aggch_feed_adc_stereo(struct agg_rchinfo*);
246166279Sariffstatic void	aggch_feed_adc_mono(struct agg_rchinfo*);
24765543Scg
248166279Sariff#ifdef AGG_JITTER_CORRECTION
249166279Sariffstatic void	suppress_jitter(struct agg_chinfo*);
250166279Sariffstatic void	suppress_rec_jitter(struct agg_rchinfo*);
251166279Sariff#endif
25265543Scg
253166279Sariffstatic void	set_timer(struct agg_info*);
25465543Scg
255166279Sariffstatic void	agg_intr(void *);
256166279Sariffstatic int	agg_probe(device_t);
257166279Sariffstatic int	agg_attach(device_t);
258166279Sariffstatic int	agg_detach(device_t);
259166279Sariffstatic int	agg_suspend(device_t);
260166279Sariffstatic int	agg_resume(device_t);
261166279Sariffstatic int	agg_shutdown(device_t);
26265543Scg
263137500Sjulianstatic void	*dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*);
264166279Sariffstatic void	dma_free(bus_dma_tag_t, void *);
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
289166279Sariffstatic __inline u_int32_t
290137500Sjulianagg_rd(struct agg_info *sc, int regno, int size)
291137500Sjulian{
292137500Sjulian	switch (size) {
293137500Sjulian	case 1:
294137500Sjulian		return bus_space_read_1(sc->st, sc->sh, regno);
295137500Sjulian	case 2:
296137500Sjulian		return bus_space_read_2(sc->st, sc->sh, regno);
297137500Sjulian	case 4:
298137500Sjulian		return bus_space_read_4(sc->st, sc->sh, regno);
299137500Sjulian	default:
300137500Sjulian		return ~(u_int32_t)0;
301137500Sjulian	}
302137500Sjulian}
303137500Sjulian
304137500Sjulian#define AGG_RD(sc, regno, size)           \
305137500Sjulian	bus_space_read_##size(            \
306137500Sjulian	    ((struct agg_info*)(sc))->st, \
307137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno))
308137500Sjulian
309166279Sariffstatic __inline void
310137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size)
311137500Sjulian{
312137500Sjulian	switch (size) {
313137500Sjulian	case 1:
314137500Sjulian		bus_space_write_1(sc->st, sc->sh, regno, data);
315137500Sjulian		break;
316137500Sjulian	case 2:
317137500Sjulian		bus_space_write_2(sc->st, sc->sh, regno, data);
318137500Sjulian		break;
319137500Sjulian	case 4:
320137500Sjulian		bus_space_write_4(sc->st, sc->sh, regno, data);
321137500Sjulian		break;
322137500Sjulian	}
323137500Sjulian}
324137500Sjulian
325137500Sjulian#define AGG_WR(sc, regno, data, size)     \
326137500Sjulian	bus_space_write_##size(           \
327137500Sjulian	    ((struct agg_info*)(sc))->st, \
328137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno), (data))
329137500Sjulian
33070134Scg/* -------------------------------------------------------------------- */
33170134Scg
332137500Sjulian/* Codec/Ringbus */
333137500Sjulian
334166279Sariffstatic int
335137500Sjulianagg_codec_wait4idle(struct agg_info *ess)
33665543Scg{
337137500Sjulian	unsigned t = 26;
33870134Scg
339137500Sjulian	while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) {
340137500Sjulian		if (--t == 0)
341137500Sjulian			return EBUSY;
342137500Sjulian		DELAY(2);	/* 20.8us / 13 */
343137500Sjulian	}
344137500Sjulian	return 0;
34570134Scg}
34670134Scg
347137500Sjulian
348166279Sariffstatic int
349137500Sjulianagg_rdcodec(struct agg_info *ess, int regno)
35070134Scg{
351137500Sjulian	int ret;
35265543Scg
35365543Scg	/* We have to wait for a SAFE time to write addr/data */
354137500Sjulian	if (agg_codec_wait4idle(ess)) {
355137500Sjulian		/* Timed out. No read performed. */
356137500Sjulian		device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n");
357137500Sjulian		return -1;
35865543Scg	}
35965543Scg
360137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1);
361137500Sjulian	/*DELAY(21);	* AC97 cycle = 20.8usec */
36265543Scg
36365543Scg	/* Wait for data retrieve */
364137500Sjulian	if (!agg_codec_wait4idle(ess)) {
365137500Sjulian		ret = AGG_RD(ess, PORT_CODEC_REG, 2);
366137500Sjulian	} else {
367137500Sjulian		/* Timed out. No read performed. */
368137500Sjulian		device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n");
369137500Sjulian		ret = -1;
37065543Scg	}
37165543Scg
372137500Sjulian	return ret;
37365543Scg}
37465543Scg
375166279Sariffstatic int
376137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data)
37765543Scg{
37865543Scg	/* We have to wait for a SAFE time to write addr/data */
379137500Sjulian	if (agg_codec_wait4idle(ess)) {
38065543Scg		/* Timed out. Abort writing. */
38165543Scg		device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n");
38270134Scg		return -1;
38365543Scg	}
38465543Scg
385137500Sjulian	AGG_WR(ess, PORT_CODEC_REG, data, 2);
386137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1);
38770134Scg
388137500Sjulian	/* Wait for write completion */
389137500Sjulian	if (agg_codec_wait4idle(ess)) {
390137500Sjulian		/* Timed out. */
391137500Sjulian		device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n");
392137500Sjulian		return -1;
393137500Sjulian	}
394137500Sjulian
39570134Scg	return 0;
39665543Scg}
39765543Scg
398166279Sariffstatic void
39965543Scgringbus_setdest(struct agg_info *ess, int src, int dest)
40065543Scg{
40165543Scg	u_int32_t	data;
40265543Scg
403137500Sjulian	data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4);
40465543Scg	data &= ~(0xfU << src);
40565543Scg	data |= (0xfU & dest) << src;
406137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4);
40765543Scg}
40865543Scg
409137500Sjulian/* -------------------------------------------------------------------- */
410137500Sjulian
41165543Scg/* Wave Processor */
41265543Scg
413166279Sariffstatic u_int16_t
41465543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg)
41565543Scg{
416137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
417137500Sjulian	return AGG_RD(ess, PORT_DSP_DATA, 2);
41865543Scg}
41965543Scg
420166279Sariffstatic void
42165543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
42265543Scg{
423137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
424137500Sjulian	AGG_WR(ess, PORT_DSP_DATA, data, 2);
42565543Scg}
42665543Scg
427166279Sariffstatic int
428137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data)
42965543Scg{
430137500Sjulian	unsigned t = 0;
43165543Scg
432137500Sjulian	while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) {
433137500Sjulian		if (++t == 1000) {
434137500Sjulian			return EAGAIN;
435137500Sjulian		}
436137500Sjulian		AGG_WR(ess, PORT_DSP_DATA, data, 2);
43765543Scg	}
438137500Sjulian
439137500Sjulian	return 0;
44065543Scg}
44165543Scg
442166279Sariffstatic u_int16_t
443137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg)
44465543Scg{
445137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
446137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) != 0)
447137500Sjulian		device_printf(ess->dev, "wp_rdapu() indexing timed out.\n");
448137500Sjulian	return wp_rdreg(ess, WPREG_DATA_PORT);
44965543Scg}
45065543Scg
451166279Sariffstatic void
452137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data)
45365543Scg{
454137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
455137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) == 0) {
456137500Sjulian		wp_wrreg(ess, WPREG_DATA_PORT, data);
457137500Sjulian		if (wp_wait_data(ess, data) != 0)
458166279Sariff			device_printf(ess->dev,
459166279Sariff			    "wp_wrapu() write timed out.\n");
460137500Sjulian	} else {
461137500Sjulian		device_printf(ess->dev, "wp_wrapu() indexing timed out.\n");
46265543Scg	}
46365543Scg}
46465543Scg
465137573Srustatic void
466137500Sjulianapu_setparam(struct agg_info *ess, int apuch,
467137500Sjulian    u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv)
46865543Scg{
469137500Sjulian	wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK);
470137500Sjulian	wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa);
471137500Sjulian	wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size);
472137500Sjulian	wp_wrapu(ess, apuch, APUREG_LOOPLEN, size);
473137500Sjulian	wp_wrapu(ess, apuch, APUREG_ROUTING, 0);
474137500Sjulian	wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000);
475137500Sjulian	wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00
476137500Sjulian	    | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT))
477137500Sjulian	    | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT)));
478137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE,
479137500Sjulian	    APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT));
480137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8);
481137500Sjulian}
48265543Scg
483166279Sariffstatic void
484137500Sjulianwp_settimer(struct agg_info *ess, u_int divide)
485137500Sjulian{
486137500Sjulian	u_int prescale = 0;
48765543Scg
488137500Sjulian	RANGE(divide, 2, 32 << 7);
489137500Sjulian
490137500Sjulian	for (; divide > 32; divide >>= 1) {
49165543Scg		prescale++;
492137500Sjulian		divide++;
493137500Sjulian	}
49465543Scg
49565543Scg	for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1)
49665543Scg		prescale++;
49765543Scg
49865543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 0);
499137500Sjulian	wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 |
50065543Scg	    (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1));
50165543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 1);
50265543Scg}
50365543Scg
504166279Sariffstatic void
50565543Scgwp_starttimer(struct agg_info *ess)
50665543Scg{
507137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
508137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED
509137500Sjulian	       | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
51065543Scg	wp_wrreg(ess, WPREG_TIMER_START, 1);
51165543Scg}
51265543Scg
513166279Sariffstatic void
51465543Scgwp_stoptimer(struct agg_info *ess)
51565543Scg{
516137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED
517137500Sjulian	       & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
518137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
51965543Scg	wp_wrreg(ess, WPREG_TIMER_START, 0);
52065543Scg}
52165543Scg
522137500Sjulian/* -------------------------------------------------------------------- */
523137500Sjulian
52465543Scg/* WaveCache */
52565543Scg
526166279Sariff#if 0
527166279Sariffstatic u_int16_t
52865543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg)
52965543Scg{
530137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
531137500Sjulian	return AGG_RD(ess, PORT_WAVCACHE_DATA, 2);
53265543Scg}
533166279Sariff#endif
53465543Scg
535166279Sariffstatic void
53665543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
53765543Scg{
538137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
539137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2);
54065543Scg}
54165543Scg
542166279Sariff#if 0
543166279Sariffstatic u_int16_t
54465543Scgwc_rdchctl(struct agg_info *ess, int ch)
54565543Scg{
54665543Scg	return wc_rdreg(ess, ch << 3);
54765543Scg}
548166279Sariff#endif
54965543Scg
550166279Sariffstatic void
55165543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data)
55265543Scg{
55365543Scg	wc_wrreg(ess, ch << 3, data);
55465543Scg}
55565543Scg
556137500Sjulian/* -------------------------------------------------------------------- */
557137500Sjulian
55865543Scg/* Power management */
559166279Sariffstatic void
560137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st)
56165543Scg{
562137500Sjulian	u_int32_t data;
56365543Scg
564137500Sjulian	data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4);
565137500Sjulian	if (part < 16) {
566137500Sjulian		if (st == PCI_POWERSTATE_D1)
567137500Sjulian			data &= ~(1 << part);
568137500Sjulian		else
569137500Sjulian			data |= (1 << part);
570137500Sjulian		if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2)
571137500Sjulian			data |= (0x10000 << part);
572137500Sjulian		else
573137500Sjulian			data &= ~(0x10000 << part);
574137500Sjulian		pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4);
575137500Sjulian	}
57665543Scg}
57765543Scg
57865543Scg
57965543Scg/* -----------------------------
58065543Scg * Controller.
58165543Scg */
58265543Scg
583166279Sariffstatic void
58465543Scgagg_initcodec(struct agg_info* ess)
58565543Scg{
58665543Scg	u_int16_t data;
58765543Scg
588137500Sjulian	if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) {
589137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
59065543Scg		DELAY(104);	/* 20.8us * (4 + 1) */
59165543Scg	}
59265543Scg	/* XXX - 2nd codec should be looked at. */
593137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4);
59465543Scg	DELAY(2);
595137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
596137500Sjulian	DELAY(50);
59765543Scg
598137500Sjulian	if (agg_rdcodec(ess, 0) < 0) {
599137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
60065543Scg		DELAY(21);
60165543Scg
60265543Scg		/* Try cold reset. */
60365543Scg		device_printf(ess->dev, "will perform cold reset.\n");
604137500Sjulian		data = AGG_RD(ess, PORT_GPIO_DIR, 2);
60565543Scg		if (pci_read_config(ess->dev, 0x58, 2) & 1)
60665543Scg			data |= 0x10;
607137500Sjulian		data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2);
608137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2);
609137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2);
610137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2);
61165543Scg		DELAY(2);
612137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2);
61365543Scg		DELAY(1);
614137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2);
615137500Sjulian		agg_sleep(ess, "agginicd", 500);
616137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data, 2);
61765543Scg		DELAY(84);	/* 20.8us * 4 */
618137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
619137500Sjulian		DELAY(50);
62065543Scg	}
62165543Scg}
62265543Scg
62365543Scgstatic void
62465543Scgagg_init(struct agg_info* ess)
62565543Scg{
62665543Scg	u_int32_t data;
62765543Scg
62865543Scg	/* Setup PCI config registers. */
62965543Scg
63065543Scg	/* Disable all legacy emulations. */
63165543Scg	data = pci_read_config(ess->dev, CONF_LEGACY, 2);
63265543Scg	data |= LEGACY_DISABLED;
63365543Scg	pci_write_config(ess->dev, CONF_LEGACY, data, 2);
63465543Scg
63565543Scg	/* Disconnect from CHI. (Makes Dell inspiron 7500 work?)
63665543Scg	 * Enable posted write.
63765543Scg	 * Prefer PCI timing rather than that of ISA.
63865543Scg	 * Don't swap L/R. */
63965543Scg	data = pci_read_config(ess->dev, CONF_MAESTRO, 4);
640137500Sjulian	data |= MAESTRO_PMC;
64165543Scg	data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING;
64265543Scg	data &= ~MAESTRO_SWAP_LR;
64365543Scg	pci_write_config(ess->dev, CONF_MAESTRO, data, 4);
64465543Scg
645137500Sjulian	/* Turn off unused parts if necessary. */
646137500Sjulian	/* consult CONF_MAESTRO. */
647137500Sjulian	if (data & MAESTRO_SPDIF)
648137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D2);
649137500Sjulian	else
650137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D1);
651137500Sjulian	if (data & MAESTRO_HWVOL)
652137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D3);
653137500Sjulian	else
654137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D1);
655137500Sjulian
656137500Sjulian	/* parts that never be used */
657137500Sjulian	agg_stopclock(ess, ACPI_PART_978,	PCI_POWERSTATE_D1);
658137500Sjulian	agg_stopclock(ess, ACPI_PART_DAA,	PCI_POWERSTATE_D1);
659137500Sjulian	agg_stopclock(ess, ACPI_PART_GPIO,	PCI_POWERSTATE_D1);
660137500Sjulian	agg_stopclock(ess, ACPI_PART_SB,	PCI_POWERSTATE_D1);
661137500Sjulian	agg_stopclock(ess, ACPI_PART_FM,	PCI_POWERSTATE_D1);
662137500Sjulian	agg_stopclock(ess, ACPI_PART_MIDI,	PCI_POWERSTATE_D1);
663137500Sjulian	agg_stopclock(ess, ACPI_PART_GAME_PORT,	PCI_POWERSTATE_D1);
664137500Sjulian
665137500Sjulian	/* parts that will be used only when play/recording */
666137500Sjulian	agg_stopclock(ess, ACPI_PART_WP,	PCI_POWERSTATE_D2);
667137500Sjulian
668137500Sjulian	/* parts that should always be turned on */
669137500Sjulian	agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3);
670137500Sjulian	agg_stopclock(ess, ACPI_PART_GLUE,	PCI_POWERSTATE_D3);
671137500Sjulian	agg_stopclock(ess, ACPI_PART_PCI_IF,	PCI_POWERSTATE_D3);
672137500Sjulian	agg_stopclock(ess, ACPI_PART_RINGBUS,	PCI_POWERSTATE_D3);
673137500Sjulian
67465543Scg	/* Reset direct sound. */
675137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2);
676137500Sjulian	DELAY(100);
677137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
678137500Sjulian	DELAY(100);
679137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2);
680137500Sjulian	DELAY(100);
681137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
682137500Sjulian	DELAY(100);
68365543Scg
684137500Sjulian	/* Enable hardware volume control interruption. */
685137500Sjulian	if (data & MAESTRO_HWVOL)	/* XXX - why not use device flags? */
686137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2);
68765543Scg
68865543Scg	/* Setup Wave Processor. */
68965543Scg
69065543Scg	/* Enable WaveCache, set DMA base address. */
69165543Scg	wp_wrreg(ess, WPREG_WAVE_ROMRAM,
69265543Scg	    WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED);
693137500Sjulian	wp_wrreg(ess, WPREG_CRAM_DATA, 0);
69465543Scg
695137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_CTRL,
696137500Sjulian	       WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2);
697137500Sjulian
69865543Scg	for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++)
699137500Sjulian		wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT);
70065543Scg
70165543Scg	/* Setup Codec/Ringbus. */
70265543Scg	agg_initcodec(ess);
703137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL,
704137500Sjulian	       RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4);
70565543Scg
706137500Sjulian	wp_wrreg(ess, 0x08, 0xB004);
707137500Sjulian	wp_wrreg(ess, 0x09, 0x001B);
708137500Sjulian	wp_wrreg(ess, 0x0A, 0x8000);
709137500Sjulian	wp_wrreg(ess, 0x0B, 0x3F37);
710137500Sjulian	wp_wrreg(ess, WPREG_BASE, 0x8598);	/* Parallel I/O */
711137500Sjulian	wp_wrreg(ess, WPREG_BASE + 1, 0x7632);
71265543Scg	ringbus_setdest(ess, RINGBUS_SRC_ADC,
71365543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN);
71465543Scg	ringbus_setdest(ess, RINGBUS_SRC_DSOUND,
71565543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC);
71665543Scg
717137500Sjulian	/* Enable S/PDIF if necessary. */
718137500Sjulian	if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF)
719137500Sjulian		/* XXX - why not use device flags? */
720137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF |
721137500Sjulian		       AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1);
722137500Sjulian
72365543Scg	/* Setup ASSP. Needed for Dell Inspiron 7500? */
724137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1);
725137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1);
726137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1);
72765543Scg
72865543Scg	/*
72965543Scg	 * Setup GPIO.
73065543Scg	 * There seems to be speciality with NEC systems.
73165543Scg	 */
73265543Scg	switch (pci_get_subvendor(ess->dev)
73365543Scg	    | (pci_get_subdevice(ess->dev) << 16)) {
73465543Scg	case NEC_SUBID1:
73565543Scg	case NEC_SUBID2:
73665543Scg		/* Matthew Braithwaite <matt@braithwaite.net> reported that
73765543Scg		 * NEC Versa LX doesn't need GPIO operation. */
738137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2);
739137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR,
740137500Sjulian		       AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2);
741137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2);
74265543Scg		break;
74365543Scg	}
74465543Scg}
74565543Scg
746137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */
747137500Sjulianstatic void
748137500Sjulianagg_power(struct agg_info *ess, int status)
749137500Sjulian{
750137500Sjulian	u_int8_t lastpwr;
751137500Sjulian
752137500Sjulian	lastpwr = ess->curpwr;
753137500Sjulian	if (lastpwr == status)
754137500Sjulian		return;
755137500Sjulian
756137500Sjulian	switch (status) {
757137500Sjulian	case PCI_POWERSTATE_D0:
758137500Sjulian	case PCI_POWERSTATE_D1:
759137500Sjulian		switch (lastpwr) {
760137500Sjulian		case PCI_POWERSTATE_D2:
761137500Sjulian			pci_set_powerstate(ess->dev, status);
762137500Sjulian			/* Turn on PCM-related parts. */
763137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0);
764137500Sjulian			DELAY(100);
765137500Sjulian#if 0
766137500Sjulian			if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3)
767166279Sariff				device_printf(ess->dev,
768166279Sariff				    "warning: codec not ready.\n");
769137500Sjulian#endif
770137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
771137500Sjulian			       (AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
772137500Sjulian				& ~RINGBUS_CTRL_ACLINK_ENABLED)
773137500Sjulian			       | RINGBUS_CTRL_RINGBUS_ENABLED, 4);
774137500Sjulian			DELAY(50);
775137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
776137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
777137500Sjulian			       | RINGBUS_CTRL_ACLINK_ENABLED, 4);
778137500Sjulian			break;
779137500Sjulian		case PCI_POWERSTATE_D3:
780137500Sjulian			/* Initialize. */
781137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
782137500Sjulian			DELAY(100);
783137500Sjulian			agg_init(ess);
784137500Sjulian			/* FALLTHROUGH */
785137500Sjulian		case PCI_POWERSTATE_D0:
786137500Sjulian		case PCI_POWERSTATE_D1:
787137500Sjulian			pci_set_powerstate(ess->dev, status);
788137500Sjulian			break;
789137500Sjulian		}
790137500Sjulian		break;
791137500Sjulian	case PCI_POWERSTATE_D2:
792137500Sjulian		switch (lastpwr) {
793137500Sjulian		case PCI_POWERSTATE_D3:
794137500Sjulian			/* Initialize. */
795137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
796137500Sjulian			DELAY(100);
797137500Sjulian			agg_init(ess);
798137500Sjulian			/* FALLTHROUGH */
799137500Sjulian		case PCI_POWERSTATE_D0:
800137500Sjulian		case PCI_POWERSTATE_D1:
801137500Sjulian			/* Turn off PCM-related parts. */
802137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
803137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
804137500Sjulian			       & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4);
805137500Sjulian			DELAY(100);
806137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0x300);
807137500Sjulian			DELAY(100);
808137500Sjulian			break;
809137500Sjulian		}
810137500Sjulian		pci_set_powerstate(ess->dev, status);
811137500Sjulian		break;
812137500Sjulian	case PCI_POWERSTATE_D3:
813137500Sjulian		/* Entirely power down. */
814137500Sjulian		agg_wrcodec(ess, AC97_REG_POWER, 0xdf00);
815137500Sjulian		DELAY(100);
816137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
817137500Sjulian		/*DELAY(1);*/
818137500Sjulian		if (lastpwr != PCI_POWERSTATE_D2)
819137500Sjulian			wp_stoptimer(ess);
820137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
821137500Sjulian		AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1);
822137500Sjulian		pci_set_powerstate(ess->dev, status);
823137500Sjulian		break;
824137500Sjulian	default:
825137500Sjulian		/* Invalid power state; let it ignored. */
826137500Sjulian		status = lastpwr;
827137500Sjulian		break;
828137500Sjulian	}
829137500Sjulian
830137500Sjulian	ess->curpwr = status;
831137500Sjulian}
832137500Sjulian
833137500Sjulian/* -------------------------------------------------------------------- */
834137500Sjulian
83565543Scg/* Channel controller. */
83665543Scg
83765543Scgstatic void
83865543Scgaggch_start_dac(struct agg_chinfo *ch)
83965543Scg{
840137500Sjulian	bus_addr_t	wpwa;
841137500Sjulian	u_int32_t	speed;
842137500Sjulian	u_int16_t	size, apuch, wtbar, wcreg, aputype;
843137500Sjulian	u_int		dv;
844137500Sjulian	int		pan;
84565543Scg
846137500Sjulian	speed = ch->speed;
847137500Sjulian	wpwa = (ch->phys - ch->base) >> 1;
848137500Sjulian	wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2));
849137500Sjulian	wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
850137500Sjulian	size  = ch->buflen;
851137500Sjulian	apuch = (ch->num << 1) | 32;
852137500Sjulian	pan = PAN_RIGHT - PAN_FRONT;
853137500Sjulian
854137500Sjulian	if (ch->stereo) {
855137500Sjulian		wcreg |= WAVCACHE_CHCTL_STEREO;
856137500Sjulian		if (ch->qs16) {
857137500Sjulian			aputype = APUTYPE_16BITSTEREO;
858137500Sjulian			wpwa >>= 1;
859137500Sjulian			size >>= 1;
860137500Sjulian			pan = -pan;
861137500Sjulian		} else
862137500Sjulian			aputype = APUTYPE_8BITSTEREO;
863137500Sjulian	} else {
864137500Sjulian		pan = 0;
865137500Sjulian		if (ch->qs16)
866137500Sjulian			aputype = APUTYPE_16BITLINEAR;
867137500Sjulian		else {
868137500Sjulian			aputype = APUTYPE_8BITLINEAR;
869137500Sjulian			speed >>= 1;
870137500Sjulian		}
87165543Scg	}
872137500Sjulian	if (ch->us)
873137500Sjulian		wcreg |= WAVCACHE_CHCTL_U8;
87465543Scg
875137500Sjulian	if (wtbar > 8)
876137500Sjulian		wtbar = (wtbar >> 1) + 4;
877137500Sjulian
87865543Scg	dv = (((speed % 48000) << 16) + 24000) / 48000
87965543Scg	    + ((speed / 48000) << 16);
88065543Scg
881137500Sjulian	agg_lock(ch->parent);
882137500Sjulian	agg_power(ch->parent, powerstate_active);
88365543Scg
884137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar,
885137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
886137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1,
887137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
888137500Sjulian	if (wtbar < 8) {
889137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2,
890137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
891137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3,
892137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
893137500Sjulian	}
894137500Sjulian	wc_wrchctl(ch->parent, apuch, wcreg);
895137500Sjulian	wc_wrchctl(ch->parent, apuch + 1, wcreg);
89665543Scg
897137500Sjulian	apu_setparam(ch->parent, apuch, wpwa, size, pan, dv);
898137500Sjulian	if (ch->stereo) {
899137500Sjulian		if (ch->qs16)
900137500Sjulian			wpwa |= (WPWA_STEREO >> 1);
901137500Sjulian		apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv);
90265543Scg
903137500Sjulian		critical_enter();
904137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
905137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
90665543Scg		wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE,
907137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
908137500Sjulian		critical_exit();
909137500Sjulian	} else {
910137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
911137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
912137500Sjulian	}
913137500Sjulian
914137500Sjulian	/* to mark that this channel is ready for intr. */
915137500Sjulian	ch->parent->active |= (1 << ch->num);
916137500Sjulian
917137500Sjulian	set_timer(ch->parent);
918137500Sjulian	wp_starttimer(ch->parent);
919137500Sjulian	agg_unlock(ch->parent);
92065543Scg}
92165543Scg
92265543Scgstatic void
92365543Scgaggch_stop_dac(struct agg_chinfo *ch)
92465543Scg{
925137500Sjulian	agg_lock(ch->parent);
926137500Sjulian
927137500Sjulian	/* to mark that this channel no longer needs further intrs. */
928137500Sjulian	ch->parent->active &= ~(1 << ch->num);
929137500Sjulian
930137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE,
93165543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
932137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE,
93365543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
934137500Sjulian
935137500Sjulian	if (ch->parent->active) {
936137500Sjulian		set_timer(ch->parent);
937137500Sjulian		wp_starttimer(ch->parent);
938137500Sjulian	} else {
939137500Sjulian		wp_stoptimer(ch->parent);
940137500Sjulian		agg_power(ch->parent, powerstate_idle);
941137500Sjulian	}
942137500Sjulian	agg_unlock(ch->parent);
94365543Scg}
94465543Scg
945137500Sjulianstatic void
946137500Sjulianaggch_start_adc(struct agg_rchinfo *ch)
947137500Sjulian{
948137500Sjulian	bus_addr_t	wpwa, wpwa2;
949137500Sjulian	u_int16_t	wcreg, wcreg2;
950137500Sjulian	u_int	dv;
951137500Sjulian	int	pan;
952137500Sjulian
953137500Sjulian	/* speed > 48000 not cared */
954137500Sjulian	dv = ((ch->speed << 16) + 24000) / 48000;
955137500Sjulian
956137500Sjulian	/* RATECONV doesn't seem to like dv == 0x10000. */
957137500Sjulian	if (dv == 0x10000)
958137500Sjulian		dv--;
959137500Sjulian
960137500Sjulian	if (ch->stereo) {
961137500Sjulian		wpwa = (ch->srcphys - ch->base) >> 1;
962137500Sjulian		wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1;
963137500Sjulian		wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
964137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
965137500Sjulian		pan = PAN_LEFT - PAN_FRONT;
966137500Sjulian	} else {
967137500Sjulian		wpwa = (ch->phys - ch->base) >> 1;
968137500Sjulian		wpwa2 = (ch->srcphys - ch->base) >> 1;
969137500Sjulian		wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
970137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
971137500Sjulian		pan = 0;
972137500Sjulian	}
973137500Sjulian
974137500Sjulian	agg_lock(ch->parent);
975137500Sjulian
976137500Sjulian	ch->hwptr = 0;
977137500Sjulian	agg_power(ch->parent, powerstate_active);
978137500Sjulian
979137500Sjulian	/* Invalidate WaveCache. */
980137500Sjulian	wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO);
981137500Sjulian	wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO);
982137500Sjulian	wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO);
983137500Sjulian	wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO);
984137500Sjulian
985137500Sjulian	/* Load APU registers. */
986137500Sjulian	/* APU #0 : Sample rate converter for left/center. */
987137500Sjulian	apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa,
988137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
989137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0);
990137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT);
991137500Sjulian
992137500Sjulian	/* APU #1 : Sample rate converter for right. */
993137500Sjulian	apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2,
994137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
995137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0);
996137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT);
997137500Sjulian
998137500Sjulian	/* APU #2 : Input mixer for left. */
999137500Sjulian	apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0,
1000137500Sjulian		     ch->parent->bufsz >> 2, pan, 0x10000);
1001137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0);
1002137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0);
1003137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT);
1004137500Sjulian
1005137500Sjulian	/* APU #3 : Input mixer for right. */
1006137500Sjulian	apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2),
1007137500Sjulian		     ch->parent->bufsz >> 2, -pan, 0x10000);
1008137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0);
1009137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0);
1010137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT);
1011137500Sjulian
1012137500Sjulian	/* to mark this channel ready for intr. */
1013137500Sjulian	ch->parent->active |= (1 << ch->parent->playchns);
1014137500Sjulian
1015137500Sjulian	/* start adc */
1016137500Sjulian	critical_enter();
1017137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_APUTYPE,
1018137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1019137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_APUTYPE,
1020137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1021137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_APUTYPE,
1022137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1023137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_APUTYPE,
1024137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1025137500Sjulian	critical_exit();
1026137500Sjulian
1027137500Sjulian	set_timer(ch->parent);
1028137500Sjulian	wp_starttimer(ch->parent);
1029137500Sjulian	agg_unlock(ch->parent);
1030137500Sjulian}
1031137500Sjulian
1032137500Sjulianstatic void
1033137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch)
1034137500Sjulian{
1035137500Sjulian	int apuch;
1036137500Sjulian
1037137500Sjulian	agg_lock(ch->parent);
1038137500Sjulian
1039137500Sjulian	/* to mark that this channel no longer needs further intrs. */
1040137500Sjulian	ch->parent->active &= ~(1 << ch->parent->playchns);
1041137500Sjulian
1042137500Sjulian	for (apuch = 0; apuch < 4; apuch++)
1043137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
1044137500Sjulian		    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
1045137500Sjulian
1046137500Sjulian	if (ch->parent->active) {
1047137500Sjulian		set_timer(ch->parent);
1048137500Sjulian		wp_starttimer(ch->parent);
1049137500Sjulian	} else {
1050137500Sjulian		wp_stoptimer(ch->parent);
1051137500Sjulian		agg_power(ch->parent, powerstate_idle);
1052137500Sjulian	}
1053137500Sjulian	agg_unlock(ch->parent);
1054137500Sjulian}
1055137500Sjulian
105665543Scg/*
1057137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving.
1058137500Sjulian * This function expects n not overwrapping the buffer boundary.
1059137500Sjulian * Note that n is measured in sample unit.
1060137500Sjulian *
1061137500Sjulian * XXX - this function works in 16bit stereo format only.
1062137500Sjulian */
1063166279Sariffstatic void
1064137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n)
1065137500Sjulian{
1066137500Sjulian	int16_t *end;
1067137500Sjulian
1068137500Sjulian	for (end = l + n; l < end; ) {
1069137500Sjulian		*p++ = *l++;
1070137500Sjulian		*p++ = *r++;
1071137500Sjulian	}
1072137500Sjulian}
1073137500Sjulian
1074137500Sjulianstatic void
1075137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch)
1076137500Sjulian{
1077137500Sjulian	unsigned cur, last;
1078137500Sjulian	int16_t *src2;
1079137500Sjulian
1080137500Sjulian	agg_lock(ch->parent);
1081137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1082137500Sjulian	agg_unlock(ch->parent);
1083137500Sjulian	cur -= 0xffff & ((ch->srcphys - ch->base) >> 1);
1084137500Sjulian	last = ch->hwptr;
1085137500Sjulian	src2 = ch->src + ch->parent->bufsz/4;
1086137500Sjulian
1087137500Sjulian	if (cur < last) {
1088137500Sjulian		interleave(ch->src + last, src2 + last,
1089137500Sjulian			   ch->sink + 2*last, ch->buflen/2 - last);
1090137500Sjulian		interleave(ch->src, src2,
1091137500Sjulian			   ch->sink, cur);
1092137500Sjulian	} else if (cur > last)
1093137500Sjulian		interleave(ch->src + last, src2 + last,
1094137500Sjulian			   ch->sink + 2*last, cur - last);
1095137500Sjulian	ch->hwptr = cur;
1096137500Sjulian}
1097137500Sjulian
1098137500Sjulian/*
1099137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center.
1100137500Sjulian * This function expects n not overwrapping the buffer boundary.
1101137500Sjulian * Note that n is measured in sample unit.
1102137500Sjulian *
1103137500Sjulian * XXX - this function works in 16bit monoral format only.
1104137500Sjulian */
1105166279Sariffstatic void
1106137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n)
1107137500Sjulian{
1108137500Sjulian	int16_t *end;
1109137500Sjulian
1110137500Sjulian	for (end = dest + n; dest < end; dest++)
1111137500Sjulian		*dest = (int16_t)(((int)*dest - (int)*src++) / 2);
1112137500Sjulian}
1113137500Sjulian
1114137500Sjulianstatic void
1115137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch)
1116137500Sjulian{
1117137500Sjulian	unsigned cur, last;
1118137500Sjulian
1119137500Sjulian	agg_lock(ch->parent);
1120137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1121137500Sjulian	agg_unlock(ch->parent);
1122137500Sjulian	cur -= 0xffff & ((ch->phys - ch->base) >> 1);
1123137500Sjulian	last = ch->hwptr;
1124137500Sjulian
1125137500Sjulian	if (cur < last) {
1126137500Sjulian		mixdown(ch->src + last, ch->sink + last, ch->buflen - last);
1127137500Sjulian		mixdown(ch->src, ch->sink, cur);
1128137500Sjulian	} else if (cur > last)
1129137500Sjulian		mixdown(ch->src + last, ch->sink + last, cur - last);
1130137500Sjulian	ch->hwptr = cur;
1131137500Sjulian}
1132137500Sjulian
1133166279Sariff#ifdef AGG_JITTER_CORRECTION
1134137500Sjulian/*
113565543Scg * Stereo jitter suppressor.
113665543Scg * Sometimes playback pointers differ in stereo-paired channels.
113765543Scg * Calling this routine within intr fixes the problem.
113865543Scg */
1139166279Sariffstatic void
114065543Scgsuppress_jitter(struct agg_chinfo *ch)
114165543Scg{
1142137500Sjulian	if (ch->stereo) {
1143137500Sjulian		int cp1, cp2, diff /*, halfsize*/ ;
114465543Scg
1145137500Sjulian		/*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/
1146137500Sjulian		cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1147137500Sjulian		cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR);
1148137500Sjulian		if (cp1 != cp2) {
1149137500Sjulian			diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1150137500Sjulian			if (diff > 1 /* && diff < halfsize*/ )
1151137500Sjulian				AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1152137500Sjulian		}
115365543Scg	}
115465543Scg}
115565543Scg
1156166279Sariffstatic void
1157137500Sjuliansuppress_rec_jitter(struct agg_rchinfo *ch)
1158137500Sjulian{
1159137500Sjulian	int cp1, cp2, diff /*, halfsize*/ ;
1160137500Sjulian
1161137500Sjulian	/*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/
1162137500Sjulian	cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1)
1163137500Sjulian		+ wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1164137500Sjulian	cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR);
1165137500Sjulian	if (cp1 != cp2) {
1166137500Sjulian		diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1167137500Sjulian		if (diff > 1 /* && diff < halfsize*/ )
1168137500Sjulian			AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1169137500Sjulian	}
1170137500Sjulian}
1171166279Sariff#endif
1172137500Sjulian
1173166279Sariffstatic u_int
1174137500Sjuliancalc_timer_div(struct agg_chinfo *ch)
117565543Scg{
1176137500Sjulian	u_int speed;
117765543Scg
1178137500Sjulian	speed = ch->speed;
1179137500Sjulian#ifdef INVARIANTS
1180137500Sjulian	if (speed == 0) {
1181137500Sjulian		printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n",
1182137500Sjulian		       ch->num);
1183137500Sjulian		speed = 1;
1184137500Sjulian	}
1185137500Sjulian#endif
1186137500Sjulian	return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo))
1187137500Sjulian		+ speed - 1) / speed;
1188137500Sjulian}
118965543Scg
1190166279Sariffstatic u_int
1191137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch)
1192137500Sjulian{
1193137500Sjulian	u_int speed;
1194137500Sjulian
1195137500Sjulian	speed = ch->speed;
1196137500Sjulian#ifdef INVARIANTS
1197137500Sjulian	if (speed == 0) {
1198137500Sjulian		printf("snd_maestro: rch.speed == 0, which shouldn't\n");
1199137500Sjulian		speed = 1;
1200137500Sjulian	}
1201137500Sjulian#endif
1202137500Sjulian	return (48000 * (ch->blklen << (!ch->stereo))
1203137500Sjulian		+ speed - 1) / speed;
120465543Scg}
120565543Scg
120665543Scgstatic void
120765543Scgset_timer(struct agg_info *ess)
120865543Scg{
120965543Scg	int i;
1210137500Sjulian	u_int	dv = 32 << 7, newdv;
121165543Scg
121265543Scg	for (i = 0; i < ess->playchns; i++)
121365543Scg		if ((ess->active & (1 << i)) &&
1214137500Sjulian		    (dv > (newdv = calc_timer_div(ess->pch + i))))
1215137500Sjulian			dv = newdv;
1216137500Sjulian	if ((ess->active & (1 << i)) &&
1217137500Sjulian	    (dv > (newdv = calc_timer_div_rch(&ess->rch))))
1218137500Sjulian		dv = newdv;
121965543Scg
1220137500Sjulian	wp_settimer(ess, dv);
122165543Scg}
122265543Scg
122365543Scg
122465543Scg/* -----------------------------
122565543Scg * Newpcm glue.
122665543Scg */
122765543Scg
1228137500Sjulian/* AC97 mixer interface. */
1229137500Sjulian
1230137500Sjulianstatic u_int32_t
1231137500Sjulianagg_ac97_init(kobj_t obj, void *sc)
1232137500Sjulian{
1233137500Sjulian	struct agg_info *ess = sc;
1234137500Sjulian
1235137500Sjulian	return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1;
1236137500Sjulian}
1237137500Sjulian
1238137500Sjulianstatic int
1239137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno)
1240137500Sjulian{
1241137500Sjulian	struct agg_info *ess = sc;
1242137500Sjulian	int ret;
1243137500Sjulian
1244154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1245137500Sjulian	ret = agg_rdcodec(ess, regno);
1246154067Sariff	/* agg_unlock(ess); */
1247137500Sjulian	return ret;
1248137500Sjulian}
1249137500Sjulian
1250137500Sjulianstatic int
1251137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data)
1252137500Sjulian{
1253137500Sjulian	struct agg_info *ess = sc;
1254137500Sjulian	int ret;
1255137500Sjulian
1256154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1257137500Sjulian	ret = agg_wrcodec(ess, regno, data);
1258154067Sariff	/* agg_unlock(ess); */
1259137500Sjulian	return ret;
1260137500Sjulian}
1261137500Sjulian
1262137500Sjulian
1263137500Sjulianstatic kobj_method_t agg_ac97_methods[] = {
1264137500Sjulian    	KOBJMETHOD(ac97_init,		agg_ac97_init),
1265137500Sjulian    	KOBJMETHOD(ac97_read,		agg_ac97_read),
1266137500Sjulian    	KOBJMETHOD(ac97_write,		agg_ac97_write),
1267193640Sariff	KOBJMETHOD_END
1268137500Sjulian};
1269137500SjulianAC97_DECLARE(agg_ac97);
1270137500Sjulian
1271137500Sjulian
1272137500Sjulian/* -------------------------------------------------------------------- */
1273137500Sjulian
1274137500Sjulian/* Playback channel. */
1275137500Sjulian
127665543Scgstatic void *
1277166279Sariffaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1278166279Sariff						struct pcm_channel *c, int dir)
127965543Scg{
128065543Scg	struct agg_info *ess = devinfo;
128165543Scg	struct agg_chinfo *ch;
128265543Scg	bus_addr_t physaddr;
128370291Scg	void *p;
128465543Scg
1285137500Sjulian	KASSERT((dir == PCMDIR_PLAY),
1286137500Sjulian	    ("aggpch_init() called for RECORDING channel!"));
1287137500Sjulian	ch = ess->pch + ess->playchns;
128865543Scg
128965543Scg	ch->parent = ess;
129065543Scg	ch->channel = c;
129165543Scg	ch->buffer = b;
129265543Scg	ch->num = ess->playchns;
129365543Scg
1294137500Sjulian	p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr);
1295137500Sjulian	if (p == NULL)
1296137500Sjulian		return NULL;
1297137500Sjulian	ch->phys = physaddr;
1298137500Sjulian	ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT);
1299137500Sjulian
130084658Scg	sndbuf_setup(b, p, ess->bufsz);
1301137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1302137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1303137500Sjulian	ess->playchns++;
130465543Scg
1305137500Sjulian	return ch;
1306137500Sjulian}
130765543Scg
1308137500Sjulianstatic void
1309137500Sjulianadjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size)
1310137500Sjulian{
1311137500Sjulian	struct agg_chinfo *pchs[AGG_MAXPLAYCH];
1312137500Sjulian	u_int i, j, k;
1313137500Sjulian	bus_addr_t base;
131465543Scg
1315137500Sjulian	/* sort pchs by phys address */
1316137500Sjulian	for (i = 0; i < n; i++) {
1317137500Sjulian		for (j = 0; j < i; j++)
1318137500Sjulian			if (chans[i].phys < pchs[j]->phys) {
1319137500Sjulian				for (k = i; k > j; k--)
1320137500Sjulian					pchs[k] = pchs[k - 1];
1321137500Sjulian				break;
1322137500Sjulian			}
1323137500Sjulian		pchs[j] = chans + i;
1324137500Sjulian	}
1325137500Sjulian
1326137500Sjulian	/* use new base register if next buffer can not be addressed
1327137500Sjulian	   via current base. */
1328137500Sjulian#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1)
1329137500Sjulian	base = pchs[0]->base;
1330137500Sjulian	for (k = 1, i = 1; i < n; i++) {
1331137500Sjulian		if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT)
1332137500Sjulian			/* not addressable: assign new base */
1333137500Sjulian			base = (pchs[i]->base -= k++ << BASE_SHIFT);
1334137500Sjulian		else
1335137500Sjulian			pchs[i]->base = base;
1336137500Sjulian	}
1337137500Sjulian#undef BASE_SHIFT
1338137500Sjulian
1339137500Sjulian	if (bootverbose) {
1340137500Sjulian		printf("Total of %d bases are assigned.\n", k);
1341137500Sjulian		for (i = 0; i < n; i++) {
1342137500Sjulian			printf("ch.%d: phys 0x%llx, wpwa 0x%llx\n",
1343137500Sjulian			       i, (long long)chans[i].phys,
1344137500Sjulian			       (long long)(chans[i].phys -
1345137500Sjulian					   chans[i].base) >> 1);
1346137500Sjulian		}
1347137500Sjulian	}
134865543Scg}
134965543Scg
135065543Scgstatic int
1351137500Sjulianaggpch_free(kobj_t obj, void *data)
135265543Scg{
1353137500Sjulian	struct agg_chinfo *ch = data;
1354137500Sjulian	struct agg_info *ess = ch->parent;
1355137500Sjulian
1356137500Sjulian	/* free up buffer - called after channel stopped */
1357137500Sjulian	dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer));
1358137500Sjulian
135965543Scg	/* return 0 if ok */
136065543Scg	return 0;
136165543Scg}
136265543Scg
136365543Scgstatic int
1364137500Sjulianaggpch_setformat(kobj_t obj, void *data, u_int32_t format)
136565543Scg{
136665543Scg	struct agg_chinfo *ch = data;
136765543Scg
1368137500Sjulian	if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE)
1369137500Sjulian		return EINVAL;
1370137500Sjulian	ch->stereo = ch->qs16 = ch->us = 0;
1371193640Sariff	if (AFMT_CHANNEL(format) > 1)
1372137500Sjulian		ch->stereo = 1;
137365543Scg
137465543Scg	if (format & AFMT_U8 || format & AFMT_S8) {
137565543Scg		if (format & AFMT_U8)
1376137500Sjulian			ch->us = 1;
1377137500Sjulian	} else
1378137500Sjulian		ch->qs16 = 1;
137989887Sscottl	return 0;
138065543Scg}
138165543Scg
1382193640Sariffstatic u_int32_t
1383137500Sjulianaggpch_setspeed(kobj_t obj, void *data, u_int32_t speed)
138465543Scg{
1385193640Sariff
1386193640Sariff	((struct agg_chinfo*)data)->speed = speed;
1387193640Sariff
1388193640Sariff	return (speed);
138965543Scg}
139065543Scg
1391193640Sariffstatic u_int32_t
1392137500Sjulianaggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
139365543Scg{
1394137500Sjulian	struct agg_chinfo *ch = data;
1395137500Sjulian	int blkcnt;
1396137500Sjulian
1397137500Sjulian	/* try to keep at least 20msec DMA space */
1398137500Sjulian	blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize);
1399137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1400137500Sjulian
1401137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1402137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1403137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1404137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1405137500Sjulian	} else {
1406137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1407137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1408137500Sjulian	}
1409137500Sjulian
1410137500Sjulian	ch->blklen = blocksize / 2;
1411137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1412137500Sjulian	return blocksize;
141365543Scg}
141465543Scg
141565543Scgstatic int
1416137500Sjulianaggpch_trigger(kobj_t obj, void *data, int go)
141765543Scg{
141865543Scg	struct agg_chinfo *ch = data;
141965543Scg
142065543Scg	switch (go) {
142165543Scg	case PCMTRIG_EMLDMAWR:
1422137500Sjulian		break;
142365543Scg	case PCMTRIG_START:
1424137500Sjulian		aggch_start_dac(ch);
142565543Scg		break;
142665543Scg	case PCMTRIG_ABORT:
142765543Scg	case PCMTRIG_STOP:
1428137500Sjulian		aggch_stop_dac(ch);
142965543Scg		break;
143065543Scg	}
143165543Scg	return 0;
143265543Scg}
143365543Scg
1434193640Sariffstatic u_int32_t
1435137500Sjulianaggpch_getptr(kobj_t obj, void *data)
143665543Scg{
143765543Scg	struct agg_chinfo *ch = data;
1438193640Sariff	u_int32_t cp;
143965543Scg
1440137500Sjulian	agg_lock(ch->parent);
1441137500Sjulian	cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1442137500Sjulian	agg_unlock(ch->parent);
144365543Scg
1444137500Sjulian	return ch->qs16 && ch->stereo
1445137500Sjulian		? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base))
1446137500Sjulian		: (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base));
144765543Scg}
144865543Scg
144974763Scgstatic struct pcmchan_caps *
1450137500Sjulianaggpch_getcaps(kobj_t obj, void *data)
145165543Scg{
145265543Scg	static u_int32_t playfmt[] = {
1453193640Sariff		SND_FORMAT(AFMT_U8, 1, 0),
1454193640Sariff		SND_FORMAT(AFMT_U8, 2, 0),
1455193640Sariff		SND_FORMAT(AFMT_S8, 1, 0),
1456193640Sariff		SND_FORMAT(AFMT_S8, 2, 0),
1457193640Sariff		SND_FORMAT(AFMT_S16_LE, 1, 0),
1458193640Sariff		SND_FORMAT(AFMT_S16_LE, 2, 0),
145965543Scg		0
146065543Scg	};
1461154241Sariff	static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0};
146265543Scg
1463137500Sjulian	return &playcaps;
1464137500Sjulian}
1465137500Sjulian
1466137500Sjulian
1467137500Sjulianstatic kobj_method_t aggpch_methods[] = {
1468137500Sjulian    	KOBJMETHOD(channel_init,		aggpch_init),
1469137500Sjulian    	KOBJMETHOD(channel_free,		aggpch_free),
1470137500Sjulian    	KOBJMETHOD(channel_setformat,		aggpch_setformat),
1471137500Sjulian    	KOBJMETHOD(channel_setspeed,		aggpch_setspeed),
1472137500Sjulian    	KOBJMETHOD(channel_setblocksize,	aggpch_setblocksize),
1473137500Sjulian    	KOBJMETHOD(channel_trigger,		aggpch_trigger),
1474137500Sjulian    	KOBJMETHOD(channel_getptr,		aggpch_getptr),
1475137500Sjulian    	KOBJMETHOD(channel_getcaps,		aggpch_getcaps),
1476193640Sariff	KOBJMETHOD_END
1477137500Sjulian};
1478137500SjulianCHANNEL_DECLARE(aggpch);
1479137500Sjulian
1480137500Sjulian
1481137500Sjulian/* -------------------------------------------------------------------- */
1482137500Sjulian
1483137500Sjulian/* Recording channel. */
1484137500Sjulian
1485137500Sjulianstatic void *
1486166279Sariffaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1487166279Sariff						struct pcm_channel *c, int dir)
1488137500Sjulian{
1489137500Sjulian	struct agg_info *ess = devinfo;
1490137500Sjulian	struct agg_rchinfo *ch;
1491137500Sjulian	u_int8_t *p;
1492137500Sjulian
1493137500Sjulian	KASSERT((dir == PCMDIR_REC),
1494137500Sjulian	    ("aggrch_init() called for PLAYBACK channel!"));
1495137500Sjulian	ch = &ess->rch;
1496137500Sjulian
1497137500Sjulian	ch->parent = ess;
1498137500Sjulian	ch->channel = c;
1499137500Sjulian	ch->buffer = b;
1500137500Sjulian
1501137500Sjulian	/* Uses the bottom-half of the status buffer. */
1502137500Sjulian	p        = ess->stat + ess->bufsz;
1503137500Sjulian	ch->phys = ess->phys + ess->bufsz;
1504137500Sjulian	ch->base = ess->phys;
1505137500Sjulian	ch->src  = (int16_t *)(p + ess->bufsz);
1506137500Sjulian	ch->srcphys = ch->phys + ess->bufsz;
1507137500Sjulian	ch->sink = (int16_t *)p;
1508137500Sjulian
1509137500Sjulian	sndbuf_setup(b, p, ess->bufsz);
1510137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1511137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1512137500Sjulian
1513137500Sjulian	return ch;
1514137500Sjulian}
1515137500Sjulian
1516137500Sjulianstatic int
1517137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format)
1518137500Sjulian{
1519137500Sjulian	struct agg_rchinfo *ch = data;
1520137500Sjulian
1521137500Sjulian	if (!(format & AFMT_S16_LE))
1522137500Sjulian		return EINVAL;
1523193640Sariff	if (AFMT_CHANNEL(format) > 1)
1524137500Sjulian		ch->stereo = 1;
1525137500Sjulian	else
1526137500Sjulian		ch->stereo = 0;
1527137500Sjulian	return 0;
1528137500Sjulian}
1529137500Sjulian
1530193640Sariffstatic u_int32_t
1531137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed)
1532137500Sjulian{
1533193640Sariff
1534193640Sariff	((struct agg_rchinfo*)data)->speed = speed;
1535193640Sariff
1536193640Sariff	return (speed);
1537137500Sjulian}
1538137500Sjulian
1539193640Sariffstatic u_int32_t
1540137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1541137500Sjulian{
1542137500Sjulian	struct agg_rchinfo *ch = data;
1543137500Sjulian	int blkcnt;
1544137500Sjulian
1545137500Sjulian	/* try to keep at least 20msec DMA space */
1546137500Sjulian	blkcnt = (ch->speed << ch->stereo) / (25 * blocksize);
1547137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1548137500Sjulian
1549137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1550137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1551137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1552137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1553137500Sjulian	} else {
1554137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1555137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1556137500Sjulian	}
1557137500Sjulian
1558137500Sjulian	ch->blklen = blocksize / 2;
1559137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1560137500Sjulian	return blocksize;
1561137500Sjulian}
1562137500Sjulian
1563137500Sjulianstatic int
1564137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go)
1565137500Sjulian{
1566137500Sjulian	struct agg_rchinfo *ch = sc;
1567137500Sjulian
1568137500Sjulian	switch (go) {
1569137500Sjulian	case PCMTRIG_EMLDMARD:
1570137500Sjulian		if (ch->stereo)
1571137500Sjulian			aggch_feed_adc_stereo(ch);
1572137500Sjulian		else
1573137500Sjulian			aggch_feed_adc_mono(ch);
1574137500Sjulian		break;
1575137500Sjulian	case PCMTRIG_START:
1576137500Sjulian		aggch_start_adc(ch);
1577137500Sjulian		break;
1578137500Sjulian	case PCMTRIG_ABORT:
1579137500Sjulian	case PCMTRIG_STOP:
1580137500Sjulian		aggch_stop_adc(ch);
1581137500Sjulian		break;
1582137500Sjulian	}
1583137500Sjulian	return 0;
1584137500Sjulian}
1585137500Sjulian
1586193640Sariffstatic u_int32_t
1587137500Sjulianaggrch_getptr(kobj_t obj, void *sc)
1588137500Sjulian{
1589137500Sjulian	struct agg_rchinfo *ch = sc;
1590137500Sjulian
1591137500Sjulian	return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1;
1592137500Sjulian}
1593137500Sjulian
1594137500Sjulianstatic struct pcmchan_caps *
1595137500Sjulianaggrch_getcaps(kobj_t obj, void *sc)
1596137500Sjulian{
159765543Scg	static u_int32_t recfmt[] = {
1598193640Sariff		SND_FORMAT(AFMT_S16_LE, 1, 0),
1599193640Sariff		SND_FORMAT(AFMT_S16_LE, 2, 0),
160065543Scg		0
160165543Scg	};
1602137500Sjulian	static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0};
160365543Scg
1604137500Sjulian	return &reccaps;
160565543Scg}
160665543Scg
1607137500Sjulianstatic kobj_method_t aggrch_methods[] = {
1608137500Sjulian	KOBJMETHOD(channel_init,		aggrch_init),
1609137500Sjulian	/* channel_free: no-op */
1610137500Sjulian	KOBJMETHOD(channel_setformat,		aggrch_setformat),
1611137500Sjulian	KOBJMETHOD(channel_setspeed,		aggrch_setspeed),
1612137500Sjulian	KOBJMETHOD(channel_setblocksize,	aggrch_setblocksize),
1613137500Sjulian	KOBJMETHOD(channel_trigger,		aggrch_trigger),
1614137500Sjulian	KOBJMETHOD(channel_getptr,		aggrch_getptr),
1615137500Sjulian	KOBJMETHOD(channel_getcaps,		aggrch_getcaps),
1616193640Sariff	KOBJMETHOD_END
161770134Scg};
1618137500SjulianCHANNEL_DECLARE(aggrch);
161965543Scg
1620137500Sjulian
162165543Scg/* -----------------------------
162265543Scg * Bus space.
162365543Scg */
162465543Scg
162565543Scgstatic void
162665543Scgagg_intr(void *sc)
162765543Scg{
162865543Scg	struct agg_info* ess = sc;
1629137500Sjulian	register u_int8_t status;
163065543Scg	int i;
1631137500Sjulian	u_int m;
163265543Scg
1633137500Sjulian	status = AGG_RD(ess, PORT_HOSTINT_STAT, 1);
163465543Scg	if (!status)
163565543Scg		return;
163665543Scg
1637137500Sjulian	/* Acknowledge intr. */
1638137500Sjulian	AGG_WR(ess, PORT_HOSTINT_STAT, status, 1);
163970619Sjhb
1640137500Sjulian	if (status & HOSTINT_STAT_DSOUND) {
1641137500Sjulian#ifdef AGG_JITTER_CORRECTION
1642137500Sjulian		agg_lock(ess);
1643137500Sjulian#endif
1644137500Sjulian		if (ess->curpwr <= PCI_POWERSTATE_D1) {
1645137500Sjulian			AGG_WR(ess, PORT_INT_STAT, 1, 2);
1646137500Sjulian#ifdef AGG_JITTER_CORRECTION
1647137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1648137500Sjulian				if (ess->active & m)
1649137500Sjulian					suppress_jitter(ess->pch + i);
1650137500Sjulian			}
1651137500Sjulian			if (ess->active & m)
1652137500Sjulian				suppress_rec_jitter(&ess->rch);
1653137500Sjulian			agg_unlock(ess);
1654137500Sjulian#endif
1655137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1656137500Sjulian				if (ess->active & m) {
1657137500Sjulian					if (ess->curpwr <= PCI_POWERSTATE_D1)
1658137500Sjulian						chn_intr(ess->pch[i].channel);
1659137500Sjulian					else {
1660137500Sjulian						m = 0;
1661137500Sjulian						break;
1662137500Sjulian					}
1663137500Sjulian				}
1664137500Sjulian			}
1665137500Sjulian			if ((ess->active & m)
1666137500Sjulian			    && ess->curpwr <= PCI_POWERSTATE_D1)
1667137500Sjulian				chn_intr(ess->rch.channel);
1668137500Sjulian		}
1669137500Sjulian#ifdef AGG_JITTER_CORRECTION
1670137500Sjulian		else
1671137500Sjulian			agg_unlock(ess);
1672137500Sjulian#endif
1673137500Sjulian	}
1674137500Sjulian
167565543Scg	if (status & HOSTINT_STAT_HWVOL) {
1676137500Sjulian		register u_int8_t event;
167770619Sjhb
1678137500Sjulian		agg_lock(ess);
1679137500Sjulian		event = AGG_RD(ess, PORT_HWVOL_MASTER, 1);
1680137500Sjulian		AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1);
1681137500Sjulian		agg_unlock(ess);
1682137500Sjulian
168370619Sjhb		switch (event) {
168470619Sjhb		case HWVOL_UP:
168570945Sjhb			mixer_hwvol_step(ess->dev, 1, 1);
168670619Sjhb			break;
168770619Sjhb		case HWVOL_DOWN:
168870945Sjhb			mixer_hwvol_step(ess->dev, -1, -1);
168970619Sjhb			break;
169070619Sjhb		case HWVOL_NOP:
169170619Sjhb			break;
169270619Sjhb		default:
1693137500Sjulian			if (event & HWVOL_MUTE) {
1694137500Sjulian				mixer_hwvol_mute(ess->dev);
1695137500Sjulian				break;
1696137500Sjulian			}
1697137500Sjulian			device_printf(ess->dev,
1698137500Sjulian				      "%s: unknown HWVOL event 0x%x\n",
1699137500Sjulian				      device_get_nameunit(ess->dev), event);
170065543Scg		}
170165543Scg	}
170265543Scg}
170365543Scg
170465543Scgstatic void
170565543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
170665543Scg{
170765543Scg	bus_addr_t *phys = arg;
170865543Scg
170965543Scg	*phys = error? 0 : segs->ds_addr;
171065543Scg
171165543Scg	if (bootverbose) {
171265543Scg		printf("setmap (%lx, %lx), nseg=%d, error=%d\n",
171365543Scg		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
171465543Scg		    nseg, error);
171565543Scg	}
171665543Scg}
171765543Scg
171865543Scgstatic void *
1719137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys)
172065543Scg{
172165543Scg	void *buf;
172265543Scg	bus_dmamap_t map;
172365543Scg
1724137500Sjulian	if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map))
172565543Scg		return NULL;
1726137500Sjulian	if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0)
1727137500Sjulian	    || !*phys || map) {
1728137500Sjulian		bus_dmamem_free(dmat, buf, map);
172965543Scg		return NULL;
173065543Scg	}
173165543Scg	return buf;
173265543Scg}
173365543Scg
173465543Scgstatic void
1735137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf)
173665543Scg{
1737137500Sjulian	bus_dmamem_free(dmat, buf, NULL);
173865543Scg}
173965543Scg
174065543Scgstatic int
174165543Scgagg_probe(device_t dev)
174265543Scg{
174365543Scg	char *s = NULL;
174465543Scg
174565543Scg	switch (pci_get_devid(dev)) {
174665543Scg	case MAESTRO_1_PCI_ID:
174765543Scg		s = "ESS Technology Maestro-1";
174865543Scg		break;
174965543Scg
175065543Scg	case MAESTRO_2_PCI_ID:
175165543Scg		s = "ESS Technology Maestro-2";
175265543Scg		break;
175365543Scg
175465543Scg	case MAESTRO_2E_PCI_ID:
175565543Scg		s = "ESS Technology Maestro-2E";
175665543Scg		break;
175765543Scg	}
175865543Scg
175965543Scg	if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) {
176065543Scg		device_set_desc(dev, s);
1761142890Simp		return BUS_PROBE_DEFAULT;
176265543Scg	}
176365543Scg	return ENXIO;
176465543Scg}
176565543Scg
176665543Scgstatic int
176765543Scgagg_attach(device_t dev)
176865543Scg{
176965543Scg	struct agg_info	*ess = NULL;
177065543Scg	u_int32_t	data;
1771119690Sjhb	int	regid = PCIR_BAR(0);
177265543Scg	struct resource	*reg = NULL;
177365543Scg	struct ac97_info	*codec = NULL;
177465543Scg	int	irqid = 0;
177565543Scg	struct resource	*irq = NULL;
177665543Scg	void	*ih = NULL;
177765543Scg	char	status[SND_STATUSLEN];
1778174582Sariff	int	dacn, ret = 0;
177965543Scg
1780170873Sariff	ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO);
178165543Scg	ess->dev = dev;
178265543Scg
1783167608Sariff	mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc",
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
1791174582Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1792174582Sariff	    "dac", &dacn) == 0) {
1793174582Sariff	    	if (dacn < 1)
1794174582Sariff			dacn = 1;
1795174582Sariff		else if (dacn > AGG_MAXPLAYCH)
1796174582Sariff			dacn = AGG_MAXPLAYCH;
1797174582Sariff	} else
1798174582Sariff		dacn = AGG_MAXPLAYCH;
1799174582Sariff
180084658Scg	ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536);
1801166904Snetchild	if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev),
1802137500Sjulian			       /*align */ 4, 1 << (16+1),
1803137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1804137500Sjulian			       /*filter*/ NULL, NULL,
1805137500Sjulian			       /*size  */ ess->bufsz, 1, 0x3ffff,
1806137500Sjulian			       /*flags */ 0,
1807137500Sjulian#if __FreeBSD_version >= 501102
1808137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1809137500Sjulian#endif
1810137500Sjulian			       &ess->buf_dmat) != 0) {
1811137500Sjulian		device_printf(dev, "unable to create dma tag\n");
1812137500Sjulian		ret = ENOMEM;
1813137500Sjulian		goto bad;
1814137500Sjulian	}
181584658Scg
1816166904Snetchild	if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev),
1817137500Sjulian			       /*align */ 1 << WAVCACHE_BASEADDR_SHIFT,
1818137500Sjulian			                  1 << (16+1),
1819137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1820137500Sjulian			       /*filter*/ NULL, NULL,
1821137500Sjulian			       /*size  */ 3*ess->bufsz, 1, 0x3ffff,
1822137500Sjulian			       /*flags */ 0,
1823137500Sjulian#if __FreeBSD_version >= 501102
1824137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1825137500Sjulian#endif
1826137500Sjulian			       &ess->stat_dmat) != 0) {
182765543Scg		device_printf(dev, "unable to create dma tag\n");
1828137500Sjulian		ret = ENOMEM;
182965543Scg		goto bad;
183065543Scg	}
183165543Scg
1832137500Sjulian	/* Allocate the room for brain-damaging status buffer. */
1833137500Sjulian	ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys);
183465543Scg	if (ess->stat == NULL) {
1835137500Sjulian		device_printf(dev, "cannot allocate status buffer\n");
1836137500Sjulian		ret = ENOMEM;
183765543Scg		goto bad;
183865543Scg	}
183965543Scg	if (bootverbose)
1840137500Sjulian		device_printf(dev, "Maestro status/record buffer: %#llx\n",
1841137500Sjulian		    (long long)ess->phys);
184265543Scg
1843137500Sjulian	/* State D0-uninitialized. */
1844137500Sjulian	ess->curpwr = PCI_POWERSTATE_D3;
1845137500Sjulian	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
184665543Scg
1847254263Sscottl	pci_enable_busmaster(dev);
184865543Scg
1849137500Sjulian	/* Allocate resources. */
1850254263Sscottl	reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &regid, RF_ACTIVE);
1851137500Sjulian	if (reg != NULL) {
1852137500Sjulian		ess->reg = reg;
1853137500Sjulian		ess->regid = regid;
1854137500Sjulian		ess->st = rman_get_bustag(reg);
1855137500Sjulian		ess->sh = rman_get_bushandle(reg);
1856137500Sjulian	} else {
185765543Scg		device_printf(dev, "unable to map register space\n");
1858137500Sjulian		ret = ENXIO;
185965543Scg		goto bad;
186065543Scg	}
1861141095Simp	irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid,
1862141095Simp	    RF_ACTIVE | RF_SHAREABLE);
1863137500Sjulian	if (irq != NULL) {
1864137500Sjulian		ess->irq = irq;
1865137500Sjulian		ess->irqid = irqid;
1866137500Sjulian	} else {
1867137500Sjulian		device_printf(dev, "unable to map interrupt\n");
1868137500Sjulian		ret = ENXIO;
1869137500Sjulian		goto bad;
1870137500Sjulian	}
187165543Scg
1872137500Sjulian	/* Setup resources. */
1873137500Sjulian	if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) {
1874137500Sjulian		device_printf(dev, "unable to setup interrupt\n");
1875137500Sjulian		ret = ENXIO;
1876137500Sjulian		goto bad;
1877137500Sjulian	} else
1878137500Sjulian		ess->ih = ih;
1879137500Sjulian
1880137500Sjulian	/* Transition from D0-uninitialized to D0. */
1881137500Sjulian	agg_lock(ess);
1882137500Sjulian	agg_power(ess, PCI_POWERSTATE_D0);
1883137500Sjulian	if (agg_rdcodec(ess, 0) == 0x80) {
1884137500Sjulian		/* XXX - TODO: PT101 */
1885154067Sariff		agg_unlock(ess);
188665543Scg		device_printf(dev, "PT101 codec detected!\n");
1887137500Sjulian		ret = ENXIO;
188865543Scg		goto bad;
188965543Scg	}
1890154067Sariff	agg_unlock(ess);
189170134Scg	codec = AC97_CREATE(dev, ess, agg_ac97);
1892137500Sjulian	if (codec == NULL) {
1893137500Sjulian		device_printf(dev, "failed to create AC97 codec softc!\n");
1894137500Sjulian		ret = ENOMEM;
189565543Scg		goto bad;
1896137500Sjulian	}
1897137500Sjulian	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
1898137500Sjulian		device_printf(dev, "mixer initialization failed!\n");
1899137500Sjulian		ret = ENXIO;
190065543Scg		goto bad;
1901137500Sjulian	}
190265543Scg	ess->codec = codec;
190365543Scg
1904174582Sariff	ret = pcm_register(dev, ess, dacn, 1);
1905137500Sjulian	if (ret)
190665543Scg		goto bad;
190765543Scg
190870945Sjhb	mixer_hwvol_init(dev);
1909154067Sariff	agg_lock(ess);
1910137500Sjulian	agg_power(ess, powerstate_init);
1911154067Sariff	agg_unlock(ess);
1912174582Sariff	for (data = 0; data < dacn; data++)
1913137500Sjulian		pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess);
191470134Scg	pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess);
1915137500Sjulian	adjust_pchbase(ess->pch, ess->playchns, ess->bufsz);
1916137500Sjulian
1917137500Sjulian	snprintf(status, SND_STATUSLEN,
1918137500Sjulian	    "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d",
1919137500Sjulian	    rman_get_start(reg), rman_get_end(reg), rman_get_start(irq),
1920137500Sjulian	    pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev));
192165543Scg	pcm_setstatus(dev, status);
192265543Scg
192365543Scg	return 0;
192465543Scg
192565543Scg bad:
192665644Scg	if (codec != NULL)
192765644Scg		ac97_destroy(codec);
192865543Scg	if (ih != NULL)
192965543Scg		bus_teardown_intr(dev, irq, ih);
193065543Scg	if (irq != NULL)
193165543Scg		bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
193265543Scg	if (reg != NULL)
193365543Scg		bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
1934154627Snetchild	if (ess != NULL) {
1935154627Snetchild		if (ess->stat != NULL)
1936154627Snetchild			dma_free(ess->stat_dmat, ess->stat);
1937154627Snetchild		if (ess->stat_dmat != NULL)
1938154627Snetchild			bus_dma_tag_destroy(ess->stat_dmat);
1939154627Snetchild		if (ess->buf_dmat != NULL)
1940154627Snetchild			bus_dma_tag_destroy(ess->buf_dmat);
1941154627Snetchild		if (mtx_initialized(&ess->lock))
1942154627Snetchild			mtx_destroy(&ess->lock);
194365543Scg		free(ess, M_DEVBUF);
1944154627Snetchild	}
194565543Scg
1946137500Sjulian	return ret;
194765543Scg}
194865543Scg
194965543Scgstatic int
195065543Scgagg_detach(device_t dev)
195165543Scg{
195265543Scg	struct agg_info	*ess = pcm_getdevinfo(dev);
195365543Scg	int r;
1954137500Sjulian	u_int16_t icr;
195565543Scg
1956137500Sjulian	icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2);
1957137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
1958137500Sjulian
1959137500Sjulian	agg_lock(ess);
1960137500Sjulian	if (ess->active) {
1961137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
1962137500Sjulian		agg_unlock(ess);
1963137500Sjulian		return EBUSY;
1964137500Sjulian	}
1965137500Sjulian	agg_unlock(ess);
1966137500Sjulian
196765543Scg	r = pcm_unregister(dev);
1968137500Sjulian	if (r) {
1969137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
197065543Scg		return r;
1971137500Sjulian	}
197265543Scg
1973137500Sjulian	agg_lock(ess);
1974137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
1975154067Sariff	agg_unlock(ess);
197665543Scg
197765543Scg	bus_teardown_intr(dev, ess->irq, ess->ih);
197865543Scg	bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq);
197965543Scg	bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg);
1980137500Sjulian	dma_free(ess->stat_dmat, ess->stat);
1981137500Sjulian	bus_dma_tag_destroy(ess->stat_dmat);
1982137500Sjulian	bus_dma_tag_destroy(ess->buf_dmat);
1983137500Sjulian	mtx_destroy(&ess->lock);
198465543Scg	free(ess, M_DEVBUF);
198565543Scg	return 0;
198665543Scg}
198765543Scg
198865543Scgstatic int
198965543Scgagg_suspend(device_t dev)
199065543Scg{
199165543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
199265543Scg
1993137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
1994137500Sjulian	agg_lock(ess);
1995137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
1996137500Sjulian	agg_unlock(ess);
199765543Scg
199865543Scg	return 0;
199965543Scg}
200065543Scg
200165543Scgstatic int
200265543Scgagg_resume(device_t dev)
200365543Scg{
2004137500Sjulian	int i;
200565543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
200665543Scg
200765543Scg	for (i = 0; i < ess->playchns; i++)
200865543Scg		if (ess->active & (1 << i))
200965543Scg			aggch_start_dac(ess->pch + i);
201065543Scg	if (ess->active & (1 << i))
201165543Scg		aggch_start_adc(&ess->rch);
2012137500Sjulian
2013137500Sjulian	agg_lock(ess);
2014137500Sjulian	if (!ess->active)
2015137500Sjulian		agg_power(ess, powerstate_init);
2016137500Sjulian	agg_unlock(ess);
2017137500Sjulian
2018137500Sjulian	if (mixer_reinit(dev)) {
2019137500Sjulian		device_printf(dev, "unable to reinitialize the mixer\n");
2020137500Sjulian		return ENXIO;
202165543Scg	}
2022137500Sjulian
202365543Scg	return 0;
202465543Scg}
202565543Scg
202665543Scgstatic int
202765543Scgagg_shutdown(device_t dev)
202865543Scg{
202965543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
203065543Scg
2031137500Sjulian	agg_lock(ess);
2032137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2033137500Sjulian	agg_unlock(ess);
203465543Scg
203565543Scg	return 0;
203665543Scg}
203765543Scg
203865543Scg
203965543Scgstatic device_method_t agg_methods[] = {
204065543Scg    DEVMETHOD(device_probe,	agg_probe),
204165543Scg    DEVMETHOD(device_attach,	agg_attach),
204265543Scg    DEVMETHOD(device_detach,	agg_detach),
204365543Scg    DEVMETHOD(device_suspend,	agg_suspend),
204465543Scg    DEVMETHOD(device_resume,	agg_resume),
204565543Scg    DEVMETHOD(device_shutdown,	agg_shutdown),
204665543Scg
204765543Scg    { 0, 0 }
204865543Scg};
204965543Scg
205065543Scgstatic driver_t agg_driver = {
205165543Scg    "pcm",
205265543Scg    agg_methods,
205382180Scg    PCM_SOFTC_SIZE,
205465543Scg};
205565543Scg
2056137500Sjulian/*static devclass_t pcm_devclass;*/
2057137500Sjulian
205865543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
2059132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
206065543ScgMODULE_VERSION(snd_maestro, 1);
2061