maestro.c revision 65644
165543Scg/*-
265543Scg * Copyright (c) 2000 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp>
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 *
2665543Scg *	$Id: maestro.c,v 1.12 2000/09/06 03:32:34 taku Exp $
2765543Scg * $FreeBSD: head/sys/dev/sound/pci/maestro.c 65644 2000-09-09 19:21:04Z cg $
2865543Scg */
2965543Scg
3065543Scg/*
3165543Scg * Credits:
3265543Scg *
3365543Scg * Part of this code (especially in many magic numbers) was heavily inspired
3465543Scg * by the Linux driver originally written by
3565543Scg * Alan Cox <alan.cox@linux.org>, modified heavily by
3665543Scg * Zach Brown <zab@zabbo.net>.
3765543Scg *
3865543Scg * busdma()-ize and buffer size reduction were suggested by
3965543Scg * Cameron Grant <gandalf@vilnya.demon.co.uk>.
4065543Scg * Also he showed me the way to use busdma() suite.
4165543Scg *
4265543Scg * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500
4365543Scg * were looked at by
4465543Scg * Munehiro Matsuda <haro@tk.kubota.co.jp>,
4565543Scg * who brought patches based on the Linux driver with some simplification.
4665543Scg */
4765543Scg
4865543Scg#include <dev/sound/pcm/sound.h>
4965543Scg#include <dev/sound/pcm/ac97.h>
5065543Scg#include <pci/pcireg.h>
5165543Scg#include <pci/pcivar.h>
5265543Scg
5365543Scg#include <dev/sound/pci/maestro_reg.h>
5465543Scg
5565543Scg#define inline __inline
5665543Scg
5765543Scg/*
5865543Scg * PCI IDs of supported chips:
5965543Scg *
6065543Scg * MAESTRO-1	0x01001285
6165543Scg * MAESTRO-2	0x1968125d
6265543Scg * MAESTRO-2E	0x1978125d
6365543Scg */
6465543Scg
6565543Scg#define MAESTRO_1_PCI_ID	0x01001285
6665543Scg#define MAESTRO_2_PCI_ID	0x1968125d
6765543Scg#define MAESTRO_2E_PCI_ID	0x1978125d
6865543Scg
6965543Scg#define NEC_SUBID1	0x80581033	/* Taken from Linux driver */
7065543Scg#define NEC_SUBID2	0x803c1033	/* NEC VersaProNX VA26D    */
7165543Scg
7265543Scg#ifndef AGG_MAXPLAYCH
7365543Scg# define AGG_MAXPLAYCH	4
7465543Scg#endif
7565543Scg
7665543Scg#define AGG_BUFSIZ	4096
7765543Scg
7865543Scg
7965543Scg/* -----------------------------
8065543Scg * Data structures.
8165543Scg */
8265543Scgstruct agg_chinfo {
8365543Scg	struct agg_info		*parent;
8465543Scg	pcm_channel		*channel;
8565543Scg	snd_dbuf		*buffer;
8665543Scg	bus_addr_t		offset;
8765543Scg	u_int32_t		blocksize;
8865543Scg	int			dir;
8965543Scg	u_int			num;
9065543Scg	u_int16_t		aputype;
9165543Scg	u_int16_t		wcreg_tpl;
9265543Scg};
9365543Scg
9465543Scgstruct agg_info {
9565543Scg	device_t		dev;
9665543Scg	struct resource		*reg;
9765543Scg	int			regid;
9865543Scg
9965543Scg	bus_space_tag_t		st;
10065543Scg	bus_space_handle_t	sh;
10165543Scg	bus_dma_tag_t		parent_dmat;
10265543Scg
10365543Scg	struct resource		*irq;
10465543Scg	int			irqid;
10565543Scg	void			*ih;
10665543Scg
10765543Scg	u_int8_t		*stat;
10865543Scg	bus_addr_t		baseaddr;
10965543Scg
11065543Scg	struct ac97_info	*codec;
11165543Scg
11265543Scg	u_int			playchns, active;
11365543Scg	struct agg_chinfo	pch[AGG_MAXPLAYCH];
11465543Scg	struct agg_chinfo	rch;
11565543Scg};
11665543Scg
11765543Scg
11865543Scgstatic u_int32_t	 agg_rdcodec(void *, int);
11965543Scgstatic void		 agg_wrcodec(void *, int, u_int32_t);
12065543Scg
12165543Scgstatic inline void	 ringbus_setdest(struct agg_info*, int, int);
12265543Scg
12365543Scgstatic inline u_int16_t	 wp_rdreg(struct agg_info*, u_int16_t);
12465543Scgstatic inline void	 wp_wrreg(struct agg_info*, u_int16_t, u_int16_t);
12565543Scgstatic inline u_int16_t	 wp_rdapu(struct agg_info*, int, u_int16_t);
12665543Scgstatic inline void	 wp_wrapu(struct agg_info*, int, u_int16_t, u_int16_t);
12765543Scgstatic inline void	 wp_settimer(struct agg_info*, u_int);
12865543Scgstatic inline void	 wp_starttimer(struct agg_info*);
12965543Scgstatic inline void	 wp_stoptimer(struct agg_info*);
13065543Scg
13165543Scgstatic inline u_int16_t	 wc_rdreg(struct agg_info*, u_int16_t);
13265543Scgstatic inline void	 wc_wrreg(struct agg_info*, u_int16_t, u_int16_t);
13365543Scgstatic inline u_int16_t	 wc_rdchctl(struct agg_info*, int);
13465543Scgstatic inline void	 wc_wrchctl(struct agg_info*, int, u_int16_t);
13565543Scg
13665543Scgstatic inline void	 agg_power(struct agg_info*, int);
13765543Scg
13865543Scgstatic void		 agg_init(struct agg_info*);
13965543Scgstatic u_int32_t	 agg_ac97_init(void *);
14065543Scg
14165543Scgstatic void		 aggch_start_dac(struct agg_chinfo*);
14265543Scgstatic void		 aggch_stop_dac(struct agg_chinfo*);
14365543Scg
14465543Scgstatic inline void	 suppress_jitter(struct agg_chinfo*);
14565543Scg
14665543Scgstatic inline u_int	 calc_timer_freq(struct agg_chinfo*);
14765543Scgstatic void		 set_timer(struct agg_info*);
14865543Scg
14965543Scgstatic pcmchan_init_t		aggch_init;
15065543Scgstatic pcmchan_free_t		aggch_free;
15165543Scgstatic pcmchan_setformat_t	aggch_setplayformat;
15265543Scgstatic pcmchan_setspeed_t	aggch_setspeed;
15365543Scgstatic pcmchan_setblocksize_t	aggch_setblocksize;
15465543Scgstatic pcmchan_trigger_t	aggch_trigger;
15565543Scgstatic pcmchan_getptr_t		aggch_getplayptr;
15665543Scgstatic pcmchan_getcaps_t	aggch_getcaps;
15765543Scg
15865543Scgstatic void		 agg_intr(void *);
15965543Scgstatic int		 agg_probe(device_t);
16065543Scgstatic int		 agg_attach(device_t);
16165543Scgstatic int		 agg_detach(device_t);
16265543Scgstatic int		 agg_suspend(device_t);
16365543Scgstatic int		 agg_resume(device_t);
16465543Scgstatic int		 agg_shutdown(device_t);
16565543Scg
16665543Scgstatic void	*dma_malloc(struct agg_info*, u_int32_t, bus_addr_t*);
16765543Scgstatic void	 dma_free(struct agg_info*, void *);
16865543Scg
16965543Scg/* -----------------------------
17065543Scg * Subsystems.
17165543Scg */
17265543Scg
17365543Scg/* Codec/Ringbus */
17465543Scg
17565543Scgstatic u_int32_t
17665543Scgagg_rdcodec(void *sc, int regno)
17765543Scg{
17865543Scg	struct agg_info *ess = sc;
17965543Scg	unsigned t;
18065543Scg
18165543Scg	/* We have to wait for a SAFE time to write addr/data */
18265543Scg	for (t = 0; t < 20; t++) {
18365543Scg		if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT)
18465543Scg		    & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS)
18565543Scg			break;
18665543Scg		DELAY(2);	/* 20.8us / 13 */
18765543Scg	}
18865543Scg	if (t == 20)
18965543Scg		device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n");
19065543Scg
19165543Scg	bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD,
19265543Scg	    CODEC_CMD_READ | regno);
19365543Scg	DELAY(21);	/* AC97 cycle = 20.8usec */
19465543Scg
19565543Scg	/* Wait for data retrieve */
19665543Scg	for (t = 0; t < 20; t++) {
19765543Scg		if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT)
19865543Scg		    & CODEC_STAT_MASK) == CODEC_STAT_RW_DONE)
19965543Scg			break;
20065543Scg		DELAY(2);	/* 20.8us / 13 */
20165543Scg	}
20265543Scg	if (t == 20)
20365543Scg		/* Timed out, but perform dummy read. */
20465543Scg		device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n");
20565543Scg
20665543Scg	return bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG);
20765543Scg}
20865543Scg
20965543Scgstatic void
21065543Scgagg_wrcodec(void *sc, int regno, u_int32_t data)
21165543Scg{
21265543Scg	unsigned t;
21365543Scg	struct agg_info *ess = sc;
21465543Scg
21565543Scg	/* We have to wait for a SAFE time to write addr/data */
21665543Scg	for (t = 0; t < 20; t++) {
21765543Scg		if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT)
21865543Scg		    & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS)
21965543Scg			break;
22065543Scg		DELAY(2);	/* 20.8us / 13 */
22165543Scg	}
22265543Scg	if (t == 20) {
22365543Scg		/* Timed out. Abort writing. */
22465543Scg		device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n");
22565543Scg		return;
22665543Scg	}
22765543Scg
22865543Scg	bus_space_write_2(ess->st, ess->sh, PORT_CODEC_REG, data);
22965543Scg	bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD,
23065543Scg	    CODEC_CMD_WRITE | regno);
23165543Scg}
23265543Scg
23365543Scgstatic inline void
23465543Scgringbus_setdest(struct agg_info *ess, int src, int dest)
23565543Scg{
23665543Scg	u_int32_t	data;
23765543Scg
23865543Scg	data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL);
23965543Scg	data &= ~(0xfU << src);
24065543Scg	data |= (0xfU & dest) << src;
24165543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data);
24265543Scg}
24365543Scg
24465543Scg/* Wave Processor */
24565543Scg
24665543Scgstatic inline u_int16_t
24765543Scgwp_rdreg(struct agg_info *ess, u_int16_t reg)
24865543Scg{
24965543Scg	bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg);
25065543Scg	return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA);
25165543Scg}
25265543Scg
25365543Scgstatic inline void
25465543Scgwp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
25565543Scg{
25665543Scg	bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg);
25765543Scg	bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data);
25865543Scg}
25965543Scg
26065543Scgstatic inline void
26165543Scgapu_setindex(struct agg_info *ess, u_int16_t reg)
26265543Scg{
26365543Scg	int t;
26465543Scg
26565543Scg	wp_wrreg(ess, WPREG_CRAM_PTR, reg);
26665543Scg	/* Sometimes WP fails to set apu register index. */
26765543Scg	for (t = 0; t < 1000; t++) {
26865543Scg		if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg)
26965543Scg			break;
27065543Scg		bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg);
27165543Scg	}
27265543Scg	if (t == 1000)
27365543Scg		device_printf(ess->dev, "apu_setindex() timed out.\n");
27465543Scg}
27565543Scg
27665543Scgstatic inline u_int16_t
27765543Scgwp_rdapu(struct agg_info *ess, int ch, u_int16_t reg)
27865543Scg{
27965543Scg	u_int16_t ret;
28065543Scg
28165543Scg	apu_setindex(ess, ((unsigned)ch << 4) + reg);
28265543Scg	ret = wp_rdreg(ess, WPREG_DATA_PORT);
28365543Scg	return ret;
28465543Scg}
28565543Scg
28665543Scgstatic inline void
28765543Scgwp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data)
28865543Scg{
28965543Scg	int t;
29065543Scg
29165543Scg	apu_setindex(ess, ((unsigned)ch << 4) + reg);
29265543Scg	wp_wrreg(ess, WPREG_DATA_PORT, data);
29365543Scg	for (t = 0; t < 1000; t++) {
29465543Scg		if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data)
29565543Scg			break;
29665543Scg		bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data);
29765543Scg	}
29865543Scg	if (t == 1000)
29965543Scg		device_printf(ess->dev, "wp_wrapu() timed out.\n");
30065543Scg}
30165543Scg
30265543Scgstatic inline void
30365543Scgwp_settimer(struct agg_info *ess, u_int freq)
30465543Scg{
30565543Scg	u_int clock = 48000 << 2;
30665543Scg	u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0;
30765543Scg
30865543Scg	RANGE(divide, 4, 32 << 8);
30965543Scg
31065543Scg	for (; divide > 32 << 1; divide >>= 1)
31165543Scg		prescale++;
31265543Scg	divide = (divide + 1) >> 1;
31365543Scg
31465543Scg	for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1)
31565543Scg		prescale++;
31665543Scg
31765543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 0);
31865543Scg	wp_wrreg(ess, WPREG_TIMER_FREQ,
31965543Scg	    (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1));
32065543Scg	wp_wrreg(ess, WPREG_TIMER_ENABLE, 1);
32165543Scg}
32265543Scg
32365543Scgstatic inline void
32465543Scgwp_starttimer(struct agg_info *ess)
32565543Scg{
32665543Scg	wp_wrreg(ess, WPREG_TIMER_START, 1);
32765543Scg}
32865543Scg
32965543Scgstatic inline void
33065543Scgwp_stoptimer(struct agg_info *ess)
33165543Scg{
33265543Scg	wp_wrreg(ess, WPREG_TIMER_START, 0);
33365543Scg	bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1);
33465543Scg}
33565543Scg
33665543Scg/* WaveCache */
33765543Scg
33865543Scgstatic inline u_int16_t
33965543Scgwc_rdreg(struct agg_info *ess, u_int16_t reg)
34065543Scg{
34165543Scg	bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg);
34265543Scg	return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA);
34365543Scg}
34465543Scg
34565543Scgstatic inline void
34665543Scgwc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data)
34765543Scg{
34865543Scg	bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg);
34965543Scg	bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data);
35065543Scg}
35165543Scg
35265543Scgstatic inline u_int16_t
35365543Scgwc_rdchctl(struct agg_info *ess, int ch)
35465543Scg{
35565543Scg	return wc_rdreg(ess, ch << 3);
35665543Scg}
35765543Scg
35865543Scgstatic inline void
35965543Scgwc_wrchctl(struct agg_info *ess, int ch, u_int16_t data)
36065543Scg{
36165543Scg	wc_wrreg(ess, ch << 3, data);
36265543Scg}
36365543Scg
36465543Scg/* Power management */
36565543Scg
36665543Scgstatic inline void
36765543Scgagg_power(struct agg_info *ess, int status)
36865543Scg{
36965543Scg	u_int8_t data;
37065543Scg
37165543Scg	data = pci_read_config(ess->dev, CONF_PM_PTR, 1);
37265543Scg	if (pci_read_config(ess->dev, data, 1) == PPMI_CID)
37365543Scg		pci_write_config(ess->dev, data + PM_CTRL, status, 1);
37465543Scg}
37565543Scg
37665543Scg
37765543Scg/* -----------------------------
37865543Scg * Controller.
37965543Scg */
38065543Scg
38165543Scgstatic inline void
38265543Scgagg_initcodec(struct agg_info* ess)
38365543Scg{
38465543Scg	u_int16_t data;
38565543Scg
38665543Scg	if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL)
38765543Scg	    & RINGBUS_CTRL_ACLINK_ENABLED) {
38865543Scg		bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0);
38965543Scg		DELAY(104);	/* 20.8us * (4 + 1) */
39065543Scg	}
39165543Scg	/* XXX - 2nd codec should be looked at. */
39265543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL,
39365543Scg	    RINGBUS_CTRL_AC97_SWRESET);
39465543Scg	DELAY(2);
39565543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL,
39665543Scg	    RINGBUS_CTRL_ACLINK_ENABLED);
39765543Scg	DELAY(21);
39865543Scg
39965543Scg	agg_rdcodec(ess, 0);
40065543Scg	if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT)
40165543Scg	    & CODEC_STAT_MASK) {
40265543Scg		bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0);
40365543Scg		DELAY(21);
40465543Scg
40565543Scg		/* Try cold reset. */
40665543Scg		device_printf(ess->dev, "will perform cold reset.\n");
40765543Scg		data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR);
40865543Scg		if (pci_read_config(ess->dev, 0x58, 2) & 1)
40965543Scg			data |= 0x10;
41065543Scg		data |= 0x009 &
41165543Scg		    ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA);
41265543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6);
41365543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR,
41465543Scg		    data | 0x009);
41565543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000);
41665543Scg		DELAY(2);
41765543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001);
41865543Scg		DELAY(1);
41965543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009);
42065543Scg		DELAY(500000);
42165543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data);
42265543Scg		DELAY(84);	/* 20.8us * 4 */
42365543Scg		bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL,
42465543Scg		    RINGBUS_CTRL_ACLINK_ENABLED);
42565543Scg		DELAY(21);
42665543Scg	}
42765543Scg}
42865543Scg
42965543Scgstatic void
43065543Scgagg_init(struct agg_info* ess)
43165543Scg{
43265543Scg	u_int32_t data;
43365543Scg
43465543Scg	/* Setup PCI config registers. */
43565543Scg
43665543Scg	/* Disable all legacy emulations. */
43765543Scg	data = pci_read_config(ess->dev, CONF_LEGACY, 2);
43865543Scg	data |= LEGACY_DISABLED;
43965543Scg	pci_write_config(ess->dev, CONF_LEGACY, data, 2);
44065543Scg
44165543Scg	/* Disconnect from CHI. (Makes Dell inspiron 7500 work?)
44265543Scg	 * Enable posted write.
44365543Scg	 * Prefer PCI timing rather than that of ISA.
44465543Scg	 * Don't swap L/R. */
44565543Scg	data = pci_read_config(ess->dev, CONF_MAESTRO, 4);
44665543Scg	data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING;
44765543Scg	data &= ~MAESTRO_SWAP_LR;
44865543Scg	pci_write_config(ess->dev, CONF_MAESTRO, data, 4);
44965543Scg
45065543Scg	/* Reset direct sound. */
45165543Scg	bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL,
45265543Scg	    HOSTINT_CTRL_DSOUND_RESET);
45365543Scg	DELAY(10000);	/* XXX - too long? */
45465543Scg	bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0);
45565543Scg	DELAY(10000);
45665543Scg
45765543Scg	/* Enable direct sound interruption. */
45865543Scg	bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL,
45965543Scg	    HOSTINT_CTRL_DSOUND_INT_ENABLED);
46065543Scg
46165543Scg	/* Setup Wave Processor. */
46265543Scg
46365543Scg	/* Enable WaveCache, set DMA base address. */
46465543Scg	wp_wrreg(ess, WPREG_WAVE_ROMRAM,
46565543Scg	    WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED);
46665543Scg	bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL,
46765543Scg	    WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB);
46865543Scg
46965543Scg	for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++)
47065543Scg		wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT);
47165543Scg
47265543Scg	/* Setup Codec/Ringbus. */
47365543Scg	agg_initcodec(ess);
47465543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL,
47565543Scg	    RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED);
47665543Scg
47765543Scg	wp_wrreg(ess, WPREG_BASE, 0x8500);	/* Parallel I/O */
47865543Scg	ringbus_setdest(ess, RINGBUS_SRC_ADC,
47965543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN);
48065543Scg	ringbus_setdest(ess, RINGBUS_SRC_DSOUND,
48165543Scg	    RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC);
48265543Scg
48365543Scg	/* Setup ASSP. Needed for Dell Inspiron 7500? */
48465543Scg	bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00);
48565543Scg	bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03);
48665543Scg	bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00);
48765543Scg
48865543Scg	/*
48965543Scg	 * Setup GPIO.
49065543Scg	 * There seems to be speciality with NEC systems.
49165543Scg	 */
49265543Scg	switch (pci_get_subvendor(ess->dev)
49365543Scg	    | (pci_get_subdevice(ess->dev) << 16)) {
49465543Scg	case NEC_SUBID1:
49565543Scg	case NEC_SUBID2:
49665543Scg		/* Matthew Braithwaite <matt@braithwaite.net> reported that
49765543Scg		 * NEC Versa LX doesn't need GPIO operation. */
49865543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0x9ff);
49965543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR,
50065543Scg		    bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 0x600);
50165543Scg		bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x200);
50265543Scg		break;
50365543Scg	}
50465543Scg}
50565543Scg
50665543Scg/* Channel controller. */
50765543Scg
50865543Scgstatic void
50965543Scgaggch_start_dac(struct agg_chinfo *ch)
51065543Scg{
51165543Scg	u_int wpwa = APU_USE_SYSMEM | (ch->offset >> 9);
51265543Scg	u_int size = AGG_BUFSIZ >> 1;
51365543Scg	u_int speed = ch->channel->speed;
51465543Scg	u_int offset = ch->offset >> 1;
51565543Scg	u_int cp = ch->buffer->rp >> 1;
51665543Scg	u_int16_t apuch = ch->num << 1;
51765543Scg	u_int dv;
51865543Scg	int pan = 0;
51965543Scg
52065543Scg	switch (ch->aputype) {
52165543Scg	case APUTYPE_16BITSTEREO:
52265543Scg		wpwa >>= 1;
52365543Scg		size >>= 1;
52465543Scg		offset >>= 1;
52565543Scg		cp >>= 1;
52665543Scg		/* FALLTHROUGH */
52765543Scg	case APUTYPE_8BITSTEREO:
52865543Scg		pan = 8;
52965543Scg		apuch++;
53065543Scg		break;
53165543Scg	case APUTYPE_8BITLINEAR:
53265543Scg		speed >>= 1;
53365543Scg		break;
53465543Scg	}
53565543Scg
53665543Scg	dv = (((speed % 48000) << 16) + 24000) / 48000
53765543Scg	    + ((speed / 48000) << 16);
53865543Scg
53965543Scg	do {
54065543Scg		wp_wrapu(ch->parent, apuch, APUREG_WAVESPACE, wpwa & 0xff00);
54165543Scg		wp_wrapu(ch->parent, apuch, APUREG_CURPTR, offset + cp);
54265543Scg		wp_wrapu(ch->parent, apuch, APUREG_ENDPTR, offset + size);
54365543Scg		wp_wrapu(ch->parent, apuch, APUREG_LOOPLEN, size);
54465543Scg		wp_wrapu(ch->parent, apuch, APUREG_AMPLITUDE, 0xe800);
54565543Scg		wp_wrapu(ch->parent, apuch, APUREG_POSITION, 0x8f00
54665543Scg		    | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)
54765543Scg		    | ((PAN_FRONT + pan) << APU_PAN_SHIFT));
54865543Scg		wp_wrapu(ch->parent, apuch, APUREG_FREQ_LOBYTE, APU_plus6dB
54965543Scg		    | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT));
55065543Scg		wp_wrapu(ch->parent, apuch, APUREG_FREQ_HIWORD, dv >> 8);
55165543Scg
55265543Scg		if (ch->aputype == APUTYPE_16BITSTEREO)
55365543Scg			wpwa |= APU_STEREO >> 1;
55465543Scg		pan = -pan;
55565543Scg	} while (pan < 0 && apuch--);
55665543Scg
55765543Scg	wc_wrchctl(ch->parent, apuch, ch->wcreg_tpl);
55865543Scg	wc_wrchctl(ch->parent, apuch + 1, ch->wcreg_tpl);
55965543Scg
56065543Scg	wp_wrapu(ch->parent, apuch, APUREG_APUTYPE,
56165543Scg	    (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
56265543Scg	if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO)
56365543Scg		wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE,
56465543Scg		    (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf);
56565543Scg}
56665543Scg
56765543Scgstatic void
56865543Scgaggch_stop_dac(struct agg_chinfo *ch)
56965543Scg{
57065543Scg	wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE,
57165543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
57265543Scg	wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE,
57365543Scg	    APUTYPE_INACTIVE << APU_APUTYPE_SHIFT);
57465543Scg}
57565543Scg
57665543Scg/*
57765543Scg * Stereo jitter suppressor.
57865543Scg * Sometimes playback pointers differ in stereo-paired channels.
57965543Scg * Calling this routine within intr fixes the problem.
58065543Scg */
58165543Scgstatic inline void
58265543Scgsuppress_jitter(struct agg_chinfo *ch)
58365543Scg{
58465543Scg	if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) {
58565543Scg		int cp, diff, halfsize = AGG_BUFSIZ >> 2;
58665543Scg
58765543Scg		if (ch->aputype == APUTYPE_16BITSTEREO)
58865543Scg			halfsize >>= 1;
58965543Scg		cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR);
59065543Scg		diff = wp_rdapu(ch->parent, (ch->num << 1) + 1, APUREG_CURPTR);
59165543Scg		diff -= cp;
59265543Scg		if (diff >> 1 && diff > -halfsize && diff < halfsize)
59365543Scg			bus_space_write_2(ch->parent->st, ch->parent->sh,
59465543Scg			    PORT_DSP_DATA, cp);
59565543Scg	}
59665543Scg}
59765543Scg
59865543Scgstatic inline u_int
59965543Scgcalc_timer_freq(struct agg_chinfo *ch)
60065543Scg{
60165543Scg	u_int	ss = 2;
60265543Scg
60365543Scg	if (ch->aputype == APUTYPE_16BITSTEREO)
60465543Scg		ss <<= 1;
60565543Scg	if (ch->aputype == APUTYPE_8BITLINEAR)
60665543Scg		ss >>= 1;
60765543Scg
60865543Scg	return (ch->channel->speed * ss + ch->blocksize - 1) / ch->blocksize;
60965543Scg}
61065543Scg
61165543Scgstatic void
61265543Scgset_timer(struct agg_info *ess)
61365543Scg{
61465543Scg	int i;
61565543Scg	u_int	freq = 0;
61665543Scg
61765543Scg	for (i = 0; i < ess->playchns; i++)
61865543Scg		if ((ess->active & (1 << i)) &&
61965543Scg		    (freq < calc_timer_freq(ess->pch + i)))
62065543Scg			freq = calc_timer_freq(ess->pch + i);
62165543Scg
62265543Scg	wp_settimer(ess, freq);
62365543Scg}
62465543Scg
62565543Scg
62665543Scg/* -----------------------------
62765543Scg * Newpcm glue.
62865543Scg */
62965543Scg
63065543Scgstatic u_int32_t
63165543Scgagg_ac97_init(void *sc)
63265543Scg{
63365543Scg	struct agg_info *ess = sc;
63465543Scg
63565543Scg	return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1;
63665543Scg}
63765543Scg
63865543Scgstatic void *
63965543Scgaggch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
64065543Scg{
64165543Scg	struct agg_info *ess = devinfo;
64265543Scg	struct agg_chinfo *ch;
64365543Scg	bus_addr_t physaddr;
64465543Scg
64565543Scg	ch = (dir == PCMDIR_PLAY)? ess->pch + ess->playchns : &ess->rch;
64665543Scg
64765543Scg	ch->parent = ess;
64865543Scg	ch->channel = c;
64965543Scg	ch->buffer = b;
65065543Scg	ch->num = ess->playchns;
65165543Scg	ch->dir = dir;
65265543Scg
65365543Scg	b->buf = dma_malloc(ess, AGG_BUFSIZ, &physaddr);
65465543Scg	if (b->buf == NULL)
65565543Scg		return NULL;
65665543Scg
65765543Scg	ch->offset = physaddr - ess->baseaddr;
65865543Scg	if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) {
65965543Scg		device_printf(ess->dev,
66065543Scg		    "offset %#x exceeds limit. ", ch->offset);
66165543Scg		dma_free(ess, b->buf);
66265543Scg		b->buf = NULL;
66365543Scg		return NULL;
66465543Scg	}
66565543Scg
66665543Scg	b->bufsize = AGG_BUFSIZ;
66765543Scg	ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK;
66865543Scg
66965543Scg	if (dir == PCMDIR_PLAY) {
67065543Scg		ess->playchns++;
67165543Scg		if (bootverbose)
67265543Scg			device_printf(ess->dev, "pch[%d].offset = %#x\n", ch->num, ch->offset);
67365543Scg	} else if (bootverbose)
67465543Scg		device_printf(ess->dev, "rch.offset = %#x\n", ch->offset);
67565543Scg
67665543Scg	return ch;
67765543Scg}
67865543Scg
67965543Scgstatic int
68065543Scgaggch_free(void *data)
68165543Scg{
68265543Scg	struct agg_chinfo *ch = data;
68365543Scg	struct agg_info *ess = ch->parent;
68465543Scg
68565543Scg	/* free up buffer - called after channel stopped */
68665543Scg	dma_free(ess, ch->buffer->buf);
68765543Scg
68865543Scg	/* return 0 if ok */
68965543Scg	return 0;
69065543Scg}
69165543Scg
69265543Scgstatic int
69365543Scgaggch_setplayformat(void *data, u_int32_t format)
69465543Scg{
69565543Scg	struct agg_chinfo *ch = data;
69665543Scg	u_int16_t wcreg_tpl;
69765543Scg	u_int16_t aputype = APUTYPE_16BITLINEAR;
69865543Scg
69965543Scg	wcreg_tpl = ch->wcreg_tpl & WAVCACHE_CHCTL_ADDRTAG_MASK;
70065543Scg
70165543Scg	if (format & AFMT_STEREO) {
70265543Scg		wcreg_tpl |= WAVCACHE_CHCTL_STEREO;
70365543Scg		aputype += 1;
70465543Scg	}
70565543Scg	if (format & AFMT_U8 || format & AFMT_S8) {
70665543Scg		aputype += 2;
70765543Scg		if (format & AFMT_U8)
70865543Scg			wcreg_tpl |= WAVCACHE_CHCTL_U8;
70965543Scg	}
71065543Scg	if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) {
71165543Scg		format &= ~AFMT_BIGENDIAN & ~AFMT_U16_LE;
71265543Scg		format |= AFMT_S16_LE;
71365543Scg	}
71465543Scg	ch->wcreg_tpl = wcreg_tpl;
71565543Scg	ch->aputype = aputype;
71665543Scg	return format;
71765543Scg}
71865543Scg
71965543Scgstatic int
72065543Scgaggch_setspeed(void *data, u_int32_t speed)
72165543Scg{
72265543Scg	return speed;
72365543Scg}
72465543Scg
72565543Scgstatic int
72665543Scgaggch_setblocksize(void *data, u_int32_t blocksize)
72765543Scg{
72865543Scg	return ((struct agg_chinfo*)data)->blocksize = blocksize;
72965543Scg}
73065543Scg
73165543Scgstatic int
73265543Scgaggch_trigger(void *data, int go)
73365543Scg{
73465543Scg	struct agg_chinfo *ch = data;
73565543Scg
73665543Scg	switch (go) {
73765543Scg	case PCMTRIG_EMLDMAWR:
73865543Scg		return 0;
73965543Scg	case PCMTRIG_START:
74065543Scg		ch->parent->active |= (1 << ch->num);
74165543Scg		if (ch->dir == PCMDIR_PLAY)
74265543Scg			aggch_start_dac(ch);
74365543Scg#if 0	/* XXX - RECORDING */
74465543Scg		else
74565543Scg			aggch_start_adc(ch);
74665543Scg#endif
74765543Scg		break;
74865543Scg	case PCMTRIG_ABORT:
74965543Scg	case PCMTRIG_STOP:
75065543Scg		ch->parent->active &= ~(1 << ch->num);
75165543Scg		if (ch->dir == PCMDIR_PLAY)
75265543Scg			aggch_stop_dac(ch);
75365543Scg#if 0	/* XXX - RECORDING */
75465543Scg		else
75565543Scg			aggch_stop_adc(ch);
75665543Scg#endif
75765543Scg		break;
75865543Scg	}
75965543Scg
76065543Scg	if (ch->parent->active) {
76165543Scg		set_timer(ch->parent);
76265543Scg		wp_starttimer(ch->parent);
76365543Scg	} else
76465543Scg		wp_stoptimer(ch->parent);
76565543Scg
76665543Scg	return 0;
76765543Scg}
76865543Scg
76965543Scgstatic int
77065543Scgaggch_getplayptr(void *data)
77165543Scg{
77265543Scg	struct agg_chinfo *ch = data;
77365543Scg	u_int cp;
77465543Scg
77565543Scg	cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR);
77665543Scg	if (ch->aputype == APUTYPE_16BITSTEREO)
77765543Scg		cp = (0xffff << 2) & ((cp << 2) - ch->offset);
77865543Scg	else
77965543Scg		cp = (0xffff << 1) & ((cp << 1) - ch->offset);
78065543Scg
78165543Scg	return cp;
78265543Scg}
78365543Scg
78465543Scgstatic pcmchan_caps *
78565543Scgaggch_getcaps(void *data)
78665543Scg{
78765543Scg	static u_int32_t playfmt[] = {
78865543Scg		AFMT_U8,
78965543Scg		AFMT_STEREO | AFMT_U8,
79065543Scg		AFMT_S8,
79165543Scg		AFMT_STEREO | AFMT_S8,
79265543Scg		AFMT_S16_LE,
79365543Scg		AFMT_STEREO | AFMT_S16_LE,
79465543Scg		0
79565543Scg	};
79665543Scg	static pcmchan_caps playcaps = {2000, 96000, playfmt, 0};
79765543Scg
79865543Scg	static u_int32_t recfmt[] = {
79965543Scg		AFMT_S8,
80065543Scg		AFMT_STEREO | AFMT_S8,
80165543Scg		AFMT_S16_LE,
80265543Scg		AFMT_STEREO | AFMT_S16_LE,
80365543Scg		0
80465543Scg	};
80565543Scg	static pcmchan_caps reccaps = {4000, 48000, recfmt, 0};
80665543Scg
80765543Scg	return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)?
80865543Scg	    &playcaps : &reccaps;
80965543Scg}
81065543Scg
81165543Scg
81265543Scg/* -----------------------------
81365543Scg * Bus space.
81465543Scg */
81565543Scg
81665543Scgstatic void
81765543Scgagg_intr(void *sc)
81865543Scg{
81965543Scg	struct agg_info* ess = sc;
82065543Scg	u_int16_t status;
82165543Scg	int i;
82265543Scg
82365543Scg	status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT);
82465543Scg	if (!status)
82565543Scg		return;
82665543Scg
82765543Scg	/* Acknowledge all. */
82865543Scg	bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1);
82965543Scg	bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0);
83065543Scg#if 0	/* XXX - HWVOL */
83165543Scg	if (status & HOSTINT_STAT_HWVOL) {
83265543Scg		u_int delta;
83365543Scg		delta = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER)
83465543Scg		    - 0x88;
83565543Scg		if (delta & 0x11)
83665543Scg			mixer_set(device_get_softc(ess->dev),
83765543Scg			    SOUND_MIXER_VOLUME, 0);
83865543Scg		else {
83965543Scg			mixer_set(device_get_softc(ess->dev),
84065543Scg			    SOUND_MIXER_VOLUME,
84165543Scg			    mixer_get(device_get_softc(ess->dev),
84265543Scg				SOUND_MIXER_VOLUME)
84365543Scg			    + ((delta >> 5) & 0x7) - 4
84465543Scg			    + ((delta << 7) & 0x700) - 0x400);
84565543Scg		}
84665543Scg		bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 0x88);
84765543Scg	}
84865543Scg#endif	/* XXX - HWVOL */
84965543Scg
85065543Scg	for (i = 0; i < ess->playchns; i++)
85165543Scg		if (ess->active & (1 << i)) {
85265543Scg			suppress_jitter(ess->pch + i);
85365543Scg			chn_intr(ess->pch[i].channel);
85465543Scg		}
85565543Scg#if 0	/* XXX - RECORDING */
85665543Scg	if (ess->active & (1 << i))
85765543Scg		chn_intr(ess->rch.channel);
85865543Scg#endif
85965543Scg}
86065543Scg
86165543Scgstatic void
86265543Scgsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
86365543Scg{
86465543Scg	bus_addr_t *phys = arg;
86565543Scg
86665543Scg	*phys = error? 0 : segs->ds_addr;
86765543Scg
86865543Scg	if (bootverbose) {
86965543Scg		printf("setmap (%lx, %lx), nseg=%d, error=%d\n",
87065543Scg		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
87165543Scg		    nseg, error);
87265543Scg	}
87365543Scg}
87465543Scg
87565543Scgstatic void *
87665543Scgdma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys)
87765543Scg{
87865543Scg	void *buf;
87965543Scg	bus_dmamap_t map;
88065543Scg
88165543Scg	if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map))
88265543Scg		return NULL;
88365543Scg	if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, setmap, phys, 0)
88465543Scg	    || !*phys) {
88565543Scg		bus_dmamem_free(sc->parent_dmat, buf, map);
88665543Scg		return NULL;
88765543Scg	}
88865543Scg	return buf;
88965543Scg}
89065543Scg
89165543Scgstatic void
89265543Scgdma_free(struct agg_info *sc, void *buf)
89365543Scg{
89465543Scg	bus_dmamem_free(sc->parent_dmat, buf, NULL);
89565543Scg}
89665543Scg
89765543Scgstatic int
89865543Scgagg_probe(device_t dev)
89965543Scg{
90065543Scg	char *s = NULL;
90165543Scg
90265543Scg	switch (pci_get_devid(dev)) {
90365543Scg	case MAESTRO_1_PCI_ID:
90465543Scg		s = "ESS Technology Maestro-1";
90565543Scg		break;
90665543Scg
90765543Scg	case MAESTRO_2_PCI_ID:
90865543Scg		s = "ESS Technology Maestro-2";
90965543Scg		break;
91065543Scg
91165543Scg	case MAESTRO_2E_PCI_ID:
91265543Scg		s = "ESS Technology Maestro-2E";
91365543Scg		break;
91465543Scg	}
91565543Scg
91665543Scg	if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) {
91765543Scg		device_set_desc(dev, s);
91865543Scg		return 0;
91965543Scg	}
92065543Scg	return ENXIO;
92165543Scg}
92265543Scg
92365543Scgstatic int
92465543Scgagg_attach(device_t dev)
92565543Scg{
92665543Scg	struct agg_info	*ess = NULL;
92765543Scg	u_int32_t	data;
92865543Scg	int	mapped = 0;
92965543Scg	int	regid = PCIR_MAPS;
93065543Scg	struct resource	*reg = NULL;
93165543Scg	struct ac97_info	*codec = NULL;
93265543Scg	int	irqid = 0;
93365543Scg	struct resource	*irq = NULL;
93465543Scg	void	*ih = NULL;
93565543Scg	char	status[SND_STATUSLEN];
93665543Scg	static pcm_channel agg_pchtpl = {
93765543Scg	    	aggch_init,
93865543Scg		NULL, 			/* setdir */
93965543Scg	    	aggch_setplayformat,
94065543Scg	    	aggch_setspeed,
94165543Scg	    	aggch_setblocksize,
94265543Scg	    	aggch_trigger,
94365543Scg	    	aggch_getplayptr,
94465543Scg	    	aggch_getcaps,
94565543Scg		aggch_free, 		/* free */
94665543Scg		NULL, 			/* nop1 */
94765543Scg		NULL, 			/* nop2 */
94865543Scg		NULL, 			/* nop3 */
94965543Scg		NULL, 			/* nop4 */
95065543Scg		NULL, 			/* nop5 */
95165543Scg		NULL, 			/* nop6 */
95265543Scg		NULL, 			/* nop7 */
95365543Scg	};
95465543Scg
95565543Scg	if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT)) == NULL) {
95665543Scg		device_printf(dev, "cannot allocate softc\n");
95765543Scg		return ENXIO;
95865543Scg	}
95965543Scg	bzero(ess, sizeof *ess);
96065543Scg	ess->dev = dev;
96165543Scg
96265543Scg	if (bus_dma_tag_create(/*parent*/NULL,
96365543Scg	    /*alignment*/1 << WAVCACHE_BASEADDR_SHIFT,
96465543Scg	    /*boundary*/WPWA_MAXADDR + 1,
96565543Scg	    /*lowaddr*/MAESTRO_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR,
96665543Scg	    /*filter*/NULL, /*filterarg*/NULL,
96765543Scg	    /*maxsize*/AGG_BUFSIZ * 2, /*nsegments*/1, /*maxsegz*/0x3ffff,
96865543Scg	    /*flags*/0, &ess->parent_dmat) != 0) {
96965543Scg		device_printf(dev, "unable to create dma tag\n");
97065543Scg		goto bad;
97165543Scg	}
97265543Scg
97365543Scg	ess->stat = dma_malloc(ess, AGG_BUFSIZ, &ess->baseaddr);
97465543Scg	if (ess->stat == NULL) {
97565543Scg		device_printf(dev, "cannot allocate status buffer\n");
97665543Scg		goto bad;
97765543Scg	}
97865543Scg	if (bootverbose)
97965543Scg		device_printf(dev, "Maestro DMA base: %#x\n", ess->baseaddr);
98065543Scg
98165543Scg	agg_power(ess, PPMI_D0);
98265543Scg	DELAY(100000);
98365543Scg
98465543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
98565543Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN);
98665543Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
98765543Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
98865543Scg
98965543Scg	if (data & PCIM_CMD_PORTEN) {
99065543Scg		reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &regid,
99165543Scg		    0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE);
99265543Scg		if (reg != NULL) {
99365543Scg			ess->reg = reg;
99465543Scg			ess->regid = regid;
99565543Scg			ess->st = rman_get_bustag(reg);
99665543Scg			ess->sh = rman_get_bushandle(reg);
99765543Scg			mapped++;
99865543Scg		}
99965543Scg	}
100065543Scg	if (mapped == 0) {
100165543Scg		device_printf(dev, "unable to map register space\n");
100265543Scg		goto bad;
100365543Scg	}
100465543Scg
100565543Scg	agg_init(ess);
100665543Scg	if (agg_rdcodec(ess, 0) == 0x80) {
100765543Scg		device_printf(dev, "PT101 codec detected!\n");
100865543Scg		goto bad;
100965543Scg	}
101065543Scg	codec = ac97_create(dev, ess, agg_ac97_init, agg_rdcodec, agg_wrcodec);
101165543Scg	if (codec == NULL)
101265543Scg		goto bad;
101365543Scg	if (mixer_init(dev, &ac97_mixer, codec) == -1)
101465543Scg		goto bad;
101565543Scg	ess->codec = codec;
101665543Scg
101765543Scg	irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
101865543Scg	    0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE);
101965543Scg	if (irq == NULL
102065543Scg	    || bus_setup_intr(dev, irq, INTR_TYPE_TTY, agg_intr, ess, &ih)) {
102165543Scg		device_printf(dev, "unable to map interrupt\n");
102265543Scg		goto bad;
102365543Scg	}
102465543Scg	ess->irq = irq;
102565543Scg	ess->irqid = irqid;
102665543Scg	ess->ih = ih;
102765543Scg
102865543Scg	snprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld",
102965543Scg	    rman_get_start(reg), rman_get_start(irq));
103065543Scg
103165543Scg	if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1))
103265543Scg		goto bad;
103365543Scg
103465543Scg	for (data = 0; data < AGG_MAXPLAYCH; data++)
103565543Scg		pcm_addchan(dev, PCMDIR_PLAY, &agg_pchtpl, ess);
103665543Scg#if 0	/* XXX - RECORDING */
103765543Scg	pcm_addchan(dev, PCMDIR_REC, &agg_rchtpl, ess);
103865543Scg#endif
103965543Scg	pcm_setstatus(dev, status);
104065543Scg
104165543Scg	return 0;
104265543Scg
104365543Scg bad:
104465644Scg	if (codec != NULL)
104565644Scg		ac97_destroy(codec);
104665543Scg	if (ih != NULL)
104765543Scg		bus_teardown_intr(dev, irq, ih);
104865543Scg	if (irq != NULL)
104965543Scg		bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
105065543Scg	if (reg != NULL)
105165543Scg		bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
105265543Scg	if (ess != NULL) {
105365543Scg		agg_power(ess, PPMI_D3);
105465543Scg		if (ess->stat != NULL)
105565543Scg			dma_free(ess, ess->stat);
105665543Scg		if (ess->parent_dmat != NULL)
105765543Scg			bus_dma_tag_destroy(ess->parent_dmat);
105865543Scg		free(ess, M_DEVBUF);
105965543Scg	}
106065543Scg
106165543Scg	return ENXIO;
106265543Scg}
106365543Scg
106465543Scgstatic int
106565543Scgagg_detach(device_t dev)
106665543Scg{
106765543Scg	struct agg_info	*ess = pcm_getdevinfo(dev);
106865543Scg	int r;
106965543Scg
107065543Scg	r = pcm_unregister(dev);
107165543Scg	if (r)
107265543Scg		return r;
107365543Scg
107465543Scg	ess = pcm_getdevinfo(dev);
107565543Scg	dma_free(ess, ess->stat);
107665543Scg
107765543Scg	/* Power down everything except clock and vref. */
107865543Scg	agg_wrcodec(ess, AC97_REG_POWER, 0xd700);
107965543Scg	DELAY(20);
108065543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0);
108165543Scg	agg_power(ess, PPMI_D3);
108265543Scg
108365543Scg	bus_teardown_intr(dev, ess->irq, ess->ih);
108465543Scg	bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq);
108565543Scg	bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg);
108665543Scg	bus_dma_tag_destroy(ess->parent_dmat);
108765543Scg	free(ess, M_DEVBUF);
108865543Scg	return 0;
108965543Scg}
109065543Scg
109165543Scgstatic int
109265543Scgagg_suspend(device_t dev)
109365543Scg{
109465543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
109565543Scg	int i, x;
109665543Scg
109765543Scg	x = spltty();
109865543Scg	wp_stoptimer(ess);
109965543Scg	bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0);
110065543Scg
110165543Scg	for (i = 0; i < ess->playchns; i++)
110265543Scg		aggch_stop_dac(ess->pch + i);
110365543Scg
110465543Scg#if 0	/* XXX - RECORDING */
110565543Scg	aggch_stop_adc(&ess->rch);
110665543Scg#endif
110765543Scg	splx(x);
110865543Scg	/* Power down everything except clock. */
110965543Scg	agg_wrcodec(ess, AC97_REG_POWER, 0xdf00);
111065543Scg	DELAY(20);
111165543Scg	bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0);
111265543Scg	DELAY(1);
111365543Scg	agg_power(ess, PPMI_D3);
111465543Scg
111565543Scg	return 0;
111665543Scg}
111765543Scg
111865543Scgstatic int
111965543Scgagg_resume(device_t dev)
112065543Scg{
112165543Scg	int i, x;
112265543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
112365543Scg
112465543Scg	agg_power(ess, PPMI_D0);
112565543Scg	DELAY(100000);
112665543Scg	agg_init(ess);
112765543Scg	if (mixer_reinit(dev)) {
112865543Scg		device_printf(dev, "unable to reinitialize the mixer\n");
112965543Scg		return ENXIO;
113065543Scg	}
113165543Scg
113265543Scg	x = spltty();
113365543Scg	for (i = 0; i < ess->playchns; i++)
113465543Scg		if (ess->active & (1 << i))
113565543Scg			aggch_start_dac(ess->pch + i);
113665543Scg#if 0	/* XXX - RECORDING */
113765543Scg	if (ess->active & (1 << i))
113865543Scg		aggch_start_adc(&ess->rch);
113965543Scg#endif
114065543Scg	if (ess->active) {
114165543Scg		set_timer(ess);
114265543Scg		wp_starttimer(ess);
114365543Scg	}
114465543Scg	splx(x);
114565543Scg	return 0;
114665543Scg}
114765543Scg
114865543Scgstatic int
114965543Scgagg_shutdown(device_t dev)
115065543Scg{
115165543Scg	struct agg_info *ess = pcm_getdevinfo(dev);
115265543Scg	int i;
115365543Scg
115465543Scg	wp_stoptimer(ess);
115565543Scg	bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0);
115665543Scg
115765543Scg	for (i = 0; i < ess->playchns; i++)
115865543Scg		aggch_stop_dac(ess->pch + i);
115965543Scg
116065543Scg#if 0	/* XXX - RECORDING */
116165543Scg	aggch_stop_adc(&ess->rch);
116265543Scg#endif
116365543Scg	return 0;
116465543Scg}
116565543Scg
116665543Scg
116765543Scgstatic device_method_t agg_methods[] = {
116865543Scg    DEVMETHOD(device_probe,	agg_probe),
116965543Scg    DEVMETHOD(device_attach,	agg_attach),
117065543Scg    DEVMETHOD(device_detach,	agg_detach),
117165543Scg    DEVMETHOD(device_suspend,	agg_suspend),
117265543Scg    DEVMETHOD(device_resume,	agg_resume),
117365543Scg    DEVMETHOD(device_shutdown,	agg_shutdown),
117465543Scg
117565543Scg    { 0, 0 }
117665543Scg};
117765543Scg
117865543Scgstatic driver_t agg_driver = {
117965543Scg    "pcm",
118065543Scg    agg_methods,
118165543Scg    sizeof(snddev_info),
118265543Scg};
118365543Scg
118465543Scgstatic devclass_t pcm_devclass;
118565543Scg
118665543ScgDRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
118765543ScgMODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
118865543ScgMODULE_VERSION(snd_maestro, 1);
1189