xref: /freebsd/sys/dev/sound/pci/atiixp.c (revision 703c934a48e8465f0027506356f83b05f291d731)
1d5688b6aSAriff Abdullah /*-
2d5688b6aSAriff Abdullah  * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
3d5688b6aSAriff Abdullah  * All rights reserved.
4d5688b6aSAriff Abdullah  *
5d5688b6aSAriff Abdullah  * Redistribution and use in source and binary forms, with or without
6d5688b6aSAriff Abdullah  * modification, are permitted provided that the following conditions
7d5688b6aSAriff Abdullah  * are met:
8d5688b6aSAriff Abdullah  * 1. Redistributions of source code must retain the above copyright
9d5688b6aSAriff Abdullah  *    notice, this list of conditions and the following disclaimer.
10d5688b6aSAriff Abdullah  * 2. Redistributions in binary form must reproduce the above copyright
11d5688b6aSAriff Abdullah  *    notice, this list of conditions and the following disclaimer in the
12d5688b6aSAriff Abdullah  *    documentation and/or other materials provided with the distribution.
13d5688b6aSAriff Abdullah  *
14d5688b6aSAriff Abdullah  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d5688b6aSAriff Abdullah  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d5688b6aSAriff Abdullah  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d5688b6aSAriff Abdullah  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d5688b6aSAriff Abdullah  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d5688b6aSAriff Abdullah  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d5688b6aSAriff Abdullah  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d5688b6aSAriff Abdullah  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22d5688b6aSAriff Abdullah  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d5688b6aSAriff Abdullah  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24d5688b6aSAriff Abdullah  * SUCH DAMAGE.
25d5688b6aSAriff Abdullah  */
26d5688b6aSAriff Abdullah 
27d5688b6aSAriff Abdullah /*
28d5688b6aSAriff Abdullah  * FreeBSD pcm driver for ATI IXP 150/200/250/300 AC97 controllers
29d5688b6aSAriff Abdullah  *
30d5688b6aSAriff Abdullah  * Features
31d5688b6aSAriff Abdullah  *	* 16bit playback / recording
32d5688b6aSAriff Abdullah  *	* 32bit native playback - yay!
3305051995SAriff Abdullah  *	* 32bit native recording (seems broken on few hardwares)
34d5688b6aSAriff Abdullah  *
35d5688b6aSAriff Abdullah  * Issues / TODO:
36d5688b6aSAriff Abdullah  *	* SPDIF
37d5688b6aSAriff Abdullah  *	* Support for more than 2 channels.
38d5688b6aSAriff Abdullah  *	* VRA ? VRM ? DRA ?
3905051995SAriff Abdullah  *	* 32bit native recording seems broken on few hardwares, most
4005051995SAriff Abdullah  *	  probably because of incomplete VRA/DRA cleanup.
41d5688b6aSAriff Abdullah  *
42d5688b6aSAriff Abdullah  *
43d5688b6aSAriff Abdullah  * Thanks goes to:
44d5688b6aSAriff Abdullah  *
45d5688b6aSAriff Abdullah  *   Shaharil @ SCAN Associates whom relentlessly providing me the
46d5688b6aSAriff Abdullah  *   mind blowing Acer Ferrari 4002 WLMi with this ATI IXP hardware.
47d5688b6aSAriff Abdullah  *
48d5688b6aSAriff Abdullah  *   Reinoud Zandijk <reinoud@NetBSD.org> (auixp), which this driver is
49d5688b6aSAriff Abdullah  *   largely based upon although large part of it has been reworked. His
50d5688b6aSAriff Abdullah  *   driver is the primary reference and pretty much well documented.
51d5688b6aSAriff Abdullah  *
52d5688b6aSAriff Abdullah  *   Takashi Iwai (ALSA snd-atiixp), for register definitions and some
53d5688b6aSAriff Abdullah  *   random ninja hackery.
54d5688b6aSAriff Abdullah  */
55d5688b6aSAriff Abdullah 
56d5688b6aSAriff Abdullah #include <dev/sound/pcm/sound.h>
57d5688b6aSAriff Abdullah #include <dev/sound/pcm/ac97.h>
58d5688b6aSAriff Abdullah 
59d5688b6aSAriff Abdullah #include <dev/pci/pcireg.h>
60d5688b6aSAriff Abdullah #include <dev/pci/pcivar.h>
61d5688b6aSAriff Abdullah #include <sys/sysctl.h>
62d5688b6aSAriff Abdullah #include <sys/endian.h>
63d5688b6aSAriff Abdullah 
64d5688b6aSAriff Abdullah #include <dev/sound/pci/atiixp.h>
65d5688b6aSAriff Abdullah 
66d5688b6aSAriff Abdullah SND_DECLARE_FILE("$FreeBSD$");
67d5688b6aSAriff Abdullah 
689f52a325SAriff Abdullah #define ATI_IXP_DMA_RETRY_MAX	100
699f52a325SAriff Abdullah 
709f52a325SAriff Abdullah #define ATI_IXP_BUFSZ_MIN	4096
719f52a325SAriff Abdullah #define ATI_IXP_BUFSZ_MAX	65536
729f52a325SAriff Abdullah #define ATI_IXP_BUFSZ_DEFAULT	16384
739f52a325SAriff Abdullah 
741042342aSAriff Abdullah #define ATI_IXP_BLK_MIN		32
751042342aSAriff Abdullah #define ATI_IXP_BLK_ALIGN	(~(ATI_IXP_BLK_MIN - 1))
761042342aSAriff Abdullah 
77d5688b6aSAriff Abdullah struct atiixp_dma_op {
7805051995SAriff Abdullah 	volatile uint32_t addr;
7905051995SAriff Abdullah 	volatile uint16_t status;
8005051995SAriff Abdullah 	volatile uint16_t size;
8105051995SAriff Abdullah 	volatile uint32_t next;
82d5688b6aSAriff Abdullah };
83d5688b6aSAriff Abdullah 
84d5688b6aSAriff Abdullah struct atiixp_info;
85d5688b6aSAriff Abdullah 
86d5688b6aSAriff Abdullah struct atiixp_chinfo {
87d5688b6aSAriff Abdullah 	struct snd_dbuf *buffer;
88d5688b6aSAriff Abdullah 	struct pcm_channel *channel;
89d5688b6aSAriff Abdullah 	struct atiixp_info *parent;
90d5688b6aSAriff Abdullah 	struct atiixp_dma_op *sgd_table;
91d5688b6aSAriff Abdullah 	bus_addr_t sgd_addr;
92a580b31aSAriff Abdullah 	uint32_t enable_bit, flush_bit, linkptr_bit, dt_cur_bit;
93a580b31aSAriff Abdullah 	uint32_t blksz, blkcnt;
94a580b31aSAriff Abdullah 	uint32_t ptr, prevptr;
9588a50509SAriff Abdullah 	uint32_t fmt;
9688a50509SAriff Abdullah 	int caps_32bit, dir, active;
97d5688b6aSAriff Abdullah };
98d5688b6aSAriff Abdullah 
99d5688b6aSAriff Abdullah struct atiixp_info {
100d5688b6aSAriff Abdullah 	device_t dev;
101d5688b6aSAriff Abdullah 
102d5688b6aSAriff Abdullah 	bus_space_tag_t st;
103d5688b6aSAriff Abdullah 	bus_space_handle_t sh;
104d5688b6aSAriff Abdullah 	bus_dma_tag_t parent_dmat;
105d5688b6aSAriff Abdullah 	bus_dma_tag_t sgd_dmat;
106d5688b6aSAriff Abdullah 	bus_dmamap_t sgd_dmamap;
107d5688b6aSAriff Abdullah 	bus_addr_t sgd_addr;
108d5688b6aSAriff Abdullah 
109d5688b6aSAriff Abdullah 	struct resource *reg, *irq;
110d5688b6aSAriff Abdullah 	int regtype, regid, irqid;
111d5688b6aSAriff Abdullah 	void *ih;
112d5688b6aSAriff Abdullah 	struct ac97_info *codec;
113d5688b6aSAriff Abdullah 
114d5688b6aSAriff Abdullah 	struct atiixp_chinfo pch;
115d5688b6aSAriff Abdullah 	struct atiixp_chinfo rch;
116d5688b6aSAriff Abdullah 	struct atiixp_dma_op *sgd_table;
117d5688b6aSAriff Abdullah 	struct intr_config_hook delayed_attach;
118d5688b6aSAriff Abdullah 
119d5688b6aSAriff Abdullah 	uint32_t bufsz;
120d5688b6aSAriff Abdullah 	uint32_t codec_not_ready_bits, codec_idx, codec_found;
121a580b31aSAriff Abdullah 	uint32_t blkcnt;
122d5688b6aSAriff Abdullah 	int registered_channels;
123d5688b6aSAriff Abdullah 
124d5688b6aSAriff Abdullah 	struct mtx *lock;
125a580b31aSAriff Abdullah 	struct callout poll_timer;
126a580b31aSAriff Abdullah 	int poll_ticks, polling;
127d5688b6aSAriff Abdullah };
128d5688b6aSAriff Abdullah 
129d5688b6aSAriff Abdullah #define atiixp_rd(_sc, _reg)	\
130d5688b6aSAriff Abdullah 		bus_space_read_4((_sc)->st, (_sc)->sh, _reg)
131d5688b6aSAriff Abdullah #define atiixp_wr(_sc, _reg, _val)	\
132d5688b6aSAriff Abdullah 		bus_space_write_4((_sc)->st, (_sc)->sh, _reg, _val)
133d5688b6aSAriff Abdullah 
134d5688b6aSAriff Abdullah #define atiixp_lock(_sc)	snd_mtxlock((_sc)->lock)
135d5688b6aSAriff Abdullah #define atiixp_unlock(_sc)	snd_mtxunlock((_sc)->lock)
136d5688b6aSAriff Abdullah #define atiixp_assert(_sc)	snd_mtxassert((_sc)->lock)
137d5688b6aSAriff Abdullah 
138d5688b6aSAriff Abdullah static uint32_t atiixp_fmt_32bit[] = {
139d5688b6aSAriff Abdullah 	AFMT_STEREO | AFMT_S16_LE,
140d5688b6aSAriff Abdullah 	AFMT_STEREO | AFMT_S32_LE,
141d5688b6aSAriff Abdullah 	0
142d5688b6aSAriff Abdullah };
143d5688b6aSAriff Abdullah 
144d5688b6aSAriff Abdullah static uint32_t atiixp_fmt[] = {
145d5688b6aSAriff Abdullah 	AFMT_STEREO | AFMT_S16_LE,
146d5688b6aSAriff Abdullah 	0
147d5688b6aSAriff Abdullah };
148d5688b6aSAriff Abdullah 
149d5688b6aSAriff Abdullah static struct pcmchan_caps atiixp_caps_32bit = {
150d5688b6aSAriff Abdullah 	ATI_IXP_BASE_RATE,
151d5688b6aSAriff Abdullah 	ATI_IXP_BASE_RATE,
152d5688b6aSAriff Abdullah 	atiixp_fmt_32bit, 0
153d5688b6aSAriff Abdullah };
154d5688b6aSAriff Abdullah 
155d5688b6aSAriff Abdullah static struct pcmchan_caps atiixp_caps = {
156d5688b6aSAriff Abdullah 	ATI_IXP_BASE_RATE,
157d5688b6aSAriff Abdullah 	ATI_IXP_BASE_RATE,
158d5688b6aSAriff Abdullah 	atiixp_fmt, 0
159d5688b6aSAriff Abdullah };
160d5688b6aSAriff Abdullah 
161d5688b6aSAriff Abdullah static const struct {
162d5688b6aSAriff Abdullah 	uint16_t vendor;
163d5688b6aSAriff Abdullah 	uint16_t devid;
164d5688b6aSAriff Abdullah 	char	 *desc;
165d5688b6aSAriff Abdullah } atiixp_hw[] = {
166d5688b6aSAriff Abdullah 	{ ATI_VENDOR_ID, ATI_IXP_200_ID, "ATI IXP 200" },
167d5688b6aSAriff Abdullah 	{ ATI_VENDOR_ID, ATI_IXP_300_ID, "ATI IXP 300" },
168d5688b6aSAriff Abdullah 	{ ATI_VENDOR_ID, ATI_IXP_400_ID, "ATI IXP 400" },
169d5688b6aSAriff Abdullah };
170d5688b6aSAriff Abdullah 
171d5688b6aSAriff Abdullah static void atiixp_enable_interrupts(struct atiixp_info *);
172d5688b6aSAriff Abdullah static void atiixp_disable_interrupts(struct atiixp_info *);
173d5688b6aSAriff Abdullah static void atiixp_reset_aclink(struct atiixp_info *);
1749f52a325SAriff Abdullah static void atiixp_flush_dma(struct atiixp_chinfo *);
1759f52a325SAriff Abdullah static void atiixp_enable_dma(struct atiixp_chinfo *);
1769f52a325SAriff Abdullah static void atiixp_disable_dma(struct atiixp_chinfo *);
177d5688b6aSAriff Abdullah 
178d5688b6aSAriff Abdullah static int atiixp_waitready_codec(struct atiixp_info *);
179d5688b6aSAriff Abdullah static int atiixp_rdcd(kobj_t, void *, int);
180d5688b6aSAriff Abdullah static int atiixp_wrcd(kobj_t, void *, int, uint32_t);
181d5688b6aSAriff Abdullah 
182d5688b6aSAriff Abdullah static void  *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *,
183d5688b6aSAriff Abdullah 						struct pcm_channel *, int);
184d5688b6aSAriff Abdullah static int    atiixp_chan_setformat(kobj_t, void *, uint32_t);
185d5688b6aSAriff Abdullah static int    atiixp_chan_setspeed(kobj_t, void *, uint32_t);
1861042342aSAriff Abdullah static int    atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t);
187d5688b6aSAriff Abdullah static int    atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
188d5688b6aSAriff Abdullah static void   atiixp_buildsgdt(struct atiixp_chinfo *);
189d5688b6aSAriff Abdullah static int    atiixp_chan_trigger(kobj_t, void *, int);
1909f52a325SAriff Abdullah static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *);
191d5688b6aSAriff Abdullah static int    atiixp_chan_getptr(kobj_t, void *);
192d5688b6aSAriff Abdullah static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *);
193d5688b6aSAriff Abdullah 
194d5688b6aSAriff Abdullah static void atiixp_intr(void *);
195d5688b6aSAriff Abdullah static void atiixp_dma_cb(void *, bus_dma_segment_t *, int, int);
196d5688b6aSAriff Abdullah static void atiixp_chip_pre_init(struct atiixp_info *);
197d5688b6aSAriff Abdullah static void atiixp_chip_post_init(void *);
198f2a1d71aSAriff Abdullah static void atiixp_release_resource(struct atiixp_info *);
199d5688b6aSAriff Abdullah static int  atiixp_pci_probe(device_t);
200d5688b6aSAriff Abdullah static int  atiixp_pci_attach(device_t);
201d5688b6aSAriff Abdullah static int  atiixp_pci_detach(device_t);
20288a50509SAriff Abdullah static int  atiixp_pci_suspend(device_t);
20388a50509SAriff Abdullah static int  atiixp_pci_resume(device_t);
204d5688b6aSAriff Abdullah 
205d5688b6aSAriff Abdullah /*
206d5688b6aSAriff Abdullah  * ATI IXP helper functions
207d5688b6aSAriff Abdullah  */
208d5688b6aSAriff Abdullah static void
209d5688b6aSAriff Abdullah atiixp_enable_interrupts(struct atiixp_info *sc)
210d5688b6aSAriff Abdullah {
211d5688b6aSAriff Abdullah 	uint32_t value;
212d5688b6aSAriff Abdullah 
213d5688b6aSAriff Abdullah 	/* clear all pending */
214d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
215d5688b6aSAriff Abdullah 
216d5688b6aSAriff Abdullah 	/* enable all relevant interrupt sources we can handle */
217d5688b6aSAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_IER);
218d5688b6aSAriff Abdullah 
219d5688b6aSAriff Abdullah 	value |= ATI_REG_IER_IO_STATUS_EN;
220d5688b6aSAriff Abdullah 
221d5688b6aSAriff Abdullah 	/*
222d5688b6aSAriff Abdullah 	 * Disable / ignore internal xrun/spdf interrupt flags
223d5688b6aSAriff Abdullah 	 * since it doesn't interest us (for now).
224d5688b6aSAriff Abdullah 	 */
2259f52a325SAriff Abdullah #if 1
2269f52a325SAriff Abdullah 	value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN |
2279f52a325SAriff Abdullah 	    ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
2289f52a325SAriff Abdullah #else
229d5688b6aSAriff Abdullah 	value |= ATI_REG_IER_IN_XRUN_EN;
230d5688b6aSAriff Abdullah 	value |= ATI_REG_IER_OUT_XRUN_EN;
231d5688b6aSAriff Abdullah 
232d5688b6aSAriff Abdullah 	value |= ATI_REG_IER_SPDF_XRUN_EN;
233d5688b6aSAriff Abdullah 	value |= ATI_REG_IER_SPDF_STATUS_EN;
234d5688b6aSAriff Abdullah #endif
235d5688b6aSAriff Abdullah 
236d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_IER, value);
237d5688b6aSAriff Abdullah }
238d5688b6aSAriff Abdullah 
239d5688b6aSAriff Abdullah static void
240d5688b6aSAriff Abdullah atiixp_disable_interrupts(struct atiixp_info *sc)
241d5688b6aSAriff Abdullah {
242d5688b6aSAriff Abdullah 	/* disable all interrupt sources */
243d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_IER, 0);
244d5688b6aSAriff Abdullah 
245d5688b6aSAriff Abdullah 	/* clear all pending */
246d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
247d5688b6aSAriff Abdullah }
248d5688b6aSAriff Abdullah 
249d5688b6aSAriff Abdullah static void
250d5688b6aSAriff Abdullah atiixp_reset_aclink(struct atiixp_info *sc)
251d5688b6aSAriff Abdullah {
252d5688b6aSAriff Abdullah 	uint32_t value, timeout;
253d5688b6aSAriff Abdullah 
254d5688b6aSAriff Abdullah 	/* if power is down, power it up */
255d5688b6aSAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_CMD);
256d5688b6aSAriff Abdullah 	if (value & ATI_REG_CMD_POWERDOWN) {
257d5688b6aSAriff Abdullah 		/* explicitly enable power */
258d5688b6aSAriff Abdullah 		value &= ~ATI_REG_CMD_POWERDOWN;
259d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_CMD, value);
260d5688b6aSAriff Abdullah 
261d5688b6aSAriff Abdullah 		/* have to wait at least 10 usec for it to initialise */
262d5688b6aSAriff Abdullah 		DELAY(20);
2639f52a325SAriff Abdullah 	}
264d5688b6aSAriff Abdullah 
265d5688b6aSAriff Abdullah 	/* perform a soft reset */
266d5688b6aSAriff Abdullah 	value  = atiixp_rd(sc, ATI_REG_CMD);
267d5688b6aSAriff Abdullah 	value |= ATI_REG_CMD_AC_SOFT_RESET;
268d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_CMD, value);
269d5688b6aSAriff Abdullah 
270d5688b6aSAriff Abdullah 	/* need to read the CMD reg and wait aprox. 10 usec to init */
271d5688b6aSAriff Abdullah 	value  = atiixp_rd(sc, ATI_REG_CMD);
272d5688b6aSAriff Abdullah 	DELAY(20);
273d5688b6aSAriff Abdullah 
274d5688b6aSAriff Abdullah 	/* clear soft reset flag again */
275d5688b6aSAriff Abdullah 	value  = atiixp_rd(sc, ATI_REG_CMD);
276d5688b6aSAriff Abdullah 	value &= ~ATI_REG_CMD_AC_SOFT_RESET;
277d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_CMD, value);
278d5688b6aSAriff Abdullah 
279d5688b6aSAriff Abdullah 	/* check if the ac-link is working; reset device otherwise */
280d5688b6aSAriff Abdullah 	timeout = 10;
281d5688b6aSAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_CMD);
282a580b31aSAriff Abdullah 	while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) && --timeout) {
28388a50509SAriff Abdullah #if 0
284d5688b6aSAriff Abdullah 		device_printf(sc->dev, "not up; resetting aclink hardware\n");
28588a50509SAriff Abdullah #endif
286d5688b6aSAriff Abdullah 
287d5688b6aSAriff Abdullah 		/* dip aclink reset but keep the acsync */
288d5688b6aSAriff Abdullah 		value &= ~ATI_REG_CMD_AC_RESET;
289d5688b6aSAriff Abdullah 		value |=  ATI_REG_CMD_AC_SYNC;
290d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_CMD, value);
291d5688b6aSAriff Abdullah 
292d5688b6aSAriff Abdullah 		/* need to read CMD again and wait again (clocking in issue?) */
293d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_CMD);
294d5688b6aSAriff Abdullah 		DELAY(20);
295d5688b6aSAriff Abdullah 
296d5688b6aSAriff Abdullah 		/* assert aclink reset again */
297d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_CMD);
298d5688b6aSAriff Abdullah 		value |=  ATI_REG_CMD_AC_RESET;
299d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_CMD, value);
300d5688b6aSAriff Abdullah 
301d5688b6aSAriff Abdullah 		/* check if its active now */
302d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_CMD);
3039f52a325SAriff Abdullah 	}
304d5688b6aSAriff Abdullah 
305d5688b6aSAriff Abdullah 	if (timeout == 0)
306d5688b6aSAriff Abdullah 		device_printf(sc->dev, "giving up aclink reset\n");
30788a50509SAriff Abdullah #if 0
308d5688b6aSAriff Abdullah 	if (timeout != 10)
309d5688b6aSAriff Abdullah 		device_printf(sc->dev, "aclink hardware reset successful\n");
31088a50509SAriff Abdullah #endif
311d5688b6aSAriff Abdullah 
312d5688b6aSAriff Abdullah 	/* assert reset and sync for safety */
313d5688b6aSAriff Abdullah 	value  = atiixp_rd(sc, ATI_REG_CMD);
314d5688b6aSAriff Abdullah 	value |= ATI_REG_CMD_AC_SYNC | ATI_REG_CMD_AC_RESET;
315d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_CMD, value);
316d5688b6aSAriff Abdullah }
317d5688b6aSAriff Abdullah 
318d5688b6aSAriff Abdullah static void
3199f52a325SAriff Abdullah atiixp_flush_dma(struct atiixp_chinfo *ch)
320d5688b6aSAriff Abdullah {
3219f52a325SAriff Abdullah 	atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit);
322d5688b6aSAriff Abdullah }
323d5688b6aSAriff Abdullah 
324d5688b6aSAriff Abdullah static void
3259f52a325SAriff Abdullah atiixp_enable_dma(struct atiixp_chinfo *ch)
326d5688b6aSAriff Abdullah {
327d5688b6aSAriff Abdullah 	uint32_t value;
328d5688b6aSAriff Abdullah 
3299f52a325SAriff Abdullah 	value = atiixp_rd(ch->parent, ATI_REG_CMD);
330d5688b6aSAriff Abdullah 	if (!(value & ch->enable_bit)) {
331d5688b6aSAriff Abdullah 		value |= ch->enable_bit;
3329f52a325SAriff Abdullah 		atiixp_wr(ch->parent, ATI_REG_CMD, value);
333d5688b6aSAriff Abdullah 	}
334d5688b6aSAriff Abdullah }
335d5688b6aSAriff Abdullah 
336d5688b6aSAriff Abdullah static void
3379f52a325SAriff Abdullah atiixp_disable_dma(struct atiixp_chinfo *ch)
338d5688b6aSAriff Abdullah {
339d5688b6aSAriff Abdullah 	uint32_t value;
340d5688b6aSAriff Abdullah 
3419f52a325SAriff Abdullah 	value = atiixp_rd(ch->parent, ATI_REG_CMD);
342d5688b6aSAriff Abdullah 	if (value & ch->enable_bit) {
343d5688b6aSAriff Abdullah 		value &= ~ch->enable_bit;
3449f52a325SAriff Abdullah 		atiixp_wr(ch->parent, ATI_REG_CMD, value);
345d5688b6aSAriff Abdullah 	}
346d5688b6aSAriff Abdullah }
347d5688b6aSAriff Abdullah 
348d5688b6aSAriff Abdullah /*
349d5688b6aSAriff Abdullah  * AC97 interface
350d5688b6aSAriff Abdullah  */
351d5688b6aSAriff Abdullah static int
352d5688b6aSAriff Abdullah atiixp_waitready_codec(struct atiixp_info *sc)
353d5688b6aSAriff Abdullah {
354d5688b6aSAriff Abdullah 	int timeout = 500;
355d5688b6aSAriff Abdullah 
356d5688b6aSAriff Abdullah 	do {
357d5688b6aSAriff Abdullah 		if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
358d5688b6aSAriff Abdullah 		    ATI_REG_PHYS_OUT_ADDR_EN) == 0)
3599f52a325SAriff Abdullah 			return (0);
360d5688b6aSAriff Abdullah 		DELAY(1);
3619f52a325SAriff Abdullah 	} while (--timeout);
362d5688b6aSAriff Abdullah 
3639f52a325SAriff Abdullah 	return (-1);
364d5688b6aSAriff Abdullah }
365d5688b6aSAriff Abdullah 
366d5688b6aSAriff Abdullah static int
367d5688b6aSAriff Abdullah atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
368d5688b6aSAriff Abdullah {
369d5688b6aSAriff Abdullah 	struct atiixp_info *sc = devinfo;
370d5688b6aSAriff Abdullah 	uint32_t data;
371d5688b6aSAriff Abdullah 	int timeout;
372d5688b6aSAriff Abdullah 
373d5688b6aSAriff Abdullah 	if (atiixp_waitready_codec(sc))
3749f52a325SAriff Abdullah 		return (-1);
375d5688b6aSAriff Abdullah 
376d5688b6aSAriff Abdullah 	data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
377a580b31aSAriff Abdullah 	    ATI_REG_PHYS_OUT_ADDR_EN | ATI_REG_PHYS_OUT_RW | sc->codec_idx;
378d5688b6aSAriff Abdullah 
379d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
380d5688b6aSAriff Abdullah 
381d5688b6aSAriff Abdullah 	if (atiixp_waitready_codec(sc))
3829f52a325SAriff Abdullah 		return (-1);
383d5688b6aSAriff Abdullah 
384d5688b6aSAriff Abdullah 	timeout = 500;
385d5688b6aSAriff Abdullah 	do {
386d5688b6aSAriff Abdullah 		data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR);
387d5688b6aSAriff Abdullah 		if (data & ATI_REG_PHYS_IN_READ_FLAG)
3889f52a325SAriff Abdullah 			return (data >> ATI_REG_PHYS_IN_DATA_SHIFT);
389d5688b6aSAriff Abdullah 		DELAY(1);
3909f52a325SAriff Abdullah 	} while (--timeout);
391d5688b6aSAriff Abdullah 
392d5688b6aSAriff Abdullah 	if (reg < 0x7c)
393d5688b6aSAriff Abdullah 		device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg);
394d5688b6aSAriff Abdullah 
3959f52a325SAriff Abdullah 	return (-1);
396d5688b6aSAriff Abdullah }
397d5688b6aSAriff Abdullah 
398d5688b6aSAriff Abdullah static int
399d5688b6aSAriff Abdullah atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
400d5688b6aSAriff Abdullah {
401d5688b6aSAriff Abdullah 	struct atiixp_info *sc = devinfo;
402d5688b6aSAriff Abdullah 
403d5688b6aSAriff Abdullah 	if (atiixp_waitready_codec(sc))
4049f52a325SAriff Abdullah 		return (-1);
405d5688b6aSAriff Abdullah 
406d5688b6aSAriff Abdullah 	data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
407d5688b6aSAriff Abdullah 	    (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
408d5688b6aSAriff Abdullah 	    ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
409d5688b6aSAriff Abdullah 
410d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
411d5688b6aSAriff Abdullah 
4129f52a325SAriff Abdullah 	return (0);
413d5688b6aSAriff Abdullah }
414d5688b6aSAriff Abdullah 
415d5688b6aSAriff Abdullah static kobj_method_t atiixp_ac97_methods[] = {
416d5688b6aSAriff Abdullah 	KOBJMETHOD(ac97_read,		atiixp_rdcd),
417d5688b6aSAriff Abdullah 	KOBJMETHOD(ac97_write,		atiixp_wrcd),
418d5688b6aSAriff Abdullah 	{ 0, 0 }
419d5688b6aSAriff Abdullah };
420d5688b6aSAriff Abdullah AC97_DECLARE(atiixp_ac97);
421d5688b6aSAriff Abdullah 
422d5688b6aSAriff Abdullah /*
423d5688b6aSAriff Abdullah  * Playback / Record channel interface
424d5688b6aSAriff Abdullah  */
425d5688b6aSAriff Abdullah static void *
426d5688b6aSAriff Abdullah atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
427d5688b6aSAriff Abdullah 					struct pcm_channel *c, int dir)
428d5688b6aSAriff Abdullah {
429d5688b6aSAriff Abdullah 	struct atiixp_info *sc = devinfo;
430d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch;
431d5688b6aSAriff Abdullah 	int num;
432d5688b6aSAriff Abdullah 
433d5688b6aSAriff Abdullah 	atiixp_lock(sc);
434d5688b6aSAriff Abdullah 
435d5688b6aSAriff Abdullah 	if (dir == PCMDIR_PLAY) {
436d5688b6aSAriff Abdullah 		ch = &sc->pch;
437d5688b6aSAriff Abdullah 		ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR;
438d5688b6aSAriff Abdullah 		ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN;
439d5688b6aSAriff Abdullah 		ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH;
440a580b31aSAriff Abdullah 		ch->dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
441d5688b6aSAriff Abdullah 		/* Native 32bit playback working properly */
442d5688b6aSAriff Abdullah 		ch->caps_32bit = 1;
443d5688b6aSAriff Abdullah 	} else {
444d5688b6aSAriff Abdullah 		ch = &sc->rch;
445d5688b6aSAriff Abdullah 		ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
446d5688b6aSAriff Abdullah 		ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
447d5688b6aSAriff Abdullah 		ch->flush_bit = ATI_REG_FIFO_IN_FLUSH;
448a580b31aSAriff Abdullah 		ch->dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
449d5688b6aSAriff Abdullah 		/* XXX Native 32bit recording appear to be broken */
4508815a869SAriff Abdullah 		ch->caps_32bit = 1;
451d5688b6aSAriff Abdullah 	}
452d5688b6aSAriff Abdullah 
453d5688b6aSAriff Abdullah 	ch->buffer = b;
454d5688b6aSAriff Abdullah 	ch->parent = sc;
455d5688b6aSAriff Abdullah 	ch->channel = c;
456d5688b6aSAriff Abdullah 	ch->dir = dir;
457a580b31aSAriff Abdullah 	ch->blkcnt = sc->blkcnt;
458a580b31aSAriff Abdullah 	ch->blksz = sc->bufsz / ch->blkcnt;
459d5688b6aSAriff Abdullah 
460d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
461d5688b6aSAriff Abdullah 
462d5688b6aSAriff Abdullah 	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1)
4639f52a325SAriff Abdullah 		return (NULL);
464d5688b6aSAriff Abdullah 
465d5688b6aSAriff Abdullah 	atiixp_lock(sc);
466d5688b6aSAriff Abdullah 	num = sc->registered_channels++;
4671042342aSAriff Abdullah 	ch->sgd_table = &sc->sgd_table[num * ATI_IXP_DMA_CHSEGS_MAX];
4681042342aSAriff Abdullah 	ch->sgd_addr = sc->sgd_addr + (num * ATI_IXP_DMA_CHSEGS_MAX *
469a580b31aSAriff Abdullah 	    sizeof(struct atiixp_dma_op));
4709f52a325SAriff Abdullah 	atiixp_disable_dma(ch);
471d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
472d5688b6aSAriff Abdullah 
4739f52a325SAriff Abdullah 	return (ch);
474d5688b6aSAriff Abdullah }
475d5688b6aSAriff Abdullah 
476d5688b6aSAriff Abdullah static int
477d5688b6aSAriff Abdullah atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
478d5688b6aSAriff Abdullah {
479d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
480d5688b6aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
481d5688b6aSAriff Abdullah 	uint32_t value;
482d5688b6aSAriff Abdullah 
483d5688b6aSAriff Abdullah 	atiixp_lock(sc);
484d5688b6aSAriff Abdullah 	if (ch->dir == PCMDIR_REC) {
485d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_CMD);
486d5688b6aSAriff Abdullah 		value &= ~ATI_REG_CMD_INTERLEAVE_IN;
48705051995SAriff Abdullah 		if ((format & AFMT_32BIT) == 0)
488d5688b6aSAriff Abdullah 			value |= ATI_REG_CMD_INTERLEAVE_IN;
489d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_CMD, value);
490d5688b6aSAriff Abdullah 	} else {
491d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_OUT_DMA_SLOT);
492d5688b6aSAriff Abdullah 		value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
493d5688b6aSAriff Abdullah 		/* We do not have support for more than 2 channels, _yet_. */
494d5688b6aSAriff Abdullah 		value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
495d5688b6aSAriff Abdullah 		    ATI_REG_OUT_DMA_SLOT_BIT(4);
496d5688b6aSAriff Abdullah 		value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
497d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
498d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_CMD);
499d5688b6aSAriff Abdullah 		value &= ~ATI_REG_CMD_INTERLEAVE_OUT;
50005051995SAriff Abdullah 		if ((format & AFMT_32BIT) == 0)
501d5688b6aSAriff Abdullah 			value |= ATI_REG_CMD_INTERLEAVE_OUT;
502d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_CMD, value);
503d5688b6aSAriff Abdullah 		value = atiixp_rd(sc, ATI_REG_6CH_REORDER);
504d5688b6aSAriff Abdullah 		value &= ~ATI_REG_6CH_REORDER_EN;
505d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_6CH_REORDER, value);
506d5688b6aSAriff Abdullah 	}
50788a50509SAriff Abdullah 	ch->fmt = format;
508d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
509d5688b6aSAriff Abdullah 
5109f52a325SAriff Abdullah 	return (0);
511d5688b6aSAriff Abdullah }
512d5688b6aSAriff Abdullah 
513d5688b6aSAriff Abdullah static int
514d5688b6aSAriff Abdullah atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd)
515d5688b6aSAriff Abdullah {
516d5688b6aSAriff Abdullah 	/* XXX We're supposed to do VRA/DRA processing right here */
5179f52a325SAriff Abdullah 	return (ATI_IXP_BASE_RATE);
518d5688b6aSAriff Abdullah }
519d5688b6aSAriff Abdullah 
520d5688b6aSAriff Abdullah static int
5211042342aSAriff Abdullah atiixp_chan_setfragments(kobj_t obj, void *data,
5221042342aSAriff Abdullah 					uint32_t blksz, uint32_t blkcnt)
5231042342aSAriff Abdullah {
5241042342aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
5251042342aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
5261042342aSAriff Abdullah 
5271042342aSAriff Abdullah 	blksz &= ATI_IXP_BLK_ALIGN;
5281042342aSAriff Abdullah 
5291042342aSAriff Abdullah 	if (blksz > (sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN))
5301042342aSAriff Abdullah 		blksz = sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN;
5311042342aSAriff Abdullah 	if (blksz < ATI_IXP_BLK_MIN)
5321042342aSAriff Abdullah 		blksz = ATI_IXP_BLK_MIN;
5331042342aSAriff Abdullah 	if (blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
5341042342aSAriff Abdullah 		blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
5351042342aSAriff Abdullah 	if (blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
5361042342aSAriff Abdullah 		blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
5371042342aSAriff Abdullah 
5381042342aSAriff Abdullah 	while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) {
5391042342aSAriff Abdullah 		if ((blkcnt >> 1) >= ATI_IXP_DMA_CHSEGS_MIN)
5401042342aSAriff Abdullah 			blkcnt >>= 1;
5411042342aSAriff Abdullah 		else if ((blksz >> 1) >= ATI_IXP_BLK_MIN)
5421042342aSAriff Abdullah 			blksz >>= 1;
5431042342aSAriff Abdullah 		else
5441042342aSAriff Abdullah 			break;
5451042342aSAriff Abdullah 	}
5461042342aSAriff Abdullah 
5471042342aSAriff Abdullah 	if ((sndbuf_getblksz(ch->buffer) != blksz ||
5481042342aSAriff Abdullah 	    sndbuf_getblkcnt(ch->buffer) != blkcnt) &&
5491042342aSAriff Abdullah 	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
5501042342aSAriff Abdullah 		device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
5511042342aSAriff Abdullah 		    __func__, blksz, blkcnt);
5521042342aSAriff Abdullah 
5531042342aSAriff Abdullah 	ch->blksz = sndbuf_getblksz(ch->buffer);
5541042342aSAriff Abdullah 	ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
5551042342aSAriff Abdullah 
5561042342aSAriff Abdullah 	return (1);
5571042342aSAriff Abdullah }
5581042342aSAriff Abdullah 
5591042342aSAriff Abdullah static int
560d5688b6aSAriff Abdullah atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
561d5688b6aSAriff Abdullah {
562d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
563d5688b6aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
564d5688b6aSAriff Abdullah 
5651042342aSAriff Abdullah 	atiixp_chan_setfragments(obj, data, blksz, sc->blkcnt);
566a580b31aSAriff Abdullah 
567a580b31aSAriff Abdullah 	return (ch->blksz);
568d5688b6aSAriff Abdullah }
569d5688b6aSAriff Abdullah 
570d5688b6aSAriff Abdullah static void
571d5688b6aSAriff Abdullah atiixp_buildsgdt(struct atiixp_chinfo *ch)
572d5688b6aSAriff Abdullah {
573a580b31aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
574a580b31aSAriff Abdullah 	uint32_t addr, blksz, blkcnt;
575d5688b6aSAriff Abdullah 	int i;
576d5688b6aSAriff Abdullah 
577d5688b6aSAriff Abdullah 	addr = sndbuf_getbufaddr(ch->buffer);
578d5688b6aSAriff Abdullah 
579a580b31aSAriff Abdullah 	if (sc->polling != 0) {
580a580b31aSAriff Abdullah 		blksz = ch->blksz * ch->blkcnt;
581a580b31aSAriff Abdullah 		blkcnt = 1;
582a580b31aSAriff Abdullah 	} else {
583a580b31aSAriff Abdullah 		blksz = ch->blksz;
584a580b31aSAriff Abdullah 		blkcnt = ch->blkcnt;
585d5688b6aSAriff Abdullah 	}
5869f52a325SAriff Abdullah 
587a580b31aSAriff Abdullah 	for (i = 0; i < blkcnt; i++) {
588a580b31aSAriff Abdullah 		ch->sgd_table[i].addr = htole32(addr + (i * blksz));
589a580b31aSAriff Abdullah 		ch->sgd_table[i].status = htole16(0);
590a580b31aSAriff Abdullah 		ch->sgd_table[i].size = htole16(blksz >> 2);
591a580b31aSAriff Abdullah 		ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
592a580b31aSAriff Abdullah 		    (((i + 1) % blkcnt) * sizeof(struct atiixp_dma_op)));
593a580b31aSAriff Abdullah 	}
594a580b31aSAriff Abdullah }
595a580b31aSAriff Abdullah 
596a580b31aSAriff Abdullah static __inline uint32_t
597a580b31aSAriff Abdullah atiixp_dmapos(struct atiixp_chinfo *ch)
598a580b31aSAriff Abdullah {
599a580b31aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
600a580b31aSAriff Abdullah 	uint32_t reg, addr, sz, retry;
601a580b31aSAriff Abdullah 	volatile uint32_t ptr;
602a580b31aSAriff Abdullah 
603a580b31aSAriff Abdullah 	reg = ch->dt_cur_bit;
604a580b31aSAriff Abdullah 	addr = sndbuf_getbufaddr(ch->buffer);
605a580b31aSAriff Abdullah 	sz = ch->blkcnt * ch->blksz;
606a580b31aSAriff Abdullah 	retry = ATI_IXP_DMA_RETRY_MAX;
607a580b31aSAriff Abdullah 
608a580b31aSAriff Abdullah 	do {
609a580b31aSAriff Abdullah 		ptr = atiixp_rd(sc, reg);
610a580b31aSAriff Abdullah 		if (ptr < addr)
611a580b31aSAriff Abdullah 			continue;
612a580b31aSAriff Abdullah 		ptr -= addr;
613a580b31aSAriff Abdullah 		if (ptr < sz) {
614a580b31aSAriff Abdullah #if 0
6159f52a325SAriff Abdullah #ifdef ATI_IXP_DEBUG
616a580b31aSAriff Abdullah 			if ((ptr & ~(ch->blksz - 1)) != ch->ptr) {
617a580b31aSAriff Abdullah 				uint32_t delta;
618a580b31aSAriff Abdullah 
619a580b31aSAriff Abdullah 				delta = (sz + ptr - ch->prevptr) % sz;
620a580b31aSAriff Abdullah #ifndef ATI_IXP_DEBUG_VERBOSE
621a580b31aSAriff Abdullah 				if (delta < ch->blksz)
6229f52a325SAriff Abdullah #endif
623a580b31aSAriff Abdullah 					device_printf(sc->dev,
624a580b31aSAriff Abdullah 						"PCMDIR_%s: incoherent DMA "
625a580b31aSAriff Abdullah 						"prevptr=%u ptr=%u "
626a580b31aSAriff Abdullah 						"ptr=%u blkcnt=%u "
627a580b31aSAriff Abdullah 						"[delta=%u != blksz=%u] "
628a580b31aSAriff Abdullah 						"(%s)\n",
629a580b31aSAriff Abdullah 						(ch->dir == PCMDIR_PLAY) ?
630a580b31aSAriff Abdullah 						"PLAY" : "REC",
631a580b31aSAriff Abdullah 						ch->prevptr, ptr,
632a580b31aSAriff Abdullah 						ch->ptr, ch->blkcnt,
633a580b31aSAriff Abdullah 						delta, ch->blksz,
634a580b31aSAriff Abdullah 						(delta < ch->blksz) ?
635a580b31aSAriff Abdullah 						"OVERLAPPED!" : "Ok");
636a580b31aSAriff Abdullah 				ch->ptr = ptr & ~(ch->blksz - 1);
637a580b31aSAriff Abdullah 			}
638a580b31aSAriff Abdullah 			ch->prevptr = ptr;
639a580b31aSAriff Abdullah #endif
640a580b31aSAriff Abdullah #endif
641a580b31aSAriff Abdullah 			return (ptr);
642a580b31aSAriff Abdullah 		}
643a580b31aSAriff Abdullah 	} while (--retry);
644a580b31aSAriff Abdullah 
645a580b31aSAriff Abdullah 	device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
646a580b31aSAriff Abdullah 	    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
647a580b31aSAriff Abdullah 
648a580b31aSAriff Abdullah 	return (0);
649a580b31aSAriff Abdullah }
650a580b31aSAriff Abdullah 
651a580b31aSAriff Abdullah static __inline int
652a580b31aSAriff Abdullah atiixp_poll_channel(struct atiixp_chinfo *ch)
653a580b31aSAriff Abdullah {
654a580b31aSAriff Abdullah 	uint32_t sz, delta;
655a580b31aSAriff Abdullah 	volatile uint32_t ptr;
656a580b31aSAriff Abdullah 
657a580b31aSAriff Abdullah 	if (ch->active == 0)
658a580b31aSAriff Abdullah 		return (0);
659a580b31aSAriff Abdullah 
660a580b31aSAriff Abdullah 	sz = ch->blksz * ch->blkcnt;
661a580b31aSAriff Abdullah 	ptr = atiixp_dmapos(ch);
662a580b31aSAriff Abdullah 	ch->ptr = ptr;
663a580b31aSAriff Abdullah 	ptr %= sz;
664a580b31aSAriff Abdullah 	ptr &= ~(ch->blksz - 1);
665a580b31aSAriff Abdullah 	delta = (sz + ptr - ch->prevptr) % sz;
666a580b31aSAriff Abdullah 
667a580b31aSAriff Abdullah 	if (delta < ch->blksz)
668a580b31aSAriff Abdullah 		return (0);
669a580b31aSAriff Abdullah 
670a580b31aSAriff Abdullah 	ch->prevptr = ptr;
671a580b31aSAriff Abdullah 
672a580b31aSAriff Abdullah 	return (1);
673a580b31aSAriff Abdullah }
674a580b31aSAriff Abdullah 
675a580b31aSAriff Abdullah #define atiixp_chan_active(sc)	((sc)->pch.active + (sc)->rch.active)
676a580b31aSAriff Abdullah 
677a580b31aSAriff Abdullah static void
678a580b31aSAriff Abdullah atiixp_poll_callback(void *arg)
679a580b31aSAriff Abdullah {
680a580b31aSAriff Abdullah 	struct atiixp_info *sc = arg;
681a580b31aSAriff Abdullah 	uint32_t trigger = 0;
682a580b31aSAriff Abdullah 
683a580b31aSAriff Abdullah 	if (sc == NULL)
684a580b31aSAriff Abdullah 		return;
685a580b31aSAriff Abdullah 
686a580b31aSAriff Abdullah 	atiixp_lock(sc);
687a580b31aSAriff Abdullah 	if (sc->polling == 0 || atiixp_chan_active(sc) == 0) {
688a580b31aSAriff Abdullah 		atiixp_unlock(sc);
689a580b31aSAriff Abdullah 		return;
690a580b31aSAriff Abdullah 	}
691a580b31aSAriff Abdullah 
692a580b31aSAriff Abdullah 	trigger |= (atiixp_poll_channel(&sc->pch) != 0) ? 1 : 0;
693a580b31aSAriff Abdullah 	trigger |= (atiixp_poll_channel(&sc->rch) != 0) ? 2 : 0;
694a580b31aSAriff Abdullah 
695a580b31aSAriff Abdullah 	/* XXX */
696a580b31aSAriff Abdullah 	callout_reset(&sc->poll_timer, 1/*sc->poll_ticks*/,
697a580b31aSAriff Abdullah 	    atiixp_poll_callback, sc);
698a580b31aSAriff Abdullah 
699a580b31aSAriff Abdullah 	atiixp_unlock(sc);
700a580b31aSAriff Abdullah 
701a580b31aSAriff Abdullah 	if (trigger & 1)
702a580b31aSAriff Abdullah 		chn_intr(sc->pch.channel);
703a580b31aSAriff Abdullah 	if (trigger & 2)
704a580b31aSAriff Abdullah 		chn_intr(sc->rch.channel);
705d5688b6aSAriff Abdullah }
706d5688b6aSAriff Abdullah 
707d5688b6aSAriff Abdullah static int
708d5688b6aSAriff Abdullah atiixp_chan_trigger(kobj_t obj, void *data, int go)
709d5688b6aSAriff Abdullah {
710d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
711d5688b6aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
712d5688b6aSAriff Abdullah 	uint32_t value;
713a580b31aSAriff Abdullah 	int pollticks;
714d5688b6aSAriff Abdullah 
715d5688b6aSAriff Abdullah 	atiixp_lock(sc);
716d5688b6aSAriff Abdullah 
717d5688b6aSAriff Abdullah 	switch (go) {
718d5688b6aSAriff Abdullah 	case PCMTRIG_START:
7199f52a325SAriff Abdullah 		atiixp_flush_dma(ch);
720d5688b6aSAriff Abdullah 		atiixp_buildsgdt(ch);
721d5688b6aSAriff Abdullah 		atiixp_wr(sc, ch->linkptr_bit, 0);
7229f52a325SAriff Abdullah 		atiixp_enable_dma(ch);
723d5688b6aSAriff Abdullah 		atiixp_wr(sc, ch->linkptr_bit,
724d5688b6aSAriff Abdullah 		    (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
725a580b31aSAriff Abdullah 		if (sc->polling != 0) {
726a580b31aSAriff Abdullah 			ch->ptr = 0;
727a580b31aSAriff Abdullah 			ch->prevptr = 0;
728a580b31aSAriff Abdullah 			pollticks = ((uint64_t)hz * ch->blksz) /
729a580b31aSAriff Abdullah 			    ((uint64_t)sndbuf_getbps(ch->buffer) *
730a580b31aSAriff Abdullah 			    sndbuf_getspd(ch->buffer));
731a580b31aSAriff Abdullah 			pollticks >>= 2;
732a580b31aSAriff Abdullah 			if (pollticks > hz)
733a580b31aSAriff Abdullah 				pollticks = hz;
734a580b31aSAriff Abdullah 			if (pollticks < 1)
735a580b31aSAriff Abdullah 				pollticks = 1;
736a580b31aSAriff Abdullah 			if (atiixp_chan_active(sc) == 0 ||
737a580b31aSAriff Abdullah 			    pollticks < sc->poll_ticks) {
738a580b31aSAriff Abdullah 			    	if (bootverbose) {
739a580b31aSAriff Abdullah 					if (atiixp_chan_active(sc) == 0)
740a580b31aSAriff Abdullah 						device_printf(sc->dev,
741a580b31aSAriff Abdullah 						    "%s: pollticks=%d\n",
742a580b31aSAriff Abdullah 						    __func__, pollticks);
743a580b31aSAriff Abdullah 					else
744a580b31aSAriff Abdullah 						device_printf(sc->dev,
745a580b31aSAriff Abdullah 						    "%s: pollticks %d -> %d\n",
746a580b31aSAriff Abdullah 						    __func__, sc->poll_ticks,
747a580b31aSAriff Abdullah 						    pollticks);
748a580b31aSAriff Abdullah 				}
749a580b31aSAriff Abdullah 				sc->poll_ticks = pollticks;
750a580b31aSAriff Abdullah 				callout_reset(&sc->poll_timer, 1,
751a580b31aSAriff Abdullah 				    atiixp_poll_callback, sc);
752a580b31aSAriff Abdullah 			}
753a580b31aSAriff Abdullah 		}
754a580b31aSAriff Abdullah 		ch->active = 1;
755d5688b6aSAriff Abdullah 		break;
756d5688b6aSAriff Abdullah 	case PCMTRIG_STOP:
757d5688b6aSAriff Abdullah 	case PCMTRIG_ABORT:
7589f52a325SAriff Abdullah 		atiixp_disable_dma(ch);
7599f52a325SAriff Abdullah 		atiixp_flush_dma(ch);
760a580b31aSAriff Abdullah 		ch->active = 0;
761a580b31aSAriff Abdullah 		if (sc->polling != 0) {
762a580b31aSAriff Abdullah 			if (atiixp_chan_active(sc) == 0) {
763a580b31aSAriff Abdullah 				callout_stop(&sc->poll_timer);
764a580b31aSAriff Abdullah 				sc->poll_ticks = 1;
765a580b31aSAriff Abdullah 			} else {
766a580b31aSAriff Abdullah 				if (sc->pch.active != 0)
767a580b31aSAriff Abdullah 					ch = &sc->pch;
768a580b31aSAriff Abdullah 				else
769a580b31aSAriff Abdullah 					ch = &sc->rch;
770a580b31aSAriff Abdullah 				pollticks = ((uint64_t)hz * ch->blksz) /
771a580b31aSAriff Abdullah 				    ((uint64_t)sndbuf_getbps(ch->buffer) *
772a580b31aSAriff Abdullah 				    sndbuf_getspd(ch->buffer));
773a580b31aSAriff Abdullah 				pollticks >>= 2;
774a580b31aSAriff Abdullah 				if (pollticks > hz)
775a580b31aSAriff Abdullah 					pollticks = hz;
776a580b31aSAriff Abdullah 				if (pollticks < 1)
777a580b31aSAriff Abdullah 					pollticks = 1;
778a580b31aSAriff Abdullah 				if (pollticks > sc->poll_ticks) {
779a580b31aSAriff Abdullah 					if (bootverbose)
780a580b31aSAriff Abdullah 						device_printf(sc->dev,
781a580b31aSAriff Abdullah 						    "%s: pollticks %d -> %d\n",
782a580b31aSAriff Abdullah 						    __func__, sc->poll_ticks,
783a580b31aSAriff Abdullah 						    pollticks);
784a580b31aSAriff Abdullah 					sc->poll_ticks = pollticks;
785a580b31aSAriff Abdullah 					callout_reset(&sc->poll_timer,
786a580b31aSAriff Abdullah 					    1, atiixp_poll_callback,
787a580b31aSAriff Abdullah 					    sc);
788a580b31aSAriff Abdullah 				}
789a580b31aSAriff Abdullah 			}
790a580b31aSAriff Abdullah 		}
791d5688b6aSAriff Abdullah 		break;
792d5688b6aSAriff Abdullah 	default:
793d5688b6aSAriff Abdullah 		atiixp_unlock(sc);
7949f52a325SAriff Abdullah 		return (0);
795d5688b6aSAriff Abdullah 		break;
796d5688b6aSAriff Abdullah 	}
797d5688b6aSAriff Abdullah 
798d5688b6aSAriff Abdullah 	/* Update bus busy status */
799d5688b6aSAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_IER);
800a580b31aSAriff Abdullah 	if (atiixp_rd(sc, ATI_REG_CMD) & (ATI_REG_CMD_SEND_EN |
801a580b31aSAriff Abdullah 	    ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN))
802d5688b6aSAriff Abdullah 		value |= ATI_REG_IER_SET_BUS_BUSY;
803d5688b6aSAriff Abdullah 	else
804d5688b6aSAriff Abdullah 		value &= ~ATI_REG_IER_SET_BUS_BUSY;
805d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_IER, value);
806d5688b6aSAriff Abdullah 
807d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
808d5688b6aSAriff Abdullah 
8099f52a325SAriff Abdullah 	return (0);
8109f52a325SAriff Abdullah }
8119f52a325SAriff Abdullah 
812d5688b6aSAriff Abdullah static int
813d5688b6aSAriff Abdullah atiixp_chan_getptr(kobj_t obj, void *data)
814d5688b6aSAriff Abdullah {
815d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
816d5688b6aSAriff Abdullah 	struct atiixp_info *sc = ch->parent;
8179f52a325SAriff Abdullah 	uint32_t ptr;
818d5688b6aSAriff Abdullah 
819d5688b6aSAriff Abdullah 	atiixp_lock(sc);
820a580b31aSAriff Abdullah 	if (sc->polling != 0)
821a580b31aSAriff Abdullah 		ptr = ch->ptr;
822a580b31aSAriff Abdullah 	else
8239f52a325SAriff Abdullah 		ptr = atiixp_dmapos(ch);
824d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
825d5688b6aSAriff Abdullah 
8269f52a325SAriff Abdullah 	return (ptr);
827d5688b6aSAriff Abdullah }
828d5688b6aSAriff Abdullah 
829d5688b6aSAriff Abdullah static struct pcmchan_caps *
830d5688b6aSAriff Abdullah atiixp_chan_getcaps(kobj_t obj, void *data)
831d5688b6aSAriff Abdullah {
832d5688b6aSAriff Abdullah 	struct atiixp_chinfo *ch = data;
833d5688b6aSAriff Abdullah 
834d5688b6aSAriff Abdullah 	if (ch->caps_32bit)
8359f52a325SAriff Abdullah 		return (&atiixp_caps_32bit);
8369f52a325SAriff Abdullah 	return (&atiixp_caps);
837d5688b6aSAriff Abdullah }
838d5688b6aSAriff Abdullah 
839d5688b6aSAriff Abdullah static kobj_method_t atiixp_chan_methods[] = {
840d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_init,		atiixp_chan_init),
841d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_setformat,		atiixp_chan_setformat),
842d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_setspeed,		atiixp_chan_setspeed),
843d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_setblocksize,	atiixp_chan_setblocksize),
8441042342aSAriff Abdullah 	KOBJMETHOD(channel_setfragments,	atiixp_chan_setfragments),
845d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_trigger,		atiixp_chan_trigger),
846d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_getptr,		atiixp_chan_getptr),
847d5688b6aSAriff Abdullah 	KOBJMETHOD(channel_getcaps,		atiixp_chan_getcaps),
848d5688b6aSAriff Abdullah 	{ 0, 0 }
849d5688b6aSAriff Abdullah };
850d5688b6aSAriff Abdullah CHANNEL_DECLARE(atiixp_chan);
851d5688b6aSAriff Abdullah 
852d5688b6aSAriff Abdullah /*
853d5688b6aSAriff Abdullah  * PCI driver interface
854d5688b6aSAriff Abdullah  */
855d5688b6aSAriff Abdullah static void
856d5688b6aSAriff Abdullah atiixp_intr(void *p)
857d5688b6aSAriff Abdullah {
858d5688b6aSAriff Abdullah 	struct atiixp_info *sc = p;
859d5688b6aSAriff Abdullah 	uint32_t status, enable, detected_codecs;
860a580b31aSAriff Abdullah 	uint32_t trigger = 0;
861d5688b6aSAriff Abdullah 
862d5688b6aSAriff Abdullah 	atiixp_lock(sc);
863a580b31aSAriff Abdullah 	if (sc->polling != 0) {
864a580b31aSAriff Abdullah 		atiixp_unlock(sc);
865a580b31aSAriff Abdullah 		return;
866a580b31aSAriff Abdullah 	}
867d5688b6aSAriff Abdullah 	status = atiixp_rd(sc, ATI_REG_ISR);
868d5688b6aSAriff Abdullah 
869d5688b6aSAriff Abdullah 	if (status == 0) {
870d5688b6aSAriff Abdullah 		atiixp_unlock(sc);
871d5688b6aSAriff Abdullah 		return;
872d5688b6aSAriff Abdullah 	}
873d5688b6aSAriff Abdullah 
874a580b31aSAriff Abdullah 	if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.active != 0)
875a580b31aSAriff Abdullah 		trigger |= 1;
876a580b31aSAriff Abdullah 	if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.active != 0)
877a580b31aSAriff Abdullah 		trigger |= 2;
878d5688b6aSAriff Abdullah 
879d5688b6aSAriff Abdullah #if 0
880d5688b6aSAriff Abdullah 	if (status & ATI_REG_ISR_IN_XRUN) {
881d5688b6aSAriff Abdullah 		device_printf(sc->dev,
882d5688b6aSAriff Abdullah 			"Recieve IN XRUN interrupt\n");
883d5688b6aSAriff Abdullah 	}
884d5688b6aSAriff Abdullah 	if (status & ATI_REG_ISR_OUT_XRUN) {
885d5688b6aSAriff Abdullah 		device_printf(sc->dev,
886d5688b6aSAriff Abdullah 			"Recieve OUT XRUN interrupt\n");
887d5688b6aSAriff Abdullah 	}
888d5688b6aSAriff Abdullah #endif
889d5688b6aSAriff Abdullah 
890d5688b6aSAriff Abdullah 	if (status & CODEC_CHECK_BITS) {
891d5688b6aSAriff Abdullah 		/* mark missing codecs as not ready */
892d5688b6aSAriff Abdullah 		detected_codecs = status & CODEC_CHECK_BITS;
893d5688b6aSAriff Abdullah 		sc->codec_not_ready_bits |= detected_codecs;
894d5688b6aSAriff Abdullah 
895d5688b6aSAriff Abdullah 		/* disable detected interupt sources */
896d5688b6aSAriff Abdullah 		enable  = atiixp_rd(sc, ATI_REG_IER);
897d5688b6aSAriff Abdullah 		enable &= ~detected_codecs;
898d5688b6aSAriff Abdullah 		atiixp_wr(sc, ATI_REG_IER, enable);
899d5688b6aSAriff Abdullah 	}
900d5688b6aSAriff Abdullah 
901d5688b6aSAriff Abdullah 	/* acknowledge */
902d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_ISR, status);
903d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
904a580b31aSAriff Abdullah 
905a580b31aSAriff Abdullah 	if (trigger & 1)
906a580b31aSAriff Abdullah 		chn_intr(sc->pch.channel);
907a580b31aSAriff Abdullah 	if (trigger & 2)
908a580b31aSAriff Abdullah 		chn_intr(sc->rch.channel);
909d5688b6aSAriff Abdullah }
910d5688b6aSAriff Abdullah 
911d5688b6aSAriff Abdullah static void
912d5688b6aSAriff Abdullah atiixp_dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
913d5688b6aSAriff Abdullah {
914d5688b6aSAriff Abdullah 	struct atiixp_info *sc = (struct atiixp_info *)p;
915d5688b6aSAriff Abdullah 	sc->sgd_addr = bds->ds_addr;
916d5688b6aSAriff Abdullah }
917d5688b6aSAriff Abdullah 
918d5688b6aSAriff Abdullah static void
919d5688b6aSAriff Abdullah atiixp_chip_pre_init(struct atiixp_info *sc)
920d5688b6aSAriff Abdullah {
921d5688b6aSAriff Abdullah 	uint32_t value;
922d5688b6aSAriff Abdullah 
923d5688b6aSAriff Abdullah 	atiixp_lock(sc);
924d5688b6aSAriff Abdullah 
925d5688b6aSAriff Abdullah 	/* disable interrupts */
926d5688b6aSAriff Abdullah 	atiixp_disable_interrupts(sc);
927d5688b6aSAriff Abdullah 
928d5688b6aSAriff Abdullah 	/* clear all DMA enables (preserving rest of settings) */
929d5688b6aSAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_CMD);
930d5688b6aSAriff Abdullah 	value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN |
931d5688b6aSAriff Abdullah 	    ATI_REG_CMD_SPDF_OUT_EN );
932d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_CMD, value);
933d5688b6aSAriff Abdullah 
934d5688b6aSAriff Abdullah 	/* reset aclink */
935d5688b6aSAriff Abdullah 	atiixp_reset_aclink(sc);
936d5688b6aSAriff Abdullah 
937d5688b6aSAriff Abdullah 	sc->codec_not_ready_bits = 0;
938d5688b6aSAriff Abdullah 
939d5688b6aSAriff Abdullah 	/* enable all codecs to interrupt as well as the new frame interrupt */
940d5688b6aSAriff Abdullah 	atiixp_wr(sc, ATI_REG_IER, CODEC_CHECK_BITS);
941d5688b6aSAriff Abdullah 
942d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
943d5688b6aSAriff Abdullah }
944d5688b6aSAriff Abdullah 
945a580b31aSAriff Abdullah #ifdef SND_DYNSYSCTL
946a580b31aSAriff Abdullah static int
947a580b31aSAriff Abdullah sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
948a580b31aSAriff Abdullah {
949a580b31aSAriff Abdullah 	struct atiixp_info *sc;
950a580b31aSAriff Abdullah 	device_t dev;
951a580b31aSAriff Abdullah 	int err, val;
952a580b31aSAriff Abdullah 
953a580b31aSAriff Abdullah 	dev = oidp->oid_arg1;
954a580b31aSAriff Abdullah 	sc = pcm_getdevinfo(dev);
955a580b31aSAriff Abdullah 	if (sc == NULL)
956a580b31aSAriff Abdullah 		return (EINVAL);
957a580b31aSAriff Abdullah 	atiixp_lock(sc);
958a580b31aSAriff Abdullah 	val = sc->polling;
959a580b31aSAriff Abdullah 	atiixp_unlock(sc);
960a580b31aSAriff Abdullah 	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
961a580b31aSAriff Abdullah 
962a580b31aSAriff Abdullah 	if (err || req->newptr == NULL)
963a580b31aSAriff Abdullah 		return (err);
964a580b31aSAriff Abdullah 	if (val < 0 || val > 1)
965a580b31aSAriff Abdullah 		return (EINVAL);
966a580b31aSAriff Abdullah 
967a580b31aSAriff Abdullah 	atiixp_lock(sc);
968a580b31aSAriff Abdullah 	if (val != sc->polling) {
969a580b31aSAriff Abdullah 		if (atiixp_chan_active(sc) != 0)
970a580b31aSAriff Abdullah 			err = EBUSY;
971a580b31aSAriff Abdullah 		else if (val == 0) {
972a580b31aSAriff Abdullah 			atiixp_enable_interrupts(sc);
973a580b31aSAriff Abdullah 			sc->polling = 0;
974a580b31aSAriff Abdullah 			DELAY(1000);
975a580b31aSAriff Abdullah 		} else {
976a580b31aSAriff Abdullah 			atiixp_disable_interrupts(sc);
977a580b31aSAriff Abdullah 			sc->polling = 1;
978a580b31aSAriff Abdullah 			DELAY(1000);
979a580b31aSAriff Abdullah 		}
980a580b31aSAriff Abdullah 	}
981a580b31aSAriff Abdullah 	atiixp_unlock(sc);
982a580b31aSAriff Abdullah 
983a580b31aSAriff Abdullah 	return (err);
984a580b31aSAriff Abdullah }
985a580b31aSAriff Abdullah #endif
986a580b31aSAriff Abdullah 
987d5688b6aSAriff Abdullah static void
988d5688b6aSAriff Abdullah atiixp_chip_post_init(void *arg)
989d5688b6aSAriff Abdullah {
990d5688b6aSAriff Abdullah 	struct atiixp_info *sc = (struct atiixp_info *)arg;
99105051995SAriff Abdullah 	uint32_t subdev;
992a580b31aSAriff Abdullah 	int i, timeout, found, polling;
993d5688b6aSAriff Abdullah 	char status[SND_STATUSLEN];
994d5688b6aSAriff Abdullah 
995d5688b6aSAriff Abdullah 	atiixp_lock(sc);
996d5688b6aSAriff Abdullah 
997d5688b6aSAriff Abdullah 	if (sc->delayed_attach.ich_func) {
998d5688b6aSAriff Abdullah 		config_intrhook_disestablish(&sc->delayed_attach);
999d5688b6aSAriff Abdullah 		sc->delayed_attach.ich_func = NULL;
1000d5688b6aSAriff Abdullah 	}
1001d5688b6aSAriff Abdullah 
1002a580b31aSAriff Abdullah 	polling = sc->polling;
1003a580b31aSAriff Abdullah 	sc->polling = 0;
1004a580b31aSAriff Abdullah 
1005d5688b6aSAriff Abdullah 	/* wait for the interrupts to happen */
1006f2a1d71aSAriff Abdullah 	timeout = 100;
10079f52a325SAriff Abdullah 	do {
1008f2a1d71aSAriff Abdullah 		msleep(sc, sc->lock, PWAIT, "ixpslp", 1);
1009d5688b6aSAriff Abdullah 		if (sc->codec_not_ready_bits)
1010d5688b6aSAriff Abdullah 			break;
10119f52a325SAriff Abdullah 	} while (--timeout);
1012d5688b6aSAriff Abdullah 
1013a580b31aSAriff Abdullah 	sc->polling = polling;
1014d5688b6aSAriff Abdullah 	atiixp_disable_interrupts(sc);
1015d5688b6aSAriff Abdullah 
1016d5688b6aSAriff Abdullah 	if (timeout == 0) {
1017d5688b6aSAriff Abdullah 		device_printf(sc->dev,
1018d5688b6aSAriff Abdullah 			"WARNING: timeout during codec detection; "
1019d5688b6aSAriff Abdullah 			"codecs might be present but haven't interrupted\n");
1020d5688b6aSAriff Abdullah 		atiixp_unlock(sc);
1021f2a1d71aSAriff Abdullah 		goto postinitbad;
1022d5688b6aSAriff Abdullah 	}
1023d5688b6aSAriff Abdullah 
1024d5688b6aSAriff Abdullah 	found = 0;
1025d5688b6aSAriff Abdullah 
1026d5688b6aSAriff Abdullah 	/*
1027d5688b6aSAriff Abdullah 	 * ATI IXP can have upto 3 codecs, but single codec should be
1028d5688b6aSAriff Abdullah 	 * suffice for now.
1029d5688b6aSAriff Abdullah 	 */
1030a580b31aSAriff Abdullah 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) {
1031d5688b6aSAriff Abdullah 		/* codec 0 present */
1032d5688b6aSAriff Abdullah 		sc->codec_found++;
1033d5688b6aSAriff Abdullah 		sc->codec_idx = 0;
1034d5688b6aSAriff Abdullah 		found++;
1035d5688b6aSAriff Abdullah 	}
1036d5688b6aSAriff Abdullah 
1037a580b31aSAriff Abdullah 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) {
1038d5688b6aSAriff Abdullah 		/* codec 1 present */
1039d5688b6aSAriff Abdullah 		sc->codec_found++;
1040d5688b6aSAriff Abdullah 	}
1041d5688b6aSAriff Abdullah 
1042a580b31aSAriff Abdullah 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) {
1043d5688b6aSAriff Abdullah 		/* codec 2 present */
1044d5688b6aSAriff Abdullah 		sc->codec_found++;
1045d5688b6aSAriff Abdullah 	}
1046d5688b6aSAriff Abdullah 
1047d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
1048d5688b6aSAriff Abdullah 
1049d5688b6aSAriff Abdullah 	if (found == 0)
1050f2a1d71aSAriff Abdullah 		goto postinitbad;
1051d5688b6aSAriff Abdullah 
1052d5688b6aSAriff Abdullah 	/* create/init mixer */
1053d5688b6aSAriff Abdullah 	sc->codec = AC97_CREATE(sc->dev, sc, atiixp_ac97);
1054d5688b6aSAriff Abdullah 	if (sc->codec == NULL)
1055d5688b6aSAriff Abdullah 		goto postinitbad;
1056d5688b6aSAriff Abdullah 
1057a580b31aSAriff Abdullah 	subdev = (pci_get_subdevice(sc->dev) << 16) |
1058a580b31aSAriff Abdullah 	    pci_get_subvendor(sc->dev);
105905051995SAriff Abdullah 	switch (subdev) {
10600585c315SAriff Abdullah 	case 0x11831043:	/* ASUS A6R */
106105051995SAriff Abdullah 	case 0x2043161f:	/* Maxselect x710s - http://maxselect.ru/ */
1062a580b31aSAriff Abdullah 		ac97_setflags(sc->codec, ac97_getflags(sc->codec) |
1063a580b31aSAriff Abdullah 		    AC97_F_EAPD_INV);
106405051995SAriff Abdullah 		break;
106505051995SAriff Abdullah 	default:
106605051995SAriff Abdullah 		break;
106705051995SAriff Abdullah 	}
106805051995SAriff Abdullah 
1069d5688b6aSAriff Abdullah 	mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
1070d5688b6aSAriff Abdullah 
1071d5688b6aSAriff Abdullah 	if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
1072d5688b6aSAriff Abdullah 		goto postinitbad;
1073d5688b6aSAriff Abdullah 
1074d5688b6aSAriff Abdullah 	for (i = 0; i < ATI_IXP_NPCHAN; i++)
1075d5688b6aSAriff Abdullah 		pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc);
1076d5688b6aSAriff Abdullah 	for (i = 0; i < ATI_IXP_NRCHAN; i++)
1077d5688b6aSAriff Abdullah 		pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
1078d5688b6aSAriff Abdullah 
1079a580b31aSAriff Abdullah #ifdef SND_DYNSYSCTL
1080a580b31aSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
1081a580b31aSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
1082a580b31aSAriff Abdullah 	    "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
1083a580b31aSAriff Abdullah 	    sysctl_atiixp_polling, "I", "Enable polling mode");
1084a580b31aSAriff Abdullah #endif
1085a580b31aSAriff Abdullah 
1086d5688b6aSAriff Abdullah 	snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
1087d5688b6aSAriff Abdullah 	    rman_get_start(sc->reg), rman_get_start(sc->irq),
1088d5688b6aSAriff Abdullah 	    PCM_KLDSTRING(snd_atiixp));
1089d5688b6aSAriff Abdullah 
1090d5688b6aSAriff Abdullah 	pcm_setstatus(sc->dev, status);
1091d5688b6aSAriff Abdullah 
1092d5688b6aSAriff Abdullah 	atiixp_lock(sc);
1093a580b31aSAriff Abdullah 	if (sc->polling == 0)
1094d5688b6aSAriff Abdullah 		atiixp_enable_interrupts(sc);
1095d5688b6aSAriff Abdullah 	atiixp_unlock(sc);
1096d5688b6aSAriff Abdullah 
1097d5688b6aSAriff Abdullah 	return;
1098d5688b6aSAriff Abdullah 
1099d5688b6aSAriff Abdullah postinitbad:
1100f2a1d71aSAriff Abdullah 	atiixp_release_resource(sc);
1101f2a1d71aSAriff Abdullah }
1102f2a1d71aSAriff Abdullah 
1103f2a1d71aSAriff Abdullah static void
1104f2a1d71aSAriff Abdullah atiixp_release_resource(struct atiixp_info *sc)
1105f2a1d71aSAriff Abdullah {
1106f2a1d71aSAriff Abdullah 	if (sc == NULL)
1107f2a1d71aSAriff Abdullah 		return;
1108f2a1d71aSAriff Abdullah 	if (sc->codec) {
1109d5688b6aSAriff Abdullah 		ac97_destroy(sc->codec);
1110f2a1d71aSAriff Abdullah 		sc->codec = NULL;
1111f2a1d71aSAriff Abdullah 	}
1112f2a1d71aSAriff Abdullah 	if (sc->ih) {
1113d5688b6aSAriff Abdullah 		bus_teardown_intr(sc->dev, sc->irq, sc->ih);
1114703c934aSAriff Abdullah 		sc->ih = NULL;
1115f2a1d71aSAriff Abdullah 	}
1116f2a1d71aSAriff Abdullah 	if (sc->reg) {
1117d5688b6aSAriff Abdullah 		bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg);
1118703c934aSAriff Abdullah 		sc->reg = NULL;
1119f2a1d71aSAriff Abdullah 	}
1120f2a1d71aSAriff Abdullah 	if (sc->irq) {
1121d5688b6aSAriff Abdullah 		bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq);
1122703c934aSAriff Abdullah 		sc->irq = NULL;
1123f2a1d71aSAriff Abdullah 	}
1124f2a1d71aSAriff Abdullah 	if (sc->parent_dmat) {
1125d5688b6aSAriff Abdullah 		bus_dma_tag_destroy(sc->parent_dmat);
1126703c934aSAriff Abdullah 		sc->parent_dmat = NULL;
1127f2a1d71aSAriff Abdullah 	}
1128703c934aSAriff Abdullah 	if (sc->sgd_dmamap)
1129d5688b6aSAriff Abdullah 		bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
1130703c934aSAriff Abdullah 	if (sc->sgd_table) {
1131703c934aSAriff Abdullah 		bus_dmamem_free(sc->sgd_dmat, sc->sgd_table, sc->sgd_dmamap);
1132703c934aSAriff Abdullah 		sc->sgd_table = NULL;
1133f2a1d71aSAriff Abdullah 	}
1134703c934aSAriff Abdullah 	sc->sgd_dmamap = NULL;
1135f2a1d71aSAriff Abdullah 	if (sc->sgd_dmat) {
1136d5688b6aSAriff Abdullah 		bus_dma_tag_destroy(sc->sgd_dmat);
1137703c934aSAriff Abdullah 		sc->sgd_dmat = NULL;
1138f2a1d71aSAriff Abdullah 	}
1139f2a1d71aSAriff Abdullah 	if (sc->lock) {
1140d5688b6aSAriff Abdullah 		snd_mtxfree(sc->lock);
1141f2a1d71aSAriff Abdullah 		sc->lock = NULL;
1142f2a1d71aSAriff Abdullah 	}
1143d5688b6aSAriff Abdullah }
1144d5688b6aSAriff Abdullah 
1145d5688b6aSAriff Abdullah static int
1146d5688b6aSAriff Abdullah atiixp_pci_probe(device_t dev)
1147d5688b6aSAriff Abdullah {
1148d5688b6aSAriff Abdullah 	int i;
1149d5688b6aSAriff Abdullah 	uint16_t devid, vendor;
1150d5688b6aSAriff Abdullah 
1151d5688b6aSAriff Abdullah 	vendor = pci_get_vendor(dev);
1152d5688b6aSAriff Abdullah 	devid = pci_get_device(dev);
1153d5688b6aSAriff Abdullah 	for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
1154d5688b6aSAriff Abdullah 		if (vendor == atiixp_hw[i].vendor &&
1155d5688b6aSAriff Abdullah 		    devid == atiixp_hw[i].devid) {
1156d5688b6aSAriff Abdullah 			device_set_desc(dev, atiixp_hw[i].desc);
11579f52a325SAriff Abdullah 			return (BUS_PROBE_DEFAULT);
1158d5688b6aSAriff Abdullah 		}
1159d5688b6aSAriff Abdullah 	}
1160d5688b6aSAriff Abdullah 
11619f52a325SAriff Abdullah 	return (ENXIO);
1162d5688b6aSAriff Abdullah }
1163d5688b6aSAriff Abdullah 
1164d5688b6aSAriff Abdullah static int
1165d5688b6aSAriff Abdullah atiixp_pci_attach(device_t dev)
1166d5688b6aSAriff Abdullah {
1167d5688b6aSAriff Abdullah 	struct atiixp_info *sc;
1168d5688b6aSAriff Abdullah 	int i;
1169d5688b6aSAriff Abdullah 
1170d5688b6aSAriff Abdullah 	if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
1171d5688b6aSAriff Abdullah 		device_printf(dev, "cannot allocate softc\n");
11729f52a325SAriff Abdullah 		return (ENXIO);
1173d5688b6aSAriff Abdullah 	}
1174d5688b6aSAriff Abdullah 
11754582b3a1SAriff Abdullah 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_atiixp softc");
1176d5688b6aSAriff Abdullah 	sc->dev = dev;
1177a580b31aSAriff Abdullah 
1178a580b31aSAriff Abdullah 	callout_init(&sc->poll_timer, CALLOUT_MPSAFE);
1179a580b31aSAriff Abdullah 	sc->poll_ticks = 1;
1180a580b31aSAriff Abdullah 
1181a580b31aSAriff Abdullah 	if (resource_int_value(device_get_name(sc->dev),
1182a580b31aSAriff Abdullah 	    device_get_unit(sc->dev), "polling", &i) == 0 && i != 0)
1183a580b31aSAriff Abdullah 		sc->polling = 1;
1184a580b31aSAriff Abdullah 	else
1185a580b31aSAriff Abdullah 		sc->polling = 0;
1186d5688b6aSAriff Abdullah 
1187d5688b6aSAriff Abdullah 	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
1188d5688b6aSAriff Abdullah 	pci_enable_busmaster(dev);
1189d5688b6aSAriff Abdullah 
1190d5688b6aSAriff Abdullah 	sc->regid = PCIR_BAR(0);
1191d5688b6aSAriff Abdullah 	sc->regtype = SYS_RES_MEMORY;
1192a580b31aSAriff Abdullah 	sc->reg = bus_alloc_resource_any(dev, sc->regtype,
1193a580b31aSAriff Abdullah 	    &sc->regid, RF_ACTIVE);
1194d5688b6aSAriff Abdullah 
1195d5688b6aSAriff Abdullah 	if (!sc->reg) {
1196d5688b6aSAriff Abdullah 		device_printf(dev, "unable to allocate register space\n");
1197d5688b6aSAriff Abdullah 		goto bad;
1198d5688b6aSAriff Abdullah 	}
1199d5688b6aSAriff Abdullah 
1200d5688b6aSAriff Abdullah 	sc->st = rman_get_bustag(sc->reg);
1201d5688b6aSAriff Abdullah 	sc->sh = rman_get_bushandle(sc->reg);
1202d5688b6aSAriff Abdullah 
12039f52a325SAriff Abdullah 	sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN,
12049f52a325SAriff Abdullah 	    ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
1205d5688b6aSAriff Abdullah 
1206d5688b6aSAriff Abdullah 	sc->irqid = 0;
1207d5688b6aSAriff Abdullah 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
1208d5688b6aSAriff Abdullah 	    RF_ACTIVE | RF_SHAREABLE);
1209a580b31aSAriff Abdullah 	if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
1210d5688b6aSAriff Abdullah 	    atiixp_intr, sc, &sc->ih)) {
1211d5688b6aSAriff Abdullah 		device_printf(dev, "unable to map interrupt\n");
1212d5688b6aSAriff Abdullah 		goto bad;
1213d5688b6aSAriff Abdullah 	}
1214d5688b6aSAriff Abdullah 
1215d5688b6aSAriff Abdullah 	/*
1216d5688b6aSAriff Abdullah 	 * Let the user choose the best DMA segments.
1217d5688b6aSAriff Abdullah 	 */
1218d5688b6aSAriff Abdullah 	if (resource_int_value(device_get_name(dev),
1219a580b31aSAriff Abdullah 	    device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
12201042342aSAriff Abdullah 		i &= ATI_IXP_BLK_ALIGN;
12211042342aSAriff Abdullah 		if (i < ATI_IXP_BLK_MIN)
12221042342aSAriff Abdullah 			i = ATI_IXP_BLK_MIN;
1223a580b31aSAriff Abdullah 		sc->blkcnt = sc->bufsz / i;
122405051995SAriff Abdullah 		i = 0;
1225a580b31aSAriff Abdullah 		while (sc->blkcnt >> i)
122605051995SAriff Abdullah 			i++;
1227a580b31aSAriff Abdullah 		sc->blkcnt = 1 << (i - 1);
1228a580b31aSAriff Abdullah 		if (sc->blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
1229a580b31aSAriff Abdullah 			sc->blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
1230a580b31aSAriff Abdullah 		else if (sc->blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
1231a580b31aSAriff Abdullah 			sc->blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
1232a580b31aSAriff Abdullah 
1233a580b31aSAriff Abdullah 	} else
1234a580b31aSAriff Abdullah 		sc->blkcnt = ATI_IXP_DMA_CHSEGS;
123505051995SAriff Abdullah 
123605051995SAriff Abdullah 	/*
1237d5688b6aSAriff Abdullah 	 * DMA tag for scatter-gather buffers and link pointers
1238d5688b6aSAriff Abdullah 	 */
12390b989078SAlexander Leidinger 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
12400b989078SAlexander Leidinger 		/*boundary*/0,
1241d5688b6aSAriff Abdullah 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1242d5688b6aSAriff Abdullah 		/*highaddr*/BUS_SPACE_MAXADDR,
1243d5688b6aSAriff Abdullah 		/*filter*/NULL, /*filterarg*/NULL,
1244d5688b6aSAriff Abdullah 		/*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
1245d5688b6aSAriff Abdullah 		/*flags*/0, /*lockfunc*/NULL,
1246d5688b6aSAriff Abdullah 		/*lockarg*/NULL, &sc->parent_dmat) != 0) {
1247d5688b6aSAriff Abdullah 		device_printf(dev, "unable to create dma tag\n");
1248d5688b6aSAriff Abdullah 		goto bad;
1249d5688b6aSAriff Abdullah 	}
1250d5688b6aSAriff Abdullah 
12510b989078SAlexander Leidinger 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
12520b989078SAlexander Leidinger 		/*boundary*/0,
1253d5688b6aSAriff Abdullah 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1254d5688b6aSAriff Abdullah 		/*highaddr*/BUS_SPACE_MAXADDR,
1255d5688b6aSAriff Abdullah 		/*filter*/NULL, /*filterarg*/NULL,
12561042342aSAriff Abdullah 		/*maxsize*/ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS *
1257d5688b6aSAriff Abdullah 		sizeof(struct atiixp_dma_op),
1258d5688b6aSAriff Abdullah 		/*nsegments*/1, /*maxsegz*/0x3ffff,
1259d5688b6aSAriff Abdullah 		/*flags*/0, /*lockfunc*/NULL,
1260d5688b6aSAriff Abdullah 		/*lockarg*/NULL, &sc->sgd_dmat) != 0) {
1261d5688b6aSAriff Abdullah 		device_printf(dev, "unable to create dma tag\n");
1262d5688b6aSAriff Abdullah 		goto bad;
1263d5688b6aSAriff Abdullah 	}
1264d5688b6aSAriff Abdullah 
1265d5688b6aSAriff Abdullah 	if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
1266d5688b6aSAriff Abdullah 	    BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
1267d5688b6aSAriff Abdullah 		goto bad;
1268d5688b6aSAriff Abdullah 
1269d5688b6aSAriff Abdullah 	if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
12701042342aSAriff Abdullah 	    ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS *
12711042342aSAriff Abdullah 	    sizeof(struct atiixp_dma_op), atiixp_dma_cb, sc, 0))
1272d5688b6aSAriff Abdullah 		goto bad;
1273d5688b6aSAriff Abdullah 
1274d5688b6aSAriff Abdullah 
1275d5688b6aSAriff Abdullah 	atiixp_chip_pre_init(sc);
1276d5688b6aSAriff Abdullah 
1277d5688b6aSAriff Abdullah 	sc->delayed_attach.ich_func = atiixp_chip_post_init;
1278d5688b6aSAriff Abdullah 	sc->delayed_attach.ich_arg = sc;
1279d5688b6aSAriff Abdullah 	if (cold == 0 ||
1280d5688b6aSAriff Abdullah 	    config_intrhook_establish(&sc->delayed_attach) != 0) {
1281d5688b6aSAriff Abdullah 		sc->delayed_attach.ich_func = NULL;
1282d5688b6aSAriff Abdullah 		atiixp_chip_post_init(sc);
1283d5688b6aSAriff Abdullah 	}
1284d5688b6aSAriff Abdullah 
12859f52a325SAriff Abdullah 	return (0);
1286d5688b6aSAriff Abdullah 
1287d5688b6aSAriff Abdullah bad:
1288f2a1d71aSAriff Abdullah 	atiixp_release_resource(sc);
12899f52a325SAriff Abdullah 	return (ENXIO);
1290d5688b6aSAriff Abdullah }
1291d5688b6aSAriff Abdullah 
1292d5688b6aSAriff Abdullah static int
1293d5688b6aSAriff Abdullah atiixp_pci_detach(device_t dev)
1294d5688b6aSAriff Abdullah {
1295d5688b6aSAriff Abdullah 	int r;
1296d5688b6aSAriff Abdullah 	struct atiixp_info *sc;
1297d5688b6aSAriff Abdullah 
1298f2a1d71aSAriff Abdullah 	sc = pcm_getdevinfo(dev);
1299f2a1d71aSAriff Abdullah 	if (sc != NULL) {
1300f2a1d71aSAriff Abdullah 		if (sc->codec != NULL) {
1301d5688b6aSAriff Abdullah 			r = pcm_unregister(dev);
1302d5688b6aSAriff Abdullah 			if (r)
13039f52a325SAriff Abdullah 				return (r);
1304f2a1d71aSAriff Abdullah 		}
1305f2a1d71aSAriff Abdullah 		sc->codec = NULL;
13069f52a325SAriff Abdullah 		if (sc->st != 0 && sc->sh != 0)
1307d5688b6aSAriff Abdullah 			atiixp_disable_interrupts(sc);
1308f2a1d71aSAriff Abdullah 		atiixp_release_resource(sc);
1309d5688b6aSAriff Abdullah 		free(sc, M_DEVBUF);
1310f2a1d71aSAriff Abdullah 	}
13119f52a325SAriff Abdullah 	return (0);
1312d5688b6aSAriff Abdullah }
1313d5688b6aSAriff Abdullah 
131488a50509SAriff Abdullah static int
131588a50509SAriff Abdullah atiixp_pci_suspend(device_t dev)
131688a50509SAriff Abdullah {
131788a50509SAriff Abdullah 	struct atiixp_info *sc = pcm_getdevinfo(dev);
131888a50509SAriff Abdullah 	uint32_t value;
131988a50509SAriff Abdullah 
132088a50509SAriff Abdullah 	/* quickly disable interrupts and save channels active state */
132188a50509SAriff Abdullah 	atiixp_lock(sc);
132288a50509SAriff Abdullah 	atiixp_disable_interrupts(sc);
132388a50509SAriff Abdullah 	atiixp_unlock(sc);
132488a50509SAriff Abdullah 
132588a50509SAriff Abdullah 	/* stop everything */
1326a580b31aSAriff Abdullah 	if (sc->pch.active != 0)
132788a50509SAriff Abdullah 		atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP);
1328a580b31aSAriff Abdullah 	if (sc->rch.active != 0)
132988a50509SAriff Abdullah 		atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP);
133088a50509SAriff Abdullah 
133188a50509SAriff Abdullah 	/* power down aclink and pci bus */
133288a50509SAriff Abdullah 	atiixp_lock(sc);
133388a50509SAriff Abdullah 	value = atiixp_rd(sc, ATI_REG_CMD);
133488a50509SAriff Abdullah 	value |= ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET;
133588a50509SAriff Abdullah 	atiixp_wr(sc, ATI_REG_CMD, ATI_REG_CMD_POWERDOWN);
133688a50509SAriff Abdullah 	pci_set_powerstate(dev, PCI_POWERSTATE_D3);
133788a50509SAriff Abdullah 	atiixp_unlock(sc);
133888a50509SAriff Abdullah 
13399f52a325SAriff Abdullah 	return (0);
134088a50509SAriff Abdullah }
134188a50509SAriff Abdullah 
134288a50509SAriff Abdullah static int
134388a50509SAriff Abdullah atiixp_pci_resume(device_t dev)
134488a50509SAriff Abdullah {
134588a50509SAriff Abdullah 	struct atiixp_info *sc = pcm_getdevinfo(dev);
134688a50509SAriff Abdullah 
134788a50509SAriff Abdullah 	atiixp_lock(sc);
134888a50509SAriff Abdullah 	/* power up pci bus */
134988a50509SAriff Abdullah 	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
135088a50509SAriff Abdullah 	pci_enable_io(dev, SYS_RES_MEMORY);
135188a50509SAriff Abdullah 	pci_enable_busmaster(dev);
135288a50509SAriff Abdullah 	/* reset / power up aclink */
135388a50509SAriff Abdullah 	atiixp_reset_aclink(sc);
135488a50509SAriff Abdullah 	atiixp_unlock(sc);
135588a50509SAriff Abdullah 
135688a50509SAriff Abdullah 	if (mixer_reinit(dev) == -1) {
135788a50509SAriff Abdullah 		device_printf(dev, "unable to reinitialize the mixer\n");
13589f52a325SAriff Abdullah 		return (ENXIO);
135988a50509SAriff Abdullah 	}
136088a50509SAriff Abdullah 
136188a50509SAriff Abdullah 	/*
136288a50509SAriff Abdullah 	 * Resume channel activities. Reset channel format regardless
136388a50509SAriff Abdullah 	 * of its previous state.
136488a50509SAriff Abdullah 	 */
1365a580b31aSAriff Abdullah 	if (sc->pch.channel != NULL) {
1366a580b31aSAriff Abdullah 		if (sc->pch.fmt != 0)
136788a50509SAriff Abdullah 			atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt);
1368a580b31aSAriff Abdullah 		if (sc->pch.active != 0)
136988a50509SAriff Abdullah 			atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START);
137088a50509SAriff Abdullah 	}
1371a580b31aSAriff Abdullah 	if (sc->rch.channel != NULL) {
1372a580b31aSAriff Abdullah 		if (sc->rch.fmt != 0)
137388a50509SAriff Abdullah 			atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt);
1374a580b31aSAriff Abdullah 		if (sc->rch.active != 0)
137588a50509SAriff Abdullah 			atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START);
137688a50509SAriff Abdullah 	}
137788a50509SAriff Abdullah 
137888a50509SAriff Abdullah 	/* enable interrupts */
137988a50509SAriff Abdullah 	atiixp_lock(sc);
1380a580b31aSAriff Abdullah 	if (sc->polling == 0)
138188a50509SAriff Abdullah 		atiixp_enable_interrupts(sc);
138288a50509SAriff Abdullah 	atiixp_unlock(sc);
138388a50509SAriff Abdullah 
13849f52a325SAriff Abdullah 	return (0);
138588a50509SAriff Abdullah }
138688a50509SAriff Abdullah 
1387d5688b6aSAriff Abdullah static device_method_t atiixp_methods[] = {
1388d5688b6aSAriff Abdullah 	DEVMETHOD(device_probe,		atiixp_pci_probe),
1389d5688b6aSAriff Abdullah 	DEVMETHOD(device_attach,	atiixp_pci_attach),
1390d5688b6aSAriff Abdullah 	DEVMETHOD(device_detach,	atiixp_pci_detach),
139188a50509SAriff Abdullah 	DEVMETHOD(device_suspend,	atiixp_pci_suspend),
139288a50509SAriff Abdullah 	DEVMETHOD(device_resume,	atiixp_pci_resume),
1393d5688b6aSAriff Abdullah 	{ 0, 0 }
1394d5688b6aSAriff Abdullah };
1395d5688b6aSAriff Abdullah 
1396d5688b6aSAriff Abdullah static driver_t atiixp_driver = {
1397d5688b6aSAriff Abdullah 	"pcm",
1398d5688b6aSAriff Abdullah 	atiixp_methods,
1399d5688b6aSAriff Abdullah 	PCM_SOFTC_SIZE,
1400d5688b6aSAriff Abdullah };
1401d5688b6aSAriff Abdullah 
1402d5688b6aSAriff Abdullah DRIVER_MODULE(snd_atiixp, pci, atiixp_driver, pcm_devclass, 0, 0);
1403d5688b6aSAriff Abdullah MODULE_DEPEND(snd_atiixp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
1404d5688b6aSAriff Abdullah MODULE_VERSION(snd_atiixp, 1);
1405