xref: /dflybsd-src/sys/dev/sound/pci/atiixp.c (revision 030b0c8c4cf27c560ccec70410c8e21934ae677d)
1558a398bSSimon Schubert /*-
2558a398bSSimon Schubert  * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
3558a398bSSimon Schubert  * All rights reserved.
4558a398bSSimon Schubert  *
5558a398bSSimon Schubert  * Redistribution and use in source and binary forms, with or without
6558a398bSSimon Schubert  * modification, are permitted provided that the following conditions
7558a398bSSimon Schubert  * are met:
8558a398bSSimon Schubert  * 1. Redistributions of source code must retain the above copyright
9558a398bSSimon Schubert  *    notice, this list of conditions and the following disclaimer.
10558a398bSSimon Schubert  * 2. Redistributions in binary form must reproduce the above copyright
11558a398bSSimon Schubert  *    notice, this list of conditions and the following disclaimer in the
12558a398bSSimon Schubert  *    documentation and/or other materials provided with the distribution.
13558a398bSSimon Schubert  *
14558a398bSSimon Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15558a398bSSimon Schubert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16558a398bSSimon Schubert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17558a398bSSimon Schubert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18558a398bSSimon Schubert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19558a398bSSimon Schubert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20558a398bSSimon Schubert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21558a398bSSimon Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22558a398bSSimon Schubert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23558a398bSSimon Schubert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24558a398bSSimon Schubert  * SUCH DAMAGE.
25558a398bSSimon Schubert  */
26558a398bSSimon Schubert 
27558a398bSSimon Schubert /*
28558a398bSSimon Schubert  * FreeBSD pcm driver for ATI IXP 150/200/250/300 AC97 controllers
29558a398bSSimon Schubert  *
30558a398bSSimon Schubert  * Features
31558a398bSSimon Schubert  *	* 16bit playback / recording
32558a398bSSimon Schubert  *	* 32bit native playback - yay!
33558a398bSSimon Schubert  *	* 32bit native recording (seems broken on few hardwares)
34558a398bSSimon Schubert  *
35558a398bSSimon Schubert  * Issues / TODO:
36558a398bSSimon Schubert  *	* SPDIF
37558a398bSSimon Schubert  *	* Support for more than 2 channels.
38558a398bSSimon Schubert  *	* VRA ? VRM ? DRA ?
39558a398bSSimon Schubert  *	* 32bit native recording seems broken on few hardwares, most
40558a398bSSimon Schubert  *	  probably because of incomplete VRA/DRA cleanup.
41558a398bSSimon Schubert  *
42558a398bSSimon Schubert  *
43558a398bSSimon Schubert  * Thanks goes to:
44558a398bSSimon Schubert  *
45558a398bSSimon Schubert  *   Shaharil @ SCAN Associates whom relentlessly providing me the
46558a398bSSimon Schubert  *   mind blowing Acer Ferrari 4002 WLMi with this ATI IXP hardware.
47558a398bSSimon Schubert  *
48558a398bSSimon Schubert  *   Reinoud Zandijk <reinoud@NetBSD.org> (auixp), which this driver is
49558a398bSSimon Schubert  *   largely based upon although large part of it has been reworked. His
50558a398bSSimon Schubert  *   driver is the primary reference and pretty much well documented.
51558a398bSSimon Schubert  *
52558a398bSSimon Schubert  *   Takashi Iwai (ALSA snd-atiixp), for register definitions and some
53558a398bSSimon Schubert  *   random ninja hackery.
54558a398bSSimon Schubert  */
55558a398bSSimon Schubert 
562a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
572a1ad637SFrançois Tigeot #include "opt_snd.h"
582a1ad637SFrançois Tigeot #endif
592a1ad637SFrançois Tigeot 
60558a398bSSimon Schubert #include <dev/sound/pcm/sound.h>
61558a398bSSimon Schubert #include <dev/sound/pcm/ac97.h>
62558a398bSSimon Schubert 
6367931cc4SFrançois Tigeot #include <bus/pci/pcireg.h>
6467931cc4SFrançois Tigeot #include <bus/pci/pcivar.h>
65558a398bSSimon Schubert #include <sys/sysctl.h>
66558a398bSSimon Schubert #include <sys/endian.h>
67558a398bSSimon Schubert 
68558a398bSSimon Schubert #include <dev/sound/pci/atiixp.h>
69558a398bSSimon Schubert 
702a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/atiixp.c 267581 2014-06-17 16:07:57Z jhb $");
712a1ad637SFrançois Tigeot 
722a1ad637SFrançois Tigeot #define ATI_IXP_DMA_RETRY_MAX	100
732a1ad637SFrançois Tigeot 
742a1ad637SFrançois Tigeot #define ATI_IXP_BUFSZ_MIN	4096
752a1ad637SFrançois Tigeot #define ATI_IXP_BUFSZ_MAX	65536
762a1ad637SFrançois Tigeot #define ATI_IXP_BUFSZ_DEFAULT	16384
772a1ad637SFrançois Tigeot 
782a1ad637SFrançois Tigeot #define ATI_IXP_BLK_MIN		32
792a1ad637SFrançois Tigeot #define ATI_IXP_BLK_ALIGN	(~(ATI_IXP_BLK_MIN - 1))
802a1ad637SFrançois Tigeot 
812a1ad637SFrançois Tigeot #define ATI_IXP_CHN_RUNNING	0x00000001
822a1ad637SFrançois Tigeot #define ATI_IXP_CHN_SUSPEND	0x00000002
832a1ad637SFrançois Tigeot 
84558a398bSSimon Schubert struct atiixp_dma_op {
85558a398bSSimon Schubert 	volatile uint32_t addr;
86558a398bSSimon Schubert 	volatile uint16_t status;
87558a398bSSimon Schubert 	volatile uint16_t size;
88558a398bSSimon Schubert 	volatile uint32_t next;
89558a398bSSimon Schubert };
90558a398bSSimon Schubert 
91558a398bSSimon Schubert struct atiixp_info;
92558a398bSSimon Schubert 
93558a398bSSimon Schubert struct atiixp_chinfo {
94558a398bSSimon Schubert 	struct snd_dbuf *buffer;
95558a398bSSimon Schubert 	struct pcm_channel *channel;
96558a398bSSimon Schubert 	struct atiixp_info *parent;
97558a398bSSimon Schubert 	struct atiixp_dma_op *sgd_table;
98558a398bSSimon Schubert 	bus_addr_t sgd_addr;
992a1ad637SFrançois Tigeot 	uint32_t enable_bit, flush_bit, linkptr_bit, dt_cur_bit;
1002a1ad637SFrançois Tigeot 	uint32_t blksz, blkcnt;
1012a1ad637SFrançois Tigeot 	uint32_t ptr, prevptr;
102558a398bSSimon Schubert 	uint32_t fmt;
1032a1ad637SFrançois Tigeot 	uint32_t flags;
1042a1ad637SFrançois Tigeot 	int caps_32bit, dir;
105558a398bSSimon Schubert };
106558a398bSSimon Schubert 
107558a398bSSimon Schubert struct atiixp_info {
108558a398bSSimon Schubert 	device_t dev;
109558a398bSSimon Schubert 
110558a398bSSimon Schubert 	bus_space_tag_t st;
111558a398bSSimon Schubert 	bus_space_handle_t sh;
112558a398bSSimon Schubert 	bus_dma_tag_t parent_dmat;
113558a398bSSimon Schubert 	bus_dma_tag_t sgd_dmat;
114558a398bSSimon Schubert 	bus_dmamap_t sgd_dmamap;
115558a398bSSimon Schubert 	bus_addr_t sgd_addr;
116558a398bSSimon Schubert 
117558a398bSSimon Schubert 	struct resource *reg, *irq;
118558a398bSSimon Schubert 	int regtype, regid, irqid;
119558a398bSSimon Schubert 	void *ih;
120558a398bSSimon Schubert 	struct ac97_info *codec;
121558a398bSSimon Schubert 
122558a398bSSimon Schubert 	struct atiixp_chinfo pch;
123558a398bSSimon Schubert 	struct atiixp_chinfo rch;
124558a398bSSimon Schubert 	struct atiixp_dma_op *sgd_table;
125558a398bSSimon Schubert 	struct intr_config_hook delayed_attach;
126558a398bSSimon Schubert 
127558a398bSSimon Schubert 	uint32_t bufsz;
128558a398bSSimon Schubert 	uint32_t codec_not_ready_bits, codec_idx, codec_found;
1292a1ad637SFrançois Tigeot 	uint32_t blkcnt;
130558a398bSSimon Schubert 	int registered_channels;
131558a398bSSimon Schubert 
13267931cc4SFrançois Tigeot 	struct lock *lock;
1332a1ad637SFrançois Tigeot 	struct callout poll_timer;
1342a1ad637SFrançois Tigeot 	int poll_ticks, polling;
135558a398bSSimon Schubert };
136558a398bSSimon Schubert 
137558a398bSSimon Schubert #define atiixp_rd(_sc, _reg)	\
138558a398bSSimon Schubert 		bus_space_read_4((_sc)->st, (_sc)->sh, _reg)
139558a398bSSimon Schubert #define atiixp_wr(_sc, _reg, _val)	\
140558a398bSSimon Schubert 		bus_space_write_4((_sc)->st, (_sc)->sh, _reg, _val)
141558a398bSSimon Schubert 
142558a398bSSimon Schubert #define atiixp_lock(_sc)	snd_mtxlock((_sc)->lock)
143558a398bSSimon Schubert #define atiixp_unlock(_sc)	snd_mtxunlock((_sc)->lock)
144558a398bSSimon Schubert #define atiixp_assert(_sc)	snd_mtxassert((_sc)->lock)
145558a398bSSimon Schubert 
146558a398bSSimon Schubert static uint32_t atiixp_fmt_32bit[] = {
1472a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 2, 0),
1482a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S32_LE, 2, 0),
149558a398bSSimon Schubert 	0
150558a398bSSimon Schubert };
151558a398bSSimon Schubert 
152558a398bSSimon Schubert static uint32_t atiixp_fmt[] = {
1532a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 2, 0),
154558a398bSSimon Schubert 	0
155558a398bSSimon Schubert };
156558a398bSSimon Schubert 
157558a398bSSimon Schubert static struct pcmchan_caps atiixp_caps_32bit = {
158558a398bSSimon Schubert 	ATI_IXP_BASE_RATE,
159558a398bSSimon Schubert 	ATI_IXP_BASE_RATE,
160558a398bSSimon Schubert 	atiixp_fmt_32bit, 0
161558a398bSSimon Schubert };
162558a398bSSimon Schubert 
163558a398bSSimon Schubert static struct pcmchan_caps atiixp_caps = {
164558a398bSSimon Schubert 	ATI_IXP_BASE_RATE,
165558a398bSSimon Schubert 	ATI_IXP_BASE_RATE,
166558a398bSSimon Schubert 	atiixp_fmt, 0
167558a398bSSimon Schubert };
168558a398bSSimon Schubert 
169558a398bSSimon Schubert static const struct {
170558a398bSSimon Schubert 	uint16_t vendor;
171558a398bSSimon Schubert 	uint16_t devid;
172558a398bSSimon Schubert 	char	 *desc;
173558a398bSSimon Schubert } atiixp_hw[] = {
174558a398bSSimon Schubert 	{ ATI_VENDOR_ID, ATI_IXP_200_ID, "ATI IXP 200" },
175558a398bSSimon Schubert 	{ ATI_VENDOR_ID, ATI_IXP_300_ID, "ATI IXP 300" },
176558a398bSSimon Schubert 	{ ATI_VENDOR_ID, ATI_IXP_400_ID, "ATI IXP 400" },
1772ff93029SHasso Tepper 	{ ATI_VENDOR_ID, ATI_IXP_SB600_ID, "ATI IXP SB600" },
178558a398bSSimon Schubert };
179558a398bSSimon Schubert 
180558a398bSSimon Schubert static void atiixp_enable_interrupts(struct atiixp_info *);
181558a398bSSimon Schubert static void atiixp_disable_interrupts(struct atiixp_info *);
182558a398bSSimon Schubert static void atiixp_reset_aclink(struct atiixp_info *);
1832a1ad637SFrançois Tigeot static void atiixp_flush_dma(struct atiixp_chinfo *);
1842a1ad637SFrançois Tigeot static void atiixp_enable_dma(struct atiixp_chinfo *);
1852a1ad637SFrançois Tigeot static void atiixp_disable_dma(struct atiixp_chinfo *);
186558a398bSSimon Schubert 
187558a398bSSimon Schubert static int atiixp_waitready_codec(struct atiixp_info *);
188558a398bSSimon Schubert static int atiixp_rdcd(kobj_t, void *, int);
189558a398bSSimon Schubert static int atiixp_wrcd(kobj_t, void *, int, uint32_t);
190558a398bSSimon Schubert 
191558a398bSSimon Schubert static void  *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *,
192558a398bSSimon Schubert 						struct pcm_channel *, int);
193558a398bSSimon Schubert static int    atiixp_chan_setformat(kobj_t, void *, uint32_t);
1942a1ad637SFrançois Tigeot static uint32_t    atiixp_chan_setspeed(kobj_t, void *, uint32_t);
1952a1ad637SFrançois Tigeot static int         atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t);
1962a1ad637SFrançois Tigeot static uint32_t    atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
197558a398bSSimon Schubert static void   atiixp_buildsgdt(struct atiixp_chinfo *);
198558a398bSSimon Schubert static int    atiixp_chan_trigger(kobj_t, void *, int);
1992a1ad637SFrançois Tigeot static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *);
2002a1ad637SFrançois Tigeot static uint32_t          atiixp_chan_getptr(kobj_t, void *);
201558a398bSSimon Schubert static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *);
202558a398bSSimon Schubert 
203558a398bSSimon Schubert static void atiixp_intr(void *);
204558a398bSSimon Schubert static void atiixp_dma_cb(void *, bus_dma_segment_t *, int, int);
205558a398bSSimon Schubert static void atiixp_chip_pre_init(struct atiixp_info *);
206558a398bSSimon Schubert static void atiixp_chip_post_init(void *);
207558a398bSSimon Schubert static void atiixp_release_resource(struct atiixp_info *);
208558a398bSSimon Schubert static int  atiixp_pci_probe(device_t);
209558a398bSSimon Schubert static int  atiixp_pci_attach(device_t);
210558a398bSSimon Schubert static int  atiixp_pci_detach(device_t);
211558a398bSSimon Schubert static int  atiixp_pci_suspend(device_t);
212558a398bSSimon Schubert static int  atiixp_pci_resume(device_t);
213558a398bSSimon Schubert 
214558a398bSSimon Schubert /*
215558a398bSSimon Schubert  * ATI IXP helper functions
216558a398bSSimon Schubert  */
217558a398bSSimon Schubert static void
atiixp_enable_interrupts(struct atiixp_info * sc)218558a398bSSimon Schubert atiixp_enable_interrupts(struct atiixp_info *sc)
219558a398bSSimon Schubert {
220558a398bSSimon Schubert 	uint32_t value;
221558a398bSSimon Schubert 
222558a398bSSimon Schubert 	/* clear all pending */
223558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
224558a398bSSimon Schubert 
225558a398bSSimon Schubert 	/* enable all relevant interrupt sources we can handle */
226558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_IER);
227558a398bSSimon Schubert 
228558a398bSSimon Schubert 	value |= ATI_REG_IER_IO_STATUS_EN;
229558a398bSSimon Schubert 
230558a398bSSimon Schubert 	/*
231558a398bSSimon Schubert 	 * Disable / ignore internal xrun/spdf interrupt flags
232558a398bSSimon Schubert 	 * since it doesn't interest us (for now).
233558a398bSSimon Schubert 	 */
2342a1ad637SFrançois Tigeot #if 1
2352a1ad637SFrançois Tigeot 	value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN |
2362a1ad637SFrançois Tigeot 	    ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
2372a1ad637SFrançois Tigeot #else
238558a398bSSimon Schubert 	value |= ATI_REG_IER_IN_XRUN_EN;
239558a398bSSimon Schubert 	value |= ATI_REG_IER_OUT_XRUN_EN;
240558a398bSSimon Schubert 
241558a398bSSimon Schubert 	value |= ATI_REG_IER_SPDF_XRUN_EN;
242558a398bSSimon Schubert 	value |= ATI_REG_IER_SPDF_STATUS_EN;
243558a398bSSimon Schubert #endif
244558a398bSSimon Schubert 
245558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_IER, value);
246558a398bSSimon Schubert }
247558a398bSSimon Schubert 
248558a398bSSimon Schubert static void
atiixp_disable_interrupts(struct atiixp_info * sc)249558a398bSSimon Schubert atiixp_disable_interrupts(struct atiixp_info *sc)
250558a398bSSimon Schubert {
251558a398bSSimon Schubert 	/* disable all interrupt sources */
252558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_IER, 0);
253558a398bSSimon Schubert 
254558a398bSSimon Schubert 	/* clear all pending */
255558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
256558a398bSSimon Schubert }
257558a398bSSimon Schubert 
258558a398bSSimon Schubert static void
atiixp_reset_aclink(struct atiixp_info * sc)259558a398bSSimon Schubert atiixp_reset_aclink(struct atiixp_info *sc)
260558a398bSSimon Schubert {
261558a398bSSimon Schubert 	uint32_t value, timeout;
262558a398bSSimon Schubert 
263558a398bSSimon Schubert 	/* if power is down, power it up */
264558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_CMD);
265558a398bSSimon Schubert 	if (value & ATI_REG_CMD_POWERDOWN) {
266558a398bSSimon Schubert 		/* explicitly enable power */
267558a398bSSimon Schubert 		value &= ~ATI_REG_CMD_POWERDOWN;
268558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_CMD, value);
269558a398bSSimon Schubert 
270558a398bSSimon Schubert 		/* have to wait at least 10 usec for it to initialise */
271558a398bSSimon Schubert 		DELAY(20);
2720fdb7d01SSascha Wildner 	}
273558a398bSSimon Schubert 
274558a398bSSimon Schubert 	/* perform a soft reset */
275558a398bSSimon Schubert 	value  = atiixp_rd(sc, ATI_REG_CMD);
276558a398bSSimon Schubert 	value |= ATI_REG_CMD_AC_SOFT_RESET;
277558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_CMD, value);
278558a398bSSimon Schubert 
279558a398bSSimon Schubert 	/* need to read the CMD reg and wait aprox. 10 usec to init */
280558a398bSSimon Schubert 	value  = atiixp_rd(sc, ATI_REG_CMD);
281558a398bSSimon Schubert 	DELAY(20);
282558a398bSSimon Schubert 
283558a398bSSimon Schubert 	/* clear soft reset flag again */
284558a398bSSimon Schubert 	value  = atiixp_rd(sc, ATI_REG_CMD);
285558a398bSSimon Schubert 	value &= ~ATI_REG_CMD_AC_SOFT_RESET;
286558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_CMD, value);
287558a398bSSimon Schubert 
288558a398bSSimon Schubert 	/* check if the ac-link is working; reset device otherwise */
289558a398bSSimon Schubert 	timeout = 10;
290558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_CMD);
2912a1ad637SFrançois Tigeot 	while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) && --timeout) {
292558a398bSSimon Schubert #if 0
293558a398bSSimon Schubert 		device_printf(sc->dev, "not up; resetting aclink hardware\n");
294558a398bSSimon Schubert #endif
295558a398bSSimon Schubert 
296558a398bSSimon Schubert 		/* dip aclink reset but keep the acsync */
297558a398bSSimon Schubert 		value &= ~ATI_REG_CMD_AC_RESET;
298558a398bSSimon Schubert 		value |=  ATI_REG_CMD_AC_SYNC;
299558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_CMD, value);
300558a398bSSimon Schubert 
301558a398bSSimon Schubert 		/* need to read CMD again and wait again (clocking in issue?) */
302558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_CMD);
303558a398bSSimon Schubert 		DELAY(20);
304558a398bSSimon Schubert 
305558a398bSSimon Schubert 		/* assert aclink reset again */
306558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_CMD);
307558a398bSSimon Schubert 		value |=  ATI_REG_CMD_AC_RESET;
308558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_CMD, value);
309558a398bSSimon Schubert 
310558a398bSSimon Schubert 		/* check if its active now */
311558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_CMD);
3120fdb7d01SSascha Wildner 	}
313558a398bSSimon Schubert 
314558a398bSSimon Schubert 	if (timeout == 0)
315558a398bSSimon Schubert 		device_printf(sc->dev, "giving up aclink reset\n");
316558a398bSSimon Schubert #if 0
317558a398bSSimon Schubert 	if (timeout != 10)
318558a398bSSimon Schubert 		device_printf(sc->dev, "aclink hardware reset successful\n");
319558a398bSSimon Schubert #endif
320558a398bSSimon Schubert 
321558a398bSSimon Schubert 	/* assert reset and sync for safety */
322558a398bSSimon Schubert 	value  = atiixp_rd(sc, ATI_REG_CMD);
323558a398bSSimon Schubert 	value |= ATI_REG_CMD_AC_SYNC | ATI_REG_CMD_AC_RESET;
324558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_CMD, value);
325558a398bSSimon Schubert }
326558a398bSSimon Schubert 
327558a398bSSimon Schubert static void
atiixp_flush_dma(struct atiixp_chinfo * ch)3282a1ad637SFrançois Tigeot atiixp_flush_dma(struct atiixp_chinfo *ch)
329558a398bSSimon Schubert {
3302a1ad637SFrançois Tigeot 	atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit);
331558a398bSSimon Schubert }
332558a398bSSimon Schubert 
333558a398bSSimon Schubert static void
atiixp_enable_dma(struct atiixp_chinfo * ch)3342a1ad637SFrançois Tigeot atiixp_enable_dma(struct atiixp_chinfo *ch)
335558a398bSSimon Schubert {
336558a398bSSimon Schubert 	uint32_t value;
337558a398bSSimon Schubert 
3382a1ad637SFrançois Tigeot 	value = atiixp_rd(ch->parent, ATI_REG_CMD);
339558a398bSSimon Schubert 	if (!(value & ch->enable_bit)) {
340558a398bSSimon Schubert 		value |= ch->enable_bit;
3412a1ad637SFrançois Tigeot 		atiixp_wr(ch->parent, ATI_REG_CMD, value);
342558a398bSSimon Schubert 	}
343558a398bSSimon Schubert }
344558a398bSSimon Schubert 
345558a398bSSimon Schubert static void
atiixp_disable_dma(struct atiixp_chinfo * ch)3462a1ad637SFrançois Tigeot atiixp_disable_dma(struct atiixp_chinfo *ch)
347558a398bSSimon Schubert {
348558a398bSSimon Schubert 	uint32_t value;
349558a398bSSimon Schubert 
3502a1ad637SFrançois Tigeot 	value = atiixp_rd(ch->parent, ATI_REG_CMD);
351558a398bSSimon Schubert 	if (value & ch->enable_bit) {
352558a398bSSimon Schubert 		value &= ~ch->enable_bit;
3532a1ad637SFrançois Tigeot 		atiixp_wr(ch->parent, ATI_REG_CMD, value);
354558a398bSSimon Schubert 	}
355558a398bSSimon Schubert }
356558a398bSSimon Schubert 
357558a398bSSimon Schubert /*
358558a398bSSimon Schubert  * AC97 interface
359558a398bSSimon Schubert  */
360558a398bSSimon Schubert static int
atiixp_waitready_codec(struct atiixp_info * sc)361558a398bSSimon Schubert atiixp_waitready_codec(struct atiixp_info *sc)
362558a398bSSimon Schubert {
363558a398bSSimon Schubert 	int timeout = 500;
364558a398bSSimon Schubert 
365558a398bSSimon Schubert 	do {
366558a398bSSimon Schubert 		if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
367558a398bSSimon Schubert 		    ATI_REG_PHYS_OUT_ADDR_EN) == 0)
3682a1ad637SFrançois Tigeot 			return (0);
369558a398bSSimon Schubert 		DELAY(1);
3702a1ad637SFrançois Tigeot 	} while (--timeout);
371558a398bSSimon Schubert 
3722a1ad637SFrançois Tigeot 	return (-1);
373558a398bSSimon Schubert }
374558a398bSSimon Schubert 
375558a398bSSimon Schubert static int
atiixp_rdcd(kobj_t obj,void * devinfo,int reg)376558a398bSSimon Schubert atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
377558a398bSSimon Schubert {
378558a398bSSimon Schubert 	struct atiixp_info *sc = devinfo;
379558a398bSSimon Schubert 	uint32_t data;
380558a398bSSimon Schubert 	int timeout;
381558a398bSSimon Schubert 
382558a398bSSimon Schubert 	if (atiixp_waitready_codec(sc))
3832a1ad637SFrançois Tigeot 		return (-1);
384558a398bSSimon Schubert 
385558a398bSSimon Schubert 	data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
3862a1ad637SFrançois Tigeot 	    ATI_REG_PHYS_OUT_ADDR_EN | ATI_REG_PHYS_OUT_RW | sc->codec_idx;
387558a398bSSimon Schubert 
388558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
389558a398bSSimon Schubert 
390558a398bSSimon Schubert 	if (atiixp_waitready_codec(sc))
3912a1ad637SFrançois Tigeot 		return (-1);
392558a398bSSimon Schubert 
393558a398bSSimon Schubert 	timeout = 500;
394558a398bSSimon Schubert 	do {
395558a398bSSimon Schubert 		data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR);
396558a398bSSimon Schubert 		if (data & ATI_REG_PHYS_IN_READ_FLAG)
3972a1ad637SFrançois Tigeot 			return (data >> ATI_REG_PHYS_IN_DATA_SHIFT);
398558a398bSSimon Schubert 		DELAY(1);
3992a1ad637SFrançois Tigeot 	} while (--timeout);
400558a398bSSimon Schubert 
401558a398bSSimon Schubert 	if (reg < 0x7c)
402558a398bSSimon Schubert 		device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg);
403558a398bSSimon Schubert 
4042a1ad637SFrançois Tigeot 	return (-1);
405558a398bSSimon Schubert }
406558a398bSSimon Schubert 
407558a398bSSimon Schubert static int
atiixp_wrcd(kobj_t obj,void * devinfo,int reg,uint32_t data)408558a398bSSimon Schubert atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
409558a398bSSimon Schubert {
410558a398bSSimon Schubert 	struct atiixp_info *sc = devinfo;
411558a398bSSimon Schubert 
412558a398bSSimon Schubert 	if (atiixp_waitready_codec(sc))
4132a1ad637SFrançois Tigeot 		return (-1);
414558a398bSSimon Schubert 
415558a398bSSimon Schubert 	data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
416558a398bSSimon Schubert 	    (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
417558a398bSSimon Schubert 	    ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
418558a398bSSimon Schubert 
419558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
420558a398bSSimon Schubert 
4212a1ad637SFrançois Tigeot 	return (0);
422558a398bSSimon Schubert }
423558a398bSSimon Schubert 
424558a398bSSimon Schubert static kobj_method_t atiixp_ac97_methods[] = {
425558a398bSSimon Schubert 	KOBJMETHOD(ac97_read,		atiixp_rdcd),
426558a398bSSimon Schubert 	KOBJMETHOD(ac97_write,		atiixp_wrcd),
4277774cda2SSascha Wildner 	KOBJMETHOD_END
428558a398bSSimon Schubert };
429558a398bSSimon Schubert AC97_DECLARE(atiixp_ac97);
430558a398bSSimon Schubert 
431558a398bSSimon Schubert /*
432558a398bSSimon Schubert  * Playback / Record channel interface
433558a398bSSimon Schubert  */
434558a398bSSimon Schubert static void *
atiixp_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)435558a398bSSimon Schubert atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
436558a398bSSimon Schubert 					struct pcm_channel *c, int dir)
437558a398bSSimon Schubert {
438558a398bSSimon Schubert 	struct atiixp_info *sc = devinfo;
439558a398bSSimon Schubert 	struct atiixp_chinfo *ch;
440558a398bSSimon Schubert 	int num;
441558a398bSSimon Schubert 
442558a398bSSimon Schubert 	atiixp_lock(sc);
443558a398bSSimon Schubert 
444558a398bSSimon Schubert 	if (dir == PCMDIR_PLAY) {
445558a398bSSimon Schubert 		ch = &sc->pch;
446558a398bSSimon Schubert 		ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR;
447558a398bSSimon Schubert 		ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN;
448558a398bSSimon Schubert 		ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH;
4492a1ad637SFrançois Tigeot 		ch->dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
450558a398bSSimon Schubert 		/* Native 32bit playback working properly */
451558a398bSSimon Schubert 		ch->caps_32bit = 1;
452558a398bSSimon Schubert 	} else {
453558a398bSSimon Schubert 		ch = &sc->rch;
454558a398bSSimon Schubert 		ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
455558a398bSSimon Schubert 		ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
456558a398bSSimon Schubert 		ch->flush_bit = ATI_REG_FIFO_IN_FLUSH;
4572a1ad637SFrançois Tigeot 		ch->dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
458558a398bSSimon Schubert 		/* XXX Native 32bit recording appear to be broken */
459558a398bSSimon Schubert 		ch->caps_32bit = 1;
460558a398bSSimon Schubert 	}
461558a398bSSimon Schubert 
462558a398bSSimon Schubert 	ch->buffer = b;
463558a398bSSimon Schubert 	ch->parent = sc;
464558a398bSSimon Schubert 	ch->channel = c;
465558a398bSSimon Schubert 	ch->dir = dir;
4662a1ad637SFrançois Tigeot 	ch->blkcnt = sc->blkcnt;
4672a1ad637SFrançois Tigeot 	ch->blksz = sc->bufsz / ch->blkcnt;
468558a398bSSimon Schubert 
469558a398bSSimon Schubert 	atiixp_unlock(sc);
470558a398bSSimon Schubert 
4712a1ad637SFrançois Tigeot 	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) == -1)
4722a1ad637SFrançois Tigeot 		return (NULL);
473558a398bSSimon Schubert 
474558a398bSSimon Schubert 	atiixp_lock(sc);
475558a398bSSimon Schubert 	num = sc->registered_channels++;
4762a1ad637SFrançois Tigeot 	ch->sgd_table = &sc->sgd_table[num * ATI_IXP_DMA_CHSEGS_MAX];
4772a1ad637SFrançois Tigeot 	ch->sgd_addr = sc->sgd_addr + (num * ATI_IXP_DMA_CHSEGS_MAX *
4782a1ad637SFrançois Tigeot 	    sizeof(struct atiixp_dma_op));
4792a1ad637SFrançois Tigeot 	atiixp_disable_dma(ch);
480558a398bSSimon Schubert 	atiixp_unlock(sc);
481558a398bSSimon Schubert 
4822a1ad637SFrançois Tigeot 	return (ch);
483558a398bSSimon Schubert }
484558a398bSSimon Schubert 
485558a398bSSimon Schubert static int
atiixp_chan_setformat(kobj_t obj,void * data,uint32_t format)486558a398bSSimon Schubert atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
487558a398bSSimon Schubert {
488558a398bSSimon Schubert 	struct atiixp_chinfo *ch = data;
489558a398bSSimon Schubert 	struct atiixp_info *sc = ch->parent;
490558a398bSSimon Schubert 	uint32_t value;
491558a398bSSimon Schubert 
492558a398bSSimon Schubert 	atiixp_lock(sc);
493558a398bSSimon Schubert 	if (ch->dir == PCMDIR_REC) {
494558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_CMD);
495558a398bSSimon Schubert 		value &= ~ATI_REG_CMD_INTERLEAVE_IN;
496558a398bSSimon Schubert 		if ((format & AFMT_32BIT) == 0)
497558a398bSSimon Schubert 			value |= ATI_REG_CMD_INTERLEAVE_IN;
498558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_CMD, value);
499558a398bSSimon Schubert 	} else {
500558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_OUT_DMA_SLOT);
501558a398bSSimon Schubert 		value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
502558a398bSSimon Schubert 		/* We do not have support for more than 2 channels, _yet_. */
503558a398bSSimon Schubert 		value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
504558a398bSSimon Schubert 		    ATI_REG_OUT_DMA_SLOT_BIT(4);
505558a398bSSimon Schubert 		value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
506558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
507558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_CMD);
508558a398bSSimon Schubert 		value &= ~ATI_REG_CMD_INTERLEAVE_OUT;
509558a398bSSimon Schubert 		if ((format & AFMT_32BIT) == 0)
510558a398bSSimon Schubert 			value |= ATI_REG_CMD_INTERLEAVE_OUT;
511558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_CMD, value);
512558a398bSSimon Schubert 		value = atiixp_rd(sc, ATI_REG_6CH_REORDER);
513558a398bSSimon Schubert 		value &= ~ATI_REG_6CH_REORDER_EN;
514558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_6CH_REORDER, value);
515558a398bSSimon Schubert 	}
516558a398bSSimon Schubert 	ch->fmt = format;
517558a398bSSimon Schubert 	atiixp_unlock(sc);
518558a398bSSimon Schubert 
5192a1ad637SFrançois Tigeot 	return (0);
520558a398bSSimon Schubert }
521558a398bSSimon Schubert 
5222a1ad637SFrançois Tigeot static uint32_t
atiixp_chan_setspeed(kobj_t obj,void * data,uint32_t spd)523558a398bSSimon Schubert atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd)
524558a398bSSimon Schubert {
525558a398bSSimon Schubert 	/* XXX We're supposed to do VRA/DRA processing right here */
5262a1ad637SFrançois Tigeot 	return (ATI_IXP_BASE_RATE);
527558a398bSSimon Schubert }
528558a398bSSimon Schubert 
529558a398bSSimon Schubert static int
atiixp_chan_setfragments(kobj_t obj,void * data,uint32_t blksz,uint32_t blkcnt)5302a1ad637SFrançois Tigeot atiixp_chan_setfragments(kobj_t obj, void *data,
5312a1ad637SFrançois Tigeot 					uint32_t blksz, uint32_t blkcnt)
5322a1ad637SFrançois Tigeot {
5332a1ad637SFrançois Tigeot 	struct atiixp_chinfo *ch = data;
5342a1ad637SFrançois Tigeot 	struct atiixp_info *sc = ch->parent;
5352a1ad637SFrançois Tigeot 
5362a1ad637SFrançois Tigeot 	blksz &= ATI_IXP_BLK_ALIGN;
5372a1ad637SFrançois Tigeot 
5382a1ad637SFrançois Tigeot 	if (blksz > (sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN))
5392a1ad637SFrançois Tigeot 		blksz = sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN;
5402a1ad637SFrançois Tigeot 	if (blksz < ATI_IXP_BLK_MIN)
5412a1ad637SFrançois Tigeot 		blksz = ATI_IXP_BLK_MIN;
5422a1ad637SFrançois Tigeot 	if (blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
5432a1ad637SFrançois Tigeot 		blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
5442a1ad637SFrançois Tigeot 	if (blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
5452a1ad637SFrançois Tigeot 		blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
5462a1ad637SFrançois Tigeot 
5472a1ad637SFrançois Tigeot 	while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) {
5482a1ad637SFrançois Tigeot 		if ((blkcnt >> 1) >= ATI_IXP_DMA_CHSEGS_MIN)
5492a1ad637SFrançois Tigeot 			blkcnt >>= 1;
5502a1ad637SFrançois Tigeot 		else if ((blksz >> 1) >= ATI_IXP_BLK_MIN)
5512a1ad637SFrançois Tigeot 			blksz >>= 1;
5522a1ad637SFrançois Tigeot 		else
5532a1ad637SFrançois Tigeot 			break;
5542a1ad637SFrançois Tigeot 	}
5552a1ad637SFrançois Tigeot 
5562a1ad637SFrançois Tigeot 	if ((sndbuf_getblksz(ch->buffer) != blksz ||
5572a1ad637SFrançois Tigeot 	    sndbuf_getblkcnt(ch->buffer) != blkcnt) &&
5582a1ad637SFrançois Tigeot 	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
5592a1ad637SFrançois Tigeot 		device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
5602a1ad637SFrançois Tigeot 		    __func__, blksz, blkcnt);
5612a1ad637SFrançois Tigeot 
5622a1ad637SFrançois Tigeot 	ch->blksz = sndbuf_getblksz(ch->buffer);
5632a1ad637SFrançois Tigeot 	ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
5642a1ad637SFrançois Tigeot 
5652a1ad637SFrançois Tigeot 	return (0);
5662a1ad637SFrançois Tigeot }
5672a1ad637SFrançois Tigeot 
5682a1ad637SFrançois Tigeot static uint32_t
atiixp_chan_setblocksize(kobj_t obj,void * data,uint32_t blksz)569558a398bSSimon Schubert atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
570558a398bSSimon Schubert {
571558a398bSSimon Schubert 	struct atiixp_chinfo *ch = data;
572558a398bSSimon Schubert 	struct atiixp_info *sc = ch->parent;
573558a398bSSimon Schubert 
5742a1ad637SFrançois Tigeot 	atiixp_chan_setfragments(obj, data, blksz, sc->blkcnt);
575558a398bSSimon Schubert 
5762a1ad637SFrançois Tigeot 	return (ch->blksz);
577558a398bSSimon Schubert }
578558a398bSSimon Schubert 
579558a398bSSimon Schubert static void
atiixp_buildsgdt(struct atiixp_chinfo * ch)580558a398bSSimon Schubert atiixp_buildsgdt(struct atiixp_chinfo *ch)
581558a398bSSimon Schubert {
5822a1ad637SFrançois Tigeot 	struct atiixp_info *sc = ch->parent;
5832a1ad637SFrançois Tigeot 	uint32_t addr, blksz, blkcnt;
584558a398bSSimon Schubert 	int i;
585558a398bSSimon Schubert 
586558a398bSSimon Schubert 	addr = sndbuf_getbufaddr(ch->buffer);
587558a398bSSimon Schubert 
5882a1ad637SFrançois Tigeot 	if (sc->polling != 0) {
5892a1ad637SFrançois Tigeot 		blksz = ch->blksz * ch->blkcnt;
5902a1ad637SFrançois Tigeot 		blkcnt = 1;
5912a1ad637SFrançois Tigeot 	} else {
5922a1ad637SFrançois Tigeot 		blksz = ch->blksz;
5932a1ad637SFrançois Tigeot 		blkcnt = ch->blkcnt;
5942a1ad637SFrançois Tigeot 	}
5952a1ad637SFrançois Tigeot 
5962a1ad637SFrançois Tigeot 	for (i = 0; i < blkcnt; i++) {
597558a398bSSimon Schubert 		ch->sgd_table[i].addr = htole32(addr + (i * blksz));
598558a398bSSimon Schubert 		ch->sgd_table[i].status = htole16(0);
599558a398bSSimon Schubert 		ch->sgd_table[i].size = htole16(blksz >> 2);
600558a398bSSimon Schubert 		ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
6012a1ad637SFrançois Tigeot 		    (((i + 1) % blkcnt) * sizeof(struct atiixp_dma_op)));
602558a398bSSimon Schubert 	}
603558a398bSSimon Schubert }
604558a398bSSimon Schubert 
6052a1ad637SFrançois Tigeot static __inline uint32_t
atiixp_dmapos(struct atiixp_chinfo * ch)6062a1ad637SFrançois Tigeot atiixp_dmapos(struct atiixp_chinfo *ch)
6072a1ad637SFrançois Tigeot {
6082a1ad637SFrançois Tigeot 	struct atiixp_info *sc = ch->parent;
6092a1ad637SFrançois Tigeot 	uint32_t reg, addr, sz, retry;
6102a1ad637SFrançois Tigeot 	volatile uint32_t ptr;
6112a1ad637SFrançois Tigeot 
6122a1ad637SFrançois Tigeot 	reg = ch->dt_cur_bit;
6132a1ad637SFrançois Tigeot 	addr = sndbuf_getbufaddr(ch->buffer);
6142a1ad637SFrançois Tigeot 	sz = ch->blkcnt * ch->blksz;
6152a1ad637SFrançois Tigeot 	retry = ATI_IXP_DMA_RETRY_MAX;
6162a1ad637SFrançois Tigeot 
6172a1ad637SFrançois Tigeot 	do {
6182a1ad637SFrançois Tigeot 		ptr = atiixp_rd(sc, reg);
6192a1ad637SFrançois Tigeot 		if (ptr < addr)
6202a1ad637SFrançois Tigeot 			continue;
6212a1ad637SFrançois Tigeot 		ptr -= addr;
6222a1ad637SFrançois Tigeot 		if (ptr < sz) {
6232a1ad637SFrançois Tigeot #if 0
6242a1ad637SFrançois Tigeot #ifdef ATI_IXP_DEBUG
6252a1ad637SFrançois Tigeot 			if ((ptr & ~(ch->blksz - 1)) != ch->ptr) {
6262a1ad637SFrançois Tigeot 				uint32_t delta;
6272a1ad637SFrançois Tigeot 
6282a1ad637SFrançois Tigeot 				delta = (sz + ptr - ch->prevptr) % sz;
6292a1ad637SFrançois Tigeot #ifndef ATI_IXP_DEBUG_VERBOSE
6302a1ad637SFrançois Tigeot 				if (delta < ch->blksz)
6312a1ad637SFrançois Tigeot #endif
6322a1ad637SFrançois Tigeot 					device_printf(sc->dev,
6332a1ad637SFrançois Tigeot 						"PCMDIR_%s: incoherent DMA "
6342a1ad637SFrançois Tigeot 						"prevptr=%u ptr=%u "
6352a1ad637SFrançois Tigeot 						"ptr=%u blkcnt=%u "
6362a1ad637SFrançois Tigeot 						"[delta=%u != blksz=%u] "
6372a1ad637SFrançois Tigeot 						"(%s)\n",
6382a1ad637SFrançois Tigeot 						(ch->dir == PCMDIR_PLAY) ?
6392a1ad637SFrançois Tigeot 						"PLAY" : "REC",
6402a1ad637SFrançois Tigeot 						ch->prevptr, ptr,
6412a1ad637SFrançois Tigeot 						ch->ptr, ch->blkcnt,
6422a1ad637SFrançois Tigeot 						delta, ch->blksz,
6432a1ad637SFrançois Tigeot 						(delta < ch->blksz) ?
6442a1ad637SFrançois Tigeot 						"OVERLAPPED!" : "Ok");
6452a1ad637SFrançois Tigeot 				ch->ptr = ptr & ~(ch->blksz - 1);
6462a1ad637SFrançois Tigeot 			}
6472a1ad637SFrançois Tigeot 			ch->prevptr = ptr;
6482a1ad637SFrançois Tigeot #endif
6492a1ad637SFrançois Tigeot #endif
6502a1ad637SFrançois Tigeot 			return (ptr);
6512a1ad637SFrançois Tigeot 		}
6522a1ad637SFrançois Tigeot 	} while (--retry);
6532a1ad637SFrançois Tigeot 
6542a1ad637SFrançois Tigeot 	device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
6552a1ad637SFrançois Tigeot 	    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
6562a1ad637SFrançois Tigeot 
6572a1ad637SFrançois Tigeot 	return (0);
6582a1ad637SFrançois Tigeot }
6592a1ad637SFrançois Tigeot 
6602a1ad637SFrançois Tigeot static __inline int
atiixp_poll_channel(struct atiixp_chinfo * ch)6612a1ad637SFrançois Tigeot atiixp_poll_channel(struct atiixp_chinfo *ch)
6622a1ad637SFrançois Tigeot {
6632a1ad637SFrançois Tigeot 	uint32_t sz, delta;
6642a1ad637SFrançois Tigeot 	volatile uint32_t ptr;
6652a1ad637SFrançois Tigeot 
6662a1ad637SFrançois Tigeot 	if (!(ch->flags & ATI_IXP_CHN_RUNNING))
6672a1ad637SFrançois Tigeot 		return (0);
6682a1ad637SFrançois Tigeot 
6692a1ad637SFrançois Tigeot 	sz = ch->blksz * ch->blkcnt;
6702a1ad637SFrançois Tigeot 	ptr = atiixp_dmapos(ch);
6712a1ad637SFrançois Tigeot 	ch->ptr = ptr;
6722a1ad637SFrançois Tigeot 	ptr %= sz;
6732a1ad637SFrançois Tigeot 	ptr &= ~(ch->blksz - 1);
6742a1ad637SFrançois Tigeot 	delta = (sz + ptr - ch->prevptr) % sz;
6752a1ad637SFrançois Tigeot 
6762a1ad637SFrançois Tigeot 	if (delta < ch->blksz)
6772a1ad637SFrançois Tigeot 		return (0);
6782a1ad637SFrançois Tigeot 
6792a1ad637SFrançois Tigeot 	ch->prevptr = ptr;
6802a1ad637SFrançois Tigeot 
6812a1ad637SFrançois Tigeot 	return (1);
6822a1ad637SFrançois Tigeot }
6832a1ad637SFrançois Tigeot 
6842a1ad637SFrançois Tigeot #define atiixp_chan_active(sc)	(((sc)->pch.flags | (sc)->rch.flags) &	\
6852a1ad637SFrançois Tigeot 				 ATI_IXP_CHN_RUNNING)
6862a1ad637SFrançois Tigeot 
6872a1ad637SFrançois Tigeot static void
atiixp_poll_callback(void * arg)6882a1ad637SFrançois Tigeot atiixp_poll_callback(void *arg)
6892a1ad637SFrançois Tigeot {
6902a1ad637SFrançois Tigeot 	struct atiixp_info *sc = arg;
6912a1ad637SFrançois Tigeot 	uint32_t trigger = 0;
6922a1ad637SFrançois Tigeot 
6932a1ad637SFrançois Tigeot 	if (sc == NULL)
6942a1ad637SFrançois Tigeot 		return;
6952a1ad637SFrançois Tigeot 
6962a1ad637SFrançois Tigeot 	atiixp_lock(sc);
6972a1ad637SFrançois Tigeot 	if (sc->polling == 0 || atiixp_chan_active(sc) == 0) {
6982a1ad637SFrançois Tigeot 		atiixp_unlock(sc);
6992a1ad637SFrançois Tigeot 		return;
7002a1ad637SFrançois Tigeot 	}
7012a1ad637SFrançois Tigeot 
7022a1ad637SFrançois Tigeot 	trigger |= (atiixp_poll_channel(&sc->pch) != 0) ? 1 : 0;
7032a1ad637SFrançois Tigeot 	trigger |= (atiixp_poll_channel(&sc->rch) != 0) ? 2 : 0;
7042a1ad637SFrançois Tigeot 
7052a1ad637SFrançois Tigeot 	/* XXX */
7062a1ad637SFrançois Tigeot 	callout_reset(&sc->poll_timer, 1/*sc->poll_ticks*/,
7072a1ad637SFrançois Tigeot 	    atiixp_poll_callback, sc);
7082a1ad637SFrançois Tigeot 
7092a1ad637SFrançois Tigeot 	atiixp_unlock(sc);
7102a1ad637SFrançois Tigeot 
7112a1ad637SFrançois Tigeot 	if (trigger & 1)
7122a1ad637SFrançois Tigeot 		chn_intr(sc->pch.channel);
7132a1ad637SFrançois Tigeot 	if (trigger & 2)
7142a1ad637SFrançois Tigeot 		chn_intr(sc->rch.channel);
7152a1ad637SFrançois Tigeot }
7162a1ad637SFrançois Tigeot 
717558a398bSSimon Schubert static int
atiixp_chan_trigger(kobj_t obj,void * data,int go)718558a398bSSimon Schubert atiixp_chan_trigger(kobj_t obj, void *data, int go)
719558a398bSSimon Schubert {
720558a398bSSimon Schubert 	struct atiixp_chinfo *ch = data;
721558a398bSSimon Schubert 	struct atiixp_info *sc = ch->parent;
722558a398bSSimon Schubert 	uint32_t value;
7232a1ad637SFrançois Tigeot 	int pollticks;
7242a1ad637SFrançois Tigeot 
7252a1ad637SFrançois Tigeot 	if (!PCMTRIG_COMMON(go))
7262a1ad637SFrançois Tigeot 		return (0);
727558a398bSSimon Schubert 
728558a398bSSimon Schubert 	atiixp_lock(sc);
729558a398bSSimon Schubert 
730558a398bSSimon Schubert 	switch (go) {
731558a398bSSimon Schubert 	case PCMTRIG_START:
7322a1ad637SFrançois Tigeot 		atiixp_flush_dma(ch);
733558a398bSSimon Schubert 		atiixp_buildsgdt(ch);
734558a398bSSimon Schubert 		atiixp_wr(sc, ch->linkptr_bit, 0);
7352a1ad637SFrançois Tigeot 		atiixp_enable_dma(ch);
736558a398bSSimon Schubert 		atiixp_wr(sc, ch->linkptr_bit,
737558a398bSSimon Schubert 		    (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
7382a1ad637SFrançois Tigeot 		if (sc->polling != 0) {
7392a1ad637SFrançois Tigeot 			ch->ptr = 0;
7402a1ad637SFrançois Tigeot 			ch->prevptr = 0;
7412a1ad637SFrançois Tigeot 			pollticks = ((uint64_t)hz * ch->blksz) /
7422a1ad637SFrançois Tigeot 			    ((uint64_t)sndbuf_getalign(ch->buffer) *
7432a1ad637SFrançois Tigeot 			    sndbuf_getspd(ch->buffer));
7442a1ad637SFrançois Tigeot 			pollticks >>= 2;
7452a1ad637SFrançois Tigeot 			if (pollticks > hz)
7462a1ad637SFrançois Tigeot 				pollticks = hz;
7472a1ad637SFrançois Tigeot 			if (pollticks < 1)
7482a1ad637SFrançois Tigeot 				pollticks = 1;
7492a1ad637SFrançois Tigeot 			if (atiixp_chan_active(sc) == 0 ||
7502a1ad637SFrançois Tigeot 			    pollticks < sc->poll_ticks) {
7512a1ad637SFrançois Tigeot 			    	if (bootverbose) {
7522a1ad637SFrançois Tigeot 					if (atiixp_chan_active(sc) == 0)
7532a1ad637SFrançois Tigeot 						device_printf(sc->dev,
7542a1ad637SFrançois Tigeot 						    "%s: pollticks=%d\n",
7552a1ad637SFrançois Tigeot 						    __func__, pollticks);
7562a1ad637SFrançois Tigeot 					else
7572a1ad637SFrançois Tigeot 						device_printf(sc->dev,
7582a1ad637SFrançois Tigeot 						    "%s: pollticks %d -> %d\n",
7592a1ad637SFrançois Tigeot 						    __func__, sc->poll_ticks,
7602a1ad637SFrançois Tigeot 						    pollticks);
7612a1ad637SFrançois Tigeot 				}
7622a1ad637SFrançois Tigeot 				sc->poll_ticks = pollticks;
7632a1ad637SFrançois Tigeot 				callout_reset(&sc->poll_timer, 1,
7642a1ad637SFrançois Tigeot 				    atiixp_poll_callback, sc);
7652a1ad637SFrançois Tigeot 			}
7662a1ad637SFrançois Tigeot 		}
7672a1ad637SFrançois Tigeot 		ch->flags |= ATI_IXP_CHN_RUNNING;
768558a398bSSimon Schubert 		break;
769558a398bSSimon Schubert 	case PCMTRIG_STOP:
770558a398bSSimon Schubert 	case PCMTRIG_ABORT:
7712a1ad637SFrançois Tigeot 		atiixp_disable_dma(ch);
7722a1ad637SFrançois Tigeot 		atiixp_flush_dma(ch);
7732a1ad637SFrançois Tigeot 		ch->flags &= ~ATI_IXP_CHN_RUNNING;
7742a1ad637SFrançois Tigeot 		if (sc->polling != 0) {
7752a1ad637SFrançois Tigeot 			if (atiixp_chan_active(sc) == 0) {
7762a1ad637SFrançois Tigeot 				callout_stop(&sc->poll_timer);
7772a1ad637SFrançois Tigeot 				sc->poll_ticks = 1;
7782a1ad637SFrançois Tigeot 			} else {
7792a1ad637SFrançois Tigeot 				if (sc->pch.flags & ATI_IXP_CHN_RUNNING)
7802a1ad637SFrançois Tigeot 					ch = &sc->pch;
7812a1ad637SFrançois Tigeot 				else
7822a1ad637SFrançois Tigeot 					ch = &sc->rch;
7832a1ad637SFrançois Tigeot 				pollticks = ((uint64_t)hz * ch->blksz) /
7842a1ad637SFrançois Tigeot 				    ((uint64_t)sndbuf_getalign(ch->buffer) *
7852a1ad637SFrançois Tigeot 				    sndbuf_getspd(ch->buffer));
7862a1ad637SFrançois Tigeot 				pollticks >>= 2;
7872a1ad637SFrançois Tigeot 				if (pollticks > hz)
7882a1ad637SFrançois Tigeot 					pollticks = hz;
7892a1ad637SFrançois Tigeot 				if (pollticks < 1)
7902a1ad637SFrançois Tigeot 					pollticks = 1;
7912a1ad637SFrançois Tigeot 				if (pollticks > sc->poll_ticks) {
7922a1ad637SFrançois Tigeot 					if (bootverbose)
7932a1ad637SFrançois Tigeot 						device_printf(sc->dev,
7942a1ad637SFrançois Tigeot 						    "%s: pollticks %d -> %d\n",
7952a1ad637SFrançois Tigeot 						    __func__, sc->poll_ticks,
7962a1ad637SFrançois Tigeot 						    pollticks);
7972a1ad637SFrançois Tigeot 					sc->poll_ticks = pollticks;
7982a1ad637SFrançois Tigeot 					callout_reset(&sc->poll_timer,
7992a1ad637SFrançois Tigeot 					    1, atiixp_poll_callback,
8002a1ad637SFrançois Tigeot 					    sc);
8012a1ad637SFrançois Tigeot 				}
8022a1ad637SFrançois Tigeot 			}
8032a1ad637SFrançois Tigeot 		}
804558a398bSSimon Schubert 		break;
805558a398bSSimon Schubert 	default:
806558a398bSSimon Schubert 		atiixp_unlock(sc);
8072a1ad637SFrançois Tigeot 		return (0);
808558a398bSSimon Schubert 		break;
809558a398bSSimon Schubert 	}
810558a398bSSimon Schubert 
811558a398bSSimon Schubert 	/* Update bus busy status */
812558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_IER);
8132a1ad637SFrançois Tigeot 	if (atiixp_rd(sc, ATI_REG_CMD) & (ATI_REG_CMD_SEND_EN |
8142a1ad637SFrançois Tigeot 	    ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN))
815558a398bSSimon Schubert 		value |= ATI_REG_IER_SET_BUS_BUSY;
816558a398bSSimon Schubert 	else
817558a398bSSimon Schubert 		value &= ~ATI_REG_IER_SET_BUS_BUSY;
818558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_IER, value);
819558a398bSSimon Schubert 
820558a398bSSimon Schubert 	atiixp_unlock(sc);
821558a398bSSimon Schubert 
8222a1ad637SFrançois Tigeot 	return (0);
823558a398bSSimon Schubert }
824558a398bSSimon Schubert 
8252a1ad637SFrançois Tigeot static uint32_t
atiixp_chan_getptr(kobj_t obj,void * data)826558a398bSSimon Schubert atiixp_chan_getptr(kobj_t obj, void *data)
827558a398bSSimon Schubert {
828558a398bSSimon Schubert 	struct atiixp_chinfo *ch = data;
829558a398bSSimon Schubert 	struct atiixp_info *sc = ch->parent;
8302a1ad637SFrançois Tigeot 	uint32_t ptr;
831558a398bSSimon Schubert 
832558a398bSSimon Schubert 	atiixp_lock(sc);
8332a1ad637SFrançois Tigeot 	if (sc->polling != 0)
8342a1ad637SFrançois Tigeot 		ptr = ch->ptr;
8352a1ad637SFrançois Tigeot 	else
8362a1ad637SFrançois Tigeot 		ptr = atiixp_dmapos(ch);
837558a398bSSimon Schubert 	atiixp_unlock(sc);
838558a398bSSimon Schubert 
8392a1ad637SFrançois Tigeot 	return (ptr);
840558a398bSSimon Schubert }
841558a398bSSimon Schubert 
842558a398bSSimon Schubert static struct pcmchan_caps *
atiixp_chan_getcaps(kobj_t obj,void * data)843558a398bSSimon Schubert atiixp_chan_getcaps(kobj_t obj, void *data)
844558a398bSSimon Schubert {
845558a398bSSimon Schubert 	struct atiixp_chinfo *ch = data;
846558a398bSSimon Schubert 
847558a398bSSimon Schubert 	if (ch->caps_32bit)
8482a1ad637SFrançois Tigeot 		return (&atiixp_caps_32bit);
8492a1ad637SFrançois Tigeot 	return (&atiixp_caps);
850558a398bSSimon Schubert }
851558a398bSSimon Schubert 
852558a398bSSimon Schubert static kobj_method_t atiixp_chan_methods[] = {
853558a398bSSimon Schubert 	KOBJMETHOD(channel_init,		atiixp_chan_init),
854558a398bSSimon Schubert 	KOBJMETHOD(channel_setformat,		atiixp_chan_setformat),
855558a398bSSimon Schubert 	KOBJMETHOD(channel_setspeed,		atiixp_chan_setspeed),
856558a398bSSimon Schubert 	KOBJMETHOD(channel_setblocksize,	atiixp_chan_setblocksize),
8572a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setfragments,	atiixp_chan_setfragments),
858558a398bSSimon Schubert 	KOBJMETHOD(channel_trigger,		atiixp_chan_trigger),
859558a398bSSimon Schubert 	KOBJMETHOD(channel_getptr,		atiixp_chan_getptr),
860558a398bSSimon Schubert 	KOBJMETHOD(channel_getcaps,		atiixp_chan_getcaps),
8617774cda2SSascha Wildner 	KOBJMETHOD_END
862558a398bSSimon Schubert };
863558a398bSSimon Schubert CHANNEL_DECLARE(atiixp_chan);
864558a398bSSimon Schubert 
865558a398bSSimon Schubert /*
866558a398bSSimon Schubert  * PCI driver interface
867558a398bSSimon Schubert  */
868558a398bSSimon Schubert static void
atiixp_intr(void * p)869558a398bSSimon Schubert atiixp_intr(void *p)
870558a398bSSimon Schubert {
871558a398bSSimon Schubert 	struct atiixp_info *sc = p;
872558a398bSSimon Schubert 	uint32_t status, enable, detected_codecs;
8732a1ad637SFrançois Tigeot 	uint32_t trigger = 0;
874558a398bSSimon Schubert 
875558a398bSSimon Schubert 	atiixp_lock(sc);
8762a1ad637SFrançois Tigeot 	if (sc->polling != 0) {
8772a1ad637SFrançois Tigeot 		atiixp_unlock(sc);
8782a1ad637SFrançois Tigeot 		return;
8792a1ad637SFrançois Tigeot 	}
880558a398bSSimon Schubert 	status = atiixp_rd(sc, ATI_REG_ISR);
881558a398bSSimon Schubert 
882558a398bSSimon Schubert 	if (status == 0) {
883558a398bSSimon Schubert 		atiixp_unlock(sc);
884558a398bSSimon Schubert 		return;
885558a398bSSimon Schubert 	}
886558a398bSSimon Schubert 
8872a1ad637SFrançois Tigeot 	if ((status & ATI_REG_ISR_OUT_STATUS) &&
8882a1ad637SFrançois Tigeot 	    (sc->pch.flags & ATI_IXP_CHN_RUNNING))
8892a1ad637SFrançois Tigeot 		trigger |= 1;
8902a1ad637SFrançois Tigeot 	if ((status & ATI_REG_ISR_IN_STATUS) &&
8912a1ad637SFrançois Tigeot 	    (sc->rch.flags & ATI_IXP_CHN_RUNNING))
8922a1ad637SFrançois Tigeot 		trigger |= 2;
893558a398bSSimon Schubert 
894558a398bSSimon Schubert #if 0
895558a398bSSimon Schubert 	if (status & ATI_REG_ISR_IN_XRUN) {
896558a398bSSimon Schubert 		device_printf(sc->dev,
8972a1ad637SFrançois Tigeot 			"Recieve IN XRUN interrupt\n");
898558a398bSSimon Schubert 	}
899558a398bSSimon Schubert 	if (status & ATI_REG_ISR_OUT_XRUN) {
900558a398bSSimon Schubert 		device_printf(sc->dev,
9012a1ad637SFrançois Tigeot 			"Recieve OUT XRUN interrupt\n");
902558a398bSSimon Schubert 	}
903558a398bSSimon Schubert #endif
904558a398bSSimon Schubert 
905558a398bSSimon Schubert 	if (status & CODEC_CHECK_BITS) {
906558a398bSSimon Schubert 		/* mark missing codecs as not ready */
907558a398bSSimon Schubert 		detected_codecs = status & CODEC_CHECK_BITS;
908558a398bSSimon Schubert 		sc->codec_not_ready_bits |= detected_codecs;
909558a398bSSimon Schubert 
9102a1ad637SFrançois Tigeot 		/* disable detected interrupt sources */
911558a398bSSimon Schubert 		enable  = atiixp_rd(sc, ATI_REG_IER);
912558a398bSSimon Schubert 		enable &= ~detected_codecs;
913558a398bSSimon Schubert 		atiixp_wr(sc, ATI_REG_IER, enable);
9142a1ad637SFrançois Tigeot 		wakeup(sc);
915558a398bSSimon Schubert 	}
916558a398bSSimon Schubert 
917558a398bSSimon Schubert 	/* acknowledge */
918558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_ISR, status);
919558a398bSSimon Schubert 	atiixp_unlock(sc);
9202a1ad637SFrançois Tigeot 
9212a1ad637SFrançois Tigeot 	if (trigger & 1)
9222a1ad637SFrançois Tigeot 		chn_intr(sc->pch.channel);
9232a1ad637SFrançois Tigeot 	if (trigger & 2)
9242a1ad637SFrançois Tigeot 		chn_intr(sc->rch.channel);
925558a398bSSimon Schubert }
926558a398bSSimon Schubert 
927558a398bSSimon Schubert static void
atiixp_dma_cb(void * p,bus_dma_segment_t * bds,int a,int b)928558a398bSSimon Schubert atiixp_dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
929558a398bSSimon Schubert {
930558a398bSSimon Schubert 	struct atiixp_info *sc = (struct atiixp_info *)p;
931558a398bSSimon Schubert 	sc->sgd_addr = bds->ds_addr;
932558a398bSSimon Schubert }
933558a398bSSimon Schubert 
934558a398bSSimon Schubert static void
atiixp_chip_pre_init(struct atiixp_info * sc)935558a398bSSimon Schubert atiixp_chip_pre_init(struct atiixp_info *sc)
936558a398bSSimon Schubert {
937558a398bSSimon Schubert 	uint32_t value;
938558a398bSSimon Schubert 
939558a398bSSimon Schubert 	atiixp_lock(sc);
940558a398bSSimon Schubert 
941558a398bSSimon Schubert 	/* disable interrupts */
942558a398bSSimon Schubert 	atiixp_disable_interrupts(sc);
943558a398bSSimon Schubert 
944558a398bSSimon Schubert 	/* clear all DMA enables (preserving rest of settings) */
945558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_CMD);
946558a398bSSimon Schubert 	value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN |
947558a398bSSimon Schubert 	    ATI_REG_CMD_SPDF_OUT_EN );
948558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_CMD, value);
949558a398bSSimon Schubert 
950558a398bSSimon Schubert 	/* reset aclink */
951558a398bSSimon Schubert 	atiixp_reset_aclink(sc);
952558a398bSSimon Schubert 
953558a398bSSimon Schubert 	sc->codec_not_ready_bits = 0;
954558a398bSSimon Schubert 
955558a398bSSimon Schubert 	/* enable all codecs to interrupt as well as the new frame interrupt */
956558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_IER, CODEC_CHECK_BITS);
957558a398bSSimon Schubert 
958558a398bSSimon Schubert 	atiixp_unlock(sc);
959558a398bSSimon Schubert }
960558a398bSSimon Schubert 
9612a1ad637SFrançois Tigeot static int
sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)9622a1ad637SFrançois Tigeot sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
9632a1ad637SFrançois Tigeot {
9642a1ad637SFrançois Tigeot 	struct atiixp_info *sc;
9652a1ad637SFrançois Tigeot 	device_t dev;
9662a1ad637SFrançois Tigeot 	int err, val;
9672a1ad637SFrançois Tigeot 
9682a1ad637SFrançois Tigeot 	dev = oidp->oid_arg1;
9692a1ad637SFrançois Tigeot 	sc = pcm_getdevinfo(dev);
9702a1ad637SFrançois Tigeot 	if (sc == NULL)
9712a1ad637SFrançois Tigeot 		return (EINVAL);
9722a1ad637SFrançois Tigeot 	atiixp_lock(sc);
9732a1ad637SFrançois Tigeot 	val = sc->polling;
9742a1ad637SFrançois Tigeot 	atiixp_unlock(sc);
9752a1ad637SFrançois Tigeot 	err = sysctl_handle_int(oidp, &val, 0, req);
9762a1ad637SFrançois Tigeot 
9772a1ad637SFrançois Tigeot 	if (err || req->newptr == NULL)
9782a1ad637SFrançois Tigeot 		return (err);
9792a1ad637SFrançois Tigeot 	if (val < 0 || val > 1)
9802a1ad637SFrançois Tigeot 		return (EINVAL);
9812a1ad637SFrançois Tigeot 
9822a1ad637SFrançois Tigeot 	atiixp_lock(sc);
9832a1ad637SFrançois Tigeot 	if (val != sc->polling) {
9842a1ad637SFrançois Tigeot 		if (atiixp_chan_active(sc) != 0)
9852a1ad637SFrançois Tigeot 			err = EBUSY;
9862a1ad637SFrançois Tigeot 		else if (val == 0) {
9872a1ad637SFrançois Tigeot 			atiixp_enable_interrupts(sc);
9882a1ad637SFrançois Tigeot 			sc->polling = 0;
9892a1ad637SFrançois Tigeot 			DELAY(1000);
9902a1ad637SFrançois Tigeot 		} else {
9912a1ad637SFrançois Tigeot 			atiixp_disable_interrupts(sc);
9922a1ad637SFrançois Tigeot 			sc->polling = 1;
9932a1ad637SFrançois Tigeot 			DELAY(1000);
9942a1ad637SFrançois Tigeot 		}
9952a1ad637SFrançois Tigeot 	}
9962a1ad637SFrançois Tigeot 	atiixp_unlock(sc);
9972a1ad637SFrançois Tigeot 
9982a1ad637SFrançois Tigeot 	return (err);
9992a1ad637SFrançois Tigeot }
10002a1ad637SFrançois Tigeot 
1001558a398bSSimon Schubert static void
atiixp_chip_post_init(void * arg)1002558a398bSSimon Schubert atiixp_chip_post_init(void *arg)
1003558a398bSSimon Schubert {
1004558a398bSSimon Schubert 	struct atiixp_info *sc = (struct atiixp_info *)arg;
1005558a398bSSimon Schubert 	uint32_t subdev;
10062a1ad637SFrançois Tigeot 	int i, timeout, found, polling;
1007558a398bSSimon Schubert 	char status[SND_STATUSLEN];
1008558a398bSSimon Schubert 
1009558a398bSSimon Schubert 	atiixp_lock(sc);
1010558a398bSSimon Schubert 
1011558a398bSSimon Schubert 	if (sc->delayed_attach.ich_func) {
1012558a398bSSimon Schubert 		config_intrhook_disestablish(&sc->delayed_attach);
1013558a398bSSimon Schubert 		sc->delayed_attach.ich_func = NULL;
1014558a398bSSimon Schubert 	}
1015558a398bSSimon Schubert 
10162a1ad637SFrançois Tigeot 	polling = sc->polling;
10172a1ad637SFrançois Tigeot 	sc->polling = 0;
10182a1ad637SFrançois Tigeot 
10192a1ad637SFrançois Tigeot 	timeout = 10;
10202a1ad637SFrançois Tigeot 	if (sc->codec_not_ready_bits == 0) {
1021558a398bSSimon Schubert 		/* wait for the interrupts to happen */
10222a1ad637SFrançois Tigeot 		do {
102367931cc4SFrançois Tigeot 			lksleep(sc, sc->lock, 0, "ixpslp", max(hz / 10, 1));
10242a1ad637SFrançois Tigeot 			if (sc->codec_not_ready_bits != 0)
1025558a398bSSimon Schubert 				break;
10262a1ad637SFrançois Tigeot 		} while (--timeout);
1027558a398bSSimon Schubert 	}
1028558a398bSSimon Schubert 
10292a1ad637SFrançois Tigeot 	sc->polling = polling;
1030558a398bSSimon Schubert 	atiixp_disable_interrupts(sc);
1031558a398bSSimon Schubert 
10322a1ad637SFrançois Tigeot 	if (sc->codec_not_ready_bits == 0 && timeout == 0) {
1033558a398bSSimon Schubert 		device_printf(sc->dev,
1034558a398bSSimon Schubert 			"WARNING: timeout during codec detection; "
1035558a398bSSimon Schubert 			"codecs might be present but haven't interrupted\n");
1036558a398bSSimon Schubert 		atiixp_unlock(sc);
1037558a398bSSimon Schubert 		goto postinitbad;
1038558a398bSSimon Schubert 	}
1039558a398bSSimon Schubert 
1040558a398bSSimon Schubert 	found = 0;
1041558a398bSSimon Schubert 
1042558a398bSSimon Schubert 	/*
1043558a398bSSimon Schubert 	 * ATI IXP can have upto 3 codecs, but single codec should be
1044558a398bSSimon Schubert 	 * suffice for now.
1045558a398bSSimon Schubert 	 */
10462a1ad637SFrançois Tigeot 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) {
1047558a398bSSimon Schubert 		/* codec 0 present */
1048558a398bSSimon Schubert 		sc->codec_found++;
1049558a398bSSimon Schubert 		sc->codec_idx = 0;
1050558a398bSSimon Schubert 		found++;
1051558a398bSSimon Schubert 	}
1052558a398bSSimon Schubert 
10532a1ad637SFrançois Tigeot 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) {
1054558a398bSSimon Schubert 		/* codec 1 present */
1055558a398bSSimon Schubert 		sc->codec_found++;
1056558a398bSSimon Schubert 	}
1057558a398bSSimon Schubert 
10582a1ad637SFrançois Tigeot 	if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) {
1059558a398bSSimon Schubert 		/* codec 2 present */
1060558a398bSSimon Schubert 		sc->codec_found++;
1061558a398bSSimon Schubert 	}
1062558a398bSSimon Schubert 
1063558a398bSSimon Schubert 	atiixp_unlock(sc);
1064558a398bSSimon Schubert 
1065558a398bSSimon Schubert 	if (found == 0)
1066558a398bSSimon Schubert 		goto postinitbad;
1067558a398bSSimon Schubert 
1068558a398bSSimon Schubert 	/* create/init mixer */
1069558a398bSSimon Schubert 	sc->codec = AC97_CREATE(sc->dev, sc, atiixp_ac97);
1070558a398bSSimon Schubert 	if (sc->codec == NULL)
1071558a398bSSimon Schubert 		goto postinitbad;
1072558a398bSSimon Schubert 
10732a1ad637SFrançois Tigeot 	subdev = (pci_get_subdevice(sc->dev) << 16) |
10742a1ad637SFrançois Tigeot 	    pci_get_subvendor(sc->dev);
1075558a398bSSimon Schubert 	switch (subdev) {
10764886ec58SHasso Tepper 	case 0x11831043:	/* ASUS A6R */
1077558a398bSSimon Schubert 	case 0x2043161f:	/* Maxselect x710s - http://maxselect.ru/ */
10782a1ad637SFrançois Tigeot 		ac97_setflags(sc->codec, ac97_getflags(sc->codec) |
10792a1ad637SFrançois Tigeot 		    AC97_F_EAPD_INV);
1080558a398bSSimon Schubert 		break;
1081558a398bSSimon Schubert 	default:
1082558a398bSSimon Schubert 		break;
1083558a398bSSimon Schubert 	}
1084558a398bSSimon Schubert 
1085558a398bSSimon Schubert 	mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
1086558a398bSSimon Schubert 
1087558a398bSSimon Schubert 	if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
1088558a398bSSimon Schubert 		goto postinitbad;
1089558a398bSSimon Schubert 
1090558a398bSSimon Schubert 	for (i = 0; i < ATI_IXP_NPCHAN; i++)
1091558a398bSSimon Schubert 		pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc);
1092558a398bSSimon Schubert 	for (i = 0; i < ATI_IXP_NRCHAN; i++)
1093558a398bSSimon Schubert 		pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
1094558a398bSSimon Schubert 
10952a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
10962a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
10972a1ad637SFrançois Tigeot 	    "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
10982a1ad637SFrançois Tigeot 	    sysctl_atiixp_polling, "I", "Enable polling mode");
10992a1ad637SFrançois Tigeot 
110067931cc4SFrançois Tigeot 	ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
1101558a398bSSimon Schubert 	    rman_get_start(sc->reg), rman_get_start(sc->irq),
1102558a398bSSimon Schubert 	    PCM_KLDSTRING(snd_atiixp));
1103558a398bSSimon Schubert 
1104558a398bSSimon Schubert 	pcm_setstatus(sc->dev, status);
1105558a398bSSimon Schubert 
1106558a398bSSimon Schubert 	atiixp_lock(sc);
11072a1ad637SFrançois Tigeot 	if (sc->polling == 0)
1108558a398bSSimon Schubert 		atiixp_enable_interrupts(sc);
1109558a398bSSimon Schubert 	atiixp_unlock(sc);
1110558a398bSSimon Schubert 
1111558a398bSSimon Schubert 	return;
1112558a398bSSimon Schubert 
1113558a398bSSimon Schubert postinitbad:
1114558a398bSSimon Schubert 	atiixp_release_resource(sc);
1115558a398bSSimon Schubert }
1116558a398bSSimon Schubert 
1117558a398bSSimon Schubert static void
atiixp_release_resource(struct atiixp_info * sc)1118558a398bSSimon Schubert atiixp_release_resource(struct atiixp_info *sc)
1119558a398bSSimon Schubert {
1120558a398bSSimon Schubert 	if (sc == NULL)
1121558a398bSSimon Schubert 		return;
11222a1ad637SFrançois Tigeot 	if (sc->registered_channels != 0) {
11232a1ad637SFrançois Tigeot 		atiixp_lock(sc);
11242a1ad637SFrançois Tigeot 		sc->polling = 0;
11252a1ad637SFrançois Tigeot 		callout_stop(&sc->poll_timer);
11262a1ad637SFrançois Tigeot 		atiixp_unlock(sc);
11272a1ad637SFrançois Tigeot 		callout_drain(&sc->poll_timer);
11282a1ad637SFrançois Tigeot 	}
1129558a398bSSimon Schubert 	if (sc->codec) {
1130558a398bSSimon Schubert 		ac97_destroy(sc->codec);
1131558a398bSSimon Schubert 		sc->codec = NULL;
1132558a398bSSimon Schubert 	}
1133558a398bSSimon Schubert 	if (sc->ih) {
1134558a398bSSimon Schubert 		bus_teardown_intr(sc->dev, sc->irq, sc->ih);
11354886ec58SHasso Tepper 		sc->ih = NULL;
1136558a398bSSimon Schubert 	}
1137558a398bSSimon Schubert 	if (sc->reg) {
1138558a398bSSimon Schubert 		bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg);
11394886ec58SHasso Tepper 		sc->reg = NULL;
1140558a398bSSimon Schubert 	}
1141558a398bSSimon Schubert 	if (sc->irq) {
1142558a398bSSimon Schubert 		bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq);
11434886ec58SHasso Tepper 		sc->irq = NULL;
1144558a398bSSimon Schubert 	}
1145558a398bSSimon Schubert 	if (sc->parent_dmat) {
1146558a398bSSimon Schubert 		bus_dma_tag_destroy(sc->parent_dmat);
11474886ec58SHasso Tepper 		sc->parent_dmat = NULL;
1148558a398bSSimon Schubert 	}
11492a1ad637SFrançois Tigeot 	if (sc->sgd_addr) {
1150558a398bSSimon Schubert 		bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
11512a1ad637SFrançois Tigeot 		sc->sgd_addr = 0;
11522a1ad637SFrançois Tigeot 	}
11534886ec58SHasso Tepper 	if (sc->sgd_table) {
11544886ec58SHasso Tepper 		bus_dmamem_free(sc->sgd_dmat, sc->sgd_table, sc->sgd_dmamap);
11554886ec58SHasso Tepper 		sc->sgd_table = NULL;
1156558a398bSSimon Schubert 	}
1157558a398bSSimon Schubert 	if (sc->sgd_dmat) {
1158558a398bSSimon Schubert 		bus_dma_tag_destroy(sc->sgd_dmat);
11594886ec58SHasso Tepper 		sc->sgd_dmat = NULL;
1160558a398bSSimon Schubert 	}
1161558a398bSSimon Schubert 	if (sc->lock) {
1162558a398bSSimon Schubert 		snd_mtxfree(sc->lock);
1163558a398bSSimon Schubert 		sc->lock = NULL;
1164558a398bSSimon Schubert 	}
116567931cc4SFrançois Tigeot 	kfree(sc, M_DEVBUF);
1166558a398bSSimon Schubert }
1167558a398bSSimon Schubert 
1168558a398bSSimon Schubert static int
atiixp_pci_probe(device_t dev)1169558a398bSSimon Schubert atiixp_pci_probe(device_t dev)
1170558a398bSSimon Schubert {
1171558a398bSSimon Schubert 	int i;
1172558a398bSSimon Schubert 	uint16_t devid, vendor;
1173558a398bSSimon Schubert 
1174558a398bSSimon Schubert 	vendor = pci_get_vendor(dev);
1175558a398bSSimon Schubert 	devid = pci_get_device(dev);
11762a1ad637SFrançois Tigeot 	for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
1177558a398bSSimon Schubert 		if (vendor == atiixp_hw[i].vendor &&
1178558a398bSSimon Schubert 		    devid == atiixp_hw[i].devid) {
1179558a398bSSimon Schubert 			device_set_desc(dev, atiixp_hw[i].desc);
11802a1ad637SFrançois Tigeot 			return (BUS_PROBE_DEFAULT);
1181558a398bSSimon Schubert 		}
1182558a398bSSimon Schubert 	}
1183558a398bSSimon Schubert 
11842a1ad637SFrançois Tigeot 	return (ENXIO);
1185558a398bSSimon Schubert }
1186558a398bSSimon Schubert 
1187558a398bSSimon Schubert static int
atiixp_pci_attach(device_t dev)1188558a398bSSimon Schubert atiixp_pci_attach(device_t dev)
1189558a398bSSimon Schubert {
1190558a398bSSimon Schubert 	struct atiixp_info *sc;
1191558a398bSSimon Schubert 	int i;
1192558a398bSSimon Schubert 
119367931cc4SFrançois Tigeot 	sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
11942a1ad637SFrançois Tigeot 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_atiixp softc");
1195558a398bSSimon Schubert 	sc->dev = dev;
1196558a398bSSimon Schubert 
119767931cc4SFrançois Tigeot 	callout_init_mp(&sc->poll_timer);
11982a1ad637SFrançois Tigeot 	sc->poll_ticks = 1;
11992a1ad637SFrançois Tigeot 
12002a1ad637SFrançois Tigeot 	if (resource_int_value(device_get_name(sc->dev),
12012a1ad637SFrançois Tigeot 	    device_get_unit(sc->dev), "polling", &i) == 0 && i != 0)
12022a1ad637SFrançois Tigeot 		sc->polling = 1;
12032a1ad637SFrançois Tigeot 	else
12042a1ad637SFrançois Tigeot 		sc->polling = 0;
12052a1ad637SFrançois Tigeot 
1206558a398bSSimon Schubert 	pci_enable_busmaster(dev);
1207558a398bSSimon Schubert 
1208558a398bSSimon Schubert 	sc->regid = PCIR_BAR(0);
1209558a398bSSimon Schubert 	sc->regtype = SYS_RES_MEMORY;
12102a1ad637SFrançois Tigeot 	sc->reg = bus_alloc_resource_any(dev, sc->regtype,
12112a1ad637SFrançois Tigeot 	    &sc->regid, RF_ACTIVE);
1212558a398bSSimon Schubert 
1213558a398bSSimon Schubert 	if (!sc->reg) {
1214558a398bSSimon Schubert 		device_printf(dev, "unable to allocate register space\n");
1215558a398bSSimon Schubert 		goto bad;
1216558a398bSSimon Schubert 	}
1217558a398bSSimon Schubert 
1218558a398bSSimon Schubert 	sc->st = rman_get_bustag(sc->reg);
1219558a398bSSimon Schubert 	sc->sh = rman_get_bushandle(sc->reg);
1220558a398bSSimon Schubert 
12212a1ad637SFrançois Tigeot 	sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN,
12222a1ad637SFrançois Tigeot 	    ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
1223558a398bSSimon Schubert 
1224558a398bSSimon Schubert 	sc->irqid = 0;
1225558a398bSSimon Schubert 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
1226558a398bSSimon Schubert 	    RF_ACTIVE | RF_SHAREABLE);
12272a1ad637SFrançois Tigeot 	if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
1228558a398bSSimon Schubert 	    atiixp_intr, sc, &sc->ih)) {
1229558a398bSSimon Schubert 		device_printf(dev, "unable to map interrupt\n");
1230558a398bSSimon Schubert 		goto bad;
1231558a398bSSimon Schubert 	}
1232558a398bSSimon Schubert 
1233558a398bSSimon Schubert 	/*
1234558a398bSSimon Schubert 	 * Let the user choose the best DMA segments.
1235558a398bSSimon Schubert 	 */
1236558a398bSSimon Schubert 	if (resource_int_value(device_get_name(dev),
12372a1ad637SFrançois Tigeot 	    device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
12382a1ad637SFrançois Tigeot 		i &= ATI_IXP_BLK_ALIGN;
12392a1ad637SFrançois Tigeot 		if (i < ATI_IXP_BLK_MIN)
12402a1ad637SFrançois Tigeot 			i = ATI_IXP_BLK_MIN;
12412a1ad637SFrançois Tigeot 		sc->blkcnt = sc->bufsz / i;
1242558a398bSSimon Schubert 		i = 0;
12432a1ad637SFrançois Tigeot 		while (sc->blkcnt >> i)
1244558a398bSSimon Schubert 			i++;
12452a1ad637SFrançois Tigeot 		sc->blkcnt = 1 << (i - 1);
12462a1ad637SFrançois Tigeot 		if (sc->blkcnt < ATI_IXP_DMA_CHSEGS_MIN)
12472a1ad637SFrançois Tigeot 			sc->blkcnt = ATI_IXP_DMA_CHSEGS_MIN;
12482a1ad637SFrançois Tigeot 		else if (sc->blkcnt > ATI_IXP_DMA_CHSEGS_MAX)
12492a1ad637SFrançois Tigeot 			sc->blkcnt = ATI_IXP_DMA_CHSEGS_MAX;
12502a1ad637SFrançois Tigeot 
12512a1ad637SFrançois Tigeot 	} else
12522a1ad637SFrançois Tigeot 		sc->blkcnt = ATI_IXP_DMA_CHSEGS;
1253558a398bSSimon Schubert 
1254558a398bSSimon Schubert 	/*
1255558a398bSSimon Schubert 	 * DMA tag for scatter-gather buffers and link pointers
1256558a398bSSimon Schubert 	 */
12572a1ad637SFrançois Tigeot 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
12582a1ad637SFrançois Tigeot 		/*boundary*/0,
1259558a398bSSimon Schubert 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1260558a398bSSimon Schubert 		/*highaddr*/BUS_SPACE_MAXADDR,
1261558a398bSSimon Schubert 		/*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
12624923c0eaSFrançois Tigeot 		/*flags*/0,
12634923c0eaSFrançois Tigeot 		&sc->parent_dmat) != 0) {
1264558a398bSSimon Schubert 		device_printf(dev, "unable to create dma tag\n");
1265558a398bSSimon Schubert 		goto bad;
1266558a398bSSimon Schubert 	}
1267558a398bSSimon Schubert 
12682a1ad637SFrançois Tigeot 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
12692a1ad637SFrançois Tigeot 		/*boundary*/0,
1270558a398bSSimon Schubert 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
1271558a398bSSimon Schubert 		/*highaddr*/BUS_SPACE_MAXADDR,
12722a1ad637SFrançois Tigeot 		/*maxsize*/ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS *
1273558a398bSSimon Schubert 		sizeof(struct atiixp_dma_op),
1274558a398bSSimon Schubert 		/*nsegments*/1, /*maxsegz*/0x3ffff,
12754923c0eaSFrançois Tigeot 		/*flags*/0,
12764923c0eaSFrançois Tigeot 		&sc->sgd_dmat) != 0) {
1277558a398bSSimon Schubert 		device_printf(dev, "unable to create dma tag\n");
1278558a398bSSimon Schubert 		goto bad;
1279558a398bSSimon Schubert 	}
1280558a398bSSimon Schubert 
1281558a398bSSimon Schubert 	if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
1282558a398bSSimon Schubert 	    BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
1283558a398bSSimon Schubert 		goto bad;
1284558a398bSSimon Schubert 
1285558a398bSSimon Schubert 	if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
12862a1ad637SFrançois Tigeot 	    ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS *
12872a1ad637SFrançois Tigeot 	    sizeof(struct atiixp_dma_op), atiixp_dma_cb, sc, 0))
1288558a398bSSimon Schubert 		goto bad;
1289558a398bSSimon Schubert 
1290558a398bSSimon Schubert 
1291558a398bSSimon Schubert 	atiixp_chip_pre_init(sc);
1292558a398bSSimon Schubert 
1293558a398bSSimon Schubert 	sc->delayed_attach.ich_func = atiixp_chip_post_init;
1294558a398bSSimon Schubert 	sc->delayed_attach.ich_arg = sc;
1295*ae3b72a6SSascha Wildner 	sc->delayed_attach.ich_desc = "snd_atiixp";
1296558a398bSSimon Schubert 	if (cold == 0 ||
1297558a398bSSimon Schubert 	    config_intrhook_establish(&sc->delayed_attach) != 0) {
1298558a398bSSimon Schubert 		sc->delayed_attach.ich_func = NULL;
1299558a398bSSimon Schubert 		atiixp_chip_post_init(sc);
1300558a398bSSimon Schubert 	}
1301558a398bSSimon Schubert 
13022a1ad637SFrançois Tigeot 	return (0);
1303558a398bSSimon Schubert 
1304558a398bSSimon Schubert bad:
1305558a398bSSimon Schubert 	atiixp_release_resource(sc);
13062a1ad637SFrançois Tigeot 	return (ENXIO);
1307558a398bSSimon Schubert }
1308558a398bSSimon Schubert 
1309558a398bSSimon Schubert static int
atiixp_pci_detach(device_t dev)1310558a398bSSimon Schubert atiixp_pci_detach(device_t dev)
1311558a398bSSimon Schubert {
1312558a398bSSimon Schubert 	int r;
1313558a398bSSimon Schubert 	struct atiixp_info *sc;
1314558a398bSSimon Schubert 
1315558a398bSSimon Schubert 	sc = pcm_getdevinfo(dev);
1316558a398bSSimon Schubert 	if (sc != NULL) {
1317558a398bSSimon Schubert 		if (sc->codec != NULL) {
1318558a398bSSimon Schubert 			r = pcm_unregister(dev);
1319558a398bSSimon Schubert 			if (r)
13202a1ad637SFrançois Tigeot 				return (r);
1321558a398bSSimon Schubert 		}
1322558a398bSSimon Schubert 		sc->codec = NULL;
13232a1ad637SFrançois Tigeot 		if (sc->st != 0 && sc->sh != 0)
1324558a398bSSimon Schubert 			atiixp_disable_interrupts(sc);
1325558a398bSSimon Schubert 		atiixp_release_resource(sc);
1326558a398bSSimon Schubert 	}
13272a1ad637SFrançois Tigeot 	return (0);
1328558a398bSSimon Schubert }
1329558a398bSSimon Schubert 
1330558a398bSSimon Schubert static int
atiixp_pci_suspend(device_t dev)1331558a398bSSimon Schubert atiixp_pci_suspend(device_t dev)
1332558a398bSSimon Schubert {
1333558a398bSSimon Schubert 	struct atiixp_info *sc = pcm_getdevinfo(dev);
1334558a398bSSimon Schubert 	uint32_t value;
1335558a398bSSimon Schubert 
1336558a398bSSimon Schubert 	/* quickly disable interrupts and save channels active state */
1337558a398bSSimon Schubert 	atiixp_lock(sc);
1338558a398bSSimon Schubert 	atiixp_disable_interrupts(sc);
1339558a398bSSimon Schubert 	atiixp_unlock(sc);
1340558a398bSSimon Schubert 
1341558a398bSSimon Schubert 	/* stop everything */
13422a1ad637SFrançois Tigeot 	if (sc->pch.flags & ATI_IXP_CHN_RUNNING) {
1343558a398bSSimon Schubert 		atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP);
13442a1ad637SFrançois Tigeot 		sc->pch.flags |= ATI_IXP_CHN_SUSPEND;
13452a1ad637SFrançois Tigeot 	}
13462a1ad637SFrançois Tigeot 	if (sc->rch.flags & ATI_IXP_CHN_RUNNING) {
1347558a398bSSimon Schubert 		atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP);
13482a1ad637SFrançois Tigeot 		sc->rch.flags |= ATI_IXP_CHN_SUSPEND;
13492a1ad637SFrançois Tigeot 	}
1350558a398bSSimon Schubert 
1351558a398bSSimon Schubert 	/* power down aclink and pci bus */
1352558a398bSSimon Schubert 	atiixp_lock(sc);
1353558a398bSSimon Schubert 	value = atiixp_rd(sc, ATI_REG_CMD);
1354558a398bSSimon Schubert 	value |= ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET;
1355558a398bSSimon Schubert 	atiixp_wr(sc, ATI_REG_CMD, ATI_REG_CMD_POWERDOWN);
1356558a398bSSimon Schubert 	atiixp_unlock(sc);
1357558a398bSSimon Schubert 
13582a1ad637SFrançois Tigeot 	return (0);
1359558a398bSSimon Schubert }
1360558a398bSSimon Schubert 
1361558a398bSSimon Schubert static int
atiixp_pci_resume(device_t dev)1362558a398bSSimon Schubert atiixp_pci_resume(device_t dev)
1363558a398bSSimon Schubert {
1364558a398bSSimon Schubert 	struct atiixp_info *sc = pcm_getdevinfo(dev);
1365558a398bSSimon Schubert 
1366558a398bSSimon Schubert 	atiixp_lock(sc);
1367558a398bSSimon Schubert 	/* reset / power up aclink */
1368558a398bSSimon Schubert 	atiixp_reset_aclink(sc);
1369558a398bSSimon Schubert 	atiixp_unlock(sc);
1370558a398bSSimon Schubert 
1371558a398bSSimon Schubert 	if (mixer_reinit(dev) == -1) {
1372558a398bSSimon Schubert 		device_printf(dev, "unable to reinitialize the mixer\n");
13732a1ad637SFrançois Tigeot 		return (ENXIO);
1374558a398bSSimon Schubert 	}
1375558a398bSSimon Schubert 
1376558a398bSSimon Schubert 	/*
1377558a398bSSimon Schubert 	 * Resume channel activities. Reset channel format regardless
1378558a398bSSimon Schubert 	 * of its previous state.
1379558a398bSSimon Schubert 	 */
13802a1ad637SFrançois Tigeot 	if (sc->pch.channel != NULL) {
13812a1ad637SFrançois Tigeot 		if (sc->pch.fmt != 0)
1382558a398bSSimon Schubert 			atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt);
13832a1ad637SFrançois Tigeot 		if (sc->pch.flags & ATI_IXP_CHN_SUSPEND) {
13842a1ad637SFrançois Tigeot 			sc->pch.flags &= ~ATI_IXP_CHN_SUSPEND;
1385558a398bSSimon Schubert 			atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START);
1386558a398bSSimon Schubert 		}
13872a1ad637SFrançois Tigeot 	}
13882a1ad637SFrançois Tigeot 	if (sc->rch.channel != NULL) {
13892a1ad637SFrançois Tigeot 		if (sc->rch.fmt != 0)
1390558a398bSSimon Schubert 			atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt);
13912a1ad637SFrançois Tigeot 		if (sc->rch.flags & ATI_IXP_CHN_SUSPEND) {
13922a1ad637SFrançois Tigeot 			sc->rch.flags &= ~ATI_IXP_CHN_SUSPEND;
1393558a398bSSimon Schubert 			atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START);
1394558a398bSSimon Schubert 		}
13952a1ad637SFrançois Tigeot 	}
1396558a398bSSimon Schubert 
1397558a398bSSimon Schubert 	/* enable interrupts */
1398558a398bSSimon Schubert 	atiixp_lock(sc);
13992a1ad637SFrançois Tigeot 	if (sc->polling == 0)
1400558a398bSSimon Schubert 		atiixp_enable_interrupts(sc);
1401558a398bSSimon Schubert 	atiixp_unlock(sc);
1402558a398bSSimon Schubert 
14032a1ad637SFrançois Tigeot 	return (0);
1404558a398bSSimon Schubert }
1405558a398bSSimon Schubert 
1406558a398bSSimon Schubert static device_method_t atiixp_methods[] = {
1407558a398bSSimon Schubert 	DEVMETHOD(device_probe,		atiixp_pci_probe),
1408558a398bSSimon Schubert 	DEVMETHOD(device_attach,	atiixp_pci_attach),
1409558a398bSSimon Schubert 	DEVMETHOD(device_detach,	atiixp_pci_detach),
1410558a398bSSimon Schubert 	DEVMETHOD(device_suspend,	atiixp_pci_suspend),
1411558a398bSSimon Schubert 	DEVMETHOD(device_resume,	atiixp_pci_resume),
14122a1ad637SFrançois Tigeot 	{ 0, 0 }
1413558a398bSSimon Schubert };
1414558a398bSSimon Schubert 
1415558a398bSSimon Schubert static driver_t atiixp_driver = {
1416558a398bSSimon Schubert 	"pcm",
1417558a398bSSimon Schubert 	atiixp_methods,
1418558a398bSSimon Schubert 	PCM_SOFTC_SIZE,
1419558a398bSSimon Schubert };
1420558a398bSSimon Schubert 
1421aa6ac96eSSascha Wildner DRIVER_MODULE(snd_atiixp, pci, atiixp_driver, pcm_devclass, NULL, NULL);
1422558a398bSSimon Schubert MODULE_DEPEND(snd_atiixp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
1423558a398bSSimon Schubert MODULE_VERSION(snd_atiixp, 1);
1424