summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pci/envy24ht.c
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2006-06-17 14:36:44 +0000
committernetchild <netchild@FreeBSD.org>2006-06-17 14:36:44 +0000
commit89b30c059ce7057890d61d0e541e2133ba533930 (patch)
tree91e403cf29a159cb8202f6a7c5108d0bb041236a /sys/dev/sound/pci/envy24ht.c
parent3a6d7ed2cf9128621b9e35fd816b275b2dcef3e9 (diff)
downloadFreeBSD-src-89b30c059ce7057890d61d0e541e2133ba533930.zip
FreeBSD-src-89b30c059ce7057890d61d0e541e2133ba533930.tar.gz
dd the envy24 driver as is to the tree. It's not connected to the build
yet. More commits to follow. I got no response from the author, but since the driver is BSD licensed I don't think he will complain. :-) I got it from http://people.freebsd.org/~lofi/envy24.tar.gz Written by: Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
Diffstat (limited to 'sys/dev/sound/pci/envy24ht.c')
-rw-r--r--sys/dev/sound/pci/envy24ht.c2445
1 files changed, 2445 insertions, 0 deletions
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
new file mode 100644
index 0000000..c23efc3
--- /dev/null
+++ b/sys/dev/sound/pci/envy24ht.c
@@ -0,0 +1,2445 @@
+/*
+ * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
+ * 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$
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/ak452x.h>
+#include <dev/sound/pci/envy24.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include "mixer_if.h"
+
+MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio");
+
+/* -------------------------------------------------------------------- */
+
+struct sc_info;
+
+#define ENVY24_PLAY_CHNUM 10
+#define ENVY24_REC_CHNUM 12
+#define ENVY24_PLAY_BUFUNIT (4 /* byte/sample */ * 10 /* channel */)
+#define ENVY24_REC_BUFUNIT (4 /* byte/sample */ * 12 /* channel */)
+#define ENVY24_SAMPLE_NUM 4096
+
+#define ENVY24_TIMEOUT 1000
+
+#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
+
+#define ENVY24_NAMELEN 32
+
+typedef volatile u_int32_t sample32_t;
+
+/* channel registers */
+struct sc_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_info *parent;
+ int dir;
+ unsigned num; /* hw channel number */
+
+ /* channel information */
+ u_int32_t format;
+ u_int32_t speed;
+ u_int32_t blk; /* hw block size(dword) */
+
+ /* format conversion structure */
+ u_int8_t *data;
+ unsigned int size; /* data buffer size(byte) */
+ int unit; /* sample size(byte) */
+ unsigned int offset; /* samples number offset */
+ void (*emldma)(struct sc_chinfo *);
+
+ /* flags */
+ int run;
+};
+
+/* codec interface entrys */
+struct codec_entry {
+ void *(*create)(device_t dev, void *devinfo, int dir, int num);
+ void (*destroy)(void *codec);
+ void (*init)(void *codec);
+ void (*reinit)(void *codec);
+ void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right);
+ void (*setrate)(void *codec, int which, int rate);
+};
+
+/* system configuration information */
+struct cfg_info {
+ char *name;
+ u_int16_t subvendor, subdevice;
+ u_int8_t scfg, acl, i2s, spdif;
+ u_int8_t gpiomask, gpiostate, gpiodir;
+ u_int8_t free;
+ struct codec_entry *codec;
+};
+
+/* device private data */
+struct sc_info {
+ device_t dev;
+ void *lock;
+
+ /* Control/Status registor */
+ struct resource *cs;
+ int csid;
+ bus_space_tag_t cst;
+ bus_space_handle_t csh;
+ /* DDMA registor */
+ struct resource *ddma;
+ int ddmaid;
+ bus_space_tag_t ddmat;
+ bus_space_handle_t ddmah;
+ /* Consumer Section DMA Channel Registers */
+ struct resource *ds;
+ int dsid;
+ bus_space_tag_t dst;
+ bus_space_handle_t dsh;
+ /* MultiTrack registor */
+ struct resource *mt;
+ int mtid;
+ bus_space_tag_t mtt;
+ bus_space_handle_t mth;
+ /* DMA tag */
+ bus_dma_tag_t dmat;
+ /* IRQ resource */
+ struct resource *irq;
+ int irqid;
+ void *ih;
+
+ /* system configuration data */
+ struct cfg_info *cfg;
+
+ /* ADC/DAC number and info */
+ int adcn, dacn;
+ void *adc[4], *dac[4];
+
+ /* mixer control data */
+ u_int32_t src;
+ u_int8_t left[ENVY24_CHAN_NUM];
+ u_int8_t right[ENVY24_CHAN_NUM];
+
+ /* Play/Record DMA fifo */
+ sample32_t *pbuf;
+ sample32_t *rbuf;
+ u_int32_t psize, rsize; /* DMA buffer size(byte) */
+ u_int16_t blk[2]; /* transfer check blocksize(dword) */
+ bus_dmamap_t pmap, rmap;
+
+ /* current status */
+ u_int32_t speed;
+ int run[2];
+ u_int16_t intr[2];
+ struct pcmchan_caps caps[2];
+
+ /* channel info table */
+ unsigned chnum;
+ struct sc_chinfo chan[11];
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* DMA emulator */
+static void envy24_p8u(struct sc_chinfo *);
+static void envy24_p16sl(struct sc_chinfo *);
+static void envy24_p32sl(struct sc_chinfo *);
+static void envy24_r16sl(struct sc_chinfo *);
+static void envy24_r32sl(struct sc_chinfo *);
+
+/* channel interface */
+static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
+static int envy24chan_setformat(kobj_t, void *, u_int32_t);
+static int envy24chan_setspeed(kobj_t, void *, u_int32_t);
+static int envy24chan_setblocksize(kobj_t, void *, u_int32_t);
+static int envy24chan_trigger(kobj_t, void *, int);
+static int envy24chan_getptr(kobj_t, void *);
+static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *);
+
+/* mixer interface */
+static int envy24mixer_init(struct snd_mixer *);
+static int envy24mixer_reinit(struct snd_mixer *);
+static int envy24mixer_uninit(struct snd_mixer *);
+static int envy24mixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
+static u_int32_t envy24mixer_setrecsrc(struct snd_mixer *, u_int32_t);
+
+/* M-Audio Delta series AK4524 access interface */
+static void *envy24_delta_ak4524_create(device_t, void *, int, int);
+static void envy24_delta_ak4524_destroy(void *);
+static void envy24_delta_ak4524_init(void *);
+static void envy24_delta_ak4524_reinit(void *);
+static void envy24_delta_ak4524_setvolume(void *, int, unsigned int, unsigned int);
+
+/* -------------------------------------------------------------------- */
+
+/*
+ system constant tables
+*/
+
+/* API -> hardware channel map */
+static unsigned envy24_chanmap[ENVY24_CHAN_NUM] = {
+ ENVY24_CHAN_PLAY_SPDIF, /* 0 */
+ ENVY24_CHAN_PLAY_DAC1, /* 1 */
+ ENVY24_CHAN_PLAY_DAC2, /* 2 */
+ ENVY24_CHAN_PLAY_DAC3, /* 3 */
+ ENVY24_CHAN_PLAY_DAC4, /* 4 */
+ ENVY24_CHAN_REC_MIX, /* 5 */
+ ENVY24_CHAN_REC_SPDIF, /* 6 */
+ ENVY24_CHAN_REC_ADC1, /* 7 */
+ ENVY24_CHAN_REC_ADC2, /* 8 */
+ ENVY24_CHAN_REC_ADC3, /* 9 */
+ ENVY24_CHAN_REC_ADC4, /* 10 */
+};
+
+/* mixer -> API channel map. see above */
+static int envy24_mixmap[] = {
+ -1, /* Master output level. It is depend on codec support */
+ -1, /* Treble level of all output channels */
+ -1, /* Bass level of all output channels */
+ -1, /* Volume of synthesier input */
+ 0, /* Output level for the audio device */
+ -1, /* Output level for the PC speaker */
+ 7, /* line in jack */
+ -1, /* microphone jack */
+ -1, /* CD audio input */
+ -1, /* Recording monitor */
+ 1, /* alternative codec */
+ -1, /* global recording level */
+ -1, /* Input gain */
+ -1, /* Output gain */
+ 8, /* Input source 1 */
+ 9, /* Input source 2 */
+ 10, /* Input source 3 */
+ 6, /* Digital (input) 1 */
+ -1, /* Digital (input) 2 */
+ -1, /* Digital (input) 3 */
+ -1, /* Phone input */
+ -1, /* Phone output */
+ -1, /* Video/TV (audio) in */
+ -1, /* Radio in */
+ -1, /* Monitor volume */
+};
+
+/* variable rate audio */
+static u_int32_t envy24_speed[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
+ 12000, 11025, 9600, 8000, 0
+};
+
+/* known boards configuration */
+static struct codec_entry delta_codec = {
+ envy24_delta_ak4524_create,
+ envy24_delta_ak4524_destroy,
+ envy24_delta_ak4524_init,
+ envy24_delta_ak4524_reinit,
+ envy24_delta_ak4524_setvolume,
+ NULL, /* setrate */
+};
+
+static struct cfg_info cfg_table[] = {
+ {
+ "Envy24 audio(M Audio Delta Dio 2496)",
+ 0x1412, 0xd631,
+ 0x10, 0x80, 0xf0, 0x03,
+ 0xff, 0x00, 0x00,
+ 0,
+ &delta_codec,
+ },
+ {
+ "Envy24 audio(Generic)",
+ 0, 0,
+ 0x0f, 0x00, 0x01, 0x03,
+ 0xff, 0x00, 0x00,
+ 0,
+ &delta_codec, /* default codec routines */
+ }
+};
+
+static u_int32_t envy24_recfmt[] = {
+ AFMT_STEREO | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S32_LE,
+ 0
+};
+static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0};
+
+static u_int32_t envy24_playfmt[] = {
+ AFMT_STEREO | AFMT_U8,
+ AFMT_STEREO | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S32_LE,
+ 0
+};
+
+static struct pcmchan_caps envy24_playcaps = {8000, 96000, envy24_playfmt, 0};
+
+struct envy24_emldma {
+ u_int32_t format;
+ void (*emldma)(struct sc_chinfo *);
+ int unit;
+};
+
+static struct envy24_emldma envy24_pemltab[] = {
+ {AFMT_STEREO | AFMT_U8, envy24_p8u, 2},
+ {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4},
+ {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8},
+ {0, NULL, 0}
+};
+
+static struct envy24_emldma envy24_remltab[] = {
+ {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4},
+ {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8},
+ {0, NULL, 0}
+};
+
+/* -------------------------------------------------------------------- */
+
+/* common routines */
+static u_int32_t
+envy24_rdcs(struct sc_info *sc, int regno, int size)
+{
+ switch (size) {
+ case 1:
+ return bus_space_read_1(sc->cst, sc->csh, regno);
+ case 2:
+ return bus_space_read_2(sc->cst, sc->csh, regno);
+ case 4:
+ return bus_space_read_4(sc->cst, sc->csh, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+envy24_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+ switch (size) {
+ case 1:
+ bus_space_write_1(sc->cst, sc->csh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(sc->cst, sc->csh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(sc->cst, sc->csh, regno, data);
+ break;
+ }
+}
+
+static u_int32_t
+envy24_rdmt(struct sc_info *sc, int regno, int size)
+{
+ switch (size) {
+ case 1:
+ return bus_space_read_1(sc->mtt, sc->mth, regno);
+ case 2:
+ return bus_space_read_2(sc->mtt, sc->mth, regno);
+ case 4:
+ return bus_space_read_4(sc->mtt, sc->mth, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+envy24_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+ switch (size) {
+ case 1:
+ bus_space_write_1(sc->mtt, sc->mth, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(sc->mtt, sc->mth, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(sc->mtt, sc->mth, regno, data);
+ break;
+ }
+}
+
+static u_int32_t
+envy24_rdci(struct sc_info *sc, int regno)
+{
+ envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
+ return envy24_rdcs(sc, ENVY24_CCS_DATA, 1);
+}
+
+static void
+envy24_wrci(struct sc_info *sc, int regno, u_int32_t data)
+{
+ envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
+ envy24_wrcs(sc, ENVY24_CCS_DATA, data, 1);
+}
+
+/* -------------------------------------------------------------------- */
+
+/* I2C port/E2PROM access routines */
+
+static int
+envy24_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
+{
+ u_int32_t data;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+ if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+ break;
+ DELAY(32); /* 31.25kHz */
+ }
+ if (i == ENVY24_TIMEOUT) {
+ return -1;
+ }
+ envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
+ envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
+ (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_RD, 1);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+ if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+ break;
+ DELAY(32); /* 31.25kHz */
+ }
+ if (i == ENVY24_TIMEOUT) {
+ return -1;
+ }
+ data = envy24_rdcs(sc, ENVY24_CCS_I2CDATA, 1);
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdi2c(): return 0x%x\n", data);
+#endif
+ return (int)data;
+}
+
+static int
+envy24_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
+{
+ u_int32_t tmp;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ tmp = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+ if ((tmp & ENVY24_CCS_I2CSTAT_BSY) == 0)
+ break;
+ DELAY(32); /* 31.25kHz */
+ }
+ if (i == ENVY24_TIMEOUT) {
+ return -1;
+ }
+ envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
+ envy24_wrcs(sc, ENVY24_CCS_I2CDATA, data, 1);
+ envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
+ (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_WR, 1);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+ if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+ break;
+ DELAY(32); /* 31.25kHz */
+ }
+ if (i == ENVY24_TIMEOUT) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+envy24_rdrom(struct sc_info *sc, u_int32_t addr)
+{
+ u_int32_t data;
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdrom(sc, 0x%02x)\n", addr);
+#endif
+ data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+ if ((data & ENVY24_CCS_I2CSTAT_ROM) == 0) {
+#if(0)
+ device_printf(sc->dev, "envy24_rdrom(): E2PROM not presented\n");
+#endif
+ return -1;
+ }
+
+ return envy24_rdi2c(sc, ENVY24_CCS_I2CDEV_ROM, addr);
+}
+
+static struct cfg_info *
+envy24_rom2cfg(struct sc_info *sc)
+{
+ struct cfg_info *buff;
+ int size;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_rom2cfg(sc)\n");
+#endif
+ size = envy24_rdrom(sc, ENVY24_E2PROM_SIZE);
+ if (size < ENVY24_E2PROM_GPIODIR + 1) {
+#if(0)
+ device_printf(sc->dev, "envy24_rom2cfg(): ENVY24_E2PROM_SIZE-->%d\n", size);
+#endif
+ return NULL;
+ }
+ buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+ if (buff == NULL) {
+#if(0)
+ device_printf(sc->dev, "envy24_rom2cfg(): malloc()\n");
+#endif
+ return NULL;
+ }
+ buff->free = 1;
+
+ buff->subvendor = envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR) << 8;
+ buff->subvendor += envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR + 1);
+ buff->subdevice = envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE) << 8;
+ buff->subdevice += envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE + 1);
+ buff->scfg = envy24_rdrom(sc, ENVY24_E2PROM_SCFG);
+ buff->acl = envy24_rdrom(sc, ENVY24_E2PROM_ACL);
+ buff->i2s = envy24_rdrom(sc, ENVY24_E2PROM_I2S);
+ buff->spdif = envy24_rdrom(sc, ENVY24_E2PROM_SPDIF);
+ buff->gpiomask = envy24_rdrom(sc, ENVY24_E2PROM_GPIOMASK);
+ buff->gpiostate = envy24_rdrom(sc, ENVY24_E2PROM_GPIOSTATE);
+ buff->gpiodir = envy24_rdrom(sc, ENVY24_E2PROM_GPIODIR);
+
+ for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++)
+ if (cfg_table[i].subvendor == buff->subvendor &&
+ cfg_table[i].subdevice == buff->subdevice)
+ break;
+ buff->name = cfg_table[i].name;
+ buff->codec = cfg_table[i].codec;
+
+ return buff;
+}
+
+static void
+envy24_cfgfree(struct cfg_info *cfg) {
+ if (cfg == NULL)
+ return;
+ if (cfg->free)
+ free(cfg, M_ENVY24);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* AC'97 codec access routines */
+
+static int
+envy24_coldcd(struct sc_info *sc)
+{
+ u_int32_t data;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_coldcd()\n");
+#endif
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_CLD, 1);
+ DELAY(10);
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
+ DELAY(1000);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+ if (data & ENVY24_MT_AC97CMD_RDY) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+envy24_slavecd(struct sc_info *sc)
+{
+ u_int32_t data;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_slavecd()\n");
+#endif
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD,
+ ENVY24_MT_AC97CMD_CLD | ENVY24_MT_AC97CMD_WRM, 1);
+ DELAY(10);
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
+ DELAY(1000);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+ if (data & ENVY24_MT_AC97CMD_RDY) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+envy24_rdcd(kobj_t obj, void *devinfo, int regno)
+{
+ struct sc_info *sc = (struct sc_info *)devinfo;
+ u_int32_t data;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdcd(obj, sc, 0x%02x)\n", regno);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_RD, 1);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+ if ((data & ENVY24_MT_AC97CMD_RD) == 0)
+ break;
+ }
+ data = envy24_rdmt(sc, ENVY24_MT_AC97DLO, 2);
+
+#if(0)
+ device_printf(sc->dev, "envy24_rdcd(): return 0x%x\n", data);
+#endif
+ return (int)data;
+}
+
+static int
+envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
+{
+ struct sc_info *sc = (struct sc_info *)devinfo;
+ u_int32_t cmd;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
+ envy24_wrmt(sc, ENVY24_MT_AC97DLO, (u_int32_t)data, 2);
+ envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_WR, 1);
+ for (i = 0; i < ENVY24_TIMEOUT; i++) {
+ cmd = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+ if ((cmd & ENVY24_MT_AC97CMD_WR) == 0)
+ break;
+ }
+
+ return 0;
+}
+
+static kobj_method_t envy24_ac97_methods[] = {
+ KOBJMETHOD(ac97_read, envy24_rdcd),
+ KOBJMETHOD(ac97_write, envy24_wrcd),
+ {0, 0}
+};
+AC97_DECLARE(envy24_ac97);
+
+/* -------------------------------------------------------------------- */
+
+/* GPIO access routines */
+
+static u_int32_t
+envy24_gpiord(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIODAT);
+}
+
+static void
+envy24_gpiowr(struct sc_info *sc, u_int32_t data)
+{
+#if(0)
+ device_printf(sc->dev, "envy24_gpiowr(sc, 0x%02x)\n", data & 0xff);
+ return;
+#endif
+ envy24_wrci(sc, ENVY24_CCI_GPIODAT, data);
+ return;
+}
+
+static u_int32_t
+envy24_gpiogetmask(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOMASK);
+}
+
+static void
+envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask);
+ return;
+}
+
+static u_int32_t
+envy24_gpiogetdir(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOCTL);
+}
+
+static void
+envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* M-Audio Delta series AK4524 access interface routine */
+
+struct envy24_delta_ak4524_codec {
+ struct ak452x_info *info;
+ struct sc_info *parent;
+ int dir;
+ int num;
+ int cs, cclk, cdti;
+};
+
+static void
+envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
+{
+ u_int32_t data = 0;
+ struct envy24_delta_ak4524_codec *ptr = codec;
+
+#if(0)
+ device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
+#endif
+ data = envy24_gpiord(ptr->parent);
+ data &= ~(ptr->cs | ptr->cclk | ptr->cdti);
+ if (cs) data += ptr->cs;
+ if (cclk) data += ptr->cclk;
+ if (cdti) data += ptr->cdti;
+ envy24_gpiowr(ptr->parent, data);
+ return;
+}
+
+static void *
+envy24_delta_ak4524_create(device_t dev, void *info, int dir, int num)
+{
+ struct sc_info *sc = info;
+ struct envy24_delta_ak4524_codec *buff = NULL;
+
+#if(0)
+ device_printf(sc->dev, "envy24_delta_ak4524_create(dev, sc, %d, %d)\n", dir, num);
+#endif
+
+ buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+ if (buff == NULL)
+ return NULL;
+
+ if (dir == PCMDIR_PLAY && sc->adc[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info;
+ else if (dir == PCMDIR_REC && sc->dac[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info;
+ else
+ buff->info = ak452x_create(dev, buff, num, envy24_delta_ak4524_ctl);
+ if (buff->info == NULL) {
+ free(buff, M_ENVY24);
+ return NULL;
+ }
+
+ buff->parent = sc;
+ buff->dir = dir;
+ buff->num = num;
+
+ return (void *)buff;
+}
+
+static void
+envy24_delta_ak4524_destroy(void *codec)
+{
+ struct envy24_delta_ak4524_codec *ptr = codec;
+ if (ptr == NULL)
+ return;
+#if(0)
+ device_printf(ptr->parent->dev, "envy24_delta_ak4524_destroy()\n");
+#endif
+
+ if (ptr->dir == PCMDIR_PLAY) {
+ if (ptr->parent->adc[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+ else {
+ if (ptr->parent->dac[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+
+ free(codec, M_ENVY24);
+}
+
+static void
+envy24_delta_ak4524_init(void *codec)
+{
+ u_int32_t gpiomask, gpiodir;
+ struct envy24_delta_ak4524_codec *ptr = codec;
+ if (ptr == NULL)
+ return;
+#if(0)
+ device_printf(ptr->parent->dev, "envy24_delta_ak4524_init()\n");
+#endif
+
+ /*
+ gpiomask = envy24_gpiogetmask(ptr->parent);
+ gpiomask &= ~(ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1);
+ envy24_gpiosetmask(ptr->parent, gpiomask);
+ gpiodir = envy24_gpiogetdir(ptr->parent);
+ gpiodir |= ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1;
+ envy24_gpiosetdir(ptr->parent, gpiodir);
+ */
+ envy24_gpiosetmask(ptr->parent, ENVY24_GPIO_CS8414_STATUS);
+ envy24_gpiosetdir(ptr->parent, ~ENVY24_GPIO_CS8414_STATUS);
+ if (ptr->num == 0)
+ ptr->cs = ENVY24_GPIO_AK4524_CS0;
+ else
+ ptr->cs = ENVY24_GPIO_AK4524_CS1;
+ ptr->cclk = ENVY24_GPIO_AK4524_CCLK;
+ ptr->cdti = ENVY24_GPIO_AK4524_CDTI;
+ ak452x_settype(ptr->info, AK452X_TYPE_4524);
+ ak452x_setcif(ptr->info, ENVY24_DELTA_AK4524_CIF);
+ ak452x_setformat(ptr->info,
+ AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
+ ak452x_setdvc(ptr->info, 0);
+ ak452x_init(ptr->info);
+}
+
+static void
+envy24_delta_ak4524_reinit(void *codec)
+{
+ struct envy24_delta_ak4524_codec *ptr = codec;
+ if (ptr == NULL)
+ return;
+#if(0)
+ device_printf(ptr->parent->dev, "envy24_delta_ak4524_reinit()\n");
+#endif
+
+ ak452x_reinit(ptr->info);
+}
+
+static void
+envy24_delta_ak4524_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
+{
+ struct envy24_delta_ak4524_codec *ptr = codec;
+ if (ptr == NULL)
+ return;
+#if(0)
+ device_printf(ptr->parent->dev, "envy24_delta_ak4524_set()\n");
+#endif
+
+ ak452x_set(ptr->info, dir, left, right);
+}
+
+/*
+ There is no need for AK452[48] codec to set sample rate
+ static void
+ envy24_delta_ak4524_setrate(struct envy24_delta_ak4524_codec *codec, int which, int rate)
+ {
+ }
+*/
+
+/* -------------------------------------------------------------------- */
+
+/* hardware access routeines */
+
+static struct {
+ u_int32_t speed;
+ u_int32_t code;
+} envy24_speedtab[] = {
+ {48000, ENVY24_MT_RATE_48000},
+ {24000, ENVY24_MT_RATE_24000},
+ {12000, ENVY24_MT_RATE_12000},
+ {9600, ENVY24_MT_RATE_9600},
+ {32000, ENVY24_MT_RATE_32000},
+ {16000, ENVY24_MT_RATE_16000},
+ {8000, ENVY24_MT_RATE_8000},
+ {96000, ENVY24_MT_RATE_96000},
+ {64000, ENVY24_MT_RATE_64000},
+ {44100, ENVY24_MT_RATE_44100},
+ {22050, ENVY24_MT_RATE_22050},
+ {11025, ENVY24_MT_RATE_11025},
+ {88200, ENVY24_MT_RATE_88200},
+ {0, 0x10}
+};
+
+static int
+envy24_setspeed(struct sc_info *sc, u_int32_t speed) {
+ u_int32_t code;
+ int i = 0;
+
+#if(0)
+ device_printf(sc->dev, "envy24_setspeed(sc, %d)\n", speed);
+#endif
+ if (speed == 0) {
+ code = ENVY24_MT_RATE_SPDIF; /* external master clock */
+ envy24_slavecd(sc);
+ }
+ else {
+ for (i = 0; envy24_speedtab[i].speed != 0; i++) {
+ if (envy24_speedtab[i].speed == speed)
+ break;
+ }
+ code = envy24_speedtab[i].code;
+ }
+#if(0)
+ device_printf(sc->dev, "envy24_setspeed(): speed %d/code 0x%04x\n", envy24_speedtab[i].speed, code);
+#endif
+ if (code < 0x10) {
+ envy24_wrmt(sc, ENVY24_MT_RATE, code, 1);
+ code = envy24_rdmt(sc, ENVY24_MT_RATE, 1);
+ code &= ENVY24_MT_RATE_MASK;
+ for (i = 0; envy24_speedtab[i].code < 0x10; i++) {
+ if (envy24_speedtab[i].code == code)
+ break;
+ }
+ speed = envy24_speedtab[i].speed;
+ }
+ else
+ speed = 0;
+
+#if(0)
+ device_printf(sc->dev, "envy24_setspeed(): return %d\n", speed);
+#endif
+ return speed;
+}
+
+static void
+envy24_setvolume(struct sc_info *sc, unsigned ch)
+{
+#if(0)
+ device_printf(sc->dev, "envy24_setvolume(sc, %d)\n", ch);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
+ envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
+ envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
+ envy24_wrmt(sc, ENVY24_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
+}
+
+static void
+envy24_mutevolume(struct sc_info *sc, unsigned ch)
+{
+ u_int32_t vol;
+
+#if(0)
+ device_printf(sc->dev, "envy24_mutevolume(sc, %d)\n", ch);
+#endif
+ vol = ENVY24_VOL_MUTE << 8 | ENVY24_VOL_MUTE;
+ envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
+ envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
+ envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
+ envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
+}
+
+static u_int32_t
+envy24_gethwptr(struct sc_info *sc, int dir)
+{
+ int unit, regno;
+ u_int32_t ptr, rtn;
+
+#if(0)
+ device_printf(sc->dev, "envy24_gethwptr(sc, %d)\n", dir);
+#endif
+ if (dir == PCMDIR_PLAY) {
+ rtn = sc->psize / 4;
+ unit = ENVY24_PLAY_BUFUNIT / 4;
+ regno = ENVY24_MT_PCNT;
+ }
+ else {
+ rtn = sc->rsize / 4;
+ unit = ENVY24_REC_BUFUNIT / 4;
+ regno = ENVY24_MT_RCNT;
+ }
+
+ ptr = envy24_rdmt(sc, regno, 2);
+ rtn -= (ptr + 1);
+ rtn /= unit;
+
+#if(0)
+ device_printf(sc->dev, "envy24_gethwptr(): return %d\n", rtn);
+#endif
+ return rtn;
+}
+
+static void
+envy24_updintr(struct sc_info *sc, int dir)
+{
+ int regptr, regintr;
+ u_int32_t mask, intr;
+ u_int32_t ptr, size, cnt;
+ u_int16_t blk;
+
+#if(0)
+ device_printf(sc->dev, "envy24_updintr(sc, %d)\n", dir);
+#endif
+ if (dir == PCMDIR_PLAY) {
+ blk = sc->blk[0];
+ size = sc->psize / 4;
+ regptr = ENVY24_MT_PCNT;
+ regintr = ENVY24_MT_PTERM;
+ mask = ~ENVY24_MT_INT_PMASK;
+ }
+ else {
+ blk = sc->blk[1];
+ size = sc->rsize / 4;
+ regptr = ENVY24_MT_RCNT;
+ regintr = ENVY24_MT_RTERM;
+ mask = ~ENVY24_MT_INT_RMASK;
+ }
+
+ ptr = size - envy24_rdmt(sc, regptr, 2) - 1;
+ /*
+ cnt = blk - ptr % blk - 1;
+ if (cnt == 0)
+ cnt = blk - 1;
+ */
+ cnt = blk - 1;
+#if(0)
+ device_printf(sc->dev, "envy24_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt);
+#endif
+ envy24_wrmt(sc, regintr, cnt, 2);
+ intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+#if(0)
+ device_printf(sc->dev, "envy24_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_INT, intr & mask, 1);
+#if(0)
+ device_printf(sc->dev, "envy24_updintr():INT-->0x%02x\n",
+ envy24_rdmt(sc, ENVY24_MT_INT, 1));
+#endif
+
+ return;
+}
+
+static void
+envy24_maskintr(struct sc_info *sc, int dir)
+{
+ u_int32_t mask, intr;
+
+#if(0)
+ device_printf(sc->dev, "envy24_maskintr(sc, %d)\n", dir);
+#endif
+ if (dir == PCMDIR_PLAY)
+ mask = ENVY24_MT_INT_PMASK;
+ else
+ mask = ENVY24_MT_INT_RMASK;
+ intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+ envy24_wrmt(sc, ENVY24_MT_INT, intr | mask, 1);
+
+ return;
+}
+
+static int
+envy24_checkintr(struct sc_info *sc, int dir)
+{
+ u_int32_t mask, stat, intr, rtn;
+
+#if(0)
+ device_printf(sc->dev, "envy24_checkintr(sc, %d)\n", dir);
+#endif
+ intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+ if (dir == PCMDIR_PLAY) {
+ if ((rtn = intr & ENVY24_MT_INT_PSTAT) != 0) {
+ mask = ~ENVY24_MT_INT_RSTAT;
+ stat = ENVY24_MT_INT_PSTAT | ENVY24_MT_INT_PMASK;
+ envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
+ }
+ }
+ else {
+ if ((rtn = intr & ENVY24_MT_INT_RSTAT) != 0) {
+ mask = ~ENVY24_MT_INT_PSTAT;
+ stat = ENVY24_MT_INT_RSTAT | ENVY24_MT_INT_RMASK;
+ envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
+ }
+ }
+
+ return rtn;
+}
+
+static void
+envy24_start(struct sc_info *sc, int dir)
+{
+ u_int32_t stat, sw;
+
+#if(0)
+ device_printf(sc->dev, "envy24_start(sc, %d)\n", dir);
+#endif
+ if (dir == PCMDIR_PLAY)
+ sw = ENVY24_MT_PCTL_PSTART;
+ else
+ sw = ENVY24_MT_PCTL_RSTART;
+
+ stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
+ envy24_wrmt(sc, ENVY24_MT_PCTL, stat | sw, 1);
+#if(0)
+ DELAY(100);
+ device_printf(sc->dev, "PADDR:0x%08x\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
+ device_printf(sc->dev, "PCNT:%ld\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
+#endif
+
+ return;
+}
+
+static void
+envy24_stop(struct sc_info *sc, int dir)
+{
+ u_int32_t stat, sw;
+
+#if(0)
+ device_printf(sc->dev, "envy24_stop(sc, %d)\n", dir);
+#endif
+ if (dir == PCMDIR_PLAY)
+ sw = ~ENVY24_MT_PCTL_PSTART;
+ else
+ sw = ~ENVY24_MT_PCTL_RSTART;
+
+ stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
+ envy24_wrmt(sc, ENVY24_MT_PCTL, stat & sw, 1);
+
+ return;
+}
+
+static int
+envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev)
+{
+ u_int32_t reg, mask;
+ u_int32_t left, right;
+
+#if(0)
+ device_printf(sc->dev, "envy24_route(sc, %d, %d, %d, %d)\n",
+ dac, class, adc, rev);
+#endif
+ /* parameter pattern check */
+ if (dac < 0 || ENVY24_ROUTE_DAC_SPDIF < dac)
+ return -1;
+ if (class == ENVY24_ROUTE_CLASS_MIX &&
+ (dac != ENVY24_ROUTE_DAC_1 && dac != ENVY24_ROUTE_DAC_SPDIF))
+ return -1;
+ if (rev) {
+ left = ENVY24_ROUTE_RIGHT;
+ right = ENVY24_ROUTE_LEFT;
+ }
+ else {
+ left = ENVY24_ROUTE_LEFT;
+ right = ENVY24_ROUTE_RIGHT;
+ }
+
+ if (dac == ENVY24_ROUTE_DAC_SPDIF) {
+ reg = class | class << 2 |
+ ((adc << 1 | left) | left << 3) << 8 |
+ ((adc << 1 | right) | right << 3) << 12;
+#if(0)
+ device_printf(sc->dev, "envy24_route(): MT_SPDOUT-->0x%04x\n", reg);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_SPDOUT, reg, 2);
+ }
+ else {
+ mask = ~(0x0303 << dac * 2);
+ reg = envy24_rdmt(sc, ENVY24_MT_PSDOUT, 2);
+ reg = (reg & mask) | ((class | class << 8) << dac * 2);
+#if(0)
+ device_printf(sc->dev, "envy24_route(): MT_PSDOUT-->0x%04x\n", reg);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_PSDOUT, reg, 2);
+ mask = ~(0xff << dac * 8);
+ reg = envy24_rdmt(sc, ENVY24_MT_RECORD, 4);
+ reg = (reg & mask) |
+ (((adc << 1 | left) | left << 3) |
+ ((adc << 1 | right) | right << 3) << 4) << dac * 8;
+#if(0)
+ device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4);
+ }
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* buffer copy routines */
+static void
+envy24_p32sl(struct sc_chinfo *ch)
+{
+ int length;
+ sample32_t *dmabuf;
+ u_int32_t *data;
+ int src, dst, ssize, dsize, slot;
+ int i;
+
+ length = sndbuf_getready(ch->buffer) / 8;
+ dmabuf = ch->parent->pbuf;
+ data = (u_int32_t *)ch->data;
+ src = sndbuf_getreadyptr(ch->buffer) / 4;
+ dst = src / 2 + ch->offset;
+ ssize = ch->size / 4;
+ dsize = ch->size / 8;
+ slot = ch->num * 2;
+
+ for (i = 0; i < length; i++) {
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot] = data[src];
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = data[src + 1];
+ dst++;
+ dst %= dsize;
+ src += 2;
+ src %= ssize;
+ }
+
+ return;
+}
+
+static void
+envy24_p16sl(struct sc_chinfo *ch)
+{
+ int length;
+ sample32_t *dmabuf;
+ u_int16_t *data;
+ int src, dst, ssize, dsize, slot;
+ int i;
+
+#if(0)
+ device_printf(ch->parent->dev, "envy24_p16sl()\n");
+#endif
+ length = sndbuf_getready(ch->buffer) / 4;
+ dmabuf = ch->parent->pbuf;
+ data = (u_int16_t *)ch->data;
+ src = sndbuf_getreadyptr(ch->buffer) / 2;
+ dst = src / 2 + ch->offset;
+ ssize = ch->size / 2;
+ dsize = ch->size / 4;
+ slot = ch->num * 2;
+#if(0)
+ device_printf(ch->parent->dev, "envy24_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
+#endif
+
+ for (i = 0; i < length; i++) {
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot] = (u_int32_t)data[src] << 16;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = (u_int32_t)data[src + 1] << 16;
+#if(0)
+ if (i < 16) {
+ printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot]);
+ printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1]);
+ }
+#endif
+ dst++;
+ dst %= dsize;
+ src += 2;
+ src %= ssize;
+ }
+#if(0)
+ printf("\n");
+#endif
+
+ return;
+}
+
+static void
+envy24_p8u(struct sc_chinfo *ch)
+{
+ int length;
+ sample32_t *dmabuf;
+ u_int8_t *data;
+ int src, dst, ssize, dsize, slot;
+ int i;
+
+ length = sndbuf_getready(ch->buffer) / 2;
+ dmabuf = ch->parent->pbuf;
+ data = (u_int8_t *)ch->data;
+ src = sndbuf_getreadyptr(ch->buffer);
+ dst = src / 2 + ch->offset;
+ ssize = ch->size;
+ dsize = ch->size / 4;
+ slot = ch->num * 2;
+
+ for (i = 0; i < length; i++) {
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot] = ((u_int32_t)data[src] ^ 0x80) << 24;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
+ dst++;
+ dst %= dsize;
+ src += 2;
+ src %= ssize;
+ }
+
+ return;
+}
+
+static void
+envy24_r32sl(struct sc_chinfo *ch)
+{
+ int length;
+ sample32_t *dmabuf;
+ u_int32_t *data;
+ int src, dst, ssize, dsize, slot;
+ int i;
+
+ length = sndbuf_getfree(ch->buffer) / 8;
+ dmabuf = ch->parent->rbuf;
+ data = (u_int32_t *)ch->data;
+ dst = sndbuf_getfreeptr(ch->buffer) / 4;
+ src = dst / 2 + ch->offset;
+ dsize = ch->size / 4;
+ ssize = ch->size / 8;
+ slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
+
+ for (i = 0; i < length; i++) {
+ data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ dst += 2;
+ dst %= dsize;
+ src++;
+ src %= ssize;
+ }
+
+ return;
+}
+
+static void
+envy24_r16sl(struct sc_chinfo *ch)
+{
+ int length;
+ sample32_t *dmabuf;
+ u_int32_t *data;
+ int src, dst, ssize, dsize, slot;
+ int i;
+
+ length = sndbuf_getfree(ch->buffer) / 4;
+ dmabuf = ch->parent->rbuf;
+ data = (u_int16_t *)ch->data;
+ dst = sndbuf_getfreeptr(ch->buffer) / 2;
+ src = dst / 2 + ch->offset;
+ dsize = ch->size / 2;
+ ssize = ch->size / 8;
+ slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
+
+ for (i = 0; i < length; i++) {
+ data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ dst += 2;
+ dst %= dsize;
+ src++;
+ src %= ssize;
+ }
+
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+envy24chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ struct sc_info *sc = (struct sc_info *)devinfo;
+ struct sc_chinfo *ch;
+ unsigned num;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_init(obj, devinfo, b, c, %d)\n", dir);
+#endif
+ snd_mtxlock(sc->lock);
+ if ((sc->chnum > ENVY24_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
+ (sc->chnum < ENVY24_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
+ snd_mtxunlock(sc->lock);
+ return NULL;
+ }
+ num = sc->chnum;
+
+ ch = &sc->chan[num];
+ ch->size = 8 * ENVY24_SAMPLE_NUM;
+ ch->data = malloc(ch->size, M_ENVY24, M_NOWAIT);
+ if (ch->data == NULL) {
+ ch->size = 0;
+ ch = NULL;
+ }
+ else {
+ ch->buffer = b;
+ ch->channel = c;
+ ch->parent = sc;
+ ch->dir = dir;
+ /* set channel map */
+ ch->num = envy24_chanmap[num];
+ sndbuf_setup(ch->buffer, ch->data, ch->size);
+ /* these 2 values are dummy */
+ ch->unit = 4;
+ ch->blk = 10240;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return ch;
+}
+
+static int
+envy24chan_free(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_free()\n");
+#endif
+ snd_mtxlock(sc->lock);
+ if (ch->data != NULL) {
+ free(ch->data, M_ENVY24);
+ ch->data = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static int
+envy24chan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ struct envy24_emldma *emltab;
+ unsigned int bcnt, bsize;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_setformat(obj, data, 0x%08x)\n", format);
+#endif
+ snd_mtxlock(sc->lock);
+ /* check and get format related information */
+ if (ch->dir == PCMDIR_PLAY)
+ emltab = envy24_pemltab;
+ else
+ emltab = envy24_remltab;
+ if (emltab == NULL) {
+ snd_mtxunlock(sc->lock);
+ return -1;
+ }
+ for (i = 0; emltab[i].format != 0; i++)
+ if (emltab[i].format == format)
+ break;
+ if (emltab[i].format == 0) {
+ snd_mtxunlock(sc->lock);
+ return -1;
+ }
+
+ /* set format information */
+ ch->format = format;
+ ch->emldma = emltab[i].emldma;
+ if (ch->unit > emltab[i].unit)
+ ch->blk *= ch->unit / emltab[i].unit;
+ else
+ ch->blk /= emltab[i].unit / ch->unit;
+ ch->unit = emltab[i].unit;
+
+ /* set channel buffer information */
+ ch->size = ch->unit * ENVY24_SAMPLE_NUM;
+ if (ch->dir == PCMDIR_PLAY)
+ bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
+ else
+ bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
+ bsize *= ch->unit;
+ bcnt = ch->size / bsize;
+ sndbuf_resize(ch->buffer, bcnt, bsize);
+ snd_mtxunlock(sc->lock);
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_setformat(): return 0x%08x\n", 0);
+#endif
+ return 0;
+}
+
+/*
+ IMPLEMENT NOTICE: In this driver, setspeed function only do setting
+ of speed information value. And real hardware speed setting is done
+ at start triggered(see envy24chan_trigger()). So, at this function
+ is called, any value that ENVY24 can use is able to set. But, at
+ start triggerd, some other channel is running, and that channel's
+ speed isn't same with, then trigger function will fail.
+*/
+static int
+envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ struct sc_chinfo *ch = data;
+ u_int32_t val, prev;
+ int i;
+
+#if(0)
+ device_printf(ch->parent->dev, "envy24chan_setspeed(obj, data, %d)\n", speed);
+#endif
+ prev = 0x7fffffff;
+ for (i = 0; (val = envy24_speed[i]) != 0; i++) {
+ if (abs(val - speed) < abs(prev - speed))
+ prev = val;
+ else
+ break;
+ }
+ ch->speed = prev;
+
+#if(0)
+ device_printf(ch->parent->dev, "envy24chan_setspeed(): return %d\n", ch->speed);
+#endif
+ return ch->speed;
+}
+
+static int
+envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ u_int32_t size, prev;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_setblocksize(obj, data, %d)\n", blocksize);
+#endif
+ prev = 0x7fffffff;
+ snd_mtxlock(sc->lock);
+ for (size = ch->size / 2; size > 0; size /= 2) {
+ if (abs(size - blocksize) < abs(prev - blocksize))
+ prev = size;
+ else
+ break;
+ }
+
+ ch->blk = prev / ch->unit;
+ if (ch->dir == PCMDIR_PLAY)
+ ch->blk *= ENVY24_PLAY_BUFUNIT / 4;
+ else
+ ch->blk *= ENVY24_REC_BUFUNIT / 4;
+ snd_mtxunlock(sc->lock);
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_setblocksize(): return %d\n", prev);
+#endif
+ return prev;
+}
+
+/* semantic note: must start at beginning of buffer */
+static int
+envy24chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ u_int32_t ptr;
+ int slot;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_trigger(obj, data, %d)\n", go);
+#endif
+ snd_mtxlock(sc->lock);
+ if (ch->dir == PCMDIR_PLAY)
+ slot = 0;
+ else
+ slot = 1;
+ switch (go) {
+ case PCMTRIG_START:
+#if(0)
+ device_printf(sc->dev, "envy24chan_trigger(): start\n");
+#endif
+ /* check or set channel speed */
+ if (sc->run[0] == 0 && sc->run[1] == 0) {
+ sc->speed = envy24_setspeed(sc, ch->speed);
+ sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed;
+ sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed;
+ }
+ else if (ch->speed != 0 && ch->speed != sc->speed)
+ return -1;
+ if (ch->speed == 0)
+ ch->channel->speed = sc->speed;
+ /* start or enable channel */
+ sc->run[slot]++;
+ if (sc->run[slot] == 1) {
+ /* first channel */
+ ch->offset = 0;
+ sc->blk[slot] = ch->blk;
+ }
+ else {
+ ptr = envy24_gethwptr(sc, ch->dir);
+ ch->offset = ((ptr / ch->blk + 1) * ch->blk %
+ (ch->size / 4)) * 4 / ch->unit;
+ if (ch->blk < sc->blk[slot])
+ sc->blk[slot] = ch->blk;
+ }
+ if (ch->dir == PCMDIR_PLAY) {
+ ch->emldma(ch);
+ envy24_setvolume(sc, ch->num);
+ }
+ envy24_updintr(sc, ch->dir);
+ if (sc->run[slot] == 1)
+ envy24_start(sc, ch->dir);
+ ch->run = 1;
+ break;
+ case PCMTRIG_EMLDMAWR:
+#if(0)
+ device_printf(sc->dev, "envy24chan_trigger(): emldmawr\n");
+#endif
+ if (ch->run != 1)
+ return -1;
+ ch->emldma(ch);
+ break;
+ case PCMTRIG_EMLDMARD:
+#if(0)
+ device_printf(sc->dev, "envy24chan_trigger(): emldmard\n");
+#endif
+ if (ch->run != 1)
+ return -1;
+ ch->emldma(ch);
+ break;
+ case PCMTRIG_ABORT:
+#if(0)
+ device_printf(sc->dev, "envy24chan_trigger(): abort\n");
+#endif
+ ch->run = 0;
+ sc->run[slot]--;
+ if (ch->dir == PCMDIR_PLAY)
+ envy24_mutevolume(sc, ch->num);
+ if (sc->run[slot] == 0) {
+ envy24_stop(sc, ch->dir);
+ sc->intr[slot] = 0;
+ }
+ else if (ch->blk == sc->blk[slot]) {
+ sc->blk[slot] = ENVY24_SAMPLE_NUM / 2;
+ for (i = 0; i < ENVY24_CHAN_NUM; i++) {
+ if (sc->chan[i].dir == ch->dir &&
+ sc->chan[i].run == 1 &&
+ sc->chan[i].blk < sc->blk[slot])
+ sc->blk[slot] = sc->chan[i].blk;
+ }
+ if (ch->blk != sc->blk[slot])
+ envy24_updintr(sc, ch->dir);
+ }
+ break;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static int
+envy24chan_getptr(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ u_int32_t ptr;
+ int rtn;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_getptr()\n");
+#endif
+ snd_mtxlock(sc->lock);
+ ptr = envy24_gethwptr(sc, ch->dir);
+ rtn = ptr * ch->unit;
+ snd_mtxunlock(sc->lock);
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_getptr(): return %d\n",
+ rtn);
+#endif
+ return rtn;
+}
+
+static struct pcmchan_caps *
+envy24chan_getcaps(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ struct pcmchan_caps *rtn;
+
+#if(0)
+ device_printf(sc->dev, "envy24chan_getcaps()\n");
+#endif
+ snd_mtxlock(sc->lock);
+ if (ch->dir == PCMDIR_PLAY) {
+ if (sc->run[0] == 0)
+ rtn = &envy24_playcaps;
+ else
+ rtn = &sc->caps[0];
+ }
+ else {
+ if (sc->run[1] == 0)
+ rtn = &envy24_reccaps;
+ else
+ rtn = &sc->caps[1];
+ }
+ snd_mtxunlock(sc->lock);
+
+ return rtn;
+}
+
+static kobj_method_t envy24chan_methods[] = {
+ KOBJMETHOD(channel_init, envy24chan_init),
+ KOBJMETHOD(channel_free, envy24chan_free),
+ KOBJMETHOD(channel_setformat, envy24chan_setformat),
+ KOBJMETHOD(channel_setspeed, envy24chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, envy24chan_setblocksize),
+ KOBJMETHOD(channel_trigger, envy24chan_trigger),
+ KOBJMETHOD(channel_getptr, envy24chan_getptr),
+ KOBJMETHOD(channel_getcaps, envy24chan_getcaps),
+ { 0, 0 }
+};
+CHANNEL_DECLARE(envy24chan);
+
+/* -------------------------------------------------------------------- */
+
+/* mixer interface */
+
+static int
+envy24mixer_init(struct snd_mixer *m)
+{
+ struct sc_info *sc = mix_getdevinfo(m);
+
+#if(0)
+ device_printf(sc->dev, "envy24mixer_init()\n");
+#endif
+ if (sc == NULL)
+ return -1;
+
+ /* set volume control rate */
+ snd_mtxlock(sc->lock);
+ envy24_wrmt(sc, ENVY24_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */
+
+ mix_setdevs(m, ENVY24_MIX_MASK);
+ mix_setrecdevs(m, ENVY24_MIX_REC_MASK);
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static int
+envy24mixer_reinit(struct snd_mixer *m)
+{
+ struct sc_info *sc = mix_getdevinfo(m);
+
+ if (sc == NULL)
+ return -1;
+#if(0)
+ device_printf(sc->dev, "envy24mixer_reinit()\n");
+#endif
+
+ return 0;
+}
+
+static int
+envy24mixer_uninit(struct snd_mixer *m)
+{
+ struct sc_info *sc = mix_getdevinfo(m);
+
+ if (sc == NULL)
+ return -1;
+#if(0)
+ device_printf(sc->dev, "envy24mixer_uninit()\n");
+#endif
+
+ return 0;
+}
+
+static int
+envy24mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ struct sc_info *sc = mix_getdevinfo(m);
+ int ch = envy24_mixmap[dev];
+ int hwch;
+ int i;
+
+ if (sc == NULL)
+ return -1;
+ if (dev == 0 && sc->cfg->codec->setvolume == NULL)
+ return -1;
+ if (dev != 0 && ch == -1)
+ return -1;
+ hwch = envy24_chanmap[ch];
+#if(0)
+ device_printf(sc->dev, "envy24mixer_set(m, %d, %d, %d)\n",
+ dev, left, right);
+#endif
+
+ snd_mtxlock(sc->lock);
+ if (dev == 0) {
+ for (i = 0; i < sc->dacn; i++) {
+ sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
+ }
+ }
+ else {
+ /* set volume value for hardware */
+ if ((sc->left[hwch] = 100 - left) > ENVY24_VOL_MIN)
+ sc->left[hwch] = ENVY24_VOL_MUTE;
+ if ((sc->right[hwch] = 100 - right) > ENVY24_VOL_MIN)
+ sc->right[hwch] = ENVY24_VOL_MUTE;
+
+ /* set volume for record channel and running play channel */
+ if (hwch > ENVY24_CHAN_PLAY_SPDIF || sc->chan[ch].run)
+ envy24_setvolume(sc, hwch);
+ }
+ snd_mtxunlock(sc->lock);
+
+ return right << 8 | left;
+}
+
+static u_int32_t
+envy24mixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
+{
+ struct sc_info *sc = mix_getdevinfo(m);
+ int ch = envy24_mixmap[src];
+#if(0)
+ device_printf(sc->dev, "envy24mixer_setrecsrc(m, %d)\n", src);
+#endif
+
+ if (ch > ENVY24_CHAN_PLAY_SPDIF)
+ sc->src = ch;
+ return src;
+}
+
+static kobj_method_t envy24mixer_methods[] = {
+ KOBJMETHOD(mixer_init, envy24mixer_init),
+ KOBJMETHOD(mixer_reinit, envy24mixer_reinit),
+ KOBJMETHOD(mixer_uninit, envy24mixer_uninit),
+ KOBJMETHOD(mixer_set, envy24mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, envy24mixer_setrecsrc),
+ { 0, 0 }
+};
+MIXER_DECLARE(envy24mixer);
+
+/* -------------------------------------------------------------------- */
+
+/* The interrupt handler */
+static void
+envy24_intr(void *p)
+{
+ struct sc_info *sc = (struct sc_info *)p;
+ struct sc_chinfo *ch;
+ u_int32_t ptr, dsize, feed;
+ int i;
+
+#if(0)
+ device_printf(sc->dev, "envy24_intr()\n");
+#endif
+ snd_mtxlock(sc->lock);
+ if (envy24_checkintr(sc, PCMDIR_PLAY)) {
+#if(0)
+ device_printf(sc->dev, "envy24_intr(): play\n");
+#endif
+ dsize = sc->psize / 4;
+ ptr = dsize - envy24_rdmt(sc, ENVY24_MT_PCNT, 2) - 1;
+#if(0)
+ device_printf(sc->dev, "envy24_intr(): ptr = %d-->", ptr);
+#endif
+ ptr -= ptr % sc->blk[0];
+ feed = (ptr + dsize - sc->intr[0]) % dsize;
+#if(0)
+ printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed);
+#endif
+ for (i = ENVY24_CHAN_PLAY_DAC1; i <= ENVY24_CHAN_PLAY_SPDIF; i++) {
+ ch = &sc->chan[i];
+#if(0)
+ if (ch->run)
+ device_printf(sc->dev, "envy24_intr(): chan[%d].blk = %d\n", i, ch->blk);
+#endif
+ if (ch->run && ch->blk <= feed)
+ chn_intr(ch->channel);
+ }
+ sc->intr[0] = ptr;
+ envy24_updintr(sc, PCMDIR_PLAY);
+ }
+ if (envy24_checkintr(sc, PCMDIR_REC)) {
+#if(0)
+ device_printf(sc->dev, "envy24_intr(): rec\n");
+#endif
+ dsize = sc->rsize / 4;
+ ptr = dsize - envy24_rdmt(sc, ENVY24_MT_RCNT, 2) - 1;
+ ptr -= ptr % sc->blk[1];
+ feed = (ptr + dsize - sc->intr[1]) % dsize;
+ for (i = ENVY24_CHAN_REC_ADC1; i <= ENVY24_CHAN_REC_SPDIF; i++) {
+ ch = &sc->chan[i];
+ if (ch->run && ch->blk <= feed)
+ chn_intr(ch->channel);
+ }
+ sc->intr[1] = ptr;
+ envy24_updintr(sc, PCMDIR_REC);
+ }
+ snd_mtxunlock(sc->lock);
+
+ return;
+}
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+envy24_pci_probe(device_t dev)
+{
+ u_int16_t sv, sd;
+ int i;
+
+#if(0)
+ printf("envy24_pci_probe()\n");
+#endif
+ if (pci_get_device(dev) == PCID_ENVY24 &&
+ pci_get_vendor(dev) == PCIV_ENVY24) {
+ sv = pci_get_subvendor(dev);
+ sd = pci_get_subdevice(dev);
+ for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+ if (cfg_table[i].subvendor == sv &&
+ cfg_table[i].subdevice == sd) {
+ break;
+ }
+ }
+ device_set_desc(dev, cfg_table[i].name);
+#if(0)
+ printf("envy24_pci_probe(): return 0\n");
+#endif
+ return 0;
+ }
+ else {
+#if(0)
+ printf("envy24_pci_probe(): return ENXIO\n");
+#endif
+ return ENXIO;
+ }
+}
+
+static void
+envy24_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct sc_info *sc = (struct sc_info *)arg;
+
+#if(0)
+ device_printf(sc->dev, "envy24_dmapsetmap()\n");
+#endif
+ if (bootverbose) {
+ printf("envy24(play): setmap %lx, %lx; ",
+ (unsigned long)segs->ds_addr,
+ (unsigned long)segs->ds_len);
+ printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap));
+ }
+}
+
+static void
+envy24_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct sc_info *sc = (struct sc_info *)arg;
+
+#if(0)
+ device_printf(sc->dev, "envy24_dmarsetmap()\n");
+#endif
+ if (bootverbose) {
+ printf("envy24(record): setmap %lx, %lx; ",
+ (unsigned long)segs->ds_addr,
+ (unsigned long)segs->ds_len);
+ printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap));
+ }
+}
+
+static void
+envy24_dmafree(struct sc_info *sc)
+{
+#if(0)
+ device_printf(sc->dev, "envy24_dmafree():");
+ if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap);
+ else printf(" sc->rmap(null)");
+ if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap);
+ else printf(" sc->pmap(null)");
+ if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf);
+ else printf(" sc->rbuf(null)");
+ if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf);
+ else printf(" sc->pbuf(null)\n");
+#endif
+#if(0)
+ if (sc->rmap)
+ bus_dmamap_unload(sc->dmat, sc->rmap);
+ if (sc->pmap)
+ bus_dmamap_unload(sc->dmat, sc->pmap);
+ if (sc->rbuf)
+ bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+ if (sc->pbuf)
+ bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#else
+ bus_dmamap_unload(sc->dmat, sc->rmap);
+ bus_dmamap_unload(sc->dmat, sc->pmap);
+ bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+ bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#endif
+
+ sc->rmap = sc->pmap = NULL;
+ sc->pbuf = NULL;
+ sc->rbuf = NULL;
+
+ return;
+}
+
+static int
+envy24_dmainit(struct sc_info *sc)
+{
+ u_int32_t addr;
+
+#if(0)
+ device_printf(sc->dev, "envy24_dmainit()\n");
+#endif
+ /* init values */
+ sc->psize = ENVY24_PLAY_BUFUNIT * ENVY24_SAMPLE_NUM;
+ sc->rsize = ENVY24_REC_BUFUNIT * ENVY24_SAMPLE_NUM;
+ sc->pbuf = NULL;
+ sc->rbuf = NULL;
+ sc->pmap = sc->rmap = NULL;
+ sc->blk[0] = sc->blk[1] = 0;
+
+ /* allocate DMA buffer */
+#if(0)
+ device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->pbuf\n");
+#endif
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap))
+ goto bad;
+#if(0)
+ device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->rbuf\n");
+#endif
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap))
+ goto bad;
+#if(0)
+ device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->pmap\n");
+#endif
+ if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24_dmapsetmap, sc, 0))
+ goto bad;
+#if(0)
+ device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->rmap\n");
+#endif
+ if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24_dmarsetmap, sc, 0))
+ goto bad;
+ bzero(sc->pbuf, sc->psize);
+ bzero(sc->rbuf, sc->rsize);
+
+ /* set values to register */
+ addr = vtophys(sc->pbuf);
+#if(0)
+ device_printf(sc->dev, "pbuf(0x%08x)\n", addr);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_PADDR, addr, 4);
+#if(0)
+ device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
+ device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1);
+#endif
+ envy24_wrmt(sc, ENVY24_MT_PCNT, sc->psize / 4 - 1, 2);
+#if(0)
+ device_printf(sc->dev, "PCNT-->(%ld)\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
+#endif
+ addr = vtophys(sc->rbuf);
+ envy24_wrmt(sc, ENVY24_MT_RADDR, addr, 4);
+ envy24_wrmt(sc, ENVY24_MT_RCNT, sc->rsize / 4 - 1, 2);
+
+ return 0;
+ bad:
+ envy24_dmafree(sc);
+ return ENOSPC;
+}
+
+static void
+envy24_putcfg(struct sc_info *sc)
+{
+ device_printf(sc->dev, "system configuration\n", sc->adcn, sc->dacn);
+ printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
+ sc->cfg->subvendor, sc->cfg->subdevice);
+ printf(" XIN2 Clock Source: ");
+ switch (sc->cfg->scfg & PCIM_SCFG_XIN2) {
+ case 0x00:
+ printf("22.5792MHz(44.1kHz*512)\n");
+ break;
+ case 0x40:
+ printf("16.9344MHz(44.1kHz*384)\n");
+ break;
+ case 0x80:
+ printf("from external clock synthesizer chip\n");
+ break;
+ default:
+ printf("illeagal system setting\n");
+ }
+ printf(" MPU-401 UART(s) #: ");
+ if (sc->cfg->scfg & PCIM_SCFG_MPU)
+ printf("2\n");
+ else
+ printf("1\n");
+ printf(" AC'97 codec: ");
+ if (sc->cfg->scfg & PCIM_SCFG_AC97)
+ printf("not exist\n");
+ else
+ printf("exist\n");
+ printf(" ADC #: ");
+ printf("%d\n", sc->adcn);
+ printf(" DAC #: ");
+ printf("%d\n", sc->dacn);
+ printf(" Multi-track converter type: ");
+ if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) {
+ printf("AC'97(SDATA_OUT:");
+ if (sc->cfg->acl & PCIM_ACL_OMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf("|SDATA_IN:");
+ if (sc->cfg->acl & PCIM_ACL_IMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf(")\n");
+ }
+ else {
+ printf("I2S(");
+ if (sc->cfg->i2s & PCIM_I2S_VOL)
+ printf("with volume, ");
+ if (sc->cfg->i2s & PCIM_I2S_96KHZ)
+ printf("96KHz support, ");
+ switch (sc->cfg->i2s & PCIM_I2S_RES) {
+ case PCIM_I2S_16BIT:
+ printf("16bit resolution, ");
+ break;
+ case PCIM_I2S_18BIT:
+ printf("18bit resolution, ");
+ break;
+ case PCIM_I2S_20BIT:
+ printf("20bit resolution, ");
+ break;
+ case PCIM_I2S_24BIT:
+ printf("24bit resolution, ");
+ break;
+ }
+ printf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID);
+ }
+ printf(" S/PDIF(IN/OUT): ");
+ if (sc->cfg->spdif & PCIM_SPDIF_IN)
+ printf("1/");
+ else
+ printf("0/");
+ if (sc->cfg->spdif & PCIM_SPDIF_OUT)
+ printf("1 ");
+ else
+ printf("0 ");
+ if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT))
+ printf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2);
+ printf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n",
+ sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate);
+}
+
+static int
+envy24_init(struct sc_info *sc)
+{
+ u_int32_t data;
+#if(0)
+ int rtn;
+#endif
+ int i;
+ u_int32_t sv, sd;
+
+
+#if(0)
+ device_printf(sc->dev, "envy24_init()\n");
+#endif
+
+ /* reset chip */
+ envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_RESET | ENVY24_CCS_CTL_NATIVE, 1);
+ DELAY(200);
+ envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_NATIVE, 1);
+ DELAY(200);
+
+ /* legacy hardware disable */
+ data = pci_read_config(sc->dev, PCIR_LAC, 2);
+ data |= PCIM_LAC_DISABLE;
+ pci_write_config(sc->dev, PCIR_LAC, data, 2);
+
+ /* check system configuration */
+ sc->cfg = NULL;
+ for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+ /* 1st: search configuration from table */
+ sv = pci_get_subvendor(sc->dev);
+ sd = pci_get_subdevice(sc->dev);
+ if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) {
+#if(0)
+ device_printf(sc->dev, "Set configuration from table\n");
+#endif
+ sc->cfg = &cfg_table[i];
+ break;
+ }
+ }
+ if (sc->cfg == NULL) {
+ /* 2nd: read configuration from table */
+ sc->cfg = envy24_rom2cfg(sc);
+ }
+ sc->adcn = ((sc->cfg->scfg & PCIM_SCFG_ADC) >> 2) + 1;
+ sc->dacn = (sc->cfg->scfg & PCIM_SCFG_DAC) + 1;
+
+ if (1 /* bootverbose */) {
+ envy24_putcfg(sc);
+ }
+
+ /* set system configuration */
+ pci_write_config(sc->dev, PCIR_SCFG, sc->cfg->scfg, 1);
+ pci_write_config(sc->dev, PCIR_ACL, sc->cfg->acl, 1);
+ pci_write_config(sc->dev, PCIR_I2S, sc->cfg->i2s, 1);
+ pci_write_config(sc->dev, PCIR_SPDIF, sc->cfg->spdif, 1);
+ envy24_gpiosetmask(sc, sc->cfg->gpiomask);
+ envy24_gpiosetdir(sc, sc->cfg->gpiodir);
+ envy24_gpiowr(sc, sc->cfg->gpiostate);
+ for (i = 0; i < sc->adcn; i++) {
+ sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i);
+ sc->cfg->codec->init(sc->adc[i]);
+ }
+ for (i = 0; i < sc->dacn; i++) {
+ sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i);
+ sc->cfg->codec->init(sc->dac[i]);
+ }
+
+ /* initialize DMA buffer */
+#if(0)
+ device_printf(sc->dev, "envy24_init(): initialize DMA buffer\n");
+#endif
+ if (envy24_dmainit(sc))
+ return ENOSPC;
+
+ /* initialize status */
+ sc->run[0] = sc->run[1] = 0;
+ sc->intr[0] = sc->intr[1] = 0;
+ sc->speed = 0;
+ sc->caps[0].fmtlist = envy24_playfmt;
+ sc->caps[1].fmtlist = envy24_recfmt;
+
+ /* set channel router */
+ envy24_route(sc, ENVY24_ROUTE_DAC_1, ENVY24_ROUTE_CLASS_MIX, 0, 0);
+ envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_DMA, 0, 0);
+ /* envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_MIX, 0, 0); */
+
+ /* set macro interrupt mask */
+ data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
+ envy24_wrcs(sc, ENVY24_CCS_IMASK, data & ~ENVY24_CCS_IMASK_PMT, 1);
+ data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
+#if(0)
+ device_printf(sc->dev, "envy24_init(): CCS_IMASK-->0x%02x\n", data);
+#endif
+
+ return 0;
+}
+
+static int
+envy24_alloc_resource(struct sc_info *sc)
+{
+ /* allocate I/O port resource */
+ sc->csid = PCIR_CCS;
+ sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+ &sc->csid, 0, ~0, 1, RF_ACTIVE);
+ sc->ddmaid = PCIR_DDMA;
+ sc->ddma = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+ &sc->ddmaid, 0, ~0, 1, RF_ACTIVE);
+ sc->dsid = PCIR_DS;
+ sc->ds = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+ &sc->dsid, 0, ~0, 1, RF_ACTIVE);
+ sc->mtid = PCIR_MT;
+ sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+ &sc->mtid, 0, ~0, 1, RF_ACTIVE);
+ if (!sc->cs || !sc->ddma || !sc->ds || !sc->mt) {
+ device_printf(sc->dev, "unable to map IO port space\n");
+ return ENXIO;
+ }
+ sc->cst = rman_get_bustag(sc->cs);
+ sc->csh = rman_get_bushandle(sc->cs);
+ sc->ddmat = rman_get_bustag(sc->ddma);
+ sc->ddmah = rman_get_bushandle(sc->ddma);
+ sc->dst = rman_get_bustag(sc->ds);
+ sc->dsh = rman_get_bushandle(sc->ds);
+ sc->mtt = rman_get_bustag(sc->mt);
+ sc->mth = rman_get_bushandle(sc->mt);
+#if(0)
+ device_printf(sc->dev,
+ "IO port register values\nCCS: 0x%lx\nDDMA: 0x%lx\nDS: 0x%lx\nMT: 0x%lx\n",
+ pci_read_config(sc->dev, PCIR_CCS, 4),
+ pci_read_config(sc->dev, PCIR_DDMA, 4),
+ pci_read_config(sc->dev, PCIR_DS, 4),
+ pci_read_config(sc->dev, PCIR_MT, 4));
+#endif
+
+ /* allocate interupt resource */
+ sc->irqid = 0;
+ sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->irq ||
+ snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24_intr, sc, &sc->ih)) {
+ device_printf(sc->dev, "unable to map interrupt\n");
+ return ENXIO;
+ }
+
+ /* allocate DMA resource */
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/4, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24,
+ /*highaddr*/BUS_SPACE_MAXADDR_ENVY24,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24,
+ /*nsegments*/1, /*maxsegsz*/0x3ffff,
+ /*flags*/0, &sc->dmat) != 0) {
+ device_printf(sc->dev, "unable to create dma tag\n");
+ return ENXIO;
+ }
+
+ return 0;
+}
+
+static int
+envy24_pci_attach(device_t dev)
+{
+ u_int32_t data;
+ struct sc_info *sc;
+ char status[SND_STATUSLEN];
+ char name[ENVY24_NAMELEN];
+ int err = 0;
+ int i;
+
+#if(0)
+ device_printf(dev, "envy24_pci_attach()\n");
+#endif
+ /* get sc_info data area */
+ if ((sc = malloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(sc, sizeof(*sc));
+ snprintf(name, ENVY24_NAMELEN, "%s:envy24", device_get_nameunit(dev));
+ sc->lock = snd_mtxcreate(name);
+ sc->dev = dev;
+
+ /* initialize PCI interface */
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ /* allocate resources */
+ if (err = envy24_alloc_resource(sc)) {
+ device_printf(dev, "unable to allocate system resources\n");
+ goto bad;
+ }
+
+ /* initialize card */
+ if (err = envy24_init(sc)) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ /* set multi track mixer */
+ mixer_init(dev, &envy24mixer_class, sc);
+
+ /* set channel information */
+ if (err = pcm_register(dev, sc, 5, 2 + sc->adc))
+ goto bad;
+ sc->chnum = 0;
+ for (i = 0; i < 5; i++) {
+ pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
+ sc->chnum++;
+ }
+ for (i = 0; i < 2 + sc->adcn; i++) {
+ pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc);
+ sc->chnum++;
+ }
+
+ /* set status iformation */
+ snprintf(status, SND_STATUSLEN,
+ "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld",
+ rman_get_start(sc->cs),
+ rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
+ rman_get_start(sc->ddma),
+ rman_get_end(sc->ddma) - rman_get_start(sc->ddma) + 1,
+ rman_get_start(sc->ds),
+ rman_get_end(sc->ds) - rman_get_start(sc->ds) + 1,
+ rman_get_start(sc->mt),
+ rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
+ rman_get_start(sc->irq));
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+bad:
+ if (sc->ih)
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ if (sc->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ envy24_dmafree(sc);
+ if (sc->dmat)
+ bus_dma_tag_destroy(sc->dmat);
+ envy24_cfgfree(sc->cfg);
+ if (sc->cs)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+ if (sc->ddma)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
+ if (sc->ds)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
+ if (sc->mt)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+ if (sc->lock)
+ snd_mtxfree(sc->lock);
+ free(sc, M_ENVY24);
+ return err;
+}
+
+static int
+envy24_pci_detach(device_t dev)
+{
+ struct sc_info *sc;
+ int r;
+ int i;
+
+#if(0)
+ device_printf(dev, "envy24_pci_detach()\n");
+#endif
+ sc = pcm_getdevinfo(dev);
+ if (sc == NULL)
+ return 0;
+ r = pcm_unregister(dev);
+ if (r)
+ return r;
+
+ envy24_dmafree(sc);
+ if (sc->cfg->codec->destroy != NULL) {
+ for (i = 0; i < sc->adcn; i++)
+ sc->cfg->codec->destroy(sc->adc[i]);
+ for (i = 0; i < sc->dacn; i++)
+ sc->cfg->codec->destroy(sc->dac[i]);
+ }
+ envy24_cfgfree(sc->cfg);
+ bus_dma_tag_destroy(sc->dmat);
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+ snd_mtxfree(sc->lock);
+ free(sc, M_ENVY24);
+ return 0;
+}
+
+static device_method_t envy24_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, envy24_pci_probe),
+ DEVMETHOD(device_attach, envy24_pci_attach),
+ DEVMETHOD(device_detach, envy24_pci_detach),
+ { 0, 0 }
+};
+
+static driver_t envy24_driver = {
+ "pcm",
+ envy24_methods,
+#if __FreeBSD_version > 500000
+ PCM_SOFTC_SIZE,
+#else
+ sizeof(struct snddev_info),
+#endif
+};
+
+DRIVER_MODULE(snd_envy24, pci, envy24_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_envy24, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_DEPEND(snd_envy24, snd_ak452x, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_VERSION(snd_envy24, 1);
OpenPOWER on IntegriCloud