summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2005-11-27 03:29:59 +0000
committerariff <ariff@FreeBSD.org>2005-11-27 03:29:59 +0000
commit2785e695f6d96c1714ec67ee5c2a27b292b8d100 (patch)
tree91e2fbede7a3665fdc0c10bd7a3a473a3960aff4 /sys/dev
parentf34ebe602b5901fb4e100a303eca76ffa7cfc65e (diff)
downloadFreeBSD-src-2785e695f6d96c1714ec67ee5c2a27b292b8d100.zip
FreeBSD-src-2785e695f6d96c1714ec67ee5c2a27b292b8d100.tar.gz
Support for ATI IXP 200 / 300 / 400 series audio controllers.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/sound/driver.c1
-rw-r--r--sys/dev/sound/pci/atiixp.c1006
-rw-r--r--sys/dev/sound/pci/atiixp.h203
3 files changed, 1210 insertions, 0 deletions
diff --git a/sys/dev/sound/driver.c b/sys/dev/sound/driver.c
index 7da1099..6ada898 100644
--- a/sys/dev/sound/driver.c
+++ b/sys/dev/sound/driver.c
@@ -54,6 +54,7 @@ MODULE_VERSION(snd_driver, 1);
MODULE_DEPEND(snd_driver, snd_ad1816, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_als4000, 1, 1, 1);
+MODULE_DEPEND(snd_driver, snd_atiixp, 1, 1, 1);
/* MODULE_DEPEND(snd_driver, snd_aureal, 1, 1, 1); */
MODULE_DEPEND(snd_driver, snd_cmi, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_cs4281, 1, 1, 1);
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
new file mode 100644
index 0000000..b0d7c63
--- /dev/null
+++ b/sys/dev/sound/pci/atiixp.c
@@ -0,0 +1,1006 @@
+/*-
+ * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * FreeBSD pcm driver for ATI IXP 150/200/250/300 AC97 controllers
+ *
+ * Features
+ * * 16bit playback / recording
+ * * 32bit native playback - yay!
+ *
+ * Issues / TODO:
+ * * SPDIF
+ * * Support for more than 2 channels.
+ * * VRA ? VRM ? DRA ?
+ * * Suspend / Resume.
+ * * 32bit native recording (seems broken, disabled)
+ *
+ *
+ * Thanks goes to:
+ *
+ * Shaharil @ SCAN Associates whom relentlessly providing me the
+ * mind blowing Acer Ferrari 4002 WLMi with this ATI IXP hardware.
+ *
+ * Reinoud Zandijk <reinoud@NetBSD.org> (auixp), which this driver is
+ * largely based upon although large part of it has been reworked. His
+ * driver is the primary reference and pretty much well documented.
+ *
+ * Takashi Iwai (ALSA snd-atiixp), for register definitions and some
+ * random ninja hackery.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <sys/sysctl.h>
+#include <sys/endian.h>
+
+#include <dev/sound/pci/atiixp.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+
+struct atiixp_dma_op {
+ uint32_t addr;
+ uint16_t status;
+ uint16_t size;
+ uint32_t next;
+};
+
+struct atiixp_info;
+
+struct atiixp_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct atiixp_info *parent;
+ struct atiixp_dma_op *sgd_table;
+ bus_addr_t sgd_addr;
+ uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit;
+ uint32_t dma_segs;
+ int caps_32bit, dir;
+};
+
+struct atiixp_info {
+ device_t dev;
+
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t sgd_dmat;
+ bus_dmamap_t sgd_dmamap;
+ bus_addr_t sgd_addr;
+
+ struct resource *reg, *irq;
+ int regtype, regid, irqid;
+ void *ih;
+ struct ac97_info *codec;
+
+ struct atiixp_chinfo pch;
+ struct atiixp_chinfo rch;
+ struct atiixp_dma_op *sgd_table;
+ struct intr_config_hook delayed_attach;
+
+ uint32_t bufsz;
+ uint32_t codec_not_ready_bits, codec_idx, codec_found;
+ uint32_t dma_segs;
+ int registered_channels;
+
+ struct mtx *lock;
+};
+
+#define atiixp_rd(_sc, _reg) \
+ bus_space_read_4((_sc)->st, (_sc)->sh, _reg)
+#define atiixp_wr(_sc, _reg, _val) \
+ bus_space_write_4((_sc)->st, (_sc)->sh, _reg, _val)
+
+#define atiixp_lock(_sc) snd_mtxlock((_sc)->lock)
+#define atiixp_unlock(_sc) snd_mtxunlock((_sc)->lock)
+#define atiixp_assert(_sc) snd_mtxassert((_sc)->lock)
+
+static uint32_t atiixp_fmt_32bit[] = {
+ AFMT_STEREO | AFMT_S16_LE,
+#ifdef AFMT_S32_LE
+ AFMT_STEREO | AFMT_S32_LE,
+#endif
+ 0
+};
+
+static uint32_t atiixp_fmt[] = {
+ AFMT_STEREO | AFMT_S16_LE,
+ 0
+};
+
+static struct pcmchan_caps atiixp_caps_32bit = {
+ ATI_IXP_BASE_RATE,
+ ATI_IXP_BASE_RATE,
+ atiixp_fmt_32bit, 0
+};
+
+static struct pcmchan_caps atiixp_caps = {
+ ATI_IXP_BASE_RATE,
+ ATI_IXP_BASE_RATE,
+ atiixp_fmt, 0
+};
+
+static const struct {
+ uint16_t vendor;
+ uint16_t devid;
+ char *desc;
+} atiixp_hw[] = {
+ { ATI_VENDOR_ID, ATI_IXP_200_ID, "ATI IXP 200" },
+ { ATI_VENDOR_ID, ATI_IXP_300_ID, "ATI IXP 300" },
+ { ATI_VENDOR_ID, ATI_IXP_400_ID, "ATI IXP 400" },
+};
+
+static void atiixp_enable_interrupts(struct atiixp_info *);
+static void atiixp_disable_interrupts(struct atiixp_info *);
+static void atiixp_reset_aclink(struct atiixp_info *);
+static void atiixp_flush_dma(struct atiixp_info *, struct atiixp_chinfo *);
+static void atiixp_enable_dma(struct atiixp_info *, struct atiixp_chinfo *);
+static void atiixp_disable_dma(struct atiixp_info *, struct atiixp_chinfo *);
+
+static int atiixp_waitready_codec(struct atiixp_info *);
+static int atiixp_rdcd(kobj_t, void *, int);
+static int atiixp_wrcd(kobj_t, void *, int, uint32_t);
+
+static void *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *,
+ struct pcm_channel *, int);
+static int atiixp_chan_setformat(kobj_t, void *, uint32_t);
+static int atiixp_chan_setspeed(kobj_t, void *, uint32_t);
+static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
+static void atiixp_buildsgdt(struct atiixp_chinfo *);
+static int atiixp_chan_trigger(kobj_t, void *, int);
+static int atiixp_chan_getptr(kobj_t, void *);
+static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *);
+
+static void atiixp_intr(void *);
+static void atiixp_dma_cb(void *, bus_dma_segment_t *, int, int);
+static void atiixp_chip_pre_init(struct atiixp_info *);
+static void atiixp_chip_post_init(void *);
+static int atiixp_pci_probe(device_t);
+static int atiixp_pci_attach(device_t);
+static int atiixp_pci_detach(device_t);
+
+/*
+ * ATI IXP helper functions
+ */
+static void
+atiixp_enable_interrupts(struct atiixp_info *sc)
+{
+ uint32_t value;
+
+ /* clear all pending */
+ atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
+
+ /* enable all relevant interrupt sources we can handle */
+ value = atiixp_rd(sc, ATI_REG_IER);
+
+ value |= ATI_REG_IER_IO_STATUS_EN;
+
+ /*
+ * Disable / ignore internal xrun/spdf interrupt flags
+ * since it doesn't interest us (for now).
+ */
+#if 0
+ value |= ATI_REG_IER_IN_XRUN_EN;
+ value |= ATI_REG_IER_OUT_XRUN_EN;
+
+ value |= ATI_REG_IER_SPDF_XRUN_EN;
+ value |= ATI_REG_IER_SPDF_STATUS_EN;
+#endif
+
+ atiixp_wr(sc, ATI_REG_IER, value);
+}
+
+static void
+atiixp_disable_interrupts(struct atiixp_info *sc)
+{
+ /* disable all interrupt sources */
+ atiixp_wr(sc, ATI_REG_IER, 0);
+
+ /* clear all pending */
+ atiixp_wr(sc, ATI_REG_ISR, 0xffffffff);
+}
+
+static void
+atiixp_reset_aclink(struct atiixp_info *sc)
+{
+ uint32_t value, timeout;
+
+ /* if power is down, power it up */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ if (value & ATI_REG_CMD_POWERDOWN) {
+ /* explicitly enable power */
+ value &= ~ATI_REG_CMD_POWERDOWN;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* have to wait at least 10 usec for it to initialise */
+ DELAY(20);
+ };
+
+ /* perform a soft reset */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value |= ATI_REG_CMD_AC_SOFT_RESET;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* need to read the CMD reg and wait aprox. 10 usec to init */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ DELAY(20);
+
+ /* clear soft reset flag again */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value &= ~ATI_REG_CMD_AC_SOFT_RESET;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* check if the ac-link is working; reset device otherwise */
+ timeout = 10;
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ while (!(value & ATI_REG_CMD_ACLINK_ACTIVE)
+ && --timeout) {
+ device_printf(sc->dev, "not up; resetting aclink hardware\n");
+
+ /* dip aclink reset but keep the acsync */
+ value &= ~ATI_REG_CMD_AC_RESET;
+ value |= ATI_REG_CMD_AC_SYNC;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* need to read CMD again and wait again (clocking in issue?) */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ DELAY(20);
+
+ /* assert aclink reset again */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value |= ATI_REG_CMD_AC_RESET;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* check if its active now */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ };
+
+ if (timeout == 0)
+ device_printf(sc->dev, "giving up aclink reset\n");
+ if (timeout != 10)
+ device_printf(sc->dev, "aclink hardware reset successful\n");
+
+ /* assert reset and sync for safety */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value |= ATI_REG_CMD_AC_SYNC | ATI_REG_CMD_AC_RESET;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+}
+
+static void
+atiixp_flush_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
+{
+ atiixp_wr(sc, ATI_REG_FIFO_FLUSH, ch->flush_bit);
+}
+
+static void
+atiixp_enable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
+{
+ uint32_t value;
+
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ if (!(value & ch->enable_bit)) {
+ value |= ch->enable_bit;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+ }
+}
+
+static void
+atiixp_disable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
+{
+ uint32_t value;
+
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ if (value & ch->enable_bit) {
+ value &= ~ch->enable_bit;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+ }
+}
+
+/*
+ * AC97 interface
+ */
+static int
+atiixp_waitready_codec(struct atiixp_info *sc)
+{
+ int timeout = 500;
+
+ do {
+ if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
+ ATI_REG_PHYS_OUT_ADDR_EN) == 0)
+ return 0;
+ DELAY(1);
+ } while (timeout--);
+
+ return -1;
+}
+
+static int
+atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
+{
+ struct atiixp_info *sc = devinfo;
+ uint32_t data;
+ int timeout;
+
+ if (atiixp_waitready_codec(sc))
+ return -1;
+
+ data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN |
+ ATI_REG_PHYS_OUT_RW | sc->codec_idx;
+
+ atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
+
+ if (atiixp_waitready_codec(sc))
+ return -1;
+
+ timeout = 500;
+ do {
+ data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR);
+ if (data & ATI_REG_PHYS_IN_READ_FLAG)
+ return data >> ATI_REG_PHYS_IN_DATA_SHIFT;
+ DELAY(1);
+ } while (timeout--);
+
+ if (reg < 0x7c)
+ device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg);
+
+ return -1;
+}
+
+static int
+atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
+{
+ struct atiixp_info *sc = devinfo;
+
+ if (atiixp_waitready_codec(sc))
+ return -1;
+
+ data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
+ (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx;
+
+ atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
+
+ return 0;
+}
+
+static kobj_method_t atiixp_ac97_methods[] = {
+ KOBJMETHOD(ac97_read, atiixp_rdcd),
+ KOBJMETHOD(ac97_write, atiixp_wrcd),
+ { 0, 0 }
+};
+AC97_DECLARE(atiixp_ac97);
+
+/*
+ * Playback / Record channel interface
+ */
+static void *
+atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct atiixp_info *sc = devinfo;
+ struct atiixp_chinfo *ch;
+ int num;
+
+ atiixp_lock(sc);
+
+ if (dir == PCMDIR_PLAY) {
+ ch = &sc->pch;
+ ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR;
+ ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN;
+ ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH;
+ ch->dma_dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR;
+ /* Native 32bit playback working properly */
+ ch->caps_32bit = 1;
+ } else {
+ ch = &sc->rch;
+ ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR;
+ ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN;
+ ch->flush_bit = ATI_REG_FIFO_IN_FLUSH;
+ ch->dma_dt_cur_bit = ATI_REG_IN_DMA_DT_CUR;
+ /* XXX Native 32bit recording appear to be broken */
+ ch->caps_32bit = 0;
+ }
+
+ ch->buffer = b;
+ ch->parent = sc;
+ ch->channel = c;
+ ch->dir = dir;
+ ch->dma_segs = sc->dma_segs;
+
+ atiixp_unlock(sc);
+
+ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1)
+ return NULL;
+
+ atiixp_lock(sc);
+ num = sc->registered_channels++;
+ ch->sgd_table = &sc->sgd_table[num * ch->dma_segs];
+ ch->sgd_addr = sc->sgd_addr +
+ (num * ch->dma_segs * sizeof(struct atiixp_dma_op));
+ atiixp_disable_dma(sc, ch);
+ atiixp_unlock(sc);
+
+ return ch;
+}
+
+static int
+atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct atiixp_chinfo *ch = data;
+ struct atiixp_info *sc = ch->parent;
+ uint32_t value;
+
+ atiixp_lock(sc);
+ if (ch->dir == PCMDIR_REC) {
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value &= ~ATI_REG_CMD_INTERLEAVE_IN;
+ if (ch->caps_32bit == 0 ||
+ (format & (AFMT_8BIT|AFMT_16BIT)) != 0)
+ value |= ATI_REG_CMD_INTERLEAVE_IN;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+ } else {
+ value = atiixp_rd(sc, ATI_REG_OUT_DMA_SLOT);
+ value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
+ /* We do not have support for more than 2 channels, _yet_. */
+ value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
+ ATI_REG_OUT_DMA_SLOT_BIT(4);
+ value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
+ atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value &= ~ATI_REG_CMD_INTERLEAVE_OUT;
+ if (ch->caps_32bit == 0 ||
+ (format & (AFMT_8BIT|AFMT_16BIT)) != 0)
+ value |= ATI_REG_CMD_INTERLEAVE_OUT;
+ atiixp_wr(sc, ATI_REG_CMD, value);
+ value = atiixp_rd(sc, ATI_REG_6CH_REORDER);
+ value &= ~ATI_REG_6CH_REORDER_EN;
+ atiixp_wr(sc, ATI_REG_6CH_REORDER, value);
+ }
+ atiixp_unlock(sc);
+
+ return 0;
+}
+
+static int
+atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd)
+{
+ /* XXX We're supposed to do VRA/DRA processing right here */
+ return ATI_IXP_BASE_RATE;
+}
+
+static int
+atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
+{
+ struct atiixp_chinfo *ch = data;
+ struct atiixp_info *sc = ch->parent;
+
+ if (blksz > (sc->bufsz / ch->dma_segs))
+ blksz = sc->bufsz / ch->dma_segs;
+
+ sndbuf_resize(ch->buffer, ch->dma_segs, blksz);
+
+ return sndbuf_getblksz(ch->buffer);
+}
+
+static void
+atiixp_buildsgdt(struct atiixp_chinfo *ch)
+{
+ uint32_t addr, blksz;
+ int i;
+
+ addr = sndbuf_getbufaddr(ch->buffer);
+ blksz = sndbuf_getblksz(ch->buffer);
+
+ for (i = 0; i < ch->dma_segs; i++) {
+ ch->sgd_table[i].addr = htole32(addr + (i * blksz));
+ ch->sgd_table[i].status = htole16(0);
+ ch->sgd_table[i].size = htole16(blksz >> 2);
+ ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
+ (((i + 1) % ch->dma_segs) *
+ sizeof(struct atiixp_dma_op)));
+ }
+}
+
+static int
+atiixp_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct atiixp_chinfo *ch = data;
+ struct atiixp_info *sc = ch->parent;
+ uint32_t value;
+
+ atiixp_lock(sc);
+
+ switch (go) {
+ case PCMTRIG_START:
+ atiixp_flush_dma(sc, ch);
+ atiixp_buildsgdt(ch);
+ atiixp_wr(sc, ch->linkptr_bit, 0);
+ atiixp_enable_dma(sc, ch);
+ atiixp_wr(sc, ch->linkptr_bit,
+ (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ atiixp_disable_dma(sc, ch);
+ atiixp_flush_dma(sc, ch);
+ break;
+ default:
+ atiixp_unlock(sc);
+ return 0;
+ break;
+ }
+
+ /* Update bus busy status */
+ value = atiixp_rd(sc, ATI_REG_IER);
+ if (atiixp_rd(sc, ATI_REG_CMD) & (
+ ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN |
+ ATI_REG_CMD_SPDF_OUT_EN))
+ value |= ATI_REG_IER_SET_BUS_BUSY;
+ else
+ value &= ~ATI_REG_IER_SET_BUS_BUSY;
+ atiixp_wr(sc, ATI_REG_IER, value);
+
+ atiixp_unlock(sc);
+
+ return 0;
+}
+
+static int
+atiixp_chan_getptr(kobj_t obj, void *data)
+{
+ struct atiixp_chinfo *ch = data;
+ struct atiixp_info *sc = ch->parent;
+ uint32_t ptr;
+
+ atiixp_lock(sc);
+ ptr = atiixp_rd(sc, ch->dma_dt_cur_bit);
+ atiixp_unlock(sc);
+
+ return ptr;
+}
+
+static struct pcmchan_caps *
+atiixp_chan_getcaps(kobj_t obj, void *data)
+{
+ struct atiixp_chinfo *ch = data;
+
+ if (ch->caps_32bit)
+ return &atiixp_caps_32bit;
+ return &atiixp_caps;
+}
+
+static kobj_method_t atiixp_chan_methods[] = {
+ KOBJMETHOD(channel_init, atiixp_chan_init),
+ KOBJMETHOD(channel_setformat, atiixp_chan_setformat),
+ KOBJMETHOD(channel_setspeed, atiixp_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, atiixp_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, atiixp_chan_trigger),
+ KOBJMETHOD(channel_getptr, atiixp_chan_getptr),
+ KOBJMETHOD(channel_getcaps, atiixp_chan_getcaps),
+ { 0, 0 }
+};
+CHANNEL_DECLARE(atiixp_chan);
+
+/*
+ * PCI driver interface
+ */
+static void
+atiixp_intr(void *p)
+{
+ struct atiixp_info *sc = p;
+ uint32_t status, enable, detected_codecs;
+
+ atiixp_lock(sc);
+ status = atiixp_rd(sc, ATI_REG_ISR);
+
+ if (status == 0) {
+ atiixp_unlock(sc);
+ return;
+ }
+
+ if (status & ATI_REG_ISR_IN_STATUS) {
+ atiixp_unlock(sc);
+ chn_intr(sc->rch.channel);
+ atiixp_lock(sc);
+ }
+ if (status & ATI_REG_ISR_OUT_STATUS) {
+ atiixp_unlock(sc);
+ chn_intr(sc->pch.channel);
+ atiixp_lock(sc);
+ }
+
+#if 0
+ if (status & ATI_REG_ISR_IN_XRUN) {
+ device_printf(sc->dev,
+ "Recieve IN XRUN interrupt\n");
+ }
+ if (status & ATI_REG_ISR_OUT_XRUN) {
+ device_printf(sc->dev,
+ "Recieve OUT XRUN interrupt\n");
+ }
+#endif
+
+ if (status & CODEC_CHECK_BITS) {
+ /* mark missing codecs as not ready */
+ detected_codecs = status & CODEC_CHECK_BITS;
+ sc->codec_not_ready_bits |= detected_codecs;
+
+ /* disable detected interupt sources */
+ enable = atiixp_rd(sc, ATI_REG_IER);
+ enable &= ~detected_codecs;
+ atiixp_wr(sc, ATI_REG_IER, enable);
+ }
+
+ /* acknowledge */
+ atiixp_wr(sc, ATI_REG_ISR, status);
+ atiixp_unlock(sc);
+}
+
+static void
+atiixp_dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
+{
+ struct atiixp_info *sc = (struct atiixp_info *)p;
+ sc->sgd_addr = bds->ds_addr;
+}
+
+static void
+atiixp_chip_pre_init(struct atiixp_info *sc)
+{
+ uint32_t value;
+
+ atiixp_lock(sc);
+
+ /* disable interrupts */
+ atiixp_disable_interrupts(sc);
+
+ /* clear all DMA enables (preserving rest of settings) */
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN |
+ ATI_REG_CMD_SPDF_OUT_EN );
+ atiixp_wr(sc, ATI_REG_CMD, value);
+
+ /* reset aclink */
+ atiixp_reset_aclink(sc);
+
+ sc->codec_not_ready_bits = 0;
+
+ /* enable all codecs to interrupt as well as the new frame interrupt */
+ atiixp_wr(sc, ATI_REG_IER, CODEC_CHECK_BITS);
+
+ atiixp_unlock(sc);
+}
+
+static void
+atiixp_chip_post_init(void *arg)
+{
+ struct atiixp_info *sc = (struct atiixp_info *)arg;
+ int i, timeout, found;
+ char status[SND_STATUSLEN];
+
+ atiixp_lock(sc);
+
+ if (sc->delayed_attach.ich_func) {
+ config_intrhook_disestablish(&sc->delayed_attach);
+ sc->delayed_attach.ich_func = NULL;
+ }
+
+ /* wait for the interrupts to happen */
+ timeout = 100; /* 100.000 usec -> 0.1 sec */
+
+ while (--timeout) {
+ atiixp_unlock(sc);
+ DELAY(1000);
+ atiixp_lock(sc);
+ if (sc->codec_not_ready_bits)
+ break;
+ }
+
+ atiixp_disable_interrupts(sc);
+
+ if (timeout == 0) {
+ device_printf(sc->dev,
+ "WARNING: timeout during codec detection; "
+ "codecs might be present but haven't interrupted\n");
+ atiixp_unlock(sc);
+ return;
+ }
+
+ found = 0;
+
+ /*
+ * ATI IXP can have upto 3 codecs, but single codec should be
+ * suffice for now.
+ */
+ if (!(sc->codec_not_ready_bits &
+ ATI_REG_ISR_CODEC0_NOT_READY)) {
+ /* codec 0 present */
+ sc->codec_found++;
+ sc->codec_idx = 0;
+ found++;
+ }
+
+ if (!(sc->codec_not_ready_bits &
+ ATI_REG_ISR_CODEC1_NOT_READY)) {
+ /* codec 1 present */
+ sc->codec_found++;
+ }
+
+ if (!(sc->codec_not_ready_bits &
+ ATI_REG_ISR_CODEC2_NOT_READY)) {
+ /* codec 2 present */
+ sc->codec_found++;
+ }
+
+ atiixp_unlock(sc);
+
+ if (found == 0)
+ return;
+
+ /* create/init mixer */
+ sc->codec = AC97_CREATE(sc->dev, sc, atiixp_ac97);
+ if (sc->codec == NULL)
+ goto postinitbad;
+
+ mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
+
+ if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
+ goto postinitbad;
+
+ for (i = 0; i < ATI_IXP_NPCHAN; i++)
+ pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc);
+ for (i = 0; i < ATI_IXP_NRCHAN; i++)
+ pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
+
+ snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
+ rman_get_start(sc->reg), rman_get_start(sc->irq),
+ PCM_KLDSTRING(snd_atiixp));
+
+ pcm_setstatus(sc->dev, status);
+
+ atiixp_lock(sc);
+ atiixp_enable_interrupts(sc);
+ atiixp_unlock(sc);
+
+ return;
+
+postinitbad:
+ if (sc->codec)
+ ac97_destroy(sc->codec);
+ if (sc->ih)
+ bus_teardown_intr(sc->dev, sc->irq, sc->ih);
+ if (sc->reg)
+ bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg);
+ if (sc->irq)
+ bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ if (sc->parent_dmat)
+ bus_dma_tag_destroy(sc->parent_dmat);
+ if (sc->sgd_dmamap)
+ bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
+ if (sc->sgd_dmat)
+ bus_dma_tag_destroy(sc->sgd_dmat);
+ if (sc->lock)
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+}
+
+static int
+atiixp_pci_probe(device_t dev)
+{
+ int i;
+ uint16_t devid, vendor;
+
+ vendor = pci_get_vendor(dev);
+ devid = pci_get_device(dev);
+ for (i = 0; i < sizeof(atiixp_hw)/sizeof(atiixp_hw[0]); i++) {
+ if (vendor == atiixp_hw[i].vendor &&
+ devid == atiixp_hw[i].devid) {
+ device_set_desc(dev, atiixp_hw[i].desc);
+ return BUS_PROBE_DEFAULT;
+ }
+ }
+
+ return ENXIO;
+}
+
+static int
+atiixp_pci_attach(device_t dev)
+{
+ struct atiixp_info *sc;
+ int i;
+
+ if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
+ sc->dev = dev;
+ /*
+ * Default DMA segments per playback / recording channel
+ */
+ sc->dma_segs = ATI_IXP_DMA_CHSEGS;
+
+ pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+ pci_enable_busmaster(dev);
+
+ sc->regid = PCIR_BAR(0);
+ sc->regtype = SYS_RES_MEMORY;
+ sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid,
+ RF_ACTIVE);
+
+ if (!sc->reg) {
+ device_printf(dev, "unable to allocate register space\n");
+ goto bad;
+ }
+
+ sc->st = rman_get_bustag(sc->reg);
+ sc->sh = rman_get_bushandle(sc->reg);
+
+ sc->bufsz = pcm_getbuffersize(dev, 4096, ATI_IXP_DEFAULT_BUFSZ, 65536);
+
+ sc->irqid = 0;
+ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->irq ||
+ snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
+ atiixp_intr, sc, &sc->ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ /*
+ * Let the user choose the best DMA segments.
+ */
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "dma_segs",
+ &i) == 0) {
+ if (i < ATI_IXP_DMA_CHSEGS_MIN)
+ i = ATI_IXP_DMA_CHSEGS_MIN;
+ if (i > ATI_IXP_DMA_CHSEGS_MAX)
+ i = ATI_IXP_DMA_CHSEGS_MAX;
+ /* round the value */
+ sc->dma_segs = i & ~1;
+ }
+
+ /*
+ * DMA tag for scatter-gather buffers and link pointers
+ */
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &sc->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/sc->dma_segs * ATI_IXP_NCHANS *
+ sizeof(struct atiixp_dma_op),
+ /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &sc->sgd_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
+ BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
+ goto bad;
+
+ if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
+ sc->dma_segs * ATI_IXP_NCHANS *
+ sizeof(struct atiixp_dma_op),
+ atiixp_dma_cb, sc, 0))
+ goto bad;
+
+
+ atiixp_chip_pre_init(sc);
+
+ sc->delayed_attach.ich_func = atiixp_chip_post_init;
+ sc->delayed_attach.ich_arg = sc;
+ if (cold == 0 ||
+ config_intrhook_establish(&sc->delayed_attach) != 0) {
+ sc->delayed_attach.ich_func = NULL;
+ atiixp_chip_post_init(sc);
+ }
+
+ return 0;
+
+bad:
+ if (sc->codec)
+ ac97_destroy(sc->codec);
+ if (sc->ih)
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ if (sc->reg)
+ bus_release_resource(dev, sc->regtype, sc->regid, sc->reg);
+ if (sc->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ if (sc->parent_dmat)
+ bus_dma_tag_destroy(sc->parent_dmat);
+ if (sc->sgd_dmamap)
+ bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
+ if (sc->sgd_dmat)
+ bus_dma_tag_destroy(sc->sgd_dmat);
+ if (sc->lock)
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+
+ return ENXIO;
+}
+
+static int
+atiixp_pci_detach(device_t dev)
+{
+ int r;
+ struct atiixp_info *sc;
+
+ r = pcm_unregister(dev);
+ if (r)
+ return r;
+
+ sc = pcm_getdevinfo(dev);
+
+ atiixp_disable_interrupts(sc);
+
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ bus_release_resource(dev, sc->regtype, sc->regid, sc->reg);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ bus_dma_tag_destroy(sc->parent_dmat);
+ bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
+ bus_dma_tag_destroy(sc->sgd_dmat);
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+
+ return 0;
+}
+
+static device_method_t atiixp_methods[] = {
+ DEVMETHOD(device_probe, atiixp_pci_probe),
+ DEVMETHOD(device_attach, atiixp_pci_attach),
+ DEVMETHOD(device_detach, atiixp_pci_detach),
+ { 0, 0 }
+};
+
+static driver_t atiixp_driver = {
+ "pcm",
+ atiixp_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_atiixp, pci, atiixp_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_atiixp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_atiixp, 1);
diff --git a/sys/dev/sound/pci/atiixp.h b/sys/dev/sound/pci/atiixp.h
new file mode 100644
index 0000000..95af41c
--- /dev/null
+++ b/sys/dev/sound/pci/atiixp.h
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ATIIXP_H_
+#define _ATIIXP_H_
+
+/*
+ * Constants, pretty much FreeBSD specific.
+ */
+
+/* Number of playback / recording channel */
+#define ATI_IXP_NPCHAN 1
+#define ATI_IXP_NRCHAN 1
+#define ATI_IXP_NCHANS (ATI_IXP_NPCHAN + ATI_IXP_NRCHAN)
+
+/*
+ * Maximum segments/descriptors is 256, but 2 for
+ * each channel should be more than enough for us.
+ */
+#define ATI_IXP_DMA_CHSEGS 2
+#define ATI_IXP_DMA_CHSEGS_MIN 2
+#define ATI_IXP_DMA_CHSEGS_MAX 256
+
+#define ATI_IXP_DEFAULT_BUFSZ (1 << 12) /* 4096 */
+
+#define ATI_VENDOR_ID 0x1002 /* ATI Technologies */
+#define ATI_IXP_200_ID 0x4341
+#define ATI_IXP_300_ID 0x4361
+#define ATI_IXP_400_ID 0x4370
+
+#define ATI_IXP_BASE_RATE 48000
+
+/*
+ * Register definitions for ATI IXP
+ *
+ * References: ALSA snd-atiixp.c , OpenBSD/NetBSD auixp-*.h
+ */
+
+#define ATI_IXP_CODECS 3
+
+#define ATI_REG_ISR 0x00 /* interrupt source */
+#define ATI_REG_ISR_IN_XRUN (1U<<0)
+#define ATI_REG_ISR_IN_STATUS (1U<<1)
+#define ATI_REG_ISR_OUT_XRUN (1U<<2)
+#define ATI_REG_ISR_OUT_STATUS (1U<<3)
+#define ATI_REG_ISR_SPDF_XRUN (1U<<4)
+#define ATI_REG_ISR_SPDF_STATUS (1U<<5)
+#define ATI_REG_ISR_PHYS_INTR (1U<<8)
+#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9)
+#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10)
+#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11)
+#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12)
+#define ATI_REG_ISR_NEW_FRAME (1U<<13)
+
+#define ATI_REG_IER 0x04 /* interrupt enable */
+#define ATI_REG_IER_IN_XRUN_EN (1U<<0)
+#define ATI_REG_IER_IO_STATUS_EN (1U<<1)
+#define ATI_REG_IER_OUT_XRUN_EN (1U<<2)
+#define ATI_REG_IER_OUT_XRUN_COND (1U<<3)
+#define ATI_REG_IER_SPDF_XRUN_EN (1U<<4)
+#define ATI_REG_IER_SPDF_STATUS_EN (1U<<5)
+#define ATI_REG_IER_PHYS_INTR_EN (1U<<8)
+#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9)
+#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10)
+#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11)
+#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12)
+#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO) */
+#define ATI_REG_IER_SET_BUS_BUSY (1U<<14) /* (WO) audio is running */
+
+#define ATI_REG_CMD 0x08 /* command */
+#define ATI_REG_CMD_POWERDOWN (1U<<0)
+#define ATI_REG_CMD_RECEIVE_EN (1U<<1)
+#define ATI_REG_CMD_SEND_EN (1U<<2)
+#define ATI_REG_CMD_STATUS_MEM (1U<<3)
+#define ATI_REG_CMD_SPDF_OUT_EN (1U<<4)
+#define ATI_REG_CMD_SPDF_STATUS_MEM (1U<<5)
+#define ATI_REG_CMD_SPDF_THRESHOLD (3U<<6)
+#define ATI_REG_CMD_SPDF_THRESHOLD_SHIFT 6
+#define ATI_REG_CMD_IN_DMA_EN (1U<<8)
+#define ATI_REG_CMD_OUT_DMA_EN (1U<<9)
+#define ATI_REG_CMD_SPDF_DMA_EN (1U<<10)
+#define ATI_REG_CMD_SPDF_OUT_STOPPED (1U<<11)
+#define ATI_REG_CMD_SPDF_CONFIG_MASK (7U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_34 (1U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_78 (2U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_69 (3U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_01 (4U<<12)
+#define ATI_REG_CMD_INTERLEAVE_SPDF (1U<<16)
+#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20)
+#define ATI_REG_CMD_INTERLEAVE_IN (1U<<21)
+#define ATI_REG_CMD_INTERLEAVE_OUT (1U<<22)
+#define ATI_REG_CMD_LOOPBACK_EN (1U<<23)
+#define ATI_REG_CMD_PACKED_DIS (1U<<24)
+#define ATI_REG_CMD_BURST_EN (1U<<25)
+#define ATI_REG_CMD_PANIC_EN (1U<<26)
+#define ATI_REG_CMD_MODEM_PRESENT (1U<<27)
+#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28)
+#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29)
+#define ATI_REG_CMD_AC_SYNC (1U<<30)
+#define ATI_REG_CMD_AC_RESET (1U<<31)
+
+#define ATI_REG_PHYS_OUT_ADDR 0x0c
+#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0)
+#define ATI_REG_PHYS_OUT_RW (1U<<2)
+#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8)
+#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9
+#define ATI_REG_PHYS_OUT_DATA_SHIFT 16
+
+#define ATI_REG_PHYS_IN_ADDR 0x10
+#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8)
+#define ATI_REG_PHYS_IN_ADDR_SHIFT 9
+#define ATI_REG_PHYS_IN_DATA_SHIFT 16
+
+#define ATI_REG_SLOTREQ 0x14
+
+#define ATI_REG_COUNTER 0x18
+#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */
+#define ATI_REG_COUNTER_BITCLOCK (31U<<8)
+
+#define ATI_REG_IN_FIFO_THRESHOLD 0x1c
+
+#define ATI_REG_IN_DMA_LINKPTR 0x20
+#define ATI_REG_IN_DMA_DT_START 0x24 /* RO */
+#define ATI_REG_IN_DMA_DT_NEXT 0x28 /* RO */
+#define ATI_REG_IN_DMA_DT_CUR 0x2c /* RO */
+#define ATI_REG_IN_DMA_DT_SIZE 0x30
+
+#define ATI_REG_OUT_DMA_SLOT 0x34
+#define ATI_REG_OUT_DMA_SLOT_BIT(x) (1U << ((x) - 3))
+#define ATI_REG_OUT_DMA_SLOT_MASK 0x1ff
+#define ATI_REG_OUT_DMA_THRESHOLD_MASK 0xf800
+#define ATI_REG_OUT_DMA_THRESHOLD_SHIFT 11
+
+#define ATI_REG_OUT_DMA_LINKPTR 0x38
+#define ATI_REG_OUT_DMA_DT_START 0x3c /* RO */
+#define ATI_REG_OUT_DMA_DT_NEXT 0x40 /* RO */
+#define ATI_REG_OUT_DMA_DT_CUR 0x44 /* RO */
+#define ATI_REG_OUT_DMA_DT_SIZE 0x48
+
+#define ATI_REG_SPDF_CMD 0x4c
+#define ATI_REG_SPDF_CMD_LFSR (1U<<4)
+#define ATI_REG_SPDF_CMD_SINGLE_CH (1U<<5)
+#define ATI_REG_SPDF_CMD_LFSR_ACC (0xff<<8) /* RO */
+
+#define ATI_REG_SPDF_DMA_LINKPTR 0x50
+#define ATI_REG_SPDF_DMA_DT_START 0x54 /* RO */
+#define ATI_REG_SPDF_DMA_DT_NEXT 0x58 /* RO */
+#define ATI_REG_SPDF_DMA_DT_CUR 0x5c /* RO */
+#define ATI_REG_SPDF_DMA_DT_SIZE 0x60
+
+#define ATI_REG_MODEM_MIRROR 0x7c
+#define ATI_REG_AUDIO_MIRROR 0x80
+
+#define ATI_REG_6CH_REORDER 0x84 /* reorder slots for 6ch */
+#define ATI_REG_6CH_REORDER_EN (1U<<0) /* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */
+
+#define ATI_REG_FIFO_FLUSH 0x88
+#define ATI_REG_FIFO_OUT_FLUSH (1U<<0)
+#define ATI_REG_FIFO_IN_FLUSH (1U<<1)
+
+/* LINKPTR */
+#define ATI_REG_LINKPTR_EN (1U<<0)
+
+/* [INT|OUT|SPDIF]_DMA_DT_SIZE */
+#define ATI_REG_DMA_DT_SIZE (0xffffU<<0)
+#define ATI_REG_DMA_FIFO_USED (0x1fU<<16)
+#define ATI_REG_DMA_FIFO_FREE (0x1fU<<21)
+#define ATI_REG_DMA_STATE (7U<<26)
+
+#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */
+
+/* codec detection constant indicating the interrupt flags */
+#define ALL_CODECS_NOT_READY \
+ (ATI_REG_ISR_CODEC0_NOT_READY | ATI_REG_ISR_CODEC1_NOT_READY |\
+ ATI_REG_ISR_CODEC2_NOT_READY)
+#define CODEC_CHECK_BITS (ALL_CODECS_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+
+#endif
OpenPOWER on IntegriCloud