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> */
194248085Smariusstatic 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
210260298Sdim#if 0
211166279Sariffstatic __inline u_int32_t	agg_rd(struct agg_info*, int, int size);
212166279Sariffstatic __inline void		agg_wr(struct agg_info*, int, u_int32_t data,
213166279Sariff								int size);
214260298Sdim#endif
215166279Sariffstatic int	agg_rdcodec(struct agg_info*, int);
216166279Sariffstatic int	agg_wrcodec(struct agg_info*, int, u_int32_t);
217137500Sjulian
218166279Sariffstatic void	ringbus_setdest(struct agg_info*, int, int);
219137500Sjulian
220166279Sariffstatic u_int16_t	wp_rdreg(struct agg_info*, u_int16_t);
221166279Sariffstatic void		wp_wrreg(struct agg_info*, u_int16_t, u_int16_t);
222166279Sariffstatic u_int16_t	wp_rdapu(struct agg_info*, unsigned, u_int16_t);
223166279Sariffstatic void	wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t);
224166279Sariffstatic void	wp_settimer(struct agg_info*, u_int);
225166279Sariffstatic void	wp_starttimer(struct agg_info*);
226166279Sariffstatic void	wp_stoptimer(struct agg_info*);
22765543Scg
228166279Sariff#if 0
229166279Sariffstatic u_int16_t	wc_rdreg(struct agg_info*, u_int16_t);
230166279Sariff#endif
231166279Sariffstatic void		wc_wrreg(struct agg_info*, u_int16_t, u_int16_t);
232166279Sariff#if 0
233166279Sariffstatic u_int16_t	wc_rdchctl(struct agg_info*, int);
234166279Sariff#endif
235166279Sariffstatic void		wc_wrchctl(struct agg_info*, int, u_int16_t);
23665543Scg
237166279Sariffstatic void	agg_stopclock(struct agg_info*, int part, int st);
23865543Scg
239166279Sariffstatic void	agg_initcodec(struct agg_info*);
240166279Sariffstatic void	agg_init(struct agg_info*);
241166279Sariffstatic void	agg_power(struct agg_info*, int);
24265543Scg
243166279Sariffstatic void	aggch_start_dac(struct agg_chinfo*);
244166279Sariffstatic void	aggch_stop_dac(struct agg_chinfo*);
245166279Sariffstatic void	aggch_start_adc(struct agg_rchinfo*);
246166279Sariffstatic void	aggch_stop_adc(struct agg_rchinfo*);
247166279Sariffstatic void	aggch_feed_adc_stereo(struct agg_rchinfo*);
248166279Sariffstatic void	aggch_feed_adc_mono(struct agg_rchinfo*);
24965543Scg
250166279Sariff#ifdef AGG_JITTER_CORRECTION
251166279Sariffstatic void	suppress_jitter(struct agg_chinfo*);
252166279Sariffstatic void	suppress_rec_jitter(struct agg_rchinfo*);
253166279Sariff#endif
25465543Scg
255166279Sariffstatic void	set_timer(struct agg_info*);
25665543Scg
257166279Sariffstatic void	agg_intr(void *);
258166279Sariffstatic int	agg_probe(device_t);
259166279Sariffstatic int	agg_attach(device_t);
260166279Sariffstatic int	agg_detach(device_t);
261166279Sariffstatic int	agg_suspend(device_t);
262166279Sariffstatic int	agg_resume(device_t);
263166279Sariffstatic int	agg_shutdown(device_t);
26465543Scg
265137500Sjulianstatic void	*dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*);
266166279Sariffstatic void	dma_free(bus_dma_tag_t, void *);
26765543Scg
268137500Sjulian
26965543Scg/* -----------------------------
27065543Scg * Subsystems.
27165543Scg */
27265543Scg
273137500Sjulian/* locking */
274166279Sariff#define agg_lock(sc)	snd_mtxlock(&((sc)->lock))
275166279Sariff#define agg_unlock(sc)	snd_mtxunlock(&((sc)->lock))
27665543Scg
277166279Sariffstatic void
278137500Sjulianagg_sleep(struct agg_info *sc, const char *wmesg, int msec)
279137500Sjulian{
280137500Sjulian	int timo;
281137500Sjulian
282137500Sjulian	timo = msec * hz / 1000;
283137500Sjulian	if (timo == 0)
284137500Sjulian		timo = 1;
285137500Sjulian	msleep(sc, &sc->lock, PWAIT, wmesg, timo);
286137500Sjulian}
287137500Sjulian
288137500Sjulian
289137500Sjulian/* I/O port */
290137500Sjulian
291260298Sdim#if 0
292166279Sariffstatic __inline u_int32_t
293137500Sjulianagg_rd(struct agg_info *sc, int regno, int size)
294137500Sjulian{
295137500Sjulian	switch (size) {
296137500Sjulian	case 1:
297137500Sjulian		return bus_space_read_1(sc->st, sc->sh, regno);
298137500Sjulian	case 2:
299137500Sjulian		return bus_space_read_2(sc->st, sc->sh, regno);
300137500Sjulian	case 4:
301137500Sjulian		return bus_space_read_4(sc->st, sc->sh, regno);
302137500Sjulian	default:
303137500Sjulian		return ~(u_int32_t)0;
304137500Sjulian	}
305137500Sjulian}
306260298Sdim#endif
307137500Sjulian
308137500Sjulian#define AGG_RD(sc, regno, size)           \
309137500Sjulian	bus_space_read_##size(            \
310137500Sjulian	    ((struct agg_info*)(sc))->st, \
311137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno))
312137500Sjulian
313260298Sdim#if 0
314166279Sariffstatic __inline void
315137500Sjulianagg_wr(struct agg_info *sc, int regno, u_int32_t data, int size)
316137500Sjulian{
317137500Sjulian	switch (size) {
318137500Sjulian	case 1:
319137500Sjulian		bus_space_write_1(sc->st, sc->sh, regno, data);
320137500Sjulian		break;
321137500Sjulian	case 2:
322137500Sjulian		bus_space_write_2(sc->st, sc->sh, regno, data);
323137500Sjulian		break;
324137500Sjulian	case 4:
325137500Sjulian		bus_space_write_4(sc->st, sc->sh, regno, data);
326137500Sjulian		break;
327137500Sjulian	}
328137500Sjulian}
329260298Sdim#endif
330137500Sjulian
331137500Sjulian#define AGG_WR(sc, regno, data, size)     \
332137500Sjulian	bus_space_write_##size(           \
333137500Sjulian	    ((struct agg_info*)(sc))->st, \
334137500Sjulian	    ((struct agg_info*)(sc))->sh, (regno), (data))
335137500Sjulian
33670134Scg/* -------------------------------------------------------------------- */
33770134Scg
338137500Sjulian/* Codec/Ringbus */
339137500Sjulian
340166279Sariffstatic int
341137500Sjulianagg_codec_wait4idle(struct agg_info *ess)
34265543Scg{
343137500Sjulian	unsigned t = 26;
34470134Scg
345137500Sjulian	while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) {
346137500Sjulian		if (--t == 0)
347137500Sjulian			return EBUSY;
348137500Sjulian		DELAY(2);	/* 20.8us / 13 */
349137500Sjulian	}
350137500Sjulian	return 0;
35170134Scg}
35270134Scg
353137500Sjulian
354166279Sariffstatic int
355137500Sjulianagg_rdcodec(struct agg_info *ess, int regno)
35670134Scg{
357137500Sjulian	int ret;
35865543Scg
35965543Scg	/* We have to wait for a SAFE time to write addr/data */
360137500Sjulian	if (agg_codec_wait4idle(ess)) {
361137500Sjulian		/* Timed out. No read performed. */
362137500Sjulian		device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n");
363137500Sjulian		return -1;
36465543Scg	}
36565543Scg
366137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1);
367137500Sjulian	/*DELAY(21);	* AC97 cycle = 20.8usec */
36865543Scg
36965543Scg	/* Wait for data retrieve */
370137500Sjulian	if (!agg_codec_wait4idle(ess)) {
371137500Sjulian		ret = AGG_RD(ess, PORT_CODEC_REG, 2);
372137500Sjulian	} else {
373137500Sjulian		/* Timed out. No read performed. */
374137500Sjulian		device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n");
375137500Sjulian		ret = -1;
37665543Scg	}
37765543Scg
378137500Sjulian	return ret;
37965543Scg}
38065543Scg
381166279Sariffstatic int
382137500Sjulianagg_wrcodec(struct agg_info *ess, int regno, u_int32_t data)
38365543Scg{
38465543Scg	/* We have to wait for a SAFE time to write addr/data */
385137500Sjulian	if (agg_codec_wait4idle(ess)) {
38665543Scg		/* Timed out. Abort writing. */
38765543Scg		device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n");
38870134Scg		return -1;
38965543Scg	}
39065543Scg
391137500Sjulian	AGG_WR(ess, PORT_CODEC_REG, data, 2);
392137500Sjulian	AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1);
39370134Scg
394137500Sjulian	/* Wait for write completion */
395137500Sjulian	if (agg_codec_wait4idle(ess)) {
396137500Sjulian		/* Timed out. */
397137500Sjulian		device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n");
398137500Sjulian		return -1;
399137500Sjulian	}
400137500Sjulian
40170134Scg	return 0;
40265543Scg}
40365543Scg
404166279Sariffstatic void
40565543Scgringbus_setdest(struct agg_info *ess, int src, int dest)
40665543Scg{
40765543Scg	u_int32_t	data;
40865543Scg
409137500Sjulian	data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4);
41065543Scg	data &= ~(0xfU << src);
41165543Scg	data |= (0xfU & dest) << src;
412137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4);
41365543Scg}
41465543Scg
415137500Sjulian/* -------------------------------------------------------------------- */
416137500Sjulian
41765543Scg/* Wave Processor */
41865543Scg
419166279Sariffstatic u_int16_t
42065543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg)
42165543Scg{
422137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
423137500Sjulian	return AGG_RD(ess, PORT_DSP_DATA, 2);
42465543Scg}
42565543Scg
426166279Sariffstatic void
42765543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
42865543Scg{
429137500Sjulian	AGG_WR(ess, PORT_DSP_INDEX, reg, 2);
430137500Sjulian	AGG_WR(ess, PORT_DSP_DATA, data, 2);
43165543Scg}
43265543Scg
433166279Sariffstatic int
434137500Sjulianwp_wait_data(struct agg_info *ess, u_int16_t data)
43565543Scg{
436137500Sjulian	unsigned t = 0;
43765543Scg
438137500Sjulian	while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) {
439137500Sjulian		if (++t == 1000) {
440137500Sjulian			return EAGAIN;
441137500Sjulian		}
442137500Sjulian		AGG_WR(ess, PORT_DSP_DATA, data, 2);
44365543Scg	}
444137500Sjulian
445137500Sjulian	return 0;
44665543Scg}
44765543Scg
448166279Sariffstatic u_int16_t
449137500Sjulianwp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg)
45065543Scg{
451137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
452137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) != 0)
453137500Sjulian		device_printf(ess->dev, "wp_rdapu() indexing timed out.\n");
454137500Sjulian	return wp_rdreg(ess, WPREG_DATA_PORT);
45565543Scg}
45665543Scg
457166279Sariffstatic void
458137500Sjulianwp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data)
45965543Scg{
460137500Sjulian	wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4));
461137500Sjulian	if (wp_wait_data(ess, reg | (ch << 4)) == 0) {
462137500Sjulian		wp_wrreg(ess, WPREG_DATA_PORT, data);
463137500Sjulian		if (wp_wait_data(ess, data) != 0)
464166279Sariff			device_printf(ess->dev,
465166279Sariff			    "wp_wrapu() write timed out.\n");
466137500Sjulian	} else {
467137500Sjulian		device_printf(ess->dev, "wp_wrapu() indexing timed out.\n");
46865543Scg	}
46965543Scg}
47065543Scg
471137573Srustatic void
472137500Sjulianapu_setparam(struct agg_info *ess, int apuch,
473137500Sjulian    u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv)
47465543Scg{
475137500Sjulian	wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK);
476137500Sjulian	wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa);
477137500Sjulian	wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size);
478137500Sjulian	wp_wrapu(ess, apuch, APUREG_LOOPLEN, size);
479137500Sjulian	wp_wrapu(ess, apuch, APUREG_ROUTING, 0);
480137500Sjulian	wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000);
481137500Sjulian	wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00
482137500Sjulian	    | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT))
483137500Sjulian	    | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT)));
484137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE,
485137500Sjulian	    APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT));
486137500Sjulian	wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8);
487137500Sjulian}
48865543Scg
489166279Sariffstatic void
490137500Sjulianwp_settimer(struct agg_info *ess, u_int divide)
491137500Sjulian{
492137500Sjulian	u_int prescale = 0;
49365543Scg
494137500Sjulian	RANGE(divide, 2, 32 << 7);
495137500Sjulian
496137500Sjulian	for (; divide > 32; divide >>= 1) {
49765543Scg		prescale++;
498137500Sjulian		divide++;
499137500Sjulian	}
50065543Scg
50165543Scg	for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1)
50265543Scg		prescale++;
50365543Scg
50465543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 0);
505137500Sjulian	wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 |
50665543Scg	    (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1));
50765543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 1);
50865543Scg}
50965543Scg
510166279Sariffstatic void
51165543Scgwp_starttimer(struct agg_info *ess)
51265543Scg{
513137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
514137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED
515137500Sjulian	       | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
51665543Scg	wp_wrreg(ess, WPREG_TIMER_START, 1);
51765543Scg}
51865543Scg
519166279Sariffstatic void
52065543Scgwp_stoptimer(struct agg_info *ess)
52165543Scg{
522137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED
523137500Sjulian	       & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2);
524137500Sjulian	AGG_WR(ess, PORT_INT_STAT, 1, 2);
52565543Scg	wp_wrreg(ess, WPREG_TIMER_START, 0);
52665543Scg}
52765543Scg
528137500Sjulian/* -------------------------------------------------------------------- */
529137500Sjulian
53065543Scg/* WaveCache */
53165543Scg
532166279Sariff#if 0
533166279Sariffstatic u_int16_t
53465543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg)
53565543Scg{
536137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
537137500Sjulian	return AGG_RD(ess, PORT_WAVCACHE_DATA, 2);
53865543Scg}
539166279Sariff#endif
54065543Scg
541166279Sariffstatic void
54265543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
54365543Scg{
544137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2);
545137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2);
54665543Scg}
54765543Scg
548166279Sariff#if 0
549166279Sariffstatic u_int16_t
55065543Scgwc_rdchctl(struct agg_info *ess, int ch)
55165543Scg{
55265543Scg	return wc_rdreg(ess, ch << 3);
55365543Scg}
554166279Sariff#endif
55565543Scg
556166279Sariffstatic void
55765543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data)
55865543Scg{
55965543Scg	wc_wrreg(ess, ch << 3, data);
56065543Scg}
56165543Scg
562137500Sjulian/* -------------------------------------------------------------------- */
563137500Sjulian
56465543Scg/* Power management */
565166279Sariffstatic void
566137500Sjulianagg_stopclock(struct agg_info *ess, int part, int st)
56765543Scg{
568137500Sjulian	u_int32_t data;
56965543Scg
570137500Sjulian	data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4);
571137500Sjulian	if (part < 16) {
572137500Sjulian		if (st == PCI_POWERSTATE_D1)
573137500Sjulian			data &= ~(1 << part);
574137500Sjulian		else
575137500Sjulian			data |= (1 << part);
576137500Sjulian		if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2)
577137500Sjulian			data |= (0x10000 << part);
578137500Sjulian		else
579137500Sjulian			data &= ~(0x10000 << part);
580137500Sjulian		pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4);
581137500Sjulian	}
58265543Scg}
58365543Scg
58465543Scg
58565543Scg/* -----------------------------
58665543Scg * Controller.
58765543Scg */
58865543Scg
589166279Sariffstatic void
59065543Scgagg_initcodec(struct agg_info* ess)
59165543Scg{
59265543Scg	u_int16_t data;
59365543Scg
594137500Sjulian	if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) {
595137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
59665543Scg		DELAY(104);	/* 20.8us * (4 + 1) */
59765543Scg	}
59865543Scg	/* XXX - 2nd codec should be looked at. */
599137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4);
60065543Scg	DELAY(2);
601137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
602137500Sjulian	DELAY(50);
60365543Scg
604137500Sjulian	if (agg_rdcodec(ess, 0) < 0) {
605137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
60665543Scg		DELAY(21);
60765543Scg
60865543Scg		/* Try cold reset. */
60965543Scg		device_printf(ess->dev, "will perform cold reset.\n");
610137500Sjulian		data = AGG_RD(ess, PORT_GPIO_DIR, 2);
61165543Scg		if (pci_read_config(ess->dev, 0x58, 2) & 1)
61265543Scg			data |= 0x10;
613137500Sjulian		data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2);
614137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2);
615137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2);
616137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2);
61765543Scg		DELAY(2);
618137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2);
61965543Scg		DELAY(1);
620137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2);
621137500Sjulian		agg_sleep(ess, "agginicd", 500);
622137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR, data, 2);
62365543Scg		DELAY(84);	/* 20.8us * 4 */
624137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4);
625137500Sjulian		DELAY(50);
62665543Scg	}
62765543Scg}
62865543Scg
62965543Scgstatic void
63065543Scgagg_init(struct agg_info* ess)
63165543Scg{
63265543Scg	u_int32_t data;
63365543Scg
63465543Scg	/* Setup PCI config registers. */
63565543Scg
63665543Scg	/* Disable all legacy emulations. */
63765543Scg	data = pci_read_config(ess->dev, CONF_LEGACY, 2);
63865543Scg	data |= LEGACY_DISABLED;
63965543Scg	pci_write_config(ess->dev, CONF_LEGACY, data, 2);
64065543Scg
64165543Scg	/* Disconnect from CHI. (Makes Dell inspiron 7500 work?)
64265543Scg	 * Enable posted write.
64365543Scg	 * Prefer PCI timing rather than that of ISA.
64465543Scg	 * Don't swap L/R. */
64565543Scg	data = pci_read_config(ess->dev, CONF_MAESTRO, 4);
646137500Sjulian	data |= MAESTRO_PMC;
64765543Scg	data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING;
64865543Scg	data &= ~MAESTRO_SWAP_LR;
64965543Scg	pci_write_config(ess->dev, CONF_MAESTRO, data, 4);
65065543Scg
651137500Sjulian	/* Turn off unused parts if necessary. */
652137500Sjulian	/* consult CONF_MAESTRO. */
653137500Sjulian	if (data & MAESTRO_SPDIF)
654137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D2);
655137500Sjulian	else
656137500Sjulian		agg_stopclock(ess, ACPI_PART_SPDIF,	PCI_POWERSTATE_D1);
657137500Sjulian	if (data & MAESTRO_HWVOL)
658137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D3);
659137500Sjulian	else
660137500Sjulian		agg_stopclock(ess, ACPI_PART_HW_VOL,	PCI_POWERSTATE_D1);
661137500Sjulian
662137500Sjulian	/* parts that never be used */
663137500Sjulian	agg_stopclock(ess, ACPI_PART_978,	PCI_POWERSTATE_D1);
664137500Sjulian	agg_stopclock(ess, ACPI_PART_DAA,	PCI_POWERSTATE_D1);
665137500Sjulian	agg_stopclock(ess, ACPI_PART_GPIO,	PCI_POWERSTATE_D1);
666137500Sjulian	agg_stopclock(ess, ACPI_PART_SB,	PCI_POWERSTATE_D1);
667137500Sjulian	agg_stopclock(ess, ACPI_PART_FM,	PCI_POWERSTATE_D1);
668137500Sjulian	agg_stopclock(ess, ACPI_PART_MIDI,	PCI_POWERSTATE_D1);
669137500Sjulian	agg_stopclock(ess, ACPI_PART_GAME_PORT,	PCI_POWERSTATE_D1);
670137500Sjulian
671137500Sjulian	/* parts that will be used only when play/recording */
672137500Sjulian	agg_stopclock(ess, ACPI_PART_WP,	PCI_POWERSTATE_D2);
673137500Sjulian
674137500Sjulian	/* parts that should always be turned on */
675137500Sjulian	agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3);
676137500Sjulian	agg_stopclock(ess, ACPI_PART_GLUE,	PCI_POWERSTATE_D3);
677137500Sjulian	agg_stopclock(ess, ACPI_PART_PCI_IF,	PCI_POWERSTATE_D3);
678137500Sjulian	agg_stopclock(ess, ACPI_PART_RINGBUS,	PCI_POWERSTATE_D3);
679137500Sjulian
68065543Scg	/* Reset direct sound. */
681137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2);
682137500Sjulian	DELAY(100);
683137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
684137500Sjulian	DELAY(100);
685137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2);
686137500Sjulian	DELAY(100);
687137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
688137500Sjulian	DELAY(100);
68965543Scg
690137500Sjulian	/* Enable hardware volume control interruption. */
691137500Sjulian	if (data & MAESTRO_HWVOL)	/* XXX - why not use device flags? */
692137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2);
69365543Scg
69465543Scg	/* Setup Wave Processor. */
69565543Scg
69665543Scg	/* Enable WaveCache, set DMA base address. */
69765543Scg	wp_wrreg(ess, WPREG_WAVE_ROMRAM,
69865543Scg	    WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED);
699137500Sjulian	wp_wrreg(ess, WPREG_CRAM_DATA, 0);
70065543Scg
701137500Sjulian	AGG_WR(ess, PORT_WAVCACHE_CTRL,
702137500Sjulian	       WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2);
703137500Sjulian
70465543Scg	for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++)
705137500Sjulian		wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT);
70665543Scg
70765543Scg	/* Setup Codec/Ringbus. */
70865543Scg	agg_initcodec(ess);
709137500Sjulian	AGG_WR(ess, PORT_RINGBUS_CTRL,
710137500Sjulian	       RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4);
71165543Scg
712137500Sjulian	wp_wrreg(ess, 0x08, 0xB004);
713137500Sjulian	wp_wrreg(ess, 0x09, 0x001B);
714137500Sjulian	wp_wrreg(ess, 0x0A, 0x8000);
715137500Sjulian	wp_wrreg(ess, 0x0B, 0x3F37);
716137500Sjulian	wp_wrreg(ess, WPREG_BASE, 0x8598);	/* Parallel I/O */
717137500Sjulian	wp_wrreg(ess, WPREG_BASE + 1, 0x7632);
71865543Scg	ringbus_setdest(ess, RINGBUS_SRC_ADC,
71965543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN);
72065543Scg	ringbus_setdest(ess, RINGBUS_SRC_DSOUND,
72165543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC);
72265543Scg
723137500Sjulian	/* Enable S/PDIF if necessary. */
724137500Sjulian	if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF)
725137500Sjulian		/* XXX - why not use device flags? */
726137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF |
727137500Sjulian		       AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1);
728137500Sjulian
72965543Scg	/* Setup ASSP. Needed for Dell Inspiron 7500? */
730137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1);
731137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1);
732137500Sjulian	AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1);
73365543Scg
73465543Scg	/*
73565543Scg	 * Setup GPIO.
73665543Scg	 * There seems to be speciality with NEC systems.
73765543Scg	 */
73865543Scg	switch (pci_get_subvendor(ess->dev)
73965543Scg	    | (pci_get_subdevice(ess->dev) << 16)) {
74065543Scg	case NEC_SUBID1:
74165543Scg	case NEC_SUBID2:
74265543Scg		/* Matthew Braithwaite <matt@braithwaite.net> reported that
74365543Scg		 * NEC Versa LX doesn't need GPIO operation. */
744137500Sjulian		AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2);
745137500Sjulian		AGG_WR(ess, PORT_GPIO_DIR,
746137500Sjulian		       AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2);
747137500Sjulian		AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2);
74865543Scg		break;
74965543Scg	}
75065543Scg}
75165543Scg
752137500Sjulian/* Deals power state transition. Must be called with softc->lock held. */
753137500Sjulianstatic void
754137500Sjulianagg_power(struct agg_info *ess, int status)
755137500Sjulian{
756137500Sjulian	u_int8_t lastpwr;
757137500Sjulian
758137500Sjulian	lastpwr = ess->curpwr;
759137500Sjulian	if (lastpwr == status)
760137500Sjulian		return;
761137500Sjulian
762137500Sjulian	switch (status) {
763137500Sjulian	case PCI_POWERSTATE_D0:
764137500Sjulian	case PCI_POWERSTATE_D1:
765137500Sjulian		switch (lastpwr) {
766137500Sjulian		case PCI_POWERSTATE_D2:
767137500Sjulian			pci_set_powerstate(ess->dev, status);
768137500Sjulian			/* Turn on PCM-related parts. */
769137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0);
770137500Sjulian			DELAY(100);
771137500Sjulian#if 0
772137500Sjulian			if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3)
773166279Sariff				device_printf(ess->dev,
774166279Sariff				    "warning: codec not ready.\n");
775137500Sjulian#endif
776137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
777137500Sjulian			       (AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
778137500Sjulian				& ~RINGBUS_CTRL_ACLINK_ENABLED)
779137500Sjulian			       | RINGBUS_CTRL_RINGBUS_ENABLED, 4);
780137500Sjulian			DELAY(50);
781137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
782137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
783137500Sjulian			       | RINGBUS_CTRL_ACLINK_ENABLED, 4);
784137500Sjulian			break;
785137500Sjulian		case PCI_POWERSTATE_D3:
786137500Sjulian			/* Initialize. */
787137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
788137500Sjulian			DELAY(100);
789137500Sjulian			agg_init(ess);
790137500Sjulian			/* FALLTHROUGH */
791137500Sjulian		case PCI_POWERSTATE_D0:
792137500Sjulian		case PCI_POWERSTATE_D1:
793137500Sjulian			pci_set_powerstate(ess->dev, status);
794137500Sjulian			break;
795137500Sjulian		}
796137500Sjulian		break;
797137500Sjulian	case PCI_POWERSTATE_D2:
798137500Sjulian		switch (lastpwr) {
799137500Sjulian		case PCI_POWERSTATE_D3:
800137500Sjulian			/* Initialize. */
801137500Sjulian			pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0);
802137500Sjulian			DELAY(100);
803137500Sjulian			agg_init(ess);
804137500Sjulian			/* FALLTHROUGH */
805137500Sjulian		case PCI_POWERSTATE_D0:
806137500Sjulian		case PCI_POWERSTATE_D1:
807137500Sjulian			/* Turn off PCM-related parts. */
808137500Sjulian			AGG_WR(ess, PORT_RINGBUS_CTRL,
809137500Sjulian			       AGG_RD(ess, PORT_RINGBUS_CTRL, 4)
810137500Sjulian			       & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4);
811137500Sjulian			DELAY(100);
812137500Sjulian			agg_wrcodec(ess, AC97_REG_POWER, 0x300);
813137500Sjulian			DELAY(100);
814137500Sjulian			break;
815137500Sjulian		}
816137500Sjulian		pci_set_powerstate(ess->dev, status);
817137500Sjulian		break;
818137500Sjulian	case PCI_POWERSTATE_D3:
819137500Sjulian		/* Entirely power down. */
820137500Sjulian		agg_wrcodec(ess, AC97_REG_POWER, 0xdf00);
821137500Sjulian		DELAY(100);
822137500Sjulian		AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4);
823137500Sjulian		/*DELAY(1);*/
824137500Sjulian		if (lastpwr != PCI_POWERSTATE_D2)
825137500Sjulian			wp_stoptimer(ess);
826137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
827137500Sjulian		AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1);
828137500Sjulian		pci_set_powerstate(ess->dev, status);
829137500Sjulian		break;
830137500Sjulian	default:
831137500Sjulian		/* Invalid power state; let it ignored. */
832137500Sjulian		status = lastpwr;
833137500Sjulian		break;
834137500Sjulian	}
835137500Sjulian
836137500Sjulian	ess->curpwr = status;
837137500Sjulian}
838137500Sjulian
839137500Sjulian/* -------------------------------------------------------------------- */
840137500Sjulian
84165543Scg/* Channel controller. */
84265543Scg
84365543Scgstatic void
84465543Scgaggch_start_dac(struct agg_chinfo *ch)
84565543Scg{
846137500Sjulian	bus_addr_t	wpwa;
847137500Sjulian	u_int32_t	speed;
848137500Sjulian	u_int16_t	size, apuch, wtbar, wcreg, aputype;
849137500Sjulian	u_int		dv;
850137500Sjulian	int		pan;
85165543Scg
852137500Sjulian	speed = ch->speed;
853137500Sjulian	wpwa = (ch->phys - ch->base) >> 1;
854137500Sjulian	wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2));
855137500Sjulian	wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
856137500Sjulian	size  = ch->buflen;
857137500Sjulian	apuch = (ch->num << 1) | 32;
858137500Sjulian	pan = PAN_RIGHT - PAN_FRONT;
859137500Sjulian
860137500Sjulian	if (ch->stereo) {
861137500Sjulian		wcreg |= WAVCACHE_CHCTL_STEREO;
862137500Sjulian		if (ch->qs16) {
863137500Sjulian			aputype = APUTYPE_16BITSTEREO;
864137500Sjulian			wpwa >>= 1;
865137500Sjulian			size >>= 1;
866137500Sjulian			pan = -pan;
867137500Sjulian		} else
868137500Sjulian			aputype = APUTYPE_8BITSTEREO;
869137500Sjulian	} else {
870137500Sjulian		pan = 0;
871137500Sjulian		if (ch->qs16)
872137500Sjulian			aputype = APUTYPE_16BITLINEAR;
873137500Sjulian		else {
874137500Sjulian			aputype = APUTYPE_8BITLINEAR;
875137500Sjulian			speed >>= 1;
876137500Sjulian		}
87765543Scg	}
878137500Sjulian	if (ch->us)
879137500Sjulian		wcreg |= WAVCACHE_CHCTL_U8;
88065543Scg
881137500Sjulian	if (wtbar > 8)
882137500Sjulian		wtbar = (wtbar >> 1) + 4;
883137500Sjulian
88465543Scg	dv = (((speed % 48000) << 16) + 24000) / 48000
88565543Scg	    + ((speed / 48000) << 16);
88665543Scg
887137500Sjulian	agg_lock(ch->parent);
888137500Sjulian	agg_power(ch->parent, powerstate_active);
88965543Scg
890137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar,
891137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
892137500Sjulian	wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1,
893137500Sjulian	    ch->base >> WAVCACHE_BASEADDR_SHIFT);
894137500Sjulian	if (wtbar < 8) {
895137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2,
896137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
897137500Sjulian		wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3,
898137500Sjulian		    ch->base >> WAVCACHE_BASEADDR_SHIFT);
899137500Sjulian	}
900137500Sjulian	wc_wrchctl(ch->parent, apuch, wcreg);
901137500Sjulian	wc_wrchctl(ch->parent, apuch + 1, wcreg);
90265543Scg
903137500Sjulian	apu_setparam(ch->parent, apuch, wpwa, size, pan, dv);
904137500Sjulian	if (ch->stereo) {
905137500Sjulian		if (ch->qs16)
906137500Sjulian			wpwa |= (WPWA_STEREO >> 1);
907137500Sjulian		apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv);
90865543Scg
909137500Sjulian		critical_enter();
910137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
911137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
91265543Scg		wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE,
913137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
914137500Sjulian		critical_exit();
915137500Sjulian	} else {
916137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
917137500Sjulian		    (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
918137500Sjulian	}
919137500Sjulian
920137500Sjulian	/* to mark that this channel is ready for intr. */
921137500Sjulian	ch->parent->active |= (1 << ch->num);
922137500Sjulian
923137500Sjulian	set_timer(ch->parent);
924137500Sjulian	wp_starttimer(ch->parent);
925137500Sjulian	agg_unlock(ch->parent);
92665543Scg}
92765543Scg
92865543Scgstatic void
92965543Scgaggch_stop_dac(struct agg_chinfo *ch)
93065543Scg{
931137500Sjulian	agg_lock(ch->parent);
932137500Sjulian
933137500Sjulian	/* to mark that this channel no longer needs further intrs. */
934137500Sjulian	ch->parent->active &= ~(1 << ch->num);
935137500Sjulian
936137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE,
93765543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
938137500Sjulian	wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE,
93965543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
940137500Sjulian
941137500Sjulian	if (ch->parent->active) {
942137500Sjulian		set_timer(ch->parent);
943137500Sjulian		wp_starttimer(ch->parent);
944137500Sjulian	} else {
945137500Sjulian		wp_stoptimer(ch->parent);
946137500Sjulian		agg_power(ch->parent, powerstate_idle);
947137500Sjulian	}
948137500Sjulian	agg_unlock(ch->parent);
94965543Scg}
95065543Scg
951137500Sjulianstatic void
952137500Sjulianaggch_start_adc(struct agg_rchinfo *ch)
953137500Sjulian{
954137500Sjulian	bus_addr_t	wpwa, wpwa2;
955137500Sjulian	u_int16_t	wcreg, wcreg2;
956137500Sjulian	u_int	dv;
957137500Sjulian	int	pan;
958137500Sjulian
959137500Sjulian	/* speed > 48000 not cared */
960137500Sjulian	dv = ((ch->speed << 16) + 24000) / 48000;
961137500Sjulian
962137500Sjulian	/* RATECONV doesn't seem to like dv == 0x10000. */
963137500Sjulian	if (dv == 0x10000)
964137500Sjulian		dv--;
965137500Sjulian
966137500Sjulian	if (ch->stereo) {
967137500Sjulian		wpwa = (ch->srcphys - ch->base) >> 1;
968137500Sjulian		wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1;
969137500Sjulian		wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
970137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
971137500Sjulian		pan = PAN_LEFT - PAN_FRONT;
972137500Sjulian	} else {
973137500Sjulian		wpwa = (ch->phys - ch->base) >> 1;
974137500Sjulian		wpwa2 = (ch->srcphys - ch->base) >> 1;
975137500Sjulian		wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
976137500Sjulian		wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
977137500Sjulian		pan = 0;
978137500Sjulian	}
979137500Sjulian
980137500Sjulian	agg_lock(ch->parent);
981137500Sjulian
982137500Sjulian	ch->hwptr = 0;
983137500Sjulian	agg_power(ch->parent, powerstate_active);
984137500Sjulian
985137500Sjulian	/* Invalidate WaveCache. */
986137500Sjulian	wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO);
987137500Sjulian	wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO);
988137500Sjulian	wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO);
989137500Sjulian	wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO);
990137500Sjulian
991137500Sjulian	/* Load APU registers. */
992137500Sjulian	/* APU #0 : Sample rate converter for left/center. */
993137500Sjulian	apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa,
994137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
995137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0);
996137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT);
997137500Sjulian
998137500Sjulian	/* APU #1 : Sample rate converter for right. */
999137500Sjulian	apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2,
1000137500Sjulian		     ch->buflen >> ch->stereo, 0, dv);
1001137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0);
1002137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT);
1003137500Sjulian
1004137500Sjulian	/* APU #2 : Input mixer for left. */
1005137500Sjulian	apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0,
1006137500Sjulian		     ch->parent->bufsz >> 2, pan, 0x10000);
1007137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0);
1008137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0);
1009137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT);
1010137500Sjulian
1011137500Sjulian	/* APU #3 : Input mixer for right. */
1012137500Sjulian	apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2),
1013137500Sjulian		     ch->parent->bufsz >> 2, -pan, 0x10000);
1014137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0);
1015137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0);
1016137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT);
1017137500Sjulian
1018137500Sjulian	/* to mark this channel ready for intr. */
1019137500Sjulian	ch->parent->active |= (1 << ch->parent->playchns);
1020137500Sjulian
1021137500Sjulian	/* start adc */
1022137500Sjulian	critical_enter();
1023137500Sjulian	wp_wrapu(ch->parent, 0, APUREG_APUTYPE,
1024137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1025137500Sjulian	wp_wrapu(ch->parent, 1, APUREG_APUTYPE,
1026137500Sjulian	    (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
1027137500Sjulian	wp_wrapu(ch->parent, 2, APUREG_APUTYPE,
1028137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1029137500Sjulian	wp_wrapu(ch->parent, 3, APUREG_APUTYPE,
1030137500Sjulian	    (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf);
1031137500Sjulian	critical_exit();
1032137500Sjulian
1033137500Sjulian	set_timer(ch->parent);
1034137500Sjulian	wp_starttimer(ch->parent);
1035137500Sjulian	agg_unlock(ch->parent);
1036137500Sjulian}
1037137500Sjulian
1038137500Sjulianstatic void
1039137500Sjulianaggch_stop_adc(struct agg_rchinfo *ch)
1040137500Sjulian{
1041137500Sjulian	int apuch;
1042137500Sjulian
1043137500Sjulian	agg_lock(ch->parent);
1044137500Sjulian
1045137500Sjulian	/* to mark that this channel no longer needs further intrs. */
1046137500Sjulian	ch->parent->active &= ~(1 << ch->parent->playchns);
1047137500Sjulian
1048137500Sjulian	for (apuch = 0; apuch < 4; apuch++)
1049137500Sjulian		wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
1050137500Sjulian		    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
1051137500Sjulian
1052137500Sjulian	if (ch->parent->active) {
1053137500Sjulian		set_timer(ch->parent);
1054137500Sjulian		wp_starttimer(ch->parent);
1055137500Sjulian	} else {
1056137500Sjulian		wp_stoptimer(ch->parent);
1057137500Sjulian		agg_power(ch->parent, powerstate_idle);
1058137500Sjulian	}
1059137500Sjulian	agg_unlock(ch->parent);
1060137500Sjulian}
1061137500Sjulian
106265543Scg/*
1063137500Sjulian * Feed from L/R channel of ADC to destination with stereo interleaving.
1064137500Sjulian * This function expects n not overwrapping the buffer boundary.
1065137500Sjulian * Note that n is measured in sample unit.
1066137500Sjulian *
1067137500Sjulian * XXX - this function works in 16bit stereo format only.
1068137500Sjulian */
1069166279Sariffstatic void
1070137500Sjulianinterleave(int16_t *l, int16_t *r, int16_t *p, unsigned n)
1071137500Sjulian{
1072137500Sjulian	int16_t *end;
1073137500Sjulian
1074137500Sjulian	for (end = l + n; l < end; ) {
1075137500Sjulian		*p++ = *l++;
1076137500Sjulian		*p++ = *r++;
1077137500Sjulian	}
1078137500Sjulian}
1079137500Sjulian
1080137500Sjulianstatic void
1081137500Sjulianaggch_feed_adc_stereo(struct agg_rchinfo *ch)
1082137500Sjulian{
1083137500Sjulian	unsigned cur, last;
1084137500Sjulian	int16_t *src2;
1085137500Sjulian
1086137500Sjulian	agg_lock(ch->parent);
1087137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1088137500Sjulian	agg_unlock(ch->parent);
1089137500Sjulian	cur -= 0xffff & ((ch->srcphys - ch->base) >> 1);
1090137500Sjulian	last = ch->hwptr;
1091137500Sjulian	src2 = ch->src + ch->parent->bufsz/4;
1092137500Sjulian
1093137500Sjulian	if (cur < last) {
1094137500Sjulian		interleave(ch->src + last, src2 + last,
1095137500Sjulian			   ch->sink + 2*last, ch->buflen/2 - last);
1096137500Sjulian		interleave(ch->src, src2,
1097137500Sjulian			   ch->sink, cur);
1098137500Sjulian	} else if (cur > last)
1099137500Sjulian		interleave(ch->src + last, src2 + last,
1100137500Sjulian			   ch->sink + 2*last, cur - last);
1101137500Sjulian	ch->hwptr = cur;
1102137500Sjulian}
1103137500Sjulian
1104137500Sjulian/*
1105137500Sjulian * Feed from R channel of ADC and mixdown to destination L/center.
1106137500Sjulian * This function expects n not overwrapping the buffer boundary.
1107137500Sjulian * Note that n is measured in sample unit.
1108137500Sjulian *
1109137500Sjulian * XXX - this function works in 16bit monoral format only.
1110137500Sjulian */
1111166279Sariffstatic void
1112137500Sjulianmixdown(int16_t *src, int16_t *dest, unsigned n)
1113137500Sjulian{
1114137500Sjulian	int16_t *end;
1115137500Sjulian
1116137500Sjulian	for (end = dest + n; dest < end; dest++)
1117137500Sjulian		*dest = (int16_t)(((int)*dest - (int)*src++) / 2);
1118137500Sjulian}
1119137500Sjulian
1120137500Sjulianstatic void
1121137500Sjulianaggch_feed_adc_mono(struct agg_rchinfo *ch)
1122137500Sjulian{
1123137500Sjulian	unsigned cur, last;
1124137500Sjulian
1125137500Sjulian	agg_lock(ch->parent);
1126137500Sjulian	cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1127137500Sjulian	agg_unlock(ch->parent);
1128137500Sjulian	cur -= 0xffff & ((ch->phys - ch->base) >> 1);
1129137500Sjulian	last = ch->hwptr;
1130137500Sjulian
1131137500Sjulian	if (cur < last) {
1132137500Sjulian		mixdown(ch->src + last, ch->sink + last, ch->buflen - last);
1133137500Sjulian		mixdown(ch->src, ch->sink, cur);
1134137500Sjulian	} else if (cur > last)
1135137500Sjulian		mixdown(ch->src + last, ch->sink + last, cur - last);
1136137500Sjulian	ch->hwptr = cur;
1137137500Sjulian}
1138137500Sjulian
1139166279Sariff#ifdef AGG_JITTER_CORRECTION
1140137500Sjulian/*
114165543Scg * Stereo jitter suppressor.
114265543Scg * Sometimes playback pointers differ in stereo-paired channels.
114365543Scg * Calling this routine within intr fixes the problem.
114465543Scg */
1145166279Sariffstatic void
114665543Scgsuppress_jitter(struct agg_chinfo *ch)
114765543Scg{
1148137500Sjulian	if (ch->stereo) {
1149137500Sjulian		int cp1, cp2, diff /*, halfsize*/ ;
115065543Scg
1151137500Sjulian		/*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/
1152137500Sjulian		cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1153137500Sjulian		cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR);
1154137500Sjulian		if (cp1 != cp2) {
1155137500Sjulian			diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1156137500Sjulian			if (diff > 1 /* && diff < halfsize*/ )
1157137500Sjulian				AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1158137500Sjulian		}
115965543Scg	}
116065543Scg}
116165543Scg
1162166279Sariffstatic void
1163137500Sjuliansuppress_rec_jitter(struct agg_rchinfo *ch)
1164137500Sjulian{
1165137500Sjulian	int cp1, cp2, diff /*, halfsize*/ ;
1166137500Sjulian
1167137500Sjulian	/*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/
1168137500Sjulian	cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1)
1169137500Sjulian		+ wp_rdapu(ch->parent, 0, APUREG_CURPTR);
1170137500Sjulian	cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR);
1171137500Sjulian	if (cp1 != cp2) {
1172137500Sjulian		diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
1173137500Sjulian		if (diff > 1 /* && diff < halfsize*/ )
1174137500Sjulian			AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2);
1175137500Sjulian	}
1176137500Sjulian}
1177166279Sariff#endif
1178137500Sjulian
1179166279Sariffstatic u_int
1180137500Sjuliancalc_timer_div(struct agg_chinfo *ch)
118165543Scg{
1182137500Sjulian	u_int speed;
118365543Scg
1184137500Sjulian	speed = ch->speed;
1185137500Sjulian#ifdef INVARIANTS
1186137500Sjulian	if (speed == 0) {
1187137500Sjulian		printf("snd_maestro: pch[%d].speed == 0, which shouldn't\n",
1188137500Sjulian		       ch->num);
1189137500Sjulian		speed = 1;
1190137500Sjulian	}
1191137500Sjulian#endif
1192137500Sjulian	return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo))
1193137500Sjulian		+ speed - 1) / speed;
1194137500Sjulian}
119565543Scg
1196166279Sariffstatic u_int
1197137500Sjuliancalc_timer_div_rch(struct agg_rchinfo *ch)
1198137500Sjulian{
1199137500Sjulian	u_int speed;
1200137500Sjulian
1201137500Sjulian	speed = ch->speed;
1202137500Sjulian#ifdef INVARIANTS
1203137500Sjulian	if (speed == 0) {
1204137500Sjulian		printf("snd_maestro: rch.speed == 0, which shouldn't\n");
1205137500Sjulian		speed = 1;
1206137500Sjulian	}
1207137500Sjulian#endif
1208137500Sjulian	return (48000 * (ch->blklen << (!ch->stereo))
1209137500Sjulian		+ speed - 1) / speed;
121065543Scg}
121165543Scg
121265543Scgstatic void
121365543Scgset_timer(struct agg_info *ess)
121465543Scg{
121565543Scg	int i;
1216137500Sjulian	u_int	dv = 32 << 7, newdv;
121765543Scg
121865543Scg	for (i = 0; i < ess->playchns; i++)
121965543Scg		if ((ess->active & (1 << i)) &&
1220137500Sjulian		    (dv > (newdv = calc_timer_div(ess->pch + i))))
1221137500Sjulian			dv = newdv;
1222137500Sjulian	if ((ess->active & (1 << i)) &&
1223137500Sjulian	    (dv > (newdv = calc_timer_div_rch(&ess->rch))))
1224137500Sjulian		dv = newdv;
122565543Scg
1226137500Sjulian	wp_settimer(ess, dv);
122765543Scg}
122865543Scg
122965543Scg
123065543Scg/* -----------------------------
123165543Scg * Newpcm glue.
123265543Scg */
123365543Scg
1234137500Sjulian/* AC97 mixer interface. */
1235137500Sjulian
1236137500Sjulianstatic u_int32_t
1237137500Sjulianagg_ac97_init(kobj_t obj, void *sc)
1238137500Sjulian{
1239137500Sjulian	struct agg_info *ess = sc;
1240137500Sjulian
1241137500Sjulian	return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1;
1242137500Sjulian}
1243137500Sjulian
1244137500Sjulianstatic int
1245137500Sjulianagg_ac97_read(kobj_t obj, void *sc, int regno)
1246137500Sjulian{
1247137500Sjulian	struct agg_info *ess = sc;
1248137500Sjulian	int ret;
1249137500Sjulian
1250154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1251137500Sjulian	ret = agg_rdcodec(ess, regno);
1252154067Sariff	/* agg_unlock(ess); */
1253137500Sjulian	return ret;
1254137500Sjulian}
1255137500Sjulian
1256137500Sjulianstatic int
1257137500Sjulianagg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data)
1258137500Sjulian{
1259137500Sjulian	struct agg_info *ess = sc;
1260137500Sjulian	int ret;
1261137500Sjulian
1262154067Sariff	/* XXX sound locking violation: agg_lock(ess); */
1263137500Sjulian	ret = agg_wrcodec(ess, regno, data);
1264154067Sariff	/* agg_unlock(ess); */
1265137500Sjulian	return ret;
1266137500Sjulian}
1267137500Sjulian
1268137500Sjulian
1269137500Sjulianstatic kobj_method_t agg_ac97_methods[] = {
1270137500Sjulian    	KOBJMETHOD(ac97_init,		agg_ac97_init),
1271137500Sjulian    	KOBJMETHOD(ac97_read,		agg_ac97_read),
1272137500Sjulian    	KOBJMETHOD(ac97_write,		agg_ac97_write),
1273193640Sariff	KOBJMETHOD_END
1274137500Sjulian};
1275137500SjulianAC97_DECLARE(agg_ac97);
1276137500Sjulian
1277137500Sjulian
1278137500Sjulian/* -------------------------------------------------------------------- */
1279137500Sjulian
1280137500Sjulian/* Playback channel. */
1281137500Sjulian
128265543Scgstatic void *
1283166279Sariffaggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1284166279Sariff						struct pcm_channel *c, int dir)
128565543Scg{
128665543Scg	struct agg_info *ess = devinfo;
128765543Scg	struct agg_chinfo *ch;
128865543Scg	bus_addr_t physaddr;
128970291Scg	void *p;
129065543Scg
1291137500Sjulian	KASSERT((dir == PCMDIR_PLAY),
1292137500Sjulian	    ("aggpch_init() called for RECORDING channel!"));
1293137500Sjulian	ch = ess->pch + ess->playchns;
129465543Scg
129565543Scg	ch->parent = ess;
129665543Scg	ch->channel = c;
129765543Scg	ch->buffer = b;
129865543Scg	ch->num = ess->playchns;
129965543Scg
1300137500Sjulian	p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr);
1301137500Sjulian	if (p == NULL)
1302137500Sjulian		return NULL;
1303137500Sjulian	ch->phys = physaddr;
1304137500Sjulian	ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT);
1305137500Sjulian
130684658Scg	sndbuf_setup(b, p, ess->bufsz);
1307137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1308137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1309137500Sjulian	ess->playchns++;
131065543Scg
1311137500Sjulian	return ch;
1312137500Sjulian}
131365543Scg
1314137500Sjulianstatic void
1315137500Sjulianadjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size)
1316137500Sjulian{
1317137500Sjulian	struct agg_chinfo *pchs[AGG_MAXPLAYCH];
1318137500Sjulian	u_int i, j, k;
1319137500Sjulian	bus_addr_t base;
132065543Scg
1321137500Sjulian	/* sort pchs by phys address */
1322137500Sjulian	for (i = 0; i < n; i++) {
1323137500Sjulian		for (j = 0; j < i; j++)
1324137500Sjulian			if (chans[i].phys < pchs[j]->phys) {
1325137500Sjulian				for (k = i; k > j; k--)
1326137500Sjulian					pchs[k] = pchs[k - 1];
1327137500Sjulian				break;
1328137500Sjulian			}
1329137500Sjulian		pchs[j] = chans + i;
1330137500Sjulian	}
1331137500Sjulian
1332137500Sjulian	/* use new base register if next buffer can not be addressed
1333137500Sjulian	   via current base. */
1334137500Sjulian#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1)
1335137500Sjulian	base = pchs[0]->base;
1336137500Sjulian	for (k = 1, i = 1; i < n; i++) {
1337137500Sjulian		if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT)
1338137500Sjulian			/* not addressable: assign new base */
1339137500Sjulian			base = (pchs[i]->base -= k++ << BASE_SHIFT);
1340137500Sjulian		else
1341137500Sjulian			pchs[i]->base = base;
1342137500Sjulian	}
1343137500Sjulian#undef BASE_SHIFT
1344137500Sjulian
1345137500Sjulian	if (bootverbose) {
1346137500Sjulian		printf("Total of %d bases are assigned.\n", k);
1347137500Sjulian		for (i = 0; i < n; i++) {
1348137500Sjulian			printf("ch.%d: phys 0x%llx, wpwa 0x%llx\n",
1349137500Sjulian			       i, (long long)chans[i].phys,
1350137500Sjulian			       (long long)(chans[i].phys -
1351137500Sjulian					   chans[i].base) >> 1);
1352137500Sjulian		}
1353137500Sjulian	}
135465543Scg}
135565543Scg
135665543Scgstatic int
1357137500Sjulianaggpch_free(kobj_t obj, void *data)
135865543Scg{
1359137500Sjulian	struct agg_chinfo *ch = data;
1360137500Sjulian	struct agg_info *ess = ch->parent;
1361137500Sjulian
1362137500Sjulian	/* free up buffer - called after channel stopped */
1363137500Sjulian	dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer));
1364137500Sjulian
136565543Scg	/* return 0 if ok */
136665543Scg	return 0;
136765543Scg}
136865543Scg
136965543Scgstatic int
1370137500Sjulianaggpch_setformat(kobj_t obj, void *data, u_int32_t format)
137165543Scg{
137265543Scg	struct agg_chinfo *ch = data;
137365543Scg
1374137500Sjulian	if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE)
1375137500Sjulian		return EINVAL;
1376137500Sjulian	ch->stereo = ch->qs16 = ch->us = 0;
1377193640Sariff	if (AFMT_CHANNEL(format) > 1)
1378137500Sjulian		ch->stereo = 1;
137965543Scg
138065543Scg	if (format & AFMT_U8 || format & AFMT_S8) {
138165543Scg		if (format & AFMT_U8)
1382137500Sjulian			ch->us = 1;
1383137500Sjulian	} else
1384137500Sjulian		ch->qs16 = 1;
138589887Sscottl	return 0;
138665543Scg}
138765543Scg
1388193640Sariffstatic u_int32_t
1389137500Sjulianaggpch_setspeed(kobj_t obj, void *data, u_int32_t speed)
139065543Scg{
1391193640Sariff
1392193640Sariff	((struct agg_chinfo*)data)->speed = speed;
1393193640Sariff
1394193640Sariff	return (speed);
139565543Scg}
139665543Scg
1397193640Sariffstatic u_int32_t
1398137500Sjulianaggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
139965543Scg{
1400137500Sjulian	struct agg_chinfo *ch = data;
1401137500Sjulian	int blkcnt;
1402137500Sjulian
1403137500Sjulian	/* try to keep at least 20msec DMA space */
1404137500Sjulian	blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize);
1405137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1406137500Sjulian
1407137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1408137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1409137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1410137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1411137500Sjulian	} else {
1412137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1413137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1414137500Sjulian	}
1415137500Sjulian
1416137500Sjulian	ch->blklen = blocksize / 2;
1417137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1418137500Sjulian	return blocksize;
141965543Scg}
142065543Scg
142165543Scgstatic int
1422137500Sjulianaggpch_trigger(kobj_t obj, void *data, int go)
142365543Scg{
142465543Scg	struct agg_chinfo *ch = data;
142565543Scg
142665543Scg	switch (go) {
142765543Scg	case PCMTRIG_EMLDMAWR:
1428137500Sjulian		break;
142965543Scg	case PCMTRIG_START:
1430137500Sjulian		aggch_start_dac(ch);
143165543Scg		break;
143265543Scg	case PCMTRIG_ABORT:
143365543Scg	case PCMTRIG_STOP:
1434137500Sjulian		aggch_stop_dac(ch);
143565543Scg		break;
143665543Scg	}
143765543Scg	return 0;
143865543Scg}
143965543Scg
1440193640Sariffstatic u_int32_t
1441137500Sjulianaggpch_getptr(kobj_t obj, void *data)
144265543Scg{
144365543Scg	struct agg_chinfo *ch = data;
1444193640Sariff	u_int32_t cp;
144565543Scg
1446137500Sjulian	agg_lock(ch->parent);
1447137500Sjulian	cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
1448137500Sjulian	agg_unlock(ch->parent);
144965543Scg
1450137500Sjulian	return ch->qs16 && ch->stereo
1451137500Sjulian		? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base))
1452137500Sjulian		: (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base));
145365543Scg}
145465543Scg
145574763Scgstatic struct pcmchan_caps *
1456137500Sjulianaggpch_getcaps(kobj_t obj, void *data)
145765543Scg{
145865543Scg	static u_int32_t playfmt[] = {
1459193640Sariff		SND_FORMAT(AFMT_U8, 1, 0),
1460193640Sariff		SND_FORMAT(AFMT_U8, 2, 0),
1461193640Sariff		SND_FORMAT(AFMT_S8, 1, 0),
1462193640Sariff		SND_FORMAT(AFMT_S8, 2, 0),
1463193640Sariff		SND_FORMAT(AFMT_S16_LE, 1, 0),
1464193640Sariff		SND_FORMAT(AFMT_S16_LE, 2, 0),
146565543Scg		0
146665543Scg	};
1467154241Sariff	static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0};
146865543Scg
1469137500Sjulian	return &playcaps;
1470137500Sjulian}
1471137500Sjulian
1472137500Sjulian
1473137500Sjulianstatic kobj_method_t aggpch_methods[] = {
1474137500Sjulian    	KOBJMETHOD(channel_init,		aggpch_init),
1475137500Sjulian    	KOBJMETHOD(channel_free,		aggpch_free),
1476137500Sjulian    	KOBJMETHOD(channel_setformat,		aggpch_setformat),
1477137500Sjulian    	KOBJMETHOD(channel_setspeed,		aggpch_setspeed),
1478137500Sjulian    	KOBJMETHOD(channel_setblocksize,	aggpch_setblocksize),
1479137500Sjulian    	KOBJMETHOD(channel_trigger,		aggpch_trigger),
1480137500Sjulian    	KOBJMETHOD(channel_getptr,		aggpch_getptr),
1481137500Sjulian    	KOBJMETHOD(channel_getcaps,		aggpch_getcaps),
1482193640Sariff	KOBJMETHOD_END
1483137500Sjulian};
1484137500SjulianCHANNEL_DECLARE(aggpch);
1485137500Sjulian
1486137500Sjulian
1487137500Sjulian/* -------------------------------------------------------------------- */
1488137500Sjulian
1489137500Sjulian/* Recording channel. */
1490137500Sjulian
1491137500Sjulianstatic void *
1492166279Sariffaggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
1493166279Sariff						struct pcm_channel *c, int dir)
1494137500Sjulian{
1495137500Sjulian	struct agg_info *ess = devinfo;
1496137500Sjulian	struct agg_rchinfo *ch;
1497137500Sjulian	u_int8_t *p;
1498137500Sjulian
1499137500Sjulian	KASSERT((dir == PCMDIR_REC),
1500137500Sjulian	    ("aggrch_init() called for PLAYBACK channel!"));
1501137500Sjulian	ch = &ess->rch;
1502137500Sjulian
1503137500Sjulian	ch->parent = ess;
1504137500Sjulian	ch->channel = c;
1505137500Sjulian	ch->buffer = b;
1506137500Sjulian
1507137500Sjulian	/* Uses the bottom-half of the status buffer. */
1508137500Sjulian	p        = ess->stat + ess->bufsz;
1509137500Sjulian	ch->phys = ess->phys + ess->bufsz;
1510137500Sjulian	ch->base = ess->phys;
1511137500Sjulian	ch->src  = (int16_t *)(p + ess->bufsz);
1512137500Sjulian	ch->srcphys = ch->phys + ess->bufsz;
1513137500Sjulian	ch->sink = (int16_t *)p;
1514137500Sjulian
1515137500Sjulian	sndbuf_setup(b, p, ess->bufsz);
1516137500Sjulian	ch->blklen = sndbuf_getblksz(b) / 2;
1517137500Sjulian	ch->buflen = sndbuf_getsize(b) / 2;
1518137500Sjulian
1519137500Sjulian	return ch;
1520137500Sjulian}
1521137500Sjulian
1522137500Sjulianstatic int
1523137500Sjulianaggrch_setformat(kobj_t obj, void *data, u_int32_t format)
1524137500Sjulian{
1525137500Sjulian	struct agg_rchinfo *ch = data;
1526137500Sjulian
1527137500Sjulian	if (!(format & AFMT_S16_LE))
1528137500Sjulian		return EINVAL;
1529193640Sariff	if (AFMT_CHANNEL(format) > 1)
1530137500Sjulian		ch->stereo = 1;
1531137500Sjulian	else
1532137500Sjulian		ch->stereo = 0;
1533137500Sjulian	return 0;
1534137500Sjulian}
1535137500Sjulian
1536193640Sariffstatic u_int32_t
1537137500Sjulianaggrch_setspeed(kobj_t obj, void *data, u_int32_t speed)
1538137500Sjulian{
1539193640Sariff
1540193640Sariff	((struct agg_rchinfo*)data)->speed = speed;
1541193640Sariff
1542193640Sariff	return (speed);
1543137500Sjulian}
1544137500Sjulian
1545193640Sariffstatic u_int32_t
1546137500Sjulianaggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1547137500Sjulian{
1548137500Sjulian	struct agg_rchinfo *ch = data;
1549137500Sjulian	int blkcnt;
1550137500Sjulian
1551137500Sjulian	/* try to keep at least 20msec DMA space */
1552137500Sjulian	blkcnt = (ch->speed << ch->stereo) / (25 * blocksize);
1553137500Sjulian	RANGE(blkcnt, 2, ch->parent->bufsz / blocksize);
1554137500Sjulian
1555137500Sjulian	if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) {
1556137500Sjulian		sndbuf_resize(ch->buffer, blkcnt, blocksize);
1557137500Sjulian		blkcnt = sndbuf_getblkcnt(ch->buffer);
1558137500Sjulian		blocksize = sndbuf_getblksz(ch->buffer);
1559137500Sjulian	} else {
1560137500Sjulian		sndbuf_setblkcnt(ch->buffer, blkcnt);
1561137500Sjulian		sndbuf_setblksz(ch->buffer, blocksize);
1562137500Sjulian	}
1563137500Sjulian
1564137500Sjulian	ch->blklen = blocksize / 2;
1565137500Sjulian	ch->buflen = blkcnt * blocksize / 2;
1566137500Sjulian	return blocksize;
1567137500Sjulian}
1568137500Sjulian
1569137500Sjulianstatic int
1570137500Sjulianaggrch_trigger(kobj_t obj, void *sc, int go)
1571137500Sjulian{
1572137500Sjulian	struct agg_rchinfo *ch = sc;
1573137500Sjulian
1574137500Sjulian	switch (go) {
1575137500Sjulian	case PCMTRIG_EMLDMARD:
1576137500Sjulian		if (ch->stereo)
1577137500Sjulian			aggch_feed_adc_stereo(ch);
1578137500Sjulian		else
1579137500Sjulian			aggch_feed_adc_mono(ch);
1580137500Sjulian		break;
1581137500Sjulian	case PCMTRIG_START:
1582137500Sjulian		aggch_start_adc(ch);
1583137500Sjulian		break;
1584137500Sjulian	case PCMTRIG_ABORT:
1585137500Sjulian	case PCMTRIG_STOP:
1586137500Sjulian		aggch_stop_adc(ch);
1587137500Sjulian		break;
1588137500Sjulian	}
1589137500Sjulian	return 0;
1590137500Sjulian}
1591137500Sjulian
1592193640Sariffstatic u_int32_t
1593137500Sjulianaggrch_getptr(kobj_t obj, void *sc)
1594137500Sjulian{
1595137500Sjulian	struct agg_rchinfo *ch = sc;
1596137500Sjulian
1597137500Sjulian	return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1;
1598137500Sjulian}
1599137500Sjulian
1600137500Sjulianstatic struct pcmchan_caps *
1601137500Sjulianaggrch_getcaps(kobj_t obj, void *sc)
1602137500Sjulian{
160365543Scg	static u_int32_t recfmt[] = {
1604193640Sariff		SND_FORMAT(AFMT_S16_LE, 1, 0),
1605193640Sariff		SND_FORMAT(AFMT_S16_LE, 2, 0),
160665543Scg		0
160765543Scg	};
1608137500Sjulian	static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0};
160965543Scg
1610137500Sjulian	return &reccaps;
161165543Scg}
161265543Scg
1613137500Sjulianstatic kobj_method_t aggrch_methods[] = {
1614137500Sjulian	KOBJMETHOD(channel_init,		aggrch_init),
1615137500Sjulian	/* channel_free: no-op */
1616137500Sjulian	KOBJMETHOD(channel_setformat,		aggrch_setformat),
1617137500Sjulian	KOBJMETHOD(channel_setspeed,		aggrch_setspeed),
1618137500Sjulian	KOBJMETHOD(channel_setblocksize,	aggrch_setblocksize),
1619137500Sjulian	KOBJMETHOD(channel_trigger,		aggrch_trigger),
1620137500Sjulian	KOBJMETHOD(channel_getptr,		aggrch_getptr),
1621137500Sjulian	KOBJMETHOD(channel_getcaps,		aggrch_getcaps),
1622193640Sariff	KOBJMETHOD_END
162370134Scg};
1624137500SjulianCHANNEL_DECLARE(aggrch);
162565543Scg
1626137500Sjulian
162765543Scg/* -----------------------------
162865543Scg * Bus space.
162965543Scg */
163065543Scg
163165543Scgstatic void
163265543Scgagg_intr(void *sc)
163365543Scg{
163465543Scg	struct agg_info* ess = sc;
1635137500Sjulian	register u_int8_t status;
163665543Scg	int i;
1637137500Sjulian	u_int m;
163865543Scg
1639137500Sjulian	status = AGG_RD(ess, PORT_HOSTINT_STAT, 1);
164065543Scg	if (!status)
164165543Scg		return;
164265543Scg
1643137500Sjulian	/* Acknowledge intr. */
1644137500Sjulian	AGG_WR(ess, PORT_HOSTINT_STAT, status, 1);
164570619Sjhb
1646137500Sjulian	if (status & HOSTINT_STAT_DSOUND) {
1647137500Sjulian#ifdef AGG_JITTER_CORRECTION
1648137500Sjulian		agg_lock(ess);
1649137500Sjulian#endif
1650137500Sjulian		if (ess->curpwr <= PCI_POWERSTATE_D1) {
1651137500Sjulian			AGG_WR(ess, PORT_INT_STAT, 1, 2);
1652137500Sjulian#ifdef AGG_JITTER_CORRECTION
1653137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1654137500Sjulian				if (ess->active & m)
1655137500Sjulian					suppress_jitter(ess->pch + i);
1656137500Sjulian			}
1657137500Sjulian			if (ess->active & m)
1658137500Sjulian				suppress_rec_jitter(&ess->rch);
1659137500Sjulian			agg_unlock(ess);
1660137500Sjulian#endif
1661137500Sjulian			for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) {
1662137500Sjulian				if (ess->active & m) {
1663137500Sjulian					if (ess->curpwr <= PCI_POWERSTATE_D1)
1664137500Sjulian						chn_intr(ess->pch[i].channel);
1665137500Sjulian					else {
1666137500Sjulian						m = 0;
1667137500Sjulian						break;
1668137500Sjulian					}
1669137500Sjulian				}
1670137500Sjulian			}
1671137500Sjulian			if ((ess->active & m)
1672137500Sjulian			    && ess->curpwr <= PCI_POWERSTATE_D1)
1673137500Sjulian				chn_intr(ess->rch.channel);
1674137500Sjulian		}
1675137500Sjulian#ifdef AGG_JITTER_CORRECTION
1676137500Sjulian		else
1677137500Sjulian			agg_unlock(ess);
1678137500Sjulian#endif
1679137500Sjulian	}
1680137500Sjulian
168165543Scg	if (status & HOSTINT_STAT_HWVOL) {
1682137500Sjulian		register u_int8_t event;
168370619Sjhb
1684137500Sjulian		agg_lock(ess);
1685137500Sjulian		event = AGG_RD(ess, PORT_HWVOL_MASTER, 1);
1686137500Sjulian		AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1);
1687137500Sjulian		agg_unlock(ess);
1688137500Sjulian
168970619Sjhb		switch (event) {
169070619Sjhb		case HWVOL_UP:
169170945Sjhb			mixer_hwvol_step(ess->dev, 1, 1);
169270619Sjhb			break;
169370619Sjhb		case HWVOL_DOWN:
169470945Sjhb			mixer_hwvol_step(ess->dev, -1, -1);
169570619Sjhb			break;
169670619Sjhb		case HWVOL_NOP:
169770619Sjhb			break;
169870619Sjhb		default:
1699137500Sjulian			if (event & HWVOL_MUTE) {
1700137500Sjulian				mixer_hwvol_mute(ess->dev);
1701137500Sjulian				break;
1702137500Sjulian			}
1703137500Sjulian			device_printf(ess->dev,
1704137500Sjulian				      "%s: unknown HWVOL event 0x%x\n",
1705137500Sjulian				      device_get_nameunit(ess->dev), event);
170665543Scg		}
170765543Scg	}
170865543Scg}
170965543Scg
171065543Scgstatic void
171165543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
171265543Scg{
171365543Scg	bus_addr_t *phys = arg;
171465543Scg
171565543Scg	*phys = error? 0 : segs->ds_addr;
171665543Scg
171765543Scg	if (bootverbose) {
171865543Scg		printf("setmap (%lx, %lx), nseg=%d, error=%d\n",
171965543Scg		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
172065543Scg		    nseg, error);
172165543Scg	}
172265543Scg}
172365543Scg
172465543Scgstatic void *
1725137500Sjuliandma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys)
172665543Scg{
172765543Scg	void *buf;
172865543Scg	bus_dmamap_t map;
172965543Scg
1730137500Sjulian	if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, &map))
173165543Scg		return NULL;
1732137500Sjulian	if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0)
1733137500Sjulian	    || !*phys || map) {
1734137500Sjulian		bus_dmamem_free(dmat, buf, map);
173565543Scg		return NULL;
173665543Scg	}
173765543Scg	return buf;
173865543Scg}
173965543Scg
174065543Scgstatic void
1741137500Sjuliandma_free(bus_dma_tag_t dmat, void *buf)
174265543Scg{
1743137500Sjulian	bus_dmamem_free(dmat, buf, NULL);
174465543Scg}
174565543Scg
174665543Scgstatic int
174765543Scgagg_probe(device_t dev)
174865543Scg{
174965543Scg	char *s = NULL;
175065543Scg
175165543Scg	switch (pci_get_devid(dev)) {
175265543Scg	case MAESTRO_1_PCI_ID:
175365543Scg		s = "ESS Technology Maestro-1";
175465543Scg		break;
175565543Scg
175665543Scg	case MAESTRO_2_PCI_ID:
175765543Scg		s = "ESS Technology Maestro-2";
175865543Scg		break;
175965543Scg
176065543Scg	case MAESTRO_2E_PCI_ID:
176165543Scg		s = "ESS Technology Maestro-2E";
176265543Scg		break;
176365543Scg	}
176465543Scg
176565543Scg	if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) {
176665543Scg		device_set_desc(dev, s);
1767142890Simp		return BUS_PROBE_DEFAULT;
176865543Scg	}
176965543Scg	return ENXIO;
177065543Scg}
177165543Scg
177265543Scgstatic int
177365543Scgagg_attach(device_t dev)
177465543Scg{
177565543Scg	struct agg_info	*ess = NULL;
177665543Scg	u_int32_t	data;
1777119690Sjhb	int	regid = PCIR_BAR(0);
177865543Scg	struct resource	*reg = NULL;
177965543Scg	struct ac97_info	*codec = NULL;
178065543Scg	int	irqid = 0;
178165543Scg	struct resource	*irq = NULL;
178265543Scg	void	*ih = NULL;
178365543Scg	char	status[SND_STATUSLEN];
1784174582Sariff	int	dacn, ret = 0;
178565543Scg
1786170873Sariff	ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO);
178765543Scg	ess->dev = dev;
178865543Scg
1789167608Sariff	mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc",
1790137500Sjulian		 MTX_DEF | MTX_RECURSE);
1791137500Sjulian	if (!mtx_initialized(&ess->lock)) {
1792137500Sjulian		device_printf(dev, "failed to create a mutex.\n");
1793137500Sjulian		ret = ENOMEM;
1794137500Sjulian		goto bad;
1795137500Sjulian	}
1796137500Sjulian
1797174582Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1798174582Sariff	    "dac", &dacn) == 0) {
1799174582Sariff	    	if (dacn < 1)
1800174582Sariff			dacn = 1;
1801174582Sariff		else if (dacn > AGG_MAXPLAYCH)
1802174582Sariff			dacn = AGG_MAXPLAYCH;
1803174582Sariff	} else
1804174582Sariff		dacn = AGG_MAXPLAYCH;
1805174582Sariff
180684658Scg	ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536);
1807166904Snetchild	if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev),
1808137500Sjulian			       /*align */ 4, 1 << (16+1),
1809137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1810137500Sjulian			       /*filter*/ NULL, NULL,
1811137500Sjulian			       /*size  */ ess->bufsz, 1, 0x3ffff,
1812137500Sjulian			       /*flags */ 0,
1813137500Sjulian#if __FreeBSD_version >= 501102
1814137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1815137500Sjulian#endif
1816137500Sjulian			       &ess->buf_dmat) != 0) {
1817137500Sjulian		device_printf(dev, "unable to create dma tag\n");
1818137500Sjulian		ret = ENOMEM;
1819137500Sjulian		goto bad;
1820137500Sjulian	}
182184658Scg
1822166904Snetchild	if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev),
1823137500Sjulian			       /*align */ 1 << WAVCACHE_BASEADDR_SHIFT,
1824137500Sjulian			                  1 << (16+1),
1825137500Sjulian			       /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR,
1826137500Sjulian			       /*filter*/ NULL, NULL,
1827137500Sjulian			       /*size  */ 3*ess->bufsz, 1, 0x3ffff,
1828137500Sjulian			       /*flags */ 0,
1829137500Sjulian#if __FreeBSD_version >= 501102
1830137500Sjulian			       /*lock  */ busdma_lock_mutex, &Giant,
1831137500Sjulian#endif
1832137500Sjulian			       &ess->stat_dmat) != 0) {
183365543Scg		device_printf(dev, "unable to create dma tag\n");
1834137500Sjulian		ret = ENOMEM;
183565543Scg		goto bad;
183665543Scg	}
183765543Scg
1838137500Sjulian	/* Allocate the room for brain-damaging status buffer. */
1839137500Sjulian	ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys);
184065543Scg	if (ess->stat == NULL) {
1841137500Sjulian		device_printf(dev, "cannot allocate status buffer\n");
1842137500Sjulian		ret = ENOMEM;
184365543Scg		goto bad;
184465543Scg	}
184565543Scg	if (bootverbose)
1846137500Sjulian		device_printf(dev, "Maestro status/record buffer: %#llx\n",
1847137500Sjulian		    (long long)ess->phys);
184865543Scg
1849137500Sjulian	/* State D0-uninitialized. */
1850137500Sjulian	ess->curpwr = PCI_POWERSTATE_D3;
1851137500Sjulian	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
185265543Scg
1853254306Sscottl	pci_enable_busmaster(dev);
185465543Scg
1855137500Sjulian	/* Allocate resources. */
1856254306Sscottl	reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &regid, RF_ACTIVE);
1857137500Sjulian	if (reg != NULL) {
1858137500Sjulian		ess->reg = reg;
1859137500Sjulian		ess->regid = regid;
1860137500Sjulian		ess->st = rman_get_bustag(reg);
1861137500Sjulian		ess->sh = rman_get_bushandle(reg);
1862137500Sjulian	} else {
186365543Scg		device_printf(dev, "unable to map register space\n");
1864137500Sjulian		ret = ENXIO;
186565543Scg		goto bad;
186665543Scg	}
1867141095Simp	irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid,
1868141095Simp	    RF_ACTIVE | RF_SHAREABLE);
1869137500Sjulian	if (irq != NULL) {
1870137500Sjulian		ess->irq = irq;
1871137500Sjulian		ess->irqid = irqid;
1872137500Sjulian	} else {
1873137500Sjulian		device_printf(dev, "unable to map interrupt\n");
1874137500Sjulian		ret = ENXIO;
1875137500Sjulian		goto bad;
1876137500Sjulian	}
187765543Scg
1878137500Sjulian	/* Setup resources. */
1879137500Sjulian	if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) {
1880137500Sjulian		device_printf(dev, "unable to setup interrupt\n");
1881137500Sjulian		ret = ENXIO;
1882137500Sjulian		goto bad;
1883137500Sjulian	} else
1884137500Sjulian		ess->ih = ih;
1885137500Sjulian
1886137500Sjulian	/* Transition from D0-uninitialized to D0. */
1887137500Sjulian	agg_lock(ess);
1888137500Sjulian	agg_power(ess, PCI_POWERSTATE_D0);
1889137500Sjulian	if (agg_rdcodec(ess, 0) == 0x80) {
1890137500Sjulian		/* XXX - TODO: PT101 */
1891154067Sariff		agg_unlock(ess);
189265543Scg		device_printf(dev, "PT101 codec detected!\n");
1893137500Sjulian		ret = ENXIO;
189465543Scg		goto bad;
189565543Scg	}
1896154067Sariff	agg_unlock(ess);
189770134Scg	codec = AC97_CREATE(dev, ess, agg_ac97);
1898137500Sjulian	if (codec == NULL) {
1899137500Sjulian		device_printf(dev, "failed to create AC97 codec softc!\n");
1900137500Sjulian		ret = ENOMEM;
190165543Scg		goto bad;
1902137500Sjulian	}
1903137500Sjulian	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
1904137500Sjulian		device_printf(dev, "mixer initialization failed!\n");
1905137500Sjulian		ret = ENXIO;
190665543Scg		goto bad;
1907137500Sjulian	}
190865543Scg	ess->codec = codec;
190965543Scg
1910174582Sariff	ret = pcm_register(dev, ess, dacn, 1);
1911137500Sjulian	if (ret)
191265543Scg		goto bad;
191365543Scg
191470945Sjhb	mixer_hwvol_init(dev);
1915154067Sariff	agg_lock(ess);
1916137500Sjulian	agg_power(ess, powerstate_init);
1917154067Sariff	agg_unlock(ess);
1918174582Sariff	for (data = 0; data < dacn; data++)
1919137500Sjulian		pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess);
192070134Scg	pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess);
1921137500Sjulian	adjust_pchbase(ess->pch, ess->playchns, ess->bufsz);
1922137500Sjulian
1923137500Sjulian	snprintf(status, SND_STATUSLEN,
1924137500Sjulian	    "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d",
1925137500Sjulian	    rman_get_start(reg), rman_get_end(reg), rman_get_start(irq),
1926137500Sjulian	    pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev));
192765543Scg	pcm_setstatus(dev, status);
192865543Scg
192965543Scg	return 0;
193065543Scg
193165543Scg bad:
193265644Scg	if (codec != NULL)
193365644Scg		ac97_destroy(codec);
193465543Scg	if (ih != NULL)
193565543Scg		bus_teardown_intr(dev, irq, ih);
193665543Scg	if (irq != NULL)
193765543Scg		bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
193865543Scg	if (reg != NULL)
193965543Scg		bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
1940154627Snetchild	if (ess != NULL) {
1941154627Snetchild		if (ess->stat != NULL)
1942154627Snetchild			dma_free(ess->stat_dmat, ess->stat);
1943154627Snetchild		if (ess->stat_dmat != NULL)
1944154627Snetchild			bus_dma_tag_destroy(ess->stat_dmat);
1945154627Snetchild		if (ess->buf_dmat != NULL)
1946154627Snetchild			bus_dma_tag_destroy(ess->buf_dmat);
1947154627Snetchild		if (mtx_initialized(&ess->lock))
1948154627Snetchild			mtx_destroy(&ess->lock);
194965543Scg		free(ess, M_DEVBUF);
1950154627Snetchild	}
195165543Scg
1952137500Sjulian	return ret;
195365543Scg}
195465543Scg
195565543Scgstatic int
195665543Scgagg_detach(device_t dev)
195765543Scg{
195865543Scg	struct agg_info	*ess = pcm_getdevinfo(dev);
195965543Scg	int r;
1960137500Sjulian	u_int16_t icr;
196165543Scg
1962137500Sjulian	icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2);
1963137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
1964137500Sjulian
1965137500Sjulian	agg_lock(ess);
1966137500Sjulian	if (ess->active) {
1967137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
1968137500Sjulian		agg_unlock(ess);
1969137500Sjulian		return EBUSY;
1970137500Sjulian	}
1971137500Sjulian	agg_unlock(ess);
1972137500Sjulian
197365543Scg	r = pcm_unregister(dev);
1974137500Sjulian	if (r) {
1975137500Sjulian		AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2);
197665543Scg		return r;
1977137500Sjulian	}
197865543Scg
1979137500Sjulian	agg_lock(ess);
1980137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
1981154067Sariff	agg_unlock(ess);
198265543Scg
198365543Scg	bus_teardown_intr(dev, ess->irq, ess->ih);
198465543Scg	bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq);
198565543Scg	bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg);
1986137500Sjulian	dma_free(ess->stat_dmat, ess->stat);
1987137500Sjulian	bus_dma_tag_destroy(ess->stat_dmat);
1988137500Sjulian	bus_dma_tag_destroy(ess->buf_dmat);
1989137500Sjulian	mtx_destroy(&ess->lock);
199065543Scg	free(ess, M_DEVBUF);
199165543Scg	return 0;
199265543Scg}
199365543Scg
199465543Scgstatic int
199565543Scgagg_suspend(device_t dev)
199665543Scg{
199765543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
199865543Scg
1999137500Sjulian	AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
2000137500Sjulian	agg_lock(ess);
2001137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2002137500Sjulian	agg_unlock(ess);
200365543Scg
200465543Scg	return 0;
200565543Scg}
200665543Scg
200765543Scgstatic int
200865543Scgagg_resume(device_t dev)
200965543Scg{
2010137500Sjulian	int i;
201165543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
201265543Scg
201365543Scg	for (i = 0; i < ess->playchns; i++)
201465543Scg		if (ess->active & (1 << i))
201565543Scg			aggch_start_dac(ess->pch + i);
201665543Scg	if (ess->active & (1 << i))
201765543Scg		aggch_start_adc(&ess->rch);
2018137500Sjulian
2019137500Sjulian	agg_lock(ess);
2020137500Sjulian	if (!ess->active)
2021137500Sjulian		agg_power(ess, powerstate_init);
2022137500Sjulian	agg_unlock(ess);
2023137500Sjulian
2024137500Sjulian	if (mixer_reinit(dev)) {
2025137500Sjulian		device_printf(dev, "unable to reinitialize the mixer\n");
2026137500Sjulian		return ENXIO;
202765543Scg	}
2028137500Sjulian
202965543Scg	return 0;
203065543Scg}
203165543Scg
203265543Scgstatic int
203365543Scgagg_shutdown(device_t dev)
203465543Scg{
203565543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
203665543Scg
2037137500Sjulian	agg_lock(ess);
2038137500Sjulian	agg_power(ess, PCI_POWERSTATE_D3);
2039137500Sjulian	agg_unlock(ess);
204065543Scg
204165543Scg	return 0;
204265543Scg}
204365543Scg
204465543Scg
204565543Scgstatic device_method_t agg_methods[] = {
204665543Scg    DEVMETHOD(device_probe,	agg_probe),
204765543Scg    DEVMETHOD(device_attach,	agg_attach),
204865543Scg    DEVMETHOD(device_detach,	agg_detach),
204965543Scg    DEVMETHOD(device_suspend,	agg_suspend),
205065543Scg    DEVMETHOD(device_resume,	agg_resume),
205165543Scg    DEVMETHOD(device_shutdown,	agg_shutdown),
205265543Scg
205365543Scg    { 0, 0 }
205465543Scg};
205565543Scg
205665543Scgstatic driver_t agg_driver = {
205765543Scg    "pcm",
205865543Scg    agg_methods,
205982180Scg    PCM_SOFTC_SIZE,
206065543Scg};
206165543Scg
2062137500Sjulian/*static devclass_t pcm_devclass;*/
2063137500Sjulian
206465543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
2065132236StanimuraMODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
206665543ScgMODULE_VERSION(snd_maestro, 1);
2067