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