summaryrefslogtreecommitdiffstats
path: root/sys/dev/pcm/pci/t4dwave.c
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>1999-09-01 04:08:39 +0000
committercg <cg@FreeBSD.org>1999-09-01 04:08:39 +0000
commit73a7a67d13cba01eb39397ff54125ce8d3e6f8cc (patch)
tree45f897f70e6c72dbb1af7c4e6103dfa70ae3542b /sys/dev/pcm/pci/t4dwave.c
parentfeefbc8c0eaa6a6e695c1e9c3514be8d22e881ea (diff)
downloadFreeBSD-src-73a7a67d13cba01eb39397ff54125ce8d3e6f8cc.zip
FreeBSD-src-73a7a67d13cba01eb39397ff54125ce8d3e6f8cc.tar.gz
say hello to newpcm. it is not yet enabled, requiring new pnp code from dfr
to compile successfully. further details will be provided in the commit enabling newpcm.
Diffstat (limited to 'sys/dev/pcm/pci/t4dwave.c')
-rw-r--r--sys/dev/pcm/pci/t4dwave.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/sys/dev/pcm/pci/t4dwave.c b/sys/dev/pcm/pci/t4dwave.c
new file mode 100644
index 0000000..5404fe2
--- /dev/null
+++ b/sys/dev/pcm/pci/t4dwave.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/t4dwave.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+/* -------------------------------------------------------------------- */
+
+struct tr_info;
+
+/* channel registers */
+struct tr_chinfo {
+ u_int32_t cso, alpha, fms, fmc, ec;
+ u_int32_t lba;
+ u_int32_t eso, delta;
+ u_int32_t rvol, cvol;
+ u_int32_t gvsel, pan, vol, ctrl;
+ int index;
+ snd_dbuf *buffer;
+ pcm_channel *channel;
+ struct tr_info *parent;
+};
+
+/* device private data */
+struct tr_info {
+ u_int32_t type;
+
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ struct resource *reg, *irq;
+ int regtype, regid, irqid;
+ void *ih;
+
+ u_int32_t playchns;
+ struct tr_chinfo chinfo[TR_MAXPLAYCH];
+ struct tr_chinfo recchinfo;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* channel interface */
+static void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int trchan_setdir(void *data, int dir);
+static int trchan_setformat(void *data, u_int32_t format);
+static int trchan_setspeed(void *data, u_int32_t speed);
+static int trchan_setblocksize(void *data, u_int32_t blocksize);
+static int trchan_trigger(void *data, int go);
+static int trchan_getptr(void *data);
+static pcmchan_caps *trchan_getcaps(void *data);
+
+/* talk to the codec - called from ac97.c */
+static u_int32_t tr_rdcd(void *, int);
+static void tr_wrcd(void *, int, u_int32_t);
+
+/* stuff */
+static int tr_init(struct tr_info *);
+static void tr_intr(void *);
+
+/* talk to the card */
+static u_int32_t tr_rd(struct tr_info *, int, int);
+static void tr_wr(struct tr_info *, int, u_int32_t, int);
+
+/* manipulate playback channels */
+static void tr_clrint(struct tr_info *, char);
+static void tr_enaint(struct tr_info *, char, int);
+static u_int32_t tr_testint(struct tr_info *, char);
+static void tr_rdch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_wrch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_selch(struct tr_info *, char);
+static void tr_startch(struct tr_info *, char);
+static void tr_stopch(struct tr_info *, char);
+
+/* -------------------------------------------------------------------- */
+
+static pcmchan_caps tr_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps tr_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_U16_LE
+};
+
+static pcm_channel tr_chantemplate = {
+ trchan_init,
+ trchan_setdir,
+ trchan_setformat,
+ trchan_setspeed,
+ trchan_setblocksize,
+ trchan_trigger,
+ trchan_getptr,
+ trchan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t
+tr_fmttobits(u_int32_t fmt)
+{
+ u_int32_t bits = 0;
+ bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
+ bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0;
+ bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0;
+ return bits;
+}
+
+/* Hardware */
+
+static u_int32_t
+tr_rd(struct tr_info *tr, int regno, int size)
+{
+ switch(size) {
+ case 1:
+ return bus_space_read_1(tr->st, tr->sh, regno);
+ case 2:
+ return bus_space_read_2(tr->st, tr->sh, regno);
+ case 4:
+ return bus_space_read_4(tr->st, tr->sh, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
+{
+ switch(size) {
+ case 1:
+ bus_space_write_1(tr->st, tr->sh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(tr->st, tr->sh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(tr->st, tr->sh, regno, data);
+ break;
+ }
+}
+
+/* ac97 codec */
+
+static u_int32_t
+tr_rdcd(void *devinfo, int regno)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECRD;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
+ trw=TNX_CDC_RWSTAT;
+ break;
+ default:
+ printf("!!! tr_rdcd defaulted !!!\n");
+ return 0xffffffff;
+ }
+
+ regno &= 0x7f;
+ tr_wr(tr, treg, regno | trw, 4);
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ if (i == 0) printf("codec timeout during read of register %x\n", regno);
+ return (j >> TR_CDC_DATA) & 0xffff;
+}
+
+static void
+tr_wrcd(void *devinfo, int regno, u_int32_t data)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECWR;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=TNX_REG_CODECWR;
+ trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
+ break;
+ default:
+ printf("!!! tr_wrcd defaulted !!!");
+ return;
+ }
+
+ regno &= 0x7f;
+#if 0
+ printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
+#endif
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
+#if 0
+ printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
+#endif
+ if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
+}
+
+/* playback channel interrupts */
+
+static u_int32_t
+tr_testint(struct tr_info *tr, char channel)
+{
+ return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 4) & (1<<(channel & 0x1f));
+}
+
+static void
+tr_clrint(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_enaint(struct tr_info *tr, char channel, int enable)
+{
+ u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA;
+ u_int32_t i = tr_rd(tr, reg, 4);
+ channel &= 0x1f;
+ i &= ~(1 << channel);
+ i |= (enable? 1 : 0) << channel;
+ tr_clrint(tr, channel);
+ tr_wr(tr, reg, i, 4);
+}
+
+/* playback channels */
+
+static void
+tr_selch(struct tr_info *tr, char channel)
+{
+ int i=tr_rd(tr, TR_REG_CIR, 4);
+ i &= ~TR_CIR_MASK;
+ i |= channel & 0x3f;
+ tr_wr(tr, TR_REG_CIR, i, 4);
+}
+
+static void
+tr_startch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_stopch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[TR_CHN_REGS], i;
+
+ ch->gvsel &= 0x00000001;
+ ch->fmc &= 0x00000003;
+ ch->fms &= 0x0000000f;
+ ch->ctrl &= 0x0000000f;
+ ch->pan &= 0x0000007f;
+ ch->rvol &= 0x0000007f;
+ ch->cvol &= 0x0000007f;
+ ch->vol &= 0x000000ff;
+ ch->ec &= 0x00000fff;
+ ch->alpha &= 0x00000fff;
+ ch->delta &= 0x0000ffff;
+ ch->lba &= 0x3fffffff;
+
+ cr[1]=ch->lba;
+ cr[3]=(ch->rvol<<7) | (ch->cvol);
+ cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec);
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ ch->cso &= 0x0000ffff;
+ ch->eso &= 0x0000ffff;
+ cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
+ cr[2]=(ch->eso<<16) | (ch->delta);
+ cr[3]|=0x0000c000;
+ break;
+ case TNX_PCI_ID:
+ ch->cso &= 0x00ffffff;
+ ch->eso &= 0x00ffffff;
+ cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
+ cr[2]=((ch->delta>>16)<<24) | (ch->eso);
+ cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
+ break;
+ }
+ tr_selch(tr, channel);
+ for (i=0; i<TR_CHN_REGS; i++)
+ tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
+}
+
+static void
+tr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[5], i;
+ tr_selch(tr, channel);
+ for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
+ ch->lba= (cr[1] & 0x3fffffff);
+ ch->fmc= (cr[3] & 0x0000c000) >> 14;
+ ch->rvol= (cr[3] & 0x00003f80) >> 7;
+ ch->cvol= (cr[3] & 0x0000007f);
+ ch->gvsel= (cr[4] & 0x80000000) >> 31;
+ ch->pan= (cr[4] & 0x7f000000) >> 24;
+ ch->vol= (cr[4] & 0x00ff0000) >> 16;
+ ch->ctrl= (cr[4] & 0x0000f000) >> 12;
+ ch->ec= (cr[4] & 0x00000fff);
+ switch(tr->type) {
+ case TDX_PCI_ID:
+ ch->cso= (cr[0] & 0xffff0000) >> 16;
+ ch->alpha= (cr[0] & 0x0000fff0) >> 4;
+ ch->fms= (cr[0] & 0x0000000f);
+ ch->eso= (cr[2] & 0xffff0000) >> 16;
+ ch->delta= (cr[2] & 0x0000ffff);
+ break;
+ case TNX_PCI_ID:
+ ch->cso= (cr[0] & 0x00ffffff);
+ ch->eso= (cr[2] & 0x00ffffff);
+ ch->delta= ((cr[2] & 0xff000000) >> 16) |
+ ((cr[0] & 0xff000000) >> 24);
+ ch->alpha= (cr[3] & 0xfff00000) >> 20;
+ ch->fms= (cr[3] & 0x000f0000) >> 16;
+ break;
+ }
+}
+
+/* channel interface */
+
+void *
+trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct tr_info *tr = devinfo;
+ struct tr_chinfo *ch;
+ if (dir == PCMDIR_PLAY) {
+ ch = &tr->chinfo[tr->playchns];
+ ch->index = tr->playchns++;
+ } else {
+ ch = &tr->recchinfo;
+ ch->index = -1;
+ }
+ ch->buffer = b;
+ ch->buffer->bufsize = TR_BUFFSIZE;
+ ch->parent = tr;
+ ch->channel = c;
+ if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL;
+ else return ch;
+}
+
+static int
+trchan_setdir(void *data, int dir)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (dir == PCMDIR_PLAY && ch->index >= 0) {
+ ch->fmc = ch->fms = ch->ec = ch->alpha = 0;
+ ch->lba = vtophys(ch->buffer->buf);
+ ch->cso = 0;
+ ch->eso = ch->buffer->bufsize - 1;
+ ch->rvol = ch->cvol = 0;
+ ch->gvsel = 0;
+ ch->pan = 0;
+ ch->vol = 0;
+ ch->ctrl = 0x01;
+ ch->delta = 0;
+ tr_wrch(tr, ch->index, ch);
+ tr_enaint(tr, ch->index, 1);
+ } else if (dir == PCMDIR_REC && ch->index == -1) {
+ /* set up dma mode regs */
+ u_int32_t i;
+ tr_wr(tr, TR_REG_DMAR15, 0, 1);
+ i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
+ tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
+ /* set up base address */
+ tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4);
+ /* set up buffer size */
+ i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
+ tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4);
+ } else return -1;
+ return 0;
+}
+
+static int
+trchan_setformat(void *data, u_int32_t format)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ u_int32_t bits = tr_fmttobits(format);
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->eso = (ch->buffer->bufsize / ch->buffer->sample_size) - 1;
+ ch->ctrl = bits | 0x01;
+ tr_wrch(tr, ch->index, ch);
+ } else {
+ u_int32_t i;
+ /* set # of samples between interrupts */
+ i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1;
+ tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
+ /* set sample format */
+ i = 0x18 | (bits << 4);
+ tr_wr(tr, TR_REG_SBCTRL, i, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_setspeed(void *data, u_int32_t speed)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->delta = (speed << 12) / 48000;
+ tr_wrch(tr, ch->index, ch);
+ return (ch->delta * 48000) >> 12;
+ } else {
+ /* setup speed */
+ ch->delta = (48000 << 12) / speed;
+ tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
+ return (48000 << 12) / ch->delta;
+ }
+ return 0;
+}
+
+static int
+trchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ struct tr_chinfo *ch = data;
+ return ch->buffer->bufsize / 2;
+}
+
+static int
+trchan_trigger(void *data, int go)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ if (go == PCMTRIG_START) tr_startch(tr, ch->index);
+ else tr_stopch(tr, ch->index);
+ } else {
+ u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7;
+ tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_getptr(void *data)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ return ch->cso * ch->buffer->sample_size;
+ } else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf);
+}
+
+static pcmchan_caps *
+trchan_getcaps(void *data)
+{
+ struct tr_chinfo *ch = data;
+ return (ch->index >= 0)? &tr_playcaps : &tr_reccaps;
+}
+
+/* The interrupt handler */
+
+static void
+tr_intr(void *p)
+{
+ struct tr_info *tr = (struct tr_info *)p;
+ u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
+
+ if (intsrc & TR_INT_ADDR) {
+ int i;
+ for (i = 0; i < tr->playchns; i++) {
+ if (tr_testint(tr, i)) {
+ chn_intr(tr->chinfo[i].channel);
+ tr_clrint(tr, i);
+ }
+ }
+ }
+ if (intsrc & TR_INT_SB) {
+ chn_intr(tr->recchinfo.channel);
+ tr_rd(tr, TR_REG_SBR9, 1);
+ tr_rd(tr, TR_REG_SBR10, 1);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+tr_init(struct tr_info *tr)
+{
+ if (tr->type == TDX_PCI_ID) {
+ tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
+ } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
+
+ tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
+ tr->playchns = 0;
+ return 0;
+}
+
+static int
+tr_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == TDX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave DX");
+ return 0;
+ }
+ if (pci_get_devid(dev) == TNX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave NX");
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+tr_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct tr_info *tr;
+ struct ac97_info *codec;
+ int i;
+ int mapped;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(tr, sizeof(*tr));
+ tr->type = pci_get_devid(dev);
+
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ mapped = 0;
+ /* XXX dfr: is this strictly necessary? */
+ for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
+ tr->regid = PCIR_MAPS + i*4;
+ tr->regtype = SYS_RES_MEMORY;
+ tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!tr->reg) {
+ tr->regtype = SYS_RES_IOPORT;
+ tr->reg = bus_alloc_resource(dev, tr->regtype,
+ &tr->regid, 0, ~0, 1,
+ RF_ACTIVE);
+ }
+ if (tr->reg) {
+ tr->st = rman_get_bustag(tr->reg);
+ tr->sh = rman_get_bushandle(tr->reg);
+ mapped++;
+ }
+ }
+
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (tr_init(tr) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = ac97_create(tr, tr_rdcd, tr_wrcd);
+ if (codec == NULL) goto bad;
+ mixer_init(d, &ac97_mixer, codec);
+
+ tr->irqid = 0;
+ tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (!tr->irq ||
+ bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) {
+ device_printf(dev, "unable to map interrupt\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*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &tr->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, 64, "at %s 0x%lx irq %ld",
+ (tr->regtype == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(tr->reg), rman_get_start(tr->irq));
+
+ if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr);
+ for (i = 0; i < TR_MAXPLAYCH; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+bad:
+ if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
+ if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
+ if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
+ free(tr, M_DEVBUF);
+ return ENXIO;
+}
+
+static device_method_t tr_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tr_pci_probe),
+ DEVMETHOD(device_attach, tr_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t tr_driver = {
+ "pcm",
+ tr_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(tr, pci, tr_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
OpenPOWER on IntegriCloud