maestro.c revision 166279
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 166279 2007-01-27 13:30:19Z ariff $");
5882180Scg
5965543Scg/*
6065543Scg * PCI IDs of supported chips:
6165543Scg *
6265543Scg * MAESTRO-1	0x01001285
6365543Scg * MAESTRO-2	0x1968125d
6465543Scg * MAESTRO-2E	0x1978125d
6565543Scg */
6665543Scg
6765543Scg#define MAESTRO_1_PCI_ID	0x01001285
6865543Scg#define MAESTRO_2_PCI_ID	0x1968125d
6965543Scg#define MAESTRO_2E_PCI_ID	0x1978125d
7065543Scg
7165543Scg#define NEC_SUBID1	0x80581033	/* Taken from Linux driver */
7265543Scg#define NEC_SUBID2	0x803c1033	/* NEC VersaProNX VA26D    */
7365543Scg
74137500Sjulian#ifdef AGG_MAXPLAYCH
75137500Sjulian# if AGG_MAXPLAYCH > 4
76137500Sjulian#  undef AGG_MAXPLAYCH
77137500Sjulian#  define AGG_MAXPLAYCH 4
78137500Sjulian# endif
79137500Sjulian#else
8065543Scg# define AGG_MAXPLAYCH	4
8165543Scg#endif
8265543Scg
8384658Scg#define AGG_DEFAULT_BUFSZ	0x4000 /* 0x1000, but gets underflows */
8465543Scg
8565543Scg
86137500Sjulian/* compatibility */
87137500Sjulian#if __FreeBSD_version < 500000
88137500Sjulian# define critical_enter()	disable_intr()
89137500Sjulian# define critical_exit()	enable_intr()
90137500Sjulian#endif
91137500Sjulian
92137500Sjulian#ifndef PCIR_BAR
93137500Sjulian#define PCIR_BAR(x)	(PCIR_MAPS + (x) * 4)
94137500Sjulian#endif
95137500Sjulian
96137500Sjulian
9765543Scg/* -----------------------------
9865543Scg * Data structures.
9965543Scg */
10065543Scgstruct agg_chinfo {
101137500Sjulian	/* parent softc */
10265543Scg	struct agg_info		*parent;
103137500Sjulian
104137500Sjulian	/* FreeBSD newpcm related */
10574763Scg	struct pcm_channel	*channel;
10674763Scg	struct snd_dbuf		*buffer;
107137500Sjulian
108137500Sjulian	/* OS independent */
109137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
110137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
111137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
112137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
11370291Scg	u_int32_t		speed;
114137500Sjulian	unsigned		num	: 3;
115137500Sjulian	unsigned		stereo	: 1;
116137500Sjulian	unsigned		qs16	: 1;	/* quantum size is 16bit */
117137500Sjulian	unsigned		us	: 1;	/* in unsigned format */
11865543Scg};
11965543Scg
120137500Sjulianstruct agg_rchinfo {
121137500Sjulian	/* parent softc */
122137500Sjulian	struct agg_info		*parent;
123137500Sjulian
124137500Sjulian	/* FreeBSD newpcm related */
125137500Sjulian	struct pcm_channel	*channel;
126137500Sjulian	struct snd_dbuf		*buffer;
127137500Sjulian
128137500Sjulian	/* OS independent */
129137500Sjulian	bus_addr_t		phys;	/* channel buffer physical address */
130137500Sjulian	bus_addr_t		base;	/* channel buffer segment base */
131137500Sjulian	u_int32_t		blklen;	/* DMA block length in WORDs */
132137500Sjulian	u_int32_t		buflen;	/* channel buffer length in WORDs */
133137500Sjulian	u_int32_t		speed;
134137500Sjulian	unsigned			: 3;
135137500Sjulian	unsigned		stereo	: 1;
136137500Sjulian	bus_addr_t		srcphys;
137137500Sjulian	int16_t			*src;	/* stereo peer buffer */
138137500Sjulian	int16_t			*sink;	/* channel buffer pointer */
139137500Sjulian	volatile u_int32_t	hwptr;	/* ready point in 16bit sample */
140137500Sjulian};
141137500Sjulian
14265543Scgstruct agg_info {
143137500Sjulian	/* FreeBSD newbus related */
14465543Scg	device_t		dev;
145137500Sjulian
146137500Sjulian	/* I wonder whether bus_space_* are in common in *BSD... */
14765543Scg	struct resource		*reg;
14865543Scg	int			regid;
14965543Scg	bus_space_tag_t		st;
15065543Scg	bus_space_handle_t	sh;
15165543Scg
15265543Scg	struct resource		*irq;
15365543Scg	int			irqid;
15465543Scg	void			*ih;
15565543Scg
156137500Sjulian	bus_dma_tag_t		buf_dmat;
157137500Sjulian	bus_dma_tag_t		stat_dmat;
15865543Scg
159137500Sjulian	/* FreeBSD SMPng related */
160137500Sjulian#ifdef USING_MUTEX
161137500Sjulian	struct mtx		lock;	/* mutual exclusion */
162137500Sjulian#endif
163137500Sjulian	/* FreeBSD newpcm related */
16465543Scg	struct ac97_info	*codec;
16565543Scg
166137500Sjulian	/* OS independent */
167137500Sjulian	u_int8_t		*stat;	/* status buffer pointer */
168137500Sjulian	bus_addr_t		phys;	/* status buffer physical address */
169137500Sjulian	unsigned int		bufsz;	/* channel buffer size in bytes */
170137500Sjulian	u_int			playchns;
171137500Sjulian	volatile u_int		active;
17265543Scg	struct agg_chinfo	pch[AGG_MAXPLAYCH];
173137500Sjulian	struct agg_rchinfo	rch;
174137500Sjulian	volatile u_int8_t	curpwr;	/* current power status: D[0-3] */
17565543Scg};
17665543Scg
177137500Sjulian
178137500Sjulian/* -----------------------------
179137500Sjulian * Sysctls for debug.
180137500Sjulian */
181137500Sjulianstatic unsigned int powerstate_active = PCI_POWERSTATE_D1;
182137500Sjulian#ifdef MAESTRO_AGGRESSIVE_POWERSAVE
183137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D2;
184137500Sjulian#else
185137500Sjulianstatic unsigned int powerstate_idle   = PCI_POWERSTATE_D1;
186137500Sjulian#endif
187137500Sjulianstatic unsigned int powerstate_init   = PCI_POWERSTATE_D2;
188137500Sjulian
189159732Snetchild/* XXX: this should move to a device specific sysctl dev.pcm.X.debug.Y via
190159732Snetchild   device_get_sysctl_*() as discussed on multimedia@ in msg-id
191159732Snetchild   <861wujij2q.fsf@xps.des.no> */
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,
198166279Sariff	    &powerstate_init, 0,
199166279Sariff	    "The Dx power state prior to the first use (0-2)");
200137500Sjulian
201137500Sjulian
202137500Sjulian/* -----------------------------
203137500Sjulian * Prototypes
204137500Sjulian */
205137500Sjulian
206166279Sariffstatic void	agg_sleep(struct agg_info*, const char *wmesg, int msec);
207137500Sjulian
208166279Sariffstatic __inline u_int32_t	agg_rd(struct agg_info*, int, int size);
209166279Sariffstatic __inline void		agg_wr(struct agg_info*, int, u_int32_t data,
210166279Sariff								int size);
211166279Sariffstatic int	agg_rdcodec(struct agg_info*, int);
212166279Sariffstatic int	agg_wrcodec(struct agg_info*, int, u_int32_t);
213137500Sjulian
214166279Sariffstatic void	ringbus_setdest(struct agg_info*, int, int);
215137500Sjulian
216166279Sariffstatic u_int16_t	wp_rdreg(struct agg_info*, u_int16_t);
217166279Sariffstatic void		wp_wrreg(struct agg_info*, u_int16_t, u_int16_t);
218166279Sariffstatic u_int16_t	wp_rdapu(struct agg_info*, unsigned, u_int16_t);
219166279Sariffstatic void	wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t);
220166279Sariffstatic void	wp_settimer(struct agg_info*, u_int);
221166279Sariffstatic void	wp_starttimer(struct agg_info*);
222166279Sariffstatic void	wp_stoptimer(struct agg_info*);
22365543Scg
224166279Sariff#if 0
225166279Sariffstatic u_int16_t	wc_rdreg(struct agg_info*, u_int16_t);
226166279Sariff#endif
227166279Sariffstatic void		wc_wrreg(struct agg_info*, u_int16_t, u_int16_t);
228166279Sariff#if 0
229166279Sariffstatic u_int16_t	wc_rdchctl(struct agg_info*, int);
230166279Sariff#endif
231166279Sariffstatic void		wc_wrchctl(struct agg_info*, int, u_int16_t);
23265543Scg
233166279Sariffstatic void	agg_stopclock(struct agg_info*, int part, int st);
23465543Scg
235166279Sariffstatic void	agg_initcodec(struct agg_info*);
236166279Sariffstatic void	agg_init(struct agg_info*);
237166279Sariffstatic void	agg_power(struct agg_info*, int);
23865543Scg
239166279Sariffstatic void	aggch_start_dac(struct agg_chinfo*);
240166279Sariffstatic void	aggch_stop_dac(struct agg_chinfo*);
241166279Sariffstatic void	aggch_start_adc(struct agg_rchinfo*);
242166279Sariffstatic void	aggch_stop_adc(struct agg_rchinfo*);
243166279Sariffstatic void	aggch_feed_adc_stereo(struct agg_rchinfo*);
244166279Sariffstatic void	aggch_feed_adc_mono(struct agg_rchinfo*);
24565543Scg
246166279Sariff#ifdef AGG_JITTER_CORRECTION
247166279Sariffstatic void	suppress_jitter(struct agg_chinfo*);
248166279Sariffstatic void	suppress_rec_jitter(struct agg_rchinfo*);
249166279Sariff#endif
25065543Scg
251166279Sariffstatic void	set_timer(struct agg_info*);
25265543Scg
253166279Sariffstatic void	agg_intr(void *);
254166279Sariffstatic int	agg_probe(device_t);
255166279Sariffstatic int	agg_attach(device_t);
256166279Sariffstatic int	agg_detach(device_t);
257166279Sariffstatic int	agg_suspend(device_t);
258166279Sariffstatic int	agg_resume(device_t);
259166279Sariffstatic int	agg_shutdown(device_t);
26065543Scg
261137500Sjulianstatic void	*dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*);
262166279Sariffstatic void	dma_free(bus_dma_tag_t, void *);
26365543Scg
264137500Sjulian
26565543Scg/* -----------------------------
26665543Scg * Subsystems.
26765543Scg */
26865543Scg
269137500Sjulian/* locking */
270166279Sariff#define agg_lock(sc)	snd_mtxlock(&((sc)->lock))
271166279Sariff#define agg_unlock(sc)	snd_mtxunlock(&((sc)->lock))
27265543Scg
273166279Sariffstatic void
274137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec)
275137500Sjulian{
276137500Sjulian	int timo;
277137500Sjulian
278137500Sjulian	timo = msec * hz / 1000;
279137500Sjulian	if (timo == 0)
280137500Sjulian		timo = 1;
281137500Sjulian#ifdef USING_MUTEX
282137500Sjulian	msleep(sc, &sc->lock, PWAIT, wmesg, timo);
283137500Sjulian#else
284137500Sjulian	tsleep(sc, PWAIT, wmesg, timo);
285137500Sjulian#endif
286137500Sjulian}
287137500Sjulian
288137500Sjulian
289137500Sjulian/* I/O port */
290137500Sjulian
291166279Sariffstatic __inline u_int32_t
292137500Sjulianagg_rd(struct agg_info *sc, int regno, int size)
293137500Sjulian{
294137500Sjulian	switch (size) {
295137500Sjulian	case 1:
296137500Sjulian		return bus_space_read_1(sc->st, sc->sh, regno);
297137500Sjulian	case 2:
298137500Sjulian		return bus_space_read_2(sc->st, sc->sh, regno);
299137500Sjulian	case 4:
300137500Sjulian		return bus_space_read_4(sc->st, sc->sh, regno);
301137500Sjulian	default:
302137500Sjulian		return ~(u_int32_t)0;
303137500Sjulian	}
304137500Sjulian}
305137500Sjulian
306137500Sjulian#define AGG_RD(sc, regno, size)           \
307137500Sjulian	bus_space_read_##size(            \
308137500Sjulian	    ((struct agg_info*)(sc))->st, \
309137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno))
310137500Sjulian
311166279Sariffstatic __inline void
312137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size)
313137500Sjulian{
314137500Sjulian	switch (size) {
315137500Sjulian	case 1:
316137500Sjulian		bus_space_write_1(sc->st, sc->sh, regno, data);
317137500Sjulian		break;
318137500Sjulian	case 2:
319137500Sjulian		bus_space_write_2(sc->st, sc->sh, regno, data);
320137500Sjulian		break;
321137500Sjulian	case 4:
322137500Sjulian		bus_space_write_4(sc->st, sc->sh, regno, data);
323137500Sjulian		break;
324137500Sjulian	}
325137500Sjulian}
326137500Sjulian
327137500Sjulian#define AGG_WR(sc, regno, data, size)     \
328137500Sjulian	bus_space_write_##size(           \
329137500Sjulian	    ((struct agg_info*)(sc))->st, \
330137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno), (data))
331137500Sjulian
33270134Scg/* -------------------------------------------------------------------- */
33370134Scg
334137500Sjulian/* Codec/Ringbus */
335137500Sjulian
336166279Sariffstatic int
337137500Sjulianagg_codec_wait4idle(struct agg_info *ess)
33865543Scg{
339137500Sjulian	unsigned t = 26;
34070134Scg
341137500Sjulian	while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) {
342137500Sjulian		if (--t == 0)
343137500Sjulian			return EBUSY;
344137500Sjulian		DELAY(2);	/* 20.8us / 13 */
345137500Sjulian	}
346137500Sjulian	return 0;
34770134Scg}
34870134Scg
349137500Sjulian
350166279Sariffstatic int
351137500Sjulianagg_rdcodec(struct agg_info *ess, int regno)
35270134Scg{
353137500Sjulian	int ret;
35465543Scg
35565543Scg	/* We have to wait for a SAFE time to write addr/data */
356137500Sjulian	if (agg_codec_wait4idle(ess)) {
357137500Sjulian		/* Timed out. No read performed. */
358137500Sjulian		device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n");
359137500Sjulian		return -1;
36065543Scg	}
36165543Scg
362137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1);
363137500Sjulian	/*DELAY(21);	* AC97 cycle = 20.8usec */
36465543Scg
36565543Scg	/* Wait for data retrieve */
366137500Sjulian	if (!agg_codec_wait4idle(ess)) {
367137500Sjulian		ret = AGG_RD(ess, PORT_CODEC_REG, 2);
368137500Sjulian	} else {
369137500Sjulian		/* Timed out. No read performed. */
370137500Sjulian		device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n");
371137500Sjulian		ret = -1;
37265543Scg	}
37365543Scg
374137500Sjulian	return ret;
37565543Scg}
37665543Scg
377166279Sariffstatic int
378137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data)
37965543Scg{
38065543Scg	/* We have to wait for a SAFE time to write addr/data */
381137500Sjulian	if (agg_codec_wait4idle(ess)) {
38265543Scg		/* Timed out. Abort writing. */
38365543Scg		device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n");
38470134Scg		return -1;
38565543Scg	}
38665543Scg
387137500Sjulian	AGG_WR(ess, PORT_CODEC_REG, data, 2);
388137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1);
38970134Scg
390137500Sjulian	/* Wait for write completion */
391137500Sjulian	if (agg_codec_wait4idle(ess)) {
392137500Sjulian		/* Timed out. */
393137500Sjulian		device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n");
394137500Sjulian		return -1;
395137500Sjulian	}
396137500Sjulian
39770134Scg	return 0;
39865543Scg}
39965543Scg
400166279Sariffstatic void
40165543Scgringbus_setdest(struct agg_info *ess, int src, int dest)
40265543Scg{
40365543Scg	u_int32_t	data;
40465543Scg
405137500Sjulian	data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4);
40665543Scg	data &= ~(0xfU << src);
40765543Scg	data |= (0xfU & dest) << src;
408137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4);
40965543Scg}
41065543Scg
411137500Sjulian/* -------------------------------------------------------------------- */
412137500Sjulian
41365543Scg/* Wave Processor */
41465543Scg
415166279Sariffstatic u_int16_t
41665543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg)
41765543Scg{
418137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
419137500Sjulian	return AGG_RD(ess, PORT_DSP_DATA, 2);
42065543Scg}
42165543Scg
422166279Sariffstatic void
42365543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
42465543Scg{
425137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
426137500Sjulian	AGG_WR(ess, PORT_DSP_DATA, data, 2);
42765543Scg}
42865543Scg
429166279Sariffstatic int
430137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data)
43165543Scg{
432137500Sjulian	unsigned t = 0;
43365543Scg
434137500Sjulian	while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) {
435137500Sjulian		if (++t == 1000) {
436137500Sjulian			return EAGAIN;
437137500Sjulian		}
438137500Sjulian		AGG_WR(ess, PORT_DSP_DATA, data, 2);
43965543Scg	}
440137500Sjulian
441137500Sjulian	return 0;
44265543Scg}
44365543Scg
444166279Sariffstatic u_int16_t
445137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg)
44665543Scg{
447137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
448137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) != 0)
449137500Sjulian		device_printf(ess->dev, "wp_rdapu() indexing timed out.\n");
450137500Sjulian	return wp_rdreg(ess, WPREG_DATA_PORT);
45165543Scg}
45265543Scg
453166279Sariffstatic void
454137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data)
45565543Scg{
456137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
457137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) == 0) {
458137500Sjulian		wp_wrreg(ess, WPREG_DATA_PORT, data);
459137500Sjulian		if (wp_wait_data(ess, data) != 0)
460166279Sariff			device_printf(ess->dev,
461166279Sariff			    "wp_wrapu() write timed out.\n");
462137500Sjulian	} else {
463137500Sjulian		device_printf(ess->dev, "wp_wrapu() indexing timed out.\n");
46465543Scg	}
46565543Scg}
46665543Scg
467137573Srustatic void
468137500Sjulianapu_setparam(struct agg_info *ess, int apuch,
469137500Sjulian    u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv)
47065543Scg{
471137500Sjulian	wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK);
472137500Sjulian	wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa);
473137500Sjulian	wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size);
474137500Sjulian	wp_wrapu(ess, apuch, APUREG_LOOPLEN, size);
475137500Sjulian	wp_wrapu(ess, apuch, APUREG_ROUTING, 0);
476137500Sjulian	wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000);
477137500Sjulian	wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00
478137500Sjulian	    | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT))
479137500Sjulian	    | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT)));
480137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE,
481137500Sjulian	    APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT));
482137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8);
483137500Sjulian}
48465543Scg
485166279Sariffstatic void
486137500Sjulianwp_settimer(struct agg_info *ess, u_int divide)
487137500Sjulian{
488137500Sjulian	u_int prescale = 0;
48965543Scg
490137500Sjulian	RANGE(divide, 2, 32 << 7);
491137500Sjulian
492137500Sjulian	for (; divide > 32; divide >>= 1) {
49365543Scg		prescale++;
494137500Sjulian		divide++;
495137500Sjulian	}
49665543Scg
49765543Scg	for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1)
49865543Scg		prescale++;
49965543Scg
50065543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 0);
501137500Sjulian	wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 |
50265543Scg	    (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1));
50365543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 1);
50465543Scg}
50565543Scg
506166279Sariffstatic void
50765543Scgwp_starttimer(struct agg_info *ess)
50865543Scg{
509137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
510137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED
511137500Sjulian	       | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
51265543Scg	wp_wrreg(ess, WPREG_TIMER_START, 1);
51365543Scg}
51465543Scg
515166279Sariffstatic void
51665543Scgwp_stoptimer(struct agg_info *ess)
51765543Scg{
518137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED
519137500Sjulian	       & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
520137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
52165543Scg	wp_wrreg(ess, WPREG_TIMER_START, 0);
52265543Scg}
52365543Scg
524137500Sjulian/* -------------------------------------------------------------------- */
525137500Sjulian
52665543Scg/* WaveCache */
52765543Scg
528166279Sariff#if 0
529166279Sariffstatic u_int16_t
53065543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg)
53165543Scg{
532137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
533137500Sjulian	return AGG_RD(ess, PORT_WAVCACHE_DATA, 2);
53465543Scg}
535166279Sariff#endif
53665543Scg
537166279Sariffstatic void
53865543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
53965543Scg{
540137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
541137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2);
54265543Scg}
54365543Scg
544166279Sariff#if 0
545166279Sariffstatic u_int16_t
54665543Scgwc_rdchctl(struct agg_info *ess, int ch)
54765543Scg{
54865543Scg	return wc_rdreg(ess, ch << 3);
54965543Scg}
550166279Sariff#endif
55165543Scg
552166279Sariffstatic void
55365543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data)
55465543Scg{
55565543Scg	wc_wrreg(ess, ch << 3, data);
55665543Scg}
55765543Scg
558137500Sjulian/* -------------------------------------------------------------------- */
559137500Sjulian
56065543Scg/* Power management */
561166279Sariffstatic void
562137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st)
56365543Scg{
564137500Sjulian	u_int32_t data;
56565543Scg
566137500Sjulian	data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4);
567137500Sjulian	if (part < 16) {
568137500Sjulian		if (st == PCI_POWERSTATE_D1)
569137500Sjulian			data &= ~(1 << part);
570137500Sjulian		else
571137500Sjulian			data |= (1 << part);
572137500Sjulian		if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2)
573137500Sjulian			data |= (0x10000 << part);
574137500Sjulian		else
575137500Sjulian			data &= ~(0x10000 << part);
576137500Sjulian		pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4);
577137500Sjulian	}
57865543Scg}
57965543Scg
58065543Scg
58165543Scg/* -----------------------------
58265543Scg * Controller.
58365543Scg */
58465543Scg
585166279Sariffstatic void
58665543Scgagg_initcodec(struct agg_info* ess)
58765543Scg{
58865543Scg	u_int16_t data;
58965543Scg
590137500Sjulian	if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) {
591137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
59265543Scg		DELAY(104);	/* 20.8us * (4 + 1) */
59365543Scg	}
59465543Scg	/* XXX - 2nd codec should be looked at. */
595137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4);
59665543Scg	DELAY(2);
597137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
598137500Sjulian	DELAY(50);
59965543Scg
600137500Sjulian	if (agg_rdcodec(ess, 0) < 0) {
601137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
60265543Scg		DELAY(21);
60365543Scg
60465543Scg		/* Try cold reset. */
60565543Scg		device_printf(ess->dev, "will perform cold reset.\n");
606137500Sjulian		data = AGG_RD(ess, PORT_GPIO_DIR, 2);
60765543Scg		if (pci_read_config(ess->dev, 0x58, 2) & 1)
60865543Scg			data |= 0x10;
609137500Sjulian		data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2);
610137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2);
611137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2);
612137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2);
61365543Scg		DELAY(2);
614137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2);
61565543Scg		DELAY(1);
616137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2);
617137500Sjulian		agg_sleep(ess, "agginicd", 500);
618137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data, 2);
61965543Scg		DELAY(84);	/* 20.8us * 4 */
620137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
621137500Sjulian		DELAY(50);
62265543Scg	}
62365543Scg}
62465543Scg
62565543Scgstatic void
62665543Scgagg_init(struct agg_info* ess)
62765543Scg{
62865543Scg	u_int32_t data;
62965543Scg
63065543Scg	/* Setup PCI config registers. */
63165543Scg
63265543Scg	/* Disable all legacy emulations. */
63365543Scg	data = pci_read_config(ess->dev, CONF_LEGACY, 2);
63465543Scg	data |= LEGACY_DISABLED;
63565543Scg	pci_write_config(ess->dev, CONF_LEGACY, data, 2);
63665543Scg
63765543Scg	/* Disconnect from CHI. (Makes Dell inspiron 7500 work?)
63865543Scg	 * Enable posted write.
63965543Scg	 * Prefer PCI timing rather than that of ISA.
64065543Scg	 * Don't swap L/R. */
64165543Scg	data = pci_read_config(ess->dev, CONF_MAESTRO, 4);
642137500Sjulian	data |= MAESTRO_PMC;
64365543Scg	data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING;
64465543Scg	data &= ~MAESTRO_SWAP_LR;
64565543Scg	pci_write_config(ess->dev, CONF_MAESTRO, data, 4);
64665543Scg
647137500Sjulian	/* Turn off unused parts if necessary. */
648137500Sjulian	/* consult CONF_MAESTRO. */
649137500Sjulian	if (data & MAESTRO_SPDIF)
650137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D2);
651137500Sjulian	else
652137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D1);
653137500Sjulian	if (data & MAESTRO_HWVOL)
654137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D3);
655137500Sjulian	else
656137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D1);
657137500Sjulian
658137500Sjulian	/* parts that never be used */
659137500Sjulian	agg_stopclock(ess, ACPI_PART_978,	PCI_POWERSTATE_D1);
660137500Sjulian	agg_stopclock(ess, ACPI_PART_DAA,	PCI_POWERSTATE_D1);
661137500Sjulian	agg_stopclock(ess, ACPI_PART_GPIO,	PCI_POWERSTATE_D1);
662137500Sjulian	agg_stopclock(ess, ACPI_PART_SB,	PCI_POWERSTATE_D1);
663137500Sjulian	agg_stopclock(ess, ACPI_PART_FM,	PCI_POWERSTATE_D1);
664137500Sjulian	agg_stopclock(ess, ACPI_PART_MIDI,	PCI_POWERSTATE_D1);
665137500Sjulian	agg_stopclock(ess, ACPI_PART_GAME_PORT,	PCI_POWERSTATE_D1);
666137500Sjulian
667137500Sjulian	/* parts that will be used only when play/recording */
668137500Sjulian	agg_stopclock(ess, ACPI_PART_WP,	PCI_POWERSTATE_D2);
669137500Sjulian
670137500Sjulian	/* parts that should always be turned on */
671137500Sjulian	agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3);
672137500Sjulian	agg_stopclock(ess, ACPI_PART_GLUE,	PCI_POWERSTATE_D3);
673137500Sjulian	agg_stopclock(ess, ACPI_PART_PCI_IF,	PCI_POWERSTATE_D3);
674137500Sjulian	agg_stopclock(ess, ACPI_PART_RINGBUS,	PCI_POWERSTATE_D3);
675137500Sjulian
67665543Scg	/* Reset direct sound. */
677137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2);
678137500Sjulian	DELAY(100);
679137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
680137500Sjulian	DELAY(100);
681137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2);
682137500Sjulian	DELAY(100);
683137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
684137500Sjulian	DELAY(100);
68565543Scg
686137500Sjulian	/* Enable hardware volume control interruption. */
687137500Sjulian	if (data & MAESTRO_HWVOL)	/* XXX - why not use device flags? */
688137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2);
68965543Scg
69065543Scg	/* Setup Wave Processor. */
69165543Scg
69265543Scg	/* Enable WaveCache, set DMA base address. */
69365543Scg	wp_wrreg(ess, WPREG_WAVE_ROMRAM,
69465543Scg	    WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED);
695137500Sjulian	wp_wrreg(ess, WPREG_CRAM_DATA, 0);
69665543Scg
697137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_CTRL,
698137500Sjulian	       WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2);
699137500Sjulian
70065543Scg	for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++)
701137500Sjulian		wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT);
70265543Scg
70365543Scg	/* Setup Codec/Ringbus. */
70465543Scg	agg_initcodec(ess);
705137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL,
706137500Sjulian	       RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4);
70765543Scg
708137500Sjulian	wp_wrreg(ess, 0x08, 0xB004);
709137500Sjulian	wp_wrreg(ess, 0x09, 0x001B);
710137500Sjulian	wp_wrreg(ess, 0x0A, 0x8000);
711137500Sjulian	wp_wrreg(ess, 0x0B, 0x3F37);
712137500Sjulian	wp_wrreg(ess, WPREG_BASE, 0x8598);	/* Parallel I/O */
713137500Sjulian	wp_wrreg(ess, WPREG_BASE + 1, 0x7632);
71465543Scg	ringbus_setdest(ess, RINGBUS_SRC_ADC,
71565543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN);
71665543Scg	ringbus_setdest(ess, RINGBUS_SRC_DSOUND,
71765543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC);
71865543Scg
719137500Sjulian	/* Enable S/PDIF if necessary. */
720137500Sjulian	if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF)
721137500Sjulian		/* XXX - why not use device flags? */
722137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF |
723137500Sjulian		       AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1);
724137500Sjulian
72565543Scg	/* Setup ASSP. Needed for Dell Inspiron 7500? */
726137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1);
727137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1);
728137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1);
72965543Scg
73065543Scg	/*
73165543Scg	 * Setup GPIO.
73265543Scg	 * There seems to be speciality with NEC systems.
73365543Scg	 */
73465543Scg	switch (pci_get_subvendor(ess->dev)
73565543Scg	    | (pci_get_subdevice(ess->dev) << 16)) {
73665543Scg	case NEC_SUBID1:
73765543Scg	case NEC_SUBID2:
73865543Scg		/* Matthew Braithwaite <matt@braithwaite.net> reported that
73965543Scg		 * NEC Versa LX doesn't need GPIO operation. */
740137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2);
741137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR,
742137500Sjulian		       AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2);
743137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2);
74465543Scg		break;
74565543Scg	}
74665543Scg}
74765543Scg
748137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */
749137500Sjulianstatic void
750137500Sjulianagg_power(struct agg_info *ess, int status)
751137500Sjulian{
752137500Sjulian	u_int8_t lastpwr;
753137500Sjulian
754137500Sjulian	lastpwr = ess->curpwr;
755137500Sjulian	if (lastpwr == status)
756137500Sjulian		return;
757137500Sjulian
758137500Sjulian	switch (status) {
759137500Sjulian	case PCI_POWERSTATE_D0:
760137500Sjulian	case PCI_POWERSTATE_D1:
761137500Sjulian		switch (lastpwr) {
762137500Sjulian		case PCI_POWERSTATE_D2:
763137500Sjulian			pci_set_powerstate(ess->dev, status);
764137500Sjulian			/* Turn on PCM-related parts. */
765137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0);
766137500Sjulian			DELAY(100);
767137500Sjulian#if 0
768137500Sjulian			if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3)
769166279Sariff				device_printf(ess->dev,
770166279Sariff				    "warning: codec not ready.\n");
771137500Sjulian#endif
772137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
773137500Sjulian			       (AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
774137500Sjulian				& ~RINGBUS_CTRL_ACLINK_ENABLED)
775137500Sjulian			       | RINGBUS_CTRL_RINGBUS_ENABLED, 4);
776137500Sjulian			DELAY(50);
777137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
778137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
779137500Sjulian			       | RINGBUS_CTRL_ACLINK_ENABLED, 4);
780137500Sjulian			break;
781137500Sjulian		case PCI_POWERSTATE_D3:
782137500Sjulian			/* Initialize. */
783137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
784137500Sjulian			DELAY(100);
785137500Sjulian			agg_init(ess);
786137500Sjulian			/* FALLTHROUGH */
787137500Sjulian		case PCI_POWERSTATE_D0:
788137500Sjulian		case PCI_POWERSTATE_D1:
789137500Sjulian			pci_set_powerstate(ess->dev, status);
790137500Sjulian			break;
791137500Sjulian		}
792137500Sjulian		break;
793137500Sjulian	case PCI_POWERSTATE_D2:
794137500Sjulian		switch (lastpwr) {
795137500Sjulian		case PCI_POWERSTATE_D3:
796137500Sjulian			/* Initialize. */
797137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
798137500Sjulian			DELAY(100);
799137500Sjulian			agg_init(ess);
800137500Sjulian			/* FALLTHROUGH */
801137500Sjulian		case PCI_POWERSTATE_D0:
802137500Sjulian		case PCI_POWERSTATE_D1:
803137500Sjulian			/* Turn off PCM-related parts. */
804137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
805137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
806137500Sjulian			       & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4);
807137500Sjulian			DELAY(100);
808137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0x300);
809137500Sjulian			DELAY(100);
810137500Sjulian			break;
811137500Sjulian		}
812137500Sjulian		pci_set_powerstate(ess->dev, status);
813137500Sjulian		break;
814137500Sjulian	case PCI_POWERSTATE_D3:
815137500Sjulian		/* Entirely power down. */
816137500Sjulian		agg_wrcodec(ess, AC97_REG_POWER, 0xdf00);
817137500Sjulian		DELAY(100);
818137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
819137500Sjulian		/*DELAY(1);*/
820137500Sjulian		if (lastpwr != PCI_POWERSTATE_D2)
821137500Sjulian			wp_stoptimer(ess);
822137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
823137500Sjulian		AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1);
824137500Sjulian		pci_set_powerstate(ess->dev, status);
825137500Sjulian		break;
826137500Sjulian	default:
827137500Sjulian		/* Invalid power state; let it ignored. */
828137500Sjulian		status = lastpwr;
829137500Sjulian		break;
830137500Sjulian	}
831137500Sjulian
832137500Sjulian	ess->curpwr = status;
833137500Sjulian}
834137500Sjulian
835137500Sjulian/* -------------------------------------------------------------------- */
836137500Sjulian
83765543Scg/* Channel controller. */
83865543Scg
83965543Scgstatic void
84065543Scgaggch_start_dac(struct agg_chinfo *ch)
84165543Scg{
842137500Sjulian	bus_addr_t	wpwa;
843137500Sjulian	u_int32_t	speed;
844137500Sjulian	u_int16_t	size, apuch, wtbar, wcreg, aputype;
845137500Sjulian	u_int		dv;
846137500Sjulian	int		pan;
84765543Scg
848137500Sjulian	speed = ch->speed;
849137500Sjulian	wpwa = (ch->phys - ch->base) >> 1;
850137500Sjulian	wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2));
851137500Sjulian	wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
852137500Sjulian	size  = ch->buflen;
853137500Sjulian	apuch = (ch->num << 1) | 32;
854137500Sjulian	pan = PAN_RIGHT - PAN_FRONT;
855137500Sjulian
856137500Sjulian	if (ch->stereo) {
857137500Sjulian		wcreg |= WAVCACHE_CHCTL_STEREO;
858137500Sjulian		if (ch->qs16) {
859137500Sjulian			aputype = APUTYPE_16BITSTEREO;
860137500Sjulian			wpwa >>= 1;
861137500Sjulian			size >>= 1;
862137500Sjulian			pan = -pan;
863137500Sjulian		} else
864137500Sjulian			aputype = APUTYPE_8BITSTEREO;
865137500Sjulian	} else {
866137500Sjulian		pan = 0;
867137500Sjulian		if (ch->qs16)
868137500Sjulian			aputype = APUTYPE_16BITLINEAR;
869137500Sjulian		else {
870137500Sjulian			aputype = APUTYPE_8BITLINEAR;
871137500Sjulian			speed >>= 1;
872137500Sjulian		}
87365543Scg	}
874137500Sjulian	if (ch->us)
875137500Sjulian		wcreg |= WAVCACHE_CHCTL_U8;
87665543Scg
877137500Sjulian	if (wtbar > 8)
878137500Sjulian		wtbar = (wtbar >> 1) + 4;
879137500Sjulian
88065543Scg	dv = (((speed % 48000) << 16) + 24000) / 48000
88165543Scg	    + ((speed / 48000) << 16);
88265543Scg
883137500Sjulian	agg_lock(ch->parent);
884137500Sjulian	agg_power(ch->parent, powerstate_active);
88565543Scg
886137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar,
887137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
888137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1,
889137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
890137500Sjulian	if (wtbar < 8) {
891137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2,
892137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
893137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3,
894137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
895137500Sjulian	}
896137500Sjulian	wc_wrchctl(ch->parent, apuch, wcreg);
897137500Sjulian	wc_wrchctl(ch->parent, apuch + 1, wcreg);
89865543Scg
899137500Sjulian	apu_setparam(ch->parent, apuch, wpwa, size, pan, dv);
900137500Sjulian	if (ch->stereo) {
901137500Sjulian		if (ch->qs16)
902137500Sjulian			wpwa |= (WPWA_STEREO >> 1);
903137500Sjulian		apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv);
90465543Scg
905137500Sjulian		critical_enter();
906137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
907137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
90865543Scg		wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE,
909137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
910137500Sjulian		critical_exit();
911137500Sjulian	} else {
912137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
913137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
914137500Sjulian	}
915137500Sjulian
916137500Sjulian	/* to mark that this channel is ready for intr. */
917137500Sjulian	ch->parent->active |= (1 << ch->num);
918137500Sjulian
919137500Sjulian	set_timer(ch->parent);
920137500Sjulian	wp_starttimer(ch->parent);
921137500Sjulian	agg_unlock(ch->parent);
92265543Scg}
92365543Scg
92465543Scgstatic void
92565543Scgaggch_stop_dac(struct agg_chinfo *ch)
92665543Scg{
927137500Sjulian	agg_lock(ch->parent);
928137500Sjulian
929137500Sjulian	/* to mark that this channel no longer needs further intrs. */
930137500Sjulian	ch->parent->active &= ~(1 << ch->num);
931137500Sjulian
932137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE,
93365543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
934137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE,
93565543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
936137500Sjulian
937137500Sjulian	if (ch->parent->active) {
938137500Sjulian		set_timer(ch->parent);
939137500Sjulian		wp_starttimer(ch->parent);
940137500Sjulian	} else {
941137500Sjulian		wp_stoptimer(ch->parent);
942137500Sjulian		agg_power(ch->parent, powerstate_idle);
943137500Sjulian	}
944137500Sjulian	agg_unlock(ch->parent);
94565543Scg}
94665543Scg
947137500Sjulianstatic void
948137500Sjulianaggch_start_adc(struct agg_rchinfo *ch)
949137500Sjulian{
950137500Sjulian	bus_addr_t	wpwa, wpwa2;
951137500Sjulian	u_int16_t	wcreg, wcreg2;
952137500Sjulian	u_int	dv;
953137500Sjulian	int	pan;
954137500Sjulian
955137500Sjulian	/* speed > 48000 not cared */
956137500Sjulian	dv = ((ch->speed << 16) + 24000) / 48000;
957137500Sjulian
958137500Sjulian	/* RATECONV doesn't seem to like dv == 0x10000. */
959137500Sjulian	if (dv == 0x10000)
960137500Sjulian		dv--;
961137500Sjulian
962137500Sjulian	if (ch->stereo) {
963137500Sjulian		wpwa = (ch->srcphys - ch->base) >> 1;
964137500Sjulian		wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1;
965137500Sjulian		wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
966137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
967137500Sjulian		pan = PAN_LEFT - PAN_FRONT;
968137500Sjulian	} else {
969137500Sjulian		wpwa = (ch->phys - ch->base) >> 1;
970137500Sjulian		wpwa2 = (ch->srcphys - ch->base) >> 1;
971137500Sjulian		wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
972137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
973137500Sjulian		pan = 0;
974137500Sjulian	}
975137500Sjulian
976137500Sjulian	agg_lock(ch->parent);
977137500Sjulian
978137500Sjulian	ch->hwptr = 0;
979137500Sjulian	agg_power(ch->parent, powerstate_active);
980137500Sjulian
981137500Sjulian	/* Invalidate WaveCache. */
982137500Sjulian	wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO);
983137500Sjulian	wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO);
984137500Sjulian	wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO);
985137500Sjulian	wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO);
986137500Sjulian
987137500Sjulian	/* Load APU registers. */
988137500Sjulian	/* APU #0 : Sample rate converter for left/center. */
989137500Sjulian	apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa,
990137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
991137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0);
992137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT);
993137500Sjulian
994137500Sjulian	/* APU #1 : Sample rate converter for right. */
995137500Sjulian	apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2,
996137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
997137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0);
998137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT);
999137500Sjulian
1000137500Sjulian	/* APU #2 : Input mixer for left. */
1001137500Sjulian	apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0,
1002137500Sjulian		     ch->parent->bufsz >> 2, pan, 0x10000);
1003137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0);
1004137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0);
1005137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT);
1006137500Sjulian
1007137500Sjulian	/* APU #3 : Input mixer for right. */
1008137500Sjulian	apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2),
1009137500Sjulian		     ch->parent->bufsz >> 2, -pan, 0x10000);
1010137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0);
1011137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0);
1012137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT);
1013137500Sjulian
1014137500Sjulian	/* to mark this channel ready for intr. */
1015137500Sjulian	ch->parent->active |= (1 << ch->parent->playchns);
1016137500Sjulian
1017137500Sjulian	/* start adc */
1018137500Sjulian	critical_enter();
1019137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_APUTYPE,
1020137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1021137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_APUTYPE,
1022137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1023137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_APUTYPE,
1024137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1025137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_APUTYPE,
1026137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1027137500Sjulian	critical_exit();
1028137500Sjulian
1029137500Sjulian	set_timer(ch->parent);
1030137500Sjulian	wp_starttimer(ch->parent);
1031137500Sjulian	agg_unlock(ch->parent);
1032137500Sjulian}
1033137500Sjulian
1034137500Sjulianstatic void
1035137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch)
1036137500Sjulian{
1037137500Sjulian	int apuch;
1038137500Sjulian
1039137500Sjulian	agg_lock(ch->parent);
1040137500Sjulian
1041137500Sjulian	/* to mark that this channel no longer needs further intrs. */
1042137500Sjulian	ch->parent->active &= ~(1 << ch->parent->playchns);
1043137500Sjulian
1044137500Sjulian	for (apuch = 0; apuch < 4; apuch++)
1045137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
1046137500Sjulian		    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
1047137500Sjulian
1048137500Sjulian	if (ch->parent->active) {
1049137500Sjulian		set_timer(ch->parent);
1050137500Sjulian		wp_starttimer(ch->parent);
1051137500Sjulian	} else {
1052137500Sjulian		wp_stoptimer(ch->parent);
1053137500Sjulian		agg_power(ch->parent, powerstate_idle);
1054137500Sjulian	}
1055137500Sjulian	agg_unlock(ch->parent);
1056137500Sjulian}
1057137500Sjulian
105865543Scg/*
1059137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving.
1060137500Sjulian * This function expects n not overwrapping the buffer boundary.
1061137500Sjulian * Note that n is measured in sample unit.
1062137500Sjulian *
1063137500Sjulian * XXX - this function works in 16bit stereo format only.
1064137500Sjulian */
1065166279Sariffstatic void
1066137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n)
1067137500Sjulian{
1068137500Sjulian	int16_t *end;
1069137500Sjulian
1070137500Sjulian	for (end = l + n; l < end; ) {
1071137500Sjulian		*p++ = *l++;
1072137500Sjulian		*p++ = *r++;
1073137500Sjulian	}
1074137500Sjulian}
1075137500Sjulian
1076137500Sjulianstatic void
1077137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch)
1078137500Sjulian{
1079137500Sjulian	unsigned cur, last;
1080137500Sjulian	int16_t *src2;
1081137500Sjulian
1082137500Sjulian	agg_lock(ch->parent);
1083137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1084137500Sjulian	agg_unlock(ch->parent);
1085137500Sjulian	cur -= 0xffff & ((ch->srcphys - ch->base) >> 1);
1086137500Sjulian	last = ch->hwptr;
1087137500Sjulian	src2 = ch->src + ch->parent->bufsz/4;
1088137500Sjulian
1089137500Sjulian	if (cur < last) {
1090137500Sjulian		interleave(ch->src + last, src2 + last,
1091137500Sjulian			   ch->sink + 2*last, ch->buflen/2 - last);
1092137500Sjulian		interleave(ch->src, src2,
1093137500Sjulian			   ch->sink, cur);
1094137500Sjulian	} else if (cur > last)
1095137500Sjulian		interleave(ch->src + last, src2 + last,
1096137500Sjulian			   ch->sink + 2*last, cur - last);
1097137500Sjulian	ch->hwptr = cur;
1098137500Sjulian}
1099137500Sjulian
1100137500Sjulian/*
1101137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center.
1102137500Sjulian * This function expects n not overwrapping the buffer boundary.
1103137500Sjulian * Note that n is measured in sample unit.
1104137500Sjulian *
1105137500Sjulian * XXX - this function works in 16bit monoral format only.
1106137500Sjulian */
1107166279Sariffstatic void
1108137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n)
1109137500Sjulian{
1110137500Sjulian	int16_t *end;
1111137500Sjulian
1112137500Sjulian	for (end = dest + n; dest < end; dest++)
1113137500Sjulian		*dest = (int16_t)(((int)*dest - (int)*src++) / 2);
1114137500Sjulian}
1115137500Sjulian
1116137500Sjulianstatic void
1117137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch)
1118137500Sjulian{
1119137500Sjulian	unsigned cur, last;
1120137500Sjulian
1121137500Sjulian	agg_lock(ch->parent);
1122137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1123137500Sjulian	agg_unlock(ch->parent);
1124137500Sjulian	cur -= 0xffff & ((ch->phys - ch->base) >> 1);
1125137500Sjulian	last = ch->hwptr;
1126137500Sjulian
1127137500Sjulian	if (cur < last) {
1128137500Sjulian		mixdown(ch->src + last, ch->sink + last, ch->buflen - last);
1129137500Sjulian		mixdown(ch->src, ch->sink, cur);
1130137500Sjulian	} else if (cur > last)
1131137500Sjulian		mixdown(ch->src + last, ch->sink + last, cur - last);
1132137500Sjulian	ch->hwptr = cur;
1133137500Sjulian}
1134137500Sjulian
1135166279Sariff#ifdef AGG_JITTER_CORRECTION
1136137500Sjulian/*
113765543Scg * Stereo jitter suppressor.
113865543Scg * Sometimes playback pointers differ in stereo-paired channels.
113965543Scg * Calling this routine within intr fixes the problem.
114065543Scg */
1141166279Sariffstatic void
114265543Scgsuppress_jitter(struct agg_chinfo *ch)
114365543Scg{
1144137500Sjulian	if (ch->stereo) {
1145137500Sjulian		int cp1, cp2, diff /*, halfsize*/ ;
114665543Scg
1147137500Sjulian		/*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/
1148137500Sjulian		cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1149137500Sjulian		cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR);
1150137500Sjulian		if (cp1 != cp2) {
1151137500Sjulian			diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1152137500Sjulian			if (diff > 1 /* && diff < halfsize*/ )
1153137500Sjulian				AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1154137500Sjulian		}
115565543Scg	}
115665543Scg}
115765543Scg
1158166279Sariffstatic void
1159137500Sjuliansuppress_rec_jitter(struct agg_rchinfo *ch)
1160137500Sjulian{
1161137500Sjulian	int cp1, cp2, diff /*, halfsize*/ ;
1162137500Sjulian
1163137500Sjulian	/*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/
1164137500Sjulian	cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1)
1165137500Sjulian		+ wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1166137500Sjulian	cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR);
1167137500Sjulian	if (cp1 != cp2) {
1168137500Sjulian		diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1169137500Sjulian		if (diff > 1 /* && diff < halfsize*/ )
1170137500Sjulian			AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1171137500Sjulian	}
1172137500Sjulian}
1173166279Sariff#endif
1174137500Sjulian
1175166279Sariffstatic u_int
1176137500Sjuliancalc_timer_div(struct agg_chinfo *ch)
117765543Scg{
1178137500Sjulian	u_int speed;
117965543Scg
1180137500Sjulian	speed = ch->speed;
1181137500Sjulian#ifdef INVARIANTS
1182137500Sjulian	if (speed == 0) {
1183137500Sjulian		printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n",
1184137500Sjulian		       ch->num);
1185137500Sjulian		speed = 1;
1186137500Sjulian	}
1187137500Sjulian#endif
1188137500Sjulian	return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo))
1189137500Sjulian		+ speed - 1) / speed;
1190137500Sjulian}
119165543Scg
1192166279Sariffstatic u_int
1193137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch)
1194137500Sjulian{
1195137500Sjulian	u_int speed;
1196137500Sjulian
1197137500Sjulian	speed = ch->speed;
1198137500Sjulian#ifdef INVARIANTS
1199137500Sjulian	if (speed == 0) {
1200137500Sjulian		printf("snd_maestro: rch.speed == 0, which shouldn't\n");
1201137500Sjulian		speed = 1;
1202137500Sjulian	}
1203137500Sjulian#endif
1204137500Sjulian	return (48000 * (ch->blklen << (!ch->stereo))
1205137500Sjulian		+ speed - 1) / speed;
120665543Scg}
120765543Scg
120865543Scgstatic void
120965543Scgset_timer(struct agg_info *ess)
121065543Scg{
121165543Scg	int i;
1212137500Sjulian	u_int	dv = 32 << 7, newdv;
121365543Scg
121465543Scg	for (i = 0; i < ess->playchns; i++)
121565543Scg		if ((ess->active & (1 << i)) &&
1216137500Sjulian		    (dv > (newdv = calc_timer_div(ess->pch + i))))
1217137500Sjulian			dv = newdv;
1218137500Sjulian	if ((ess->active & (1 << i)) &&
1219137500Sjulian	    (dv > (newdv = calc_timer_div_rch(&ess->rch))))
1220137500Sjulian		dv = newdv;
122165543Scg
1222137500Sjulian	wp_settimer(ess, dv);
122365543Scg}
122465543Scg
122565543Scg
122665543Scg/* -----------------------------
122765543Scg * Newpcm glue.
122865543Scg */
122965543Scg
1230137500Sjulian/* AC97 mixer interface. */
1231137500Sjulian
1232137500Sjulianstatic u_int32_t
1233137500Sjulianagg_ac97_init(kobj_t obj, void *sc)
1234137500Sjulian{
1235137500Sjulian	struct agg_info *ess = sc;
1236137500Sjulian
1237137500Sjulian	return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1;
1238137500Sjulian}
1239137500Sjulian
1240137500Sjulianstatic int
1241137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno)
1242137500Sjulian{
1243137500Sjulian	struct agg_info *ess = sc;
1244137500Sjulian	int ret;
1245137500Sjulian
1246154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1247137500Sjulian	ret = agg_rdcodec(ess, regno);
1248154067Sariff	/* agg_unlock(ess); */
1249137500Sjulian	return ret;
1250137500Sjulian}
1251137500Sjulian
1252137500Sjulianstatic int
1253137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data)
1254137500Sjulian{
1255137500Sjulian	struct agg_info *ess = sc;
1256137500Sjulian	int ret;
1257137500Sjulian
1258154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1259137500Sjulian	ret = agg_wrcodec(ess, regno, data);
1260154067Sariff	/* agg_unlock(ess); */
1261137500Sjulian	return ret;
1262137500Sjulian}
1263137500Sjulian
1264137500Sjulian
1265137500Sjulianstatic kobj_method_t agg_ac97_methods[] = {
1266137500Sjulian    	KOBJMETHOD(ac97_init,		agg_ac97_init),
1267137500Sjulian    	KOBJMETHOD(ac97_read,		agg_ac97_read),
1268137500Sjulian    	KOBJMETHOD(ac97_write,		agg_ac97_write),
1269137500Sjulian	{ 0, 0 }
1270137500Sjulian};
1271137500SjulianAC97_DECLARE(agg_ac97);
1272137500Sjulian
1273137500Sjulian
1274137500Sjulian/* -------------------------------------------------------------------- */
1275137500Sjulian
1276137500Sjulian/* Playback channel. */
1277137500Sjulian
127865543Scgstatic void *
1279166279Sariffaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1280166279Sariff						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	};
1460154241Sariff	static struct pcmchan_caps playcaps = {8000, 48000, 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 *
1485166279Sariffaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1486166279Sariff						struct pcm_channel *c, int dir)
1487137500Sjulian{
1488137500Sjulian	struct agg_info *ess = devinfo;
1489137500Sjulian	struct agg_rchinfo *ch;
1490137500Sjulian	u_int8_t *p;
1491137500Sjulian
1492137500Sjulian	KASSERT((dir == PCMDIR_REC),
1493137500Sjulian	    ("aggrch_init() called for PLAYBACK channel!"));
1494137500Sjulian	ch = &ess->rch;
1495137500Sjulian
1496137500Sjulian	ch->parent = ess;
1497137500Sjulian	ch->channel = c;
1498137500Sjulian	ch->buffer = b;
1499137500Sjulian
1500137500Sjulian	/* Uses the bottom-half of the status buffer. */
1501137500Sjulian	p        = ess->stat + ess->bufsz;
1502137500Sjulian	ch->phys = ess->phys + ess->bufsz;
1503137500Sjulian	ch->base = ess->phys;
1504137500Sjulian	ch->src  = (int16_t *)(p + ess->bufsz);
1505137500Sjulian	ch->srcphys = ch->phys + ess->bufsz;
1506137500Sjulian	ch->sink = (int16_t *)p;
1507137500Sjulian
1508137500Sjulian	sndbuf_setup(b, p, ess->bufsz);
1509137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1510137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1511137500Sjulian
1512137500Sjulian	return ch;
1513137500Sjulian}
1514137500Sjulian
1515137500Sjulianstatic int
1516137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format)
1517137500Sjulian{
1518137500Sjulian	struct agg_rchinfo *ch = data;
1519137500Sjulian
1520137500Sjulian	if (!(format & AFMT_S16_LE))
1521137500Sjulian		return EINVAL;
1522137500Sjulian	if (format & AFMT_STEREO)
1523137500Sjulian		ch->stereo = 1;
1524137500Sjulian	else
1525137500Sjulian		ch->stereo = 0;
1526137500Sjulian	return 0;
1527137500Sjulian}
1528137500Sjulian
1529137500Sjulianstatic int
1530137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed)
1531137500Sjulian{
1532137500Sjulian	return ((struct agg_rchinfo*)data)->speed = speed;
1533137500Sjulian}
1534137500Sjulian
1535137500Sjulianstatic int
1536137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1537137500Sjulian{
1538137500Sjulian	struct agg_rchinfo *ch = data;
1539137500Sjulian	int blkcnt;
1540137500Sjulian
1541137500Sjulian	/* try to keep at least 20msec DMA space */
1542137500Sjulian	blkcnt = (ch->speed << ch->stereo) / (25 * blocksize);
1543137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1544137500Sjulian
1545137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1546137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1547137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1548137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1549137500Sjulian	} else {
1550137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1551137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1552137500Sjulian	}
1553137500Sjulian
1554137500Sjulian	ch->blklen = blocksize / 2;
1555137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1556137500Sjulian	return blocksize;
1557137500Sjulian}
1558137500Sjulian
1559137500Sjulianstatic int
1560137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go)
1561137500Sjulian{
1562137500Sjulian	struct agg_rchinfo *ch = sc;
1563137500Sjulian
1564137500Sjulian	switch (go) {
1565137500Sjulian	case PCMTRIG_EMLDMARD:
1566137500Sjulian		if (ch->stereo)
1567137500Sjulian			aggch_feed_adc_stereo(ch);
1568137500Sjulian		else
1569137500Sjulian			aggch_feed_adc_mono(ch);
1570137500Sjulian		break;
1571137500Sjulian	case PCMTRIG_START:
1572137500Sjulian		aggch_start_adc(ch);
1573137500Sjulian		break;
1574137500Sjulian	case PCMTRIG_ABORT:
1575137500Sjulian	case PCMTRIG_STOP:
1576137500Sjulian		aggch_stop_adc(ch);
1577137500Sjulian		break;
1578137500Sjulian	}
1579137500Sjulian	return 0;
1580137500Sjulian}
1581137500Sjulian
1582137500Sjulianstatic int
1583137500Sjulianaggrch_getptr(kobj_t obj, void *sc)
1584137500Sjulian{
1585137500Sjulian	struct agg_rchinfo *ch = sc;
1586137500Sjulian
1587137500Sjulian	return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1;
1588137500Sjulian}
1589137500Sjulian
1590137500Sjulianstatic struct pcmchan_caps *
1591137500Sjulianaggrch_getcaps(kobj_t obj, void *sc)
1592137500Sjulian{
159365543Scg	static u_int32_t recfmt[] = {
159465543Scg		AFMT_S16_LE,
159565543Scg		AFMT_STEREO | AFMT_S16_LE,
159665543Scg		0
159765543Scg	};
1598137500Sjulian	static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0};
159965543Scg
1600137500Sjulian	return &reccaps;
160165543Scg}
160265543Scg
1603137500Sjulianstatic kobj_method_t aggrch_methods[] = {
1604137500Sjulian	KOBJMETHOD(channel_init,		aggrch_init),
1605137500Sjulian	/* channel_free: no-op */
1606137500Sjulian	KOBJMETHOD(channel_setformat,		aggrch_setformat),
1607137500Sjulian	KOBJMETHOD(channel_setspeed,		aggrch_setspeed),
1608137500Sjulian	KOBJMETHOD(channel_setblocksize,	aggrch_setblocksize),
1609137500Sjulian	KOBJMETHOD(channel_trigger,		aggrch_trigger),
1610137500Sjulian	KOBJMETHOD(channel_getptr,		aggrch_getptr),
1611137500Sjulian	KOBJMETHOD(channel_getcaps,		aggrch_getcaps),
161270134Scg	{ 0, 0 }
161370134Scg};
1614137500SjulianCHANNEL_DECLARE(aggrch);
161565543Scg
1616137500Sjulian
161765543Scg/* -----------------------------
161865543Scg * Bus space.
161965543Scg */
162065543Scg
162165543Scgstatic void
162265543Scgagg_intr(void *sc)
162365543Scg{
162465543Scg	struct agg_info* ess = sc;
1625137500Sjulian	register u_int8_t status;
162665543Scg	int i;
1627137500Sjulian	u_int m;
162865543Scg
1629137500Sjulian	status = AGG_RD(ess, PORT_HOSTINT_STAT, 1);
163065543Scg	if (!status)
163165543Scg		return;
163265543Scg
1633137500Sjulian	/* Acknowledge intr. */
1634137500Sjulian	AGG_WR(ess, PORT_HOSTINT_STAT, status, 1);
163570619Sjhb
1636137500Sjulian	if (status & HOSTINT_STAT_DSOUND) {
1637137500Sjulian#ifdef AGG_JITTER_CORRECTION
1638137500Sjulian		agg_lock(ess);
1639137500Sjulian#endif
1640137500Sjulian		if (ess->curpwr <= PCI_POWERSTATE_D1) {
1641137500Sjulian			AGG_WR(ess, PORT_INT_STAT, 1, 2);
1642137500Sjulian#ifdef AGG_JITTER_CORRECTION
1643137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1644137500Sjulian				if (ess->active & m)
1645137500Sjulian					suppress_jitter(ess->pch + i);
1646137500Sjulian			}
1647137500Sjulian			if (ess->active & m)
1648137500Sjulian				suppress_rec_jitter(&ess->rch);
1649137500Sjulian			agg_unlock(ess);
1650137500Sjulian#endif
1651137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1652137500Sjulian				if (ess->active & m) {
1653137500Sjulian					if (ess->curpwr <= PCI_POWERSTATE_D1)
1654137500Sjulian						chn_intr(ess->pch[i].channel);
1655137500Sjulian					else {
1656137500Sjulian						m = 0;
1657137500Sjulian						break;
1658137500Sjulian					}
1659137500Sjulian				}
1660137500Sjulian			}
1661137500Sjulian			if ((ess->active & m)
1662137500Sjulian			    && ess->curpwr <= PCI_POWERSTATE_D1)
1663137500Sjulian				chn_intr(ess->rch.channel);
1664137500Sjulian		}
1665137500Sjulian#ifdef AGG_JITTER_CORRECTION
1666137500Sjulian		else
1667137500Sjulian			agg_unlock(ess);
1668137500Sjulian#endif
1669137500Sjulian	}
1670137500Sjulian
167165543Scg	if (status & HOSTINT_STAT_HWVOL) {
1672137500Sjulian		register u_int8_t event;
167370619Sjhb
1674137500Sjulian		agg_lock(ess);
1675137500Sjulian		event = AGG_RD(ess, PORT_HWVOL_MASTER, 1);
1676137500Sjulian		AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1);
1677137500Sjulian		agg_unlock(ess);
1678137500Sjulian
167970619Sjhb		switch (event) {
168070619Sjhb		case HWVOL_UP:
168170945Sjhb			mixer_hwvol_step(ess->dev, 1, 1);
168270619Sjhb			break;
168370619Sjhb		case HWVOL_DOWN:
168470945Sjhb			mixer_hwvol_step(ess->dev, -1, -1);
168570619Sjhb			break;
168670619Sjhb		case HWVOL_NOP:
168770619Sjhb			break;
168870619Sjhb		default:
1689137500Sjulian			if (event & HWVOL_MUTE) {
1690137500Sjulian				mixer_hwvol_mute(ess->dev);
1691137500Sjulian				break;
1692137500Sjulian			}
1693137500Sjulian			device_printf(ess->dev,
1694137500Sjulian				      "%s: unknown HWVOL event 0x%x\n",
1695137500Sjulian				      device_get_nameunit(ess->dev), event);
169665543Scg		}
169765543Scg	}
169865543Scg}
169965543Scg
170065543Scgstatic void
170165543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
170265543Scg{
170365543Scg	bus_addr_t *phys = arg;
170465543Scg
170565543Scg	*phys = error? 0 : segs->ds_addr;
170665543Scg
170765543Scg	if (bootverbose) {
170865543Scg		printf("setmap (%lx, %lx), nseg=%d, error=%d\n",
170965543Scg		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
171065543Scg		    nseg, error);
171165543Scg	}
171265543Scg}
171365543Scg
171465543Scgstatic void *
1715137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys)
171665543Scg{
171765543Scg	void *buf;
171865543Scg	bus_dmamap_t map;
171965543Scg
1720137500Sjulian	if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map))
172165543Scg		return NULL;
1722137500Sjulian	if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0)
1723137500Sjulian	    || !*phys || map) {
1724137500Sjulian		bus_dmamem_free(dmat, buf, map);
172565543Scg		return NULL;
172665543Scg	}
172765543Scg	return buf;
172865543Scg}
172965543Scg
173065543Scgstatic void
1731137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf)
173265543Scg{
1733137500Sjulian	bus_dmamem_free(dmat, buf, NULL);
173465543Scg}
173565543Scg
173665543Scgstatic int
173765543Scgagg_probe(device_t dev)
173865543Scg{
173965543Scg	char *s = NULL;
174065543Scg
174165543Scg	switch (pci_get_devid(dev)) {
174265543Scg	case MAESTRO_1_PCI_ID:
174365543Scg		s = "ESS Technology Maestro-1";
174465543Scg		break;
174565543Scg
174665543Scg	case MAESTRO_2_PCI_ID:
174765543Scg		s = "ESS Technology Maestro-2";
174865543Scg		break;
174965543Scg
175065543Scg	case MAESTRO_2E_PCI_ID:
175165543Scg		s = "ESS Technology Maestro-2E";
175265543Scg		break;
175365543Scg	}
175465543Scg
175565543Scg	if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) {
175665543Scg		device_set_desc(dev, s);
1757142890Simp		return BUS_PROBE_DEFAULT;
175865543Scg	}
175965543Scg	return ENXIO;
176065543Scg}
176165543Scg
176265543Scgstatic int
176365543Scgagg_attach(device_t dev)
176465543Scg{
176565543Scg	struct agg_info	*ess = NULL;
176665543Scg	u_int32_t	data;
1767119690Sjhb	int	regid = PCIR_BAR(0);
176865543Scg	struct resource	*reg = NULL;
176965543Scg	struct ac97_info	*codec = NULL;
177065543Scg	int	irqid = 0;
177165543Scg	struct resource	*irq = NULL;
177265543Scg	void	*ih = NULL;
177365543Scg	char	status[SND_STATUSLEN];
1774137500Sjulian	int	ret = 0;
177565543Scg
177678564Sgreid	if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
177765543Scg		device_printf(dev, "cannot allocate softc\n");
1778137500Sjulian		ret = ENOMEM;
1779137500Sjulian		goto bad;
178065543Scg	}
178165543Scg	ess->dev = dev;
178265543Scg
1783137500Sjulian#ifdef USING_MUTEX
1784137500Sjulian	mtx_init(&ess->lock, device_get_desc(dev), "hardware status lock",
1785137500Sjulian		 MTX_DEF | MTX_RECURSE);
1786137500Sjulian	if (!mtx_initialized(&ess->lock)) {
1787137500Sjulian		device_printf(dev, "failed to create a mutex.\n");
1788137500Sjulian		ret = ENOMEM;
1789137500Sjulian		goto bad;
1790137500Sjulian	}
1791137500Sjulian#endif
1792137500Sjulian
179384658Scg	ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536);
1794137500Sjulian	if (bus_dma_tag_create(/*parent*/ NULL,
1795137500Sjulian			       /*align */ 4, 1 << (16+1),
1796137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1797137500Sjulian			       /*filter*/ NULL, NULL,
1798137500Sjulian			       /*size  */ ess->bufsz, 1, 0x3ffff,
1799137500Sjulian			       /*flags */ 0,
1800137500Sjulian#if __FreeBSD_version >= 501102
1801137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1802137500Sjulian#endif
1803137500Sjulian			       &ess->buf_dmat) != 0) {
1804137500Sjulian		device_printf(dev, "unable to create dma tag\n");
1805137500Sjulian		ret = ENOMEM;
1806137500Sjulian		goto bad;
1807137500Sjulian	}
180884658Scg
180965543Scg	if (bus_dma_tag_create(/*parent*/NULL,
1810137500Sjulian			       /*align */ 1 << WAVCACHE_BASEADDR_SHIFT,
1811137500Sjulian			                  1 << (16+1),
1812137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1813137500Sjulian			       /*filter*/ NULL, NULL,
1814137500Sjulian			       /*size  */ 3*ess->bufsz, 1, 0x3ffff,
1815137500Sjulian			       /*flags */ 0,
1816137500Sjulian#if __FreeBSD_version >= 501102
1817137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1818137500Sjulian#endif
1819137500Sjulian			       &ess->stat_dmat) != 0) {
182065543Scg		device_printf(dev, "unable to create dma tag\n");
1821137500Sjulian		ret = ENOMEM;
182265543Scg		goto bad;
182365543Scg	}
182465543Scg
1825137500Sjulian	/* Allocate the room for brain-damaging status buffer. */
1826137500Sjulian	ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys);
182765543Scg	if (ess->stat == NULL) {
1828137500Sjulian		device_printf(dev, "cannot allocate status buffer\n");
1829137500Sjulian		ret = ENOMEM;
183065543Scg		goto bad;
183165543Scg	}
183265543Scg	if (bootverbose)
1833137500Sjulian		device_printf(dev, "Maestro status/record buffer: %#llx\n",
1834137500Sjulian		    (long long)ess->phys);
183565543Scg
1836137500Sjulian	/* State D0-uninitialized. */
1837137500Sjulian	ess->curpwr = PCI_POWERSTATE_D3;
1838137500Sjulian	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
183965543Scg
184065543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
184165543Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN);
184265543Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
184365543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
184465543Scg
1845137500Sjulian	/* Allocate resources. */
1846137500Sjulian	if (data & PCIM_CMD_PORTEN)
1847141095Simp		reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &regid,
1848141095Simp		    RF_ACTIVE);
1849137500Sjulian	if (reg != NULL) {
1850137500Sjulian		ess->reg = reg;
1851137500Sjulian		ess->regid = regid;
1852137500Sjulian		ess->st = rman_get_bustag(reg);
1853137500Sjulian		ess->sh = rman_get_bushandle(reg);
1854137500Sjulian	} else {
185565543Scg		device_printf(dev, "unable to map register space\n");
1856137500Sjulian		ret = ENXIO;
185765543Scg		goto bad;
185865543Scg	}
1859141095Simp	irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid,
1860141095Simp	    RF_ACTIVE | RF_SHAREABLE);
1861137500Sjulian	if (irq != NULL) {
1862137500Sjulian		ess->irq = irq;
1863137500Sjulian		ess->irqid = irqid;
1864137500Sjulian	} else {
1865137500Sjulian		device_printf(dev, "unable to map interrupt\n");
1866137500Sjulian		ret = ENXIO;
1867137500Sjulian		goto bad;
1868137500Sjulian	}
186965543Scg
1870137500Sjulian	/* Setup resources. */
1871137500Sjulian	if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) {
1872137500Sjulian		device_printf(dev, "unable to setup interrupt\n");
1873137500Sjulian		ret = ENXIO;
1874137500Sjulian		goto bad;
1875137500Sjulian	} else
1876137500Sjulian		ess->ih = ih;
1877137500Sjulian
1878137500Sjulian	/* Transition from D0-uninitialized to D0. */
1879137500Sjulian	agg_lock(ess);
1880137500Sjulian	agg_power(ess, PCI_POWERSTATE_D0);
1881137500Sjulian	if (agg_rdcodec(ess, 0) == 0x80) {
1882137500Sjulian		/* XXX - TODO: PT101 */
1883154067Sariff		agg_unlock(ess);
188465543Scg		device_printf(dev, "PT101 codec detected!\n");
1885137500Sjulian		ret = ENXIO;
188665543Scg		goto bad;
188765543Scg	}
1888154067Sariff	agg_unlock(ess);
188970134Scg	codec = AC97_CREATE(dev, ess, agg_ac97);
1890137500Sjulian	if (codec == NULL) {
1891137500Sjulian		device_printf(dev, "failed to create AC97 codec softc!\n");
1892137500Sjulian		ret = ENOMEM;
189365543Scg		goto bad;
1894137500Sjulian	}
1895137500Sjulian	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
1896137500Sjulian		device_printf(dev, "mixer initialization failed!\n");
1897137500Sjulian		ret = ENXIO;
189865543Scg		goto bad;
1899137500Sjulian	}
190065543Scg	ess->codec = codec;
190165543Scg
1902137500Sjulian	ret = pcm_register(dev, ess, AGG_MAXPLAYCH, 1);
1903137500Sjulian	if (ret)
190465543Scg		goto bad;
190565543Scg
190670945Sjhb	mixer_hwvol_init(dev);
1907154067Sariff	agg_lock(ess);
1908137500Sjulian	agg_power(ess, powerstate_init);
1909154067Sariff	agg_unlock(ess);
191065543Scg	for (data = 0; data < AGG_MAXPLAYCH; data++)
1911137500Sjulian		pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess);
191270134Scg	pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess);
1913137500Sjulian	adjust_pchbase(ess->pch, ess->playchns, ess->bufsz);
1914137500Sjulian
1915137500Sjulian	snprintf(status, SND_STATUSLEN,
1916137500Sjulian	    "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d",
1917137500Sjulian	    rman_get_start(reg), rman_get_end(reg), rman_get_start(irq),
1918137500Sjulian	    pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev));
191965543Scg	pcm_setstatus(dev, status);
192065543Scg
192165543Scg	return 0;
192265543Scg
192365543Scg bad:
192465644Scg	if (codec != NULL)
192565644Scg		ac97_destroy(codec);
192665543Scg	if (ih != NULL)
192765543Scg		bus_teardown_intr(dev, irq, ih);
192865543Scg	if (irq != NULL)
192965543Scg		bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
193065543Scg	if (reg != NULL)
193165543Scg		bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
1932154627Snetchild	if (ess != NULL) {
1933154627Snetchild		if (ess->stat != NULL)
1934154627Snetchild			dma_free(ess->stat_dmat, ess->stat);
1935154627Snetchild		if (ess->stat_dmat != NULL)
1936154627Snetchild			bus_dma_tag_destroy(ess->stat_dmat);
1937154627Snetchild		if (ess->buf_dmat != NULL)
1938154627Snetchild			bus_dma_tag_destroy(ess->buf_dmat);
1939137500Sjulian#ifdef USING_MUTEX
1940154627Snetchild		if (mtx_initialized(&ess->lock))
1941154627Snetchild			mtx_destroy(&ess->lock);
1942137500Sjulian#endif
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#ifdef USING_MUTEX
1984137500Sjulian	mtx_destroy(&ess->lock);
1985137500Sjulian#endif
198665543Scg	free(ess, M_DEVBUF);
198765543Scg	return 0;
198865543Scg}
198965543Scg
199065543Scgstatic int
199165543Scgagg_suspend(device_t dev)
199265543Scg{
199365543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
1994137500Sjulian#ifndef USING_MUTEX
1995137500Sjulian	int x;
199665543Scg
199765543Scg	x = spltty();
199865543Scg#endif
1999137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
2000137500Sjulian	agg_lock(ess);
2001137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2002137500Sjulian	agg_unlock(ess);
2003137500Sjulian#ifndef USING_MUTEX
200465543Scg	splx(x);
2005137500Sjulian#endif
200665543Scg
200765543Scg	return 0;
200865543Scg}
200965543Scg
201065543Scgstatic int
201165543Scgagg_resume(device_t dev)
201265543Scg{
2013137500Sjulian	int i;
201465543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
2015137500Sjulian#ifndef USING_MUTEX
2016137500Sjulian	int x;
201765543Scg
201865543Scg	x = spltty();
2019137500Sjulian#endif
202065543Scg	for (i = 0; i < ess->playchns; i++)
202165543Scg		if (ess->active & (1 << i))
202265543Scg			aggch_start_dac(ess->pch + i);
202365543Scg	if (ess->active & (1 << i))
202465543Scg		aggch_start_adc(&ess->rch);
2025137500Sjulian
2026137500Sjulian	agg_lock(ess);
2027137500Sjulian	if (!ess->active)
2028137500Sjulian		agg_power(ess, powerstate_init);
2029137500Sjulian	agg_unlock(ess);
2030137500Sjulian#ifndef USING_MUTEX
2031137500Sjulian	splx(x);
203265543Scg#endif
2033137500Sjulian
2034137500Sjulian	if (mixer_reinit(dev)) {
2035137500Sjulian		device_printf(dev, "unable to reinitialize the mixer\n");
2036137500Sjulian		return ENXIO;
203765543Scg	}
2038137500Sjulian
203965543Scg	return 0;
204065543Scg}
204165543Scg
204265543Scgstatic int
204365543Scgagg_shutdown(device_t dev)
204465543Scg{
204565543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
204665543Scg
2047137500Sjulian	agg_lock(ess);
2048137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2049137500Sjulian	agg_unlock(ess);
205065543Scg
205165543Scg	return 0;
205265543Scg}
205365543Scg
205465543Scg
205565543Scgstatic device_method_t agg_methods[] = {
205665543Scg    DEVMETHOD(device_probe,	agg_probe),
205765543Scg    DEVMETHOD(device_attach,	agg_attach),
205865543Scg    DEVMETHOD(device_detach,	agg_detach),
205965543Scg    DEVMETHOD(device_suspend,	agg_suspend),
206065543Scg    DEVMETHOD(device_resume,	agg_resume),
206165543Scg    DEVMETHOD(device_shutdown,	agg_shutdown),
206265543Scg
206365543Scg    { 0, 0 }
206465543Scg};
206565543Scg
206665543Scgstatic driver_t agg_driver = {
206765543Scg    "pcm",
206865543Scg    agg_methods,
206982180Scg    PCM_SOFTC_SIZE,
207065543Scg};
207165543Scg
2072137500Sjulian/*static devclass_t pcm_devclass;*/
2073137500Sjulian
207465543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
2075132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
207665543ScgMODULE_VERSION(snd_maestro, 1);
2077