#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pci/spicds.h>
#include <dev/sound/pci/envy24ht.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include "mixer_if.h"
static MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio");
struct sc_info;
#define ENVY24HT_PLAY_CHNUM 8
#define ENVY24HT_REC_CHNUM 2
#define ENVY24HT_PLAY_BUFUNIT (4 * 8 )
#define ENVY24HT_REC_BUFUNIT (4 * 2 )
#define ENVY24HT_SAMPLE_NUM 4096
#define ENVY24HT_TIMEOUT 1000
#define ENVY24HT_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0)
#define ENVY24HT_NAMELEN 32
struct envy24ht_sample {
volatile u_int32_t buffer;
};
typedef struct envy24ht_sample sample32_t;
struct sc_chinfo {
struct snd_dbuf *buffer;
struct pcm_channel *channel;
struct sc_info *parent;
int dir;
unsigned num;
u_int32_t format;
u_int32_t speed;
u_int32_t blk;
u_int8_t *data;
unsigned int size;
int unit;
unsigned int offset;
void (*emldma)(struct sc_chinfo *);
int run;
};
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);
};
struct cfg_info {
char *name;
u_int16_t subvendor, subdevice;
u_int8_t scfg, acl, i2s, spdif;
u_int32_t gpiomask, gpiostate, gpiodir;
u_int32_t cdti, cclk, cs;
u_int8_t cif, type, free;
struct codec_entry *codec;
};
struct sc_info {
device_t dev;
struct mtx lock;
struct resource *cs;
int csid;
bus_space_tag_t cst;
bus_space_handle_t csh;
struct resource *mt;
int mtid;
bus_space_tag_t mtt;
bus_space_handle_t mth;
bus_dma_tag_t dmat;
struct resource *irq;
int irqid;
void *ih;
struct cfg_info *cfg;
int adcn, dacn;
void *adc[4], *dac[4];
u_int32_t src;
u_int8_t left[ENVY24HT_CHAN_NUM];
u_int8_t right[ENVY24HT_CHAN_NUM];
sample32_t *pbuf;
sample32_t *rbuf;
u_int32_t psize, rsize;
u_int16_t blk[2];
bus_dmamap_t pmap, rmap;
bus_addr_t paddr, raddr;
u_int32_t speed;
int run[2];
u_int16_t intr[2];
struct pcmchan_caps caps[2];
unsigned chnum;
struct sc_chinfo chan[11];
};
static void envy24ht_p8u(struct sc_chinfo *);
static void envy24ht_p16sl(struct sc_chinfo *);
static void envy24ht_p32sl(struct sc_chinfo *);
static void envy24ht_r16sl(struct sc_chinfo *);
static void envy24ht_r32sl(struct sc_chinfo *);
static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
static int envy24htchan_setformat(kobj_t, void *, u_int32_t);
static u_int32_t envy24htchan_setspeed(kobj_t, void *, u_int32_t);
static u_int32_t envy24htchan_setblocksize(kobj_t, void *, u_int32_t);
static int envy24htchan_trigger(kobj_t, void *, int);
static u_int32_t envy24htchan_getptr(kobj_t, void *);
static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *);
static int envy24htmixer_init(struct snd_mixer *);
static int envy24htmixer_reinit(struct snd_mixer *);
static int envy24htmixer_uninit(struct snd_mixer *);
static int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
static u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t);
static void *envy24ht_spi_create(device_t, void *, int, int);
static void envy24ht_spi_destroy(void *);
static void envy24ht_spi_init(void *);
static void envy24ht_spi_reinit(void *);
static void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int);
static unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = {
ENVY24HT_CHAN_PLAY_DAC1,
ENVY24HT_CHAN_PLAY_DAC2,
ENVY24HT_CHAN_PLAY_DAC3,
ENVY24HT_CHAN_PLAY_DAC4,
ENVY24HT_CHAN_PLAY_SPDIF,
ENVY24HT_CHAN_REC_MIX,
ENVY24HT_CHAN_REC_SPDIF,
ENVY24HT_CHAN_REC_ADC1,
ENVY24HT_CHAN_REC_ADC2,
ENVY24HT_CHAN_REC_ADC3,
ENVY24HT_CHAN_REC_ADC4,
};
static int envy24ht_mixmap[] = {
-1,
-1,
-1,
-1,
0,
-1,
7,
-1,
-1,
-1,
1,
-1,
-1,
-1,
8,
9,
10,
6,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
};
static u_int32_t envy24ht_speed[] = {
192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
12000, 11025, 9600, 8000, 0
};
static struct codec_entry spi_codec = {
envy24ht_spi_create,
envy24ht_spi_destroy,
envy24ht_spi_init,
envy24ht_spi_reinit,
envy24ht_spi_setvolume,
NULL,
};
static struct cfg_info cfg_table[] = {
{
"Envy24HT audio (Terratec Aureon 7.1 Space)",
0x153b, 0x1145,
0x0b, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (Terratec Aureon 5.1 Sky)",
0x153b, 0x1147,
0x0a, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (Terratec Aureon 7.1 Universe)",
0x153b, 0x1153,
0x0b, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (AudioTrak Prodigy 7.1)",
0x4933, 0x4553,
0x0b, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (Terratec PHASE 28)",
0x153b, 0x1149,
0x0b, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT-S audio (Terratec PHASE 22)",
0x153b, 0x1150,
0x10, 0x80, 0xf0, 0xc3,
0x7ffbc7, 0x7fffff, 0x438,
0x10, 0x20, 0x400, 0x01, 0x00,
0,
&spi_codec,
},
{
"Envy24HT audio (AudioTrak Prodigy 7.1 LT)",
0x3132, 0x4154,
0x4b, 0x80, 0xfc, 0xc3,
0x7ff8ff, 0x7fffff, 0x700,
0x400, 0x200, 0x100, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (AudioTrak Prodigy 7.1 XT)",
0x3136, 0x4154,
0x4b, 0x80, 0xfc, 0xc3,
0x7ff8ff, 0x7fffff, 0x700,
0x400, 0x200, 0x100, 0x00, 0x02,
0,
&spi_codec,
},
{
"Envy24HT audio (M-Audio Revolution 7.1)",
0x1412, 0x3630,
0x43, 0x80, 0xf8, 0xc1,
0x3fff85, 0x400072, 0x4000fa,
0x08, 0x02, 0x20, 0x00, 0x04,
0,
&spi_codec,
},
{
"Envy24GT audio (M-Audio Revolution 5.1)",
0x1412, 0x3631,
0x42, 0x80, 0xf8, 0xc1,
0x3fff05, 0x4000f0, 0x4000fa,
0x08, 0x02, 0x10, 0x00, 0x03,
0,
&spi_codec,
},
{
"Envy24HT audio (M-Audio Audiophile 192)",
0x1412, 0x3632,
0x68, 0x80, 0xf8, 0xc3,
0x45, 0x4000b5, 0x7fffba,
0x08, 0x02, 0x10, 0x00, 0x03,
0,
&spi_codec,
},
{
"Envy24HT audio (AudioTrak Prodigy HD2)",
0x3137, 0x4154,
0x68, 0x80, 0x78, 0xc3,
0xfff8ff, 0x200700, 0xdfffff,
0x400, 0x200, 0x100, 0x00, 0x05,
0,
&spi_codec,
},
{
"Envy24HT audio (ESI Juli@)",
0x3031, 0x4553,
0x20, 0x80, 0xf8, 0xc3,
0x7fff9f, 0x8016, 0x7fff9f,
0x08, 0x02, 0x10, 0x00, 0x03,
0,
&spi_codec,
},
{
"Envy24HT-S audio (Terrasoniq TS22PCI)",
0x153b, 0x117b,
0x10, 0x80, 0xf0, 0xc3,
0x7ffbc7, 0x7fffff, 0x438,
0x10, 0x20, 0x400, 0x01, 0x00,
0,
&spi_codec,
},
{
"Envy24HT audio (Generic)",
0, 0,
0x0b, 0x80, 0xfc, 0xc3,
0x21efff, 0x7fffff, 0x5e1000,
0x40000, 0x80000, 0x1000, 0x00, 0x02,
0,
&spi_codec,
}
};
static u_int32_t envy24ht_recfmt[] = {
SND_FORMAT(AFMT_S16_LE, 2, 0),
SND_FORMAT(AFMT_S32_LE, 2, 0),
0
};
static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0};
static u_int32_t envy24ht_playfmt[] = {
SND_FORMAT(AFMT_U8, 2, 0),
SND_FORMAT(AFMT_S16_LE, 2, 0),
SND_FORMAT(AFMT_S32_LE, 2, 0),
0
};
static struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0};
struct envy24ht_emldma {
u_int32_t format;
void (*emldma)(struct sc_chinfo *);
int unit;
};
static struct envy24ht_emldma envy24ht_pemltab[] = {
{SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2},
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4},
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8},
{0, NULL, 0}
};
static struct envy24ht_emldma envy24ht_remltab[] = {
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4},
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8},
{0, NULL, 0}
};
static u_int32_t
envy24ht_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
envy24ht_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
envy24ht_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
envy24ht_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 int
envy24ht_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, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
#endif
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
break;
DELAY(32);
}
if (i == ENVY24HT_TIMEOUT) {
return -1;
}
envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
(dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
break;
DELAY(32);
}
if (i == ENVY24HT_TIMEOUT) {
return -1;
}
data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1);
#if(0)
device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data);
#endif
return (int)data;
}
static int
envy24ht_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, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
#endif
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
break;
DELAY(32);
}
if (i == ENVY24HT_TIMEOUT) {
return -1;
}
envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
(dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
break;
DELAY(32);
}
if (i == ENVY24HT_TIMEOUT) {
return -1;
}
return 0;
}
static int
envy24ht_rdrom(struct sc_info *sc, u_int32_t addr)
{
u_int32_t data;
#if(0)
device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr);
#endif
data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) {
#if(0)
device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n");
#endif
return -1;
}
return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr);
}
static struct cfg_info *
envy24ht_rom2cfg(struct sc_info *sc)
{
struct cfg_info *buff;
int size;
int i;
#if(0)
device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n");
#endif
size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE);
if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) {
#if(0)
device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size);
#endif
buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
if (buff == NULL) {
#if(0)
device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n");
#endif
return NULL;
}
buff->free = 1;
buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
buff->scfg = 0x0b;
buff->acl = 0x80;
buff->i2s = 0xfc;
buff->spdif = 0xc3;
buff->gpiomask = 0x21efff;
buff->gpiostate = 0x7fffff;
buff->gpiodir = 0x5e1000;
buff->cdti = 0x40000;
buff->cclk = 0x80000;
buff->cs = 0x1000;
buff->cif = 0x00;
buff->type = 0x02;
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;
#if 0
return NULL;
#endif
}
buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
if (buff == NULL) {
#if(0)
device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n");
#endif
return NULL;
}
buff->free = 1;
buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG);
buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL);
buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S);
buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF);
buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16;
buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16;
buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \
envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16;
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
envy24ht_cfgfree(struct cfg_info *cfg) {
if (cfg == NULL)
return;
if (cfg->free)
free(cfg, M_ENVY24HT);
return;
}
#if 0
static int
envy24ht_coldcd(struct sc_info *sc)
{
u_int32_t data;
int i;
#if(0)
device_printf(sc->dev, "envy24ht_coldcd()\n");
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1);
DELAY(10);
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
DELAY(1000);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
if (data & ENVY24HT_MT_AC97CMD_RDY) {
return 0;
}
}
return -1;
}
static int
envy24ht_slavecd(struct sc_info *sc)
{
u_int32_t data;
int i;
#if(0)
device_printf(sc->dev, "envy24ht_slavecd()\n");
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD,
ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1);
DELAY(10);
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
DELAY(1000);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
if (data & ENVY24HT_MT_AC97CMD_RDY) {
return 0;
}
}
return -1;
}
static int
envy24ht_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, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno);
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
if ((data & ENVY24HT_MT_AC97CMD_RD) == 0)
break;
}
data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2);
#if(0)
device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data);
#endif
return (int)data;
}
static int
envy24ht_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, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2);
envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1);
for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0)
break;
}
return 0;
}
static kobj_method_t envy24ht_ac97_methods[] = {
KOBJMETHOD(ac97_read, envy24ht_rdcd),
KOBJMETHOD(ac97_write, envy24ht_wrcd),
KOBJMETHOD_END
};
AC97_DECLARE(envy24ht_ac97);
#endif
static u_int32_t
envy24ht_gpiord(struct sc_info *sc)
{
if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150)
return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2);
else
return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2));
}
static void
envy24ht_gpiowr(struct sc_info *sc, u_int32_t data)
{
#if(0)
device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF);
return;
#endif
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2);
if (sc->cfg->subdevice != 0x1150)
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1);
return;
}
#if 0
static u_int32_t
envy24ht_gpiogetmask(struct sc_info *sc)
{
return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2));
}
#endif
static void
envy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask)
{
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2);
if (sc->cfg->subdevice != 0x1150)
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1);
return;
}
#if 0
static u_int32_t
envy24ht_gpiogetdir(struct sc_info *sc)
{
return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4);
}
#endif
static void
envy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir)
{
if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150)
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2);
else
envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4);
return;
}
struct envy24ht_spi_codec {
struct spicds_info *info;
struct sc_info *parent;
int dir;
int num;
int cs, cclk, cdti;
};
static void
envy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
{
u_int32_t data = 0;
struct envy24ht_spi_codec *ptr = codec;
#if(0)
device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
#endif
data = envy24ht_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;
envy24ht_gpiowr(ptr->parent, data);
return;
}
static void *
envy24ht_spi_create(device_t dev, void *info, int dir, int num)
{
struct sc_info *sc = info;
struct envy24ht_spi_codec *buff = NULL;
#if(0)
device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num);
#endif
buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
if (buff == NULL)
return NULL;
if (dir == PCMDIR_REC && sc->adc[num] != NULL)
buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info;
else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL)
buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info;
else
buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl);
if (buff->info == NULL) {
free(buff, M_ENVY24HT);
return NULL;
}
buff->parent = sc;
buff->dir = dir;
buff->num = num;
return (void *)buff;
}
static void
envy24ht_spi_destroy(void *codec)
{
struct envy24ht_spi_codec *ptr = codec;
if (ptr == NULL)
return;
#if(0)
device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n");
#endif
if (ptr->dir == PCMDIR_PLAY) {
if (ptr->parent->dac[ptr->num] != NULL)
spicds_destroy(ptr->info);
}
else {
if (ptr->parent->adc[ptr->num] != NULL)
spicds_destroy(ptr->info);
}
free(codec, M_ENVY24HT);
}
static void
envy24ht_spi_init(void *codec)
{
struct envy24ht_spi_codec *ptr = codec;
if (ptr == NULL)
return;
#if(0)
device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n");
#endif
ptr->cs = ptr->parent->cfg->cs;
ptr->cclk = ptr->parent->cfg->cclk;
ptr->cdti = ptr->parent->cfg->cdti;
spicds_settype(ptr->info, ptr->parent->cfg->type);
spicds_setcif(ptr->info, ptr->parent->cfg->cif);
if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \
ptr->parent->cfg->type == SPICDS_TYPE_AK4528) {
spicds_setformat(ptr->info,
AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF);
}
if (ptr->num == 0)
spicds_init(ptr->info);
}
static void
envy24ht_spi_reinit(void *codec)
{
struct envy24ht_spi_codec *ptr = codec;
if (ptr == NULL)
return;
#if(0)
device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n");
#endif
spicds_reinit(ptr->info);
}
static void
envy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
{
struct envy24ht_spi_codec *ptr = codec;
if (ptr == NULL)
return;
#if(0)
device_printf(ptr->parent->dev, "envy24ht_spi_set()\n");
#endif
spicds_set(ptr->info, dir, left, right);
}
static struct {
u_int32_t speed;
u_int32_t code;
} envy24ht_speedtab[] = {
{48000, ENVY24HT_MT_RATE_48000},
{24000, ENVY24HT_MT_RATE_24000},
{12000, ENVY24HT_MT_RATE_12000},
{9600, ENVY24HT_MT_RATE_9600},
{32000, ENVY24HT_MT_RATE_32000},
{16000, ENVY24HT_MT_RATE_16000},
{8000, ENVY24HT_MT_RATE_8000},
{96000, ENVY24HT_MT_RATE_96000},
{192000, ENVY24HT_MT_RATE_192000},
{64000, ENVY24HT_MT_RATE_64000},
{44100, ENVY24HT_MT_RATE_44100},
{22050, ENVY24HT_MT_RATE_22050},
{11025, ENVY24HT_MT_RATE_11025},
{88200, ENVY24HT_MT_RATE_88200},
{176400, ENVY24HT_MT_RATE_176400},
{0, 0x10}
};
static u_int32_t
envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
u_int32_t code, i2sfmt;
int i = 0;
#if(0)
device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed);
if (speed == 0) {
code = ENVY24HT_MT_RATE_SPDIF;
envy24ht_slavecd(sc);
}
else {
#endif
for (i = 0; envy24ht_speedtab[i].speed != 0; i++) {
if (envy24ht_speedtab[i].speed == speed)
break;
}
code = envy24ht_speedtab[i].code;
#if 0
}
device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code);
#endif
if (code < 0x10) {
envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1);
if ((((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) == 0x00) && (code == ENVY24HT_MT_RATE_192000)) || \
(code == ENVY24HT_MT_RATE_176400)) {
i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
i2sfmt |= ENVY24HT_MT_I2S_MLR128;
envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
}
else {
i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
i2sfmt &= ~ENVY24HT_MT_I2S_MLR128;
envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
}
code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1);
code &= ENVY24HT_MT_RATE_MASK;
for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) {
if (envy24ht_speedtab[i].code == code)
break;
}
speed = envy24ht_speedtab[i].speed;
}
else
speed = 0;
#if(0)
device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed);
#endif
return speed;
}
static void
envy24ht_setvolume(struct sc_info *sc, unsigned ch)
{
#if(0)
device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
#endif
}
static void
envy24ht_mutevolume(struct sc_info *sc, unsigned ch)
{
#if 0
u_int32_t vol;
device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch);
vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE;
envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
#endif
}
static u_int32_t
envy24ht_gethwptr(struct sc_info *sc, int dir)
{
int unit, regno;
u_int32_t ptr, rtn;
#if(0)
device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir);
#endif
if (dir == PCMDIR_PLAY) {
rtn = sc->psize / 4;
unit = ENVY24HT_PLAY_BUFUNIT / 4;
regno = ENVY24HT_MT_PCNT;
}
else {
rtn = sc->rsize / 4;
unit = ENVY24HT_REC_BUFUNIT / 4;
regno = ENVY24HT_MT_RCNT;
}
ptr = envy24ht_rdmt(sc, regno, 2);
rtn -= (ptr + 1);
rtn /= unit;
#if(0)
device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn);
#endif
return rtn;
}
static void
envy24ht_updintr(struct sc_info *sc, int dir)
{
int regintr;
u_int32_t mask, intr;
u_int32_t cnt;
u_int16_t blk;
#if(0)
device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir);
#endif
if (dir == PCMDIR_PLAY) {
blk = sc->blk[0];
regintr = ENVY24HT_MT_PTERM;
mask = ~ENVY24HT_MT_INT_PMASK;
}
else {
blk = sc->blk[1];
regintr = ENVY24HT_MT_RTERM;
mask = ~ENVY24HT_MT_INT_RMASK;
}
cnt = blk - 1;
#if(0)
device_printf(sc->dev, "envy24ht_updintr():blk = %d, cnt = %d\n", blk, cnt);
#endif
envy24ht_wrmt(sc, regintr, cnt, 2);
intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
#if(0)
device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1);
#if(0)
device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n",
envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1));
#endif
return;
}
#if 0
static void
envy24ht_maskintr(struct sc_info *sc, int dir)
{
u_int32_t mask, intr;
#if(0)
device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir);
#endif
if (dir == PCMDIR_PLAY)
mask = ENVY24HT_MT_INT_PMASK;
else
mask = ENVY24HT_MT_INT_RMASK;
intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1);
return;
}
#endif
static int
envy24ht_checkintr(struct sc_info *sc, int dir)
{
u_int32_t mask, stat, intr, rtn;
#if(0)
device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir);
#endif
intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1);
if (dir == PCMDIR_PLAY) {
if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) {
mask = ~ENVY24HT_MT_INT_RSTAT;
envy24ht_wrmt(sc, 0x1a, 0x01, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1);
stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1);
}
}
else {
if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) {
mask = ~ENVY24HT_MT_INT_PSTAT;
#if 0
stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK;
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1);
stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1);
}
}
return rtn;
}
static void
envy24ht_start(struct sc_info *sc, int dir)
{
u_int32_t stat, sw;
#if(0)
device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir);
#endif
if (dir == PCMDIR_PLAY)
sw = ENVY24HT_MT_PCTL_PSTART;
else
sw = ENVY24HT_MT_PCTL_RSTART;
stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1);
#if(0)
DELAY(100);
device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4));
device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2));
#endif
return;
}
static void
envy24ht_stop(struct sc_info *sc, int dir)
{
u_int32_t stat, sw;
#if(0)
device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir);
#endif
if (dir == PCMDIR_PLAY)
sw = ~ENVY24HT_MT_PCTL_PSTART;
else
sw = ~ENVY24HT_MT_PCTL_RSTART;
stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1);
return;
}
#if 0
static int
envy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev)
{
return 0;
}
#endif
static void
envy24ht_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 * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src];
dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1];
dst++;
dst %= dsize;
src += 2;
src %= ssize;
}
return;
}
static void
envy24ht_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, "envy24ht_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, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
#endif
for (i = 0; i < length; i++) {
dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16;
dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16;
#if(0)
if (i < 16) {
printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]);
printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]);
}
#endif
dst++;
dst %= dsize;
src += 2;
src %= ssize;
}
#if(0)
printf("\n");
#endif
return;
}
static void
envy24ht_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 * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24;
dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
dst++;
dst %= dsize;
src += 2;
src %= ssize;
}
return;
}
static void
envy24ht_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 - ENVY24HT_CHAN_REC_ADC1) * 2;
for (i = 0; i < length; i++) {
data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
dst += 2;
dst %= dsize;
src++;
src %= ssize;
}
return;
}
static void
envy24ht_r16sl(struct sc_chinfo *ch)
{
int length;
sample32_t *dmabuf;
u_int16_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 - ENVY24HT_CHAN_REC_ADC1) * 2;
for (i = 0; i < length; i++) {
data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
dst += 2;
dst %= dsize;
src++;
src %= ssize;
}
return;
}
static void *
envy24htchan_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, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir);
#endif
mtx_lock(&sc->lock);
#if 0
if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
(sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
mtx_unlock(&sc->lock);
return NULL;
}
#endif
num = sc->chnum;
ch = &sc->chan[num];
ch->size = 8 * ENVY24HT_SAMPLE_NUM;
ch->data = malloc(ch->size, M_ENVY24HT, M_NOWAIT);
if (ch->data == NULL) {
ch->size = 0;
ch = NULL;
}
else {
ch->buffer = b;
ch->channel = c;
ch->parent = sc;
ch->dir = dir;
ch->num = envy24ht_chanmap[num];
mtx_unlock(&sc->lock);
sndbuf_setup(ch->buffer, ch->data, ch->size);
mtx_lock(&sc->lock);
ch->unit = 4;
ch->blk = 10240;
}
mtx_unlock(&sc->lock);
return ch;
}
static int
envy24htchan_free(kobj_t obj, void *data)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
#if(0)
device_printf(sc->dev, "envy24htchan_free()\n");
#endif
mtx_lock(&sc->lock);
free(ch->data, M_ENVY24HT);
ch->data = NULL;
mtx_unlock(&sc->lock);
return 0;
}
static int
envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
struct envy24ht_emldma *emltab;
int i;
#if(0)
device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format);
#endif
mtx_lock(&sc->lock);
if (ch->dir == PCMDIR_PLAY)
emltab = envy24ht_pemltab;
else
emltab = envy24ht_remltab;
if (emltab == NULL) {
mtx_unlock(&sc->lock);
return -1;
}
for (i = 0; emltab[i].format != 0; i++)
if (emltab[i].format == format)
break;
if (emltab[i].format == 0) {
mtx_unlock(&sc->lock);
return -1;
}
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;
ch->size = ch->unit * ENVY24HT_SAMPLE_NUM;
#if 0
if (ch->dir == PCMDIR_PLAY)
bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
else
bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
bsize *= ch->unit;
bcnt = ch->size / bsize;
sndbuf_resize(ch->buffer, bcnt, bsize);
#endif
mtx_unlock(&sc->lock);
#if(0)
device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0);
#endif
return 0;
}
static u_int32_t
envy24htchan_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, "envy24htchan_setspeed(obj, data, %d)\n", speed);
#endif
prev = 0x7fffffff;
for (i = 0; (val = envy24ht_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, "envy24htchan_setspeed(): return %d\n", ch->speed);
#endif
return ch->speed;
}
static u_int32_t
envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct sc_chinfo *ch = data;
u_int32_t size, prev;
unsigned int bcnt, bsize;
#if(0)
device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize);
#endif
prev = 0x7fffffff;
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 *= ENVY24HT_PLAY_BUFUNIT / 4;
else
ch->blk *= ENVY24HT_REC_BUFUNIT / 4;
if (ch->dir == PCMDIR_PLAY)
bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
else
bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
bsize *= ch->unit;
bcnt = ch->size / bsize;
sndbuf_resize(ch->buffer, bcnt, bsize);
#if(0)
device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev);
#endif
return prev;
}
static int
envy24htchan_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 error = 0;
#if 0
int i;
device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go);
#endif
mtx_lock(&sc->lock);
if (ch->dir == PCMDIR_PLAY)
slot = 0;
else
slot = 1;
switch (go) {
case PCMTRIG_START:
#if(0)
device_printf(sc->dev, "envy24htchan_trigger(): start\n");
#endif
if (sc->run[0] == 0 && sc->run[1] == 0) {
sc->speed = envy24ht_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) {
error = -1;
goto fail;
}
if (ch->speed == 0)
ch->channel->speed = sc->speed;
sc->run[slot]++;
if (sc->run[slot] == 1) {
ch->offset = 0;
sc->blk[slot] = ch->blk;
}
else {
ptr = envy24ht_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);
envy24ht_setvolume(sc, ch->num);
}
envy24ht_updintr(sc, ch->dir);
if (sc->run[slot] == 1)
envy24ht_start(sc, ch->dir);
ch->run = 1;
break;
case PCMTRIG_EMLDMAWR:
#if(0)
device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n");
#endif
if (ch->run != 1) {
error = -1;
goto fail;
}
ch->emldma(ch);
break;
case PCMTRIG_EMLDMARD:
#if(0)
device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n");
#endif
if (ch->run != 1) {
error = -1;
goto fail;
}
ch->emldma(ch);
break;
case PCMTRIG_ABORT:
if (ch->run) {
#if(0)
device_printf(sc->dev, "envy24htchan_trigger(): abort\n");
#endif
ch->run = 0;
sc->run[slot]--;
if (ch->dir == PCMDIR_PLAY)
envy24ht_mutevolume(sc, ch->num);
if (sc->run[slot] == 0) {
envy24ht_stop(sc, ch->dir);
sc->intr[slot] = 0;
}
}
break;
}
fail:
mtx_unlock(&sc->lock);
return (error);
}
static u_int32_t
envy24htchan_getptr(kobj_t obj, void *data)
{
struct sc_chinfo *ch = data;
struct sc_info *sc = ch->parent;
u_int32_t ptr, rtn;
#if(0)
device_printf(sc->dev, "envy24htchan_getptr()\n");
#endif
mtx_lock(&sc->lock);
ptr = envy24ht_gethwptr(sc, ch->dir);
rtn = ptr * ch->unit;
mtx_unlock(&sc->lock);
#if(0)
device_printf(sc->dev, "envy24htchan_getptr(): return %d\n",
rtn);
#endif
return rtn;
}
static struct pcmchan_caps *
envy24htchan_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, "envy24htchan_getcaps()\n");
#endif
mtx_lock(&sc->lock);
if (ch->dir == PCMDIR_PLAY) {
if (sc->run[0] == 0)
rtn = &envy24ht_playcaps;
else
rtn = &sc->caps[0];
}
else {
if (sc->run[1] == 0)
rtn = &envy24ht_reccaps;
else
rtn = &sc->caps[1];
}
mtx_unlock(&sc->lock);
return rtn;
}
static kobj_method_t envy24htchan_methods[] = {
KOBJMETHOD(channel_init, envy24htchan_init),
KOBJMETHOD(channel_free, envy24htchan_free),
KOBJMETHOD(channel_setformat, envy24htchan_setformat),
KOBJMETHOD(channel_setspeed, envy24htchan_setspeed),
KOBJMETHOD(channel_setblocksize, envy24htchan_setblocksize),
KOBJMETHOD(channel_trigger, envy24htchan_trigger),
KOBJMETHOD(channel_getptr, envy24htchan_getptr),
KOBJMETHOD(channel_getcaps, envy24htchan_getcaps),
KOBJMETHOD_END
};
CHANNEL_DECLARE(envy24htchan);
static int
envy24htmixer_init(struct snd_mixer *m)
{
struct sc_info *sc = mix_getdevinfo(m);
#if(0)
device_printf(sc->dev, "envy24htmixer_init()\n");
#endif
if (sc == NULL)
return -1;
mtx_lock(&sc->lock);
#if 0
envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1);
#endif
pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
mix_setdevs(m, ENVY24HT_MIX_MASK);
mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK);
mtx_unlock(&sc->lock);
return 0;
}
static int
envy24htmixer_reinit(struct snd_mixer *m)
{
struct sc_info *sc = mix_getdevinfo(m);
if (sc == NULL)
return -1;
#if(0)
device_printf(sc->dev, "envy24htmixer_reinit()\n");
#endif
return 0;
}
static int
envy24htmixer_uninit(struct snd_mixer *m)
{
struct sc_info *sc = mix_getdevinfo(m);
if (sc == NULL)
return -1;
#if(0)
device_printf(sc->dev, "envy24htmixer_uninit()\n");
#endif
return 0;
}
static int
envy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct sc_info *sc = mix_getdevinfo(m);
int ch = envy24ht_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 = envy24ht_chanmap[ch];
#if(0)
device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n",
dev, left, right);
#endif
mtx_lock(&sc->lock);
if (dev == 0) {
for (i = 0; i < sc->dacn; i++) {
sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
}
}
else {
if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN)
sc->left[hwch] = ENVY24HT_VOL_MUTE;
if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN)
sc->right[hwch] = ENVY24HT_VOL_MUTE;
if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run)
envy24ht_setvolume(sc, hwch);
}
mtx_unlock(&sc->lock);
return right << 8 | left;
}
static u_int32_t
envy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
struct sc_info *sc = mix_getdevinfo(m);
int ch = envy24ht_mixmap[src];
#if(0)
device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src);
#endif
if (ch > ENVY24HT_CHAN_PLAY_SPDIF)
sc->src = ch;
return src;
}
static kobj_method_t envy24htmixer_methods[] = {
KOBJMETHOD(mixer_init, envy24htmixer_init),
KOBJMETHOD(mixer_reinit, envy24htmixer_reinit),
KOBJMETHOD(mixer_uninit, envy24htmixer_uninit),
KOBJMETHOD(mixer_set, envy24htmixer_set),
KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc),
KOBJMETHOD_END
};
MIXER_DECLARE(envy24htmixer);
static void
envy24ht_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, "envy24ht_intr()\n");
#endif
mtx_lock(&sc->lock);
if (envy24ht_checkintr(sc, PCMDIR_PLAY)) {
#if(0)
device_printf(sc->dev, "envy24ht_intr(): play\n");
#endif
dsize = sc->psize / 4;
ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1;
#if(0)
device_printf(sc->dev, "envy24ht_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 = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) {
ch = &sc->chan[i];
#if(0)
if (ch->run)
device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk);
#endif
if (ch->run && ch->blk <= feed) {
mtx_unlock(&sc->lock);
chn_intr(ch->channel);
mtx_lock(&sc->lock);
}
}
sc->intr[0] = ptr;
envy24ht_updintr(sc, PCMDIR_PLAY);
}
if (envy24ht_checkintr(sc, PCMDIR_REC)) {
#if(0)
device_printf(sc->dev, "envy24ht_intr(): rec\n");
#endif
dsize = sc->rsize / 4;
ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1;
ptr -= ptr % sc->blk[1];
feed = (ptr + dsize - sc->intr[1]) % dsize;
for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) {
ch = &sc->chan[i];
if (ch->run && ch->blk <= feed) {
mtx_unlock(&sc->lock);
chn_intr(ch->channel);
mtx_lock(&sc->lock);
}
}
sc->intr[1] = ptr;
envy24ht_updintr(sc, PCMDIR_REC);
}
mtx_unlock(&sc->lock);
return;
}
static int
envy24ht_pci_probe(device_t dev)
{
u_int16_t sv, sd;
int i;
#if(0)
printf("envy24ht_pci_probe()\n");
#endif
if (pci_get_device(dev) == PCID_ENVY24HT &&
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("envy24ht_pci_probe(): return 0\n");
#endif
return 0;
}
else {
#if(0)
printf("envy24ht_pci_probe(): return ENXIO\n");
#endif
return ENXIO;
}
}
static void
envy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct sc_info *sc = arg;
sc->paddr = segs->ds_addr;
#if(0)
device_printf(sc->dev, "envy24ht_dmapsetmap()\n");
if (bootverbose) {
printf("envy24ht(play): setmap %lx, %lx; ",
(unsigned long)segs->ds_addr,
(unsigned long)segs->ds_len);
}
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, (uint32_t)segs->ds_addr, 4);
envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, (uint32_t)(segs->ds_len / 4 - 1), 2);
}
static void
envy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct sc_info *sc = arg;
sc->raddr = segs->ds_addr;
#if(0)
device_printf(sc->dev, "envy24ht_dmarsetmap()\n");
if (bootverbose) {
printf("envy24ht(record): setmap %lx, %lx; ",
(unsigned long)segs->ds_addr,
(unsigned long)segs->ds_len);
}
#endif
envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, (uint32_t)segs->ds_addr, 4);
envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, (uint32_t)(segs->ds_len / 4 - 1), 2);
}
static void
envy24ht_dmafree(struct sc_info *sc)
{
#if(0)
device_printf(sc->dev, "envy24ht_dmafree():");
printf(" sc->raddr(0x%08x)", (u_int32_t)sc->raddr);
printf(" sc->paddr(0x%08x)", (u_int32_t)sc->paddr);
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->raddr)
bus_dmamap_unload(sc->dmat, sc->rmap);
if (sc->paddr)
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->raddr = sc->paddr = 0;
sc->pbuf = NULL;
sc->rbuf = NULL;
return;
}
static int
envy24ht_dmainit(struct sc_info *sc)
{
#if(0)
device_printf(sc->dev, "envy24ht_dmainit()\n");
#endif
sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM;
sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM;
sc->pbuf = NULL;
sc->rbuf = NULL;
sc->paddr = sc->raddr = 0;
sc->blk[0] = sc->blk[1] = 0;
#if(0)
device_printf(sc->dev, "envy24ht_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, "envy24ht_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, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n");
#endif
if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, BUS_DMA_NOWAIT))
goto bad;
#if(0)
device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n");
#endif
if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, BUS_DMA_NOWAIT))
goto bad;
bzero(sc->pbuf, sc->psize);
bzero(sc->rbuf, sc->rsize);
return 0;
bad:
envy24ht_dmafree(sc);
return ENOSPC;
}
static void
envy24ht_putcfg(struct sc_info *sc)
{
device_printf(sc->dev, "system configuration\n");
printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
sc->cfg->subvendor, sc->cfg->subdevice);
printf(" XIN2 Clock Source: ");
switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) {
case 0x00:
printf("24.576MHz(96kHz*256)\n");
break;
case 0x40:
printf("49.152MHz(192kHz*256)\n");
break;
case 0x80:
printf("reserved\n");
break;
default:
printf("illegal system setting\n");
}
printf(" MPU-401 UART(s) #: ");
if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU)
printf("1\n");
else
printf("not implemented\n");
switch (sc->adcn) {
case 0x01:
case 0x02:
printf(" ADC #: ");
printf("%d\n", sc->adcn);
break;
case 0x03:
printf(" ADC #: ");
printf("%d", 1);
printf(" and SPDIF receiver connected\n");
break;
default:
printf(" no physical inputs\n");
}
printf(" DAC #: ");
printf("%d\n", sc->dacn);
printf(" Multi-track converter type: ");
if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) {
printf("AC'97(SDATA_OUT:");
if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE)
printf("packed");
else
printf("split");
printf(")\n");
}
else {
printf("I2S(");
if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL)
printf("with volume, ");
if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ)
printf("192KHz support, ");
else
if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ)
printf("192KHz support, ");
else
printf("48KHz support, ");
switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) {
case ENVY24HT_CCSM_I2S_16BIT:
printf("16bit resolution, ");
break;
case ENVY24HT_CCSM_I2S_18BIT:
printf("18bit resolution, ");
break;
case ENVY24HT_CCSM_I2S_20BIT:
printf("20bit resolution, ");
break;
case ENVY24HT_CCSM_I2S_24BIT:
printf("24bit resolution, ");
break;
}
printf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID);
}
printf(" S/PDIF(IN/OUT): ");
if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN)
printf("1/");
else
printf("0/");
if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT)
printf("1 ");
else
printf("0 ");
if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT))
printf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_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
envy24ht_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, "envy24ht_init()\n");
#endif
#if 0
envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1);
DELAY(200);
envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1);
DELAY(200);
data = pci_read_config(sc->dev, PCIR_LAC, 2);
data |= PCIM_LAC_DISABLE;
pci_write_config(sc->dev, PCIR_LAC, data, 2);
#endif
sc->cfg = NULL;
for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
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) {
sc->cfg = envy24ht_rom2cfg(sc);
}
sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1;
sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1;
if (1 ) {
envy24ht_putcfg(sc);
}
envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1);
envy24ht_gpiosetmask(sc, sc->cfg->gpiomask);
envy24ht_gpiosetdir(sc, sc->cfg->gpiodir);
envy24ht_gpiowr(sc, sc->cfg->gpiostate);
if ((sc->cfg->subvendor == 0x3031) && (sc->cfg->subdevice == 0x4553)) {
envy24ht_wri2c(sc, 0x22, 0x00, 0x07);
envy24ht_wri2c(sc, 0x22, 0x04, 0x5f | 0x80);
envy24ht_wri2c(sc, 0x22, 0x05, 0x5f | 0x80);
}
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]);
}
#if(0)
device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n");
#endif
if (envy24ht_dmainit(sc))
return ENOSPC;
sc->run[0] = sc->run[1] = 0;
sc->intr[0] = sc->intr[1] = 0;
sc->speed = 0;
sc->caps[0].fmtlist = envy24ht_playfmt;
sc->caps[1].fmtlist = envy24ht_recfmt;
#if 0
envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0);
envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
#endif
data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1);
data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
#if(0)
device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data);
#endif
return 0;
}
static int
envy24ht_alloc_resource(struct sc_info *sc)
{
sc->csid = PCIR_CCS;
sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_IOPORT,
&sc->csid, RF_ACTIVE);
sc->mtid = ENVY24HT_PCIR_MT;
sc->mt = bus_alloc_resource_any(sc->dev, SYS_RES_IOPORT,
&sc->mtid, RF_ACTIVE);
if (!sc->cs || !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->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\nMT: 0x%lx\n",
pci_read_config(sc->dev, PCIR_CCS, 4),
pci_read_config(sc->dev, PCIR_MT, 4));
#endif
sc->irqid = 0;
sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!sc->irq ||
snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24ht_intr, sc, &sc->ih)) {
device_printf(sc->dev, "unable to map interrupt\n");
return ENXIO;
}
if (bus_dma_tag_create(bus_get_dma_tag(sc->dev),
4,
0,
BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR,
NULL, NULL,
BUS_SPACE_MAXSIZE_ENVY24,
1, 0x3ffff,
0, NULL,
NULL, &sc->dmat) != 0) {
device_printf(sc->dev, "unable to create dma tag\n");
return ENXIO;
}
return 0;
}
static int
envy24ht_pci_attach(device_t dev)
{
struct sc_info *sc;
char status[SND_STATUSLEN];
int err = 0;
int i;
#if(0)
device_printf(dev, "envy24ht_pci_attach()\n");
#endif
if ((sc = malloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(sc, sizeof(*sc));
mtx_init(&sc->lock, device_get_nameunit(dev), "snd_eny24ht softc",
MTX_DEF);
sc->dev = dev;
pci_enable_busmaster(dev);
err = envy24ht_alloc_resource(sc);
if (err) {
device_printf(dev, "unable to allocate system resources\n");
goto bad;
}
err = envy24ht_init(sc);
if (err) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
mixer_init(dev, &envy24htmixer_class, sc);
pcm_init(dev, sc);
sc->chnum = 0;
pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc);
sc->chnum++;
for (i = 0; i < 2 + sc->adcn; i++) {
pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc);
sc->chnum++;
}
snprintf(status, SND_STATUSLEN,
"port 0x%jx:%jd,0x%jx:%jd irq %jd on %s",
rman_get_start(sc->cs),
rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
rman_get_start(sc->mt),
rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
if (pcm_register(dev, status))
goto bad;
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);
envy24ht_dmafree(sc);
if (sc->dmat)
bus_dma_tag_destroy(sc->dmat);
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]);
}
envy24ht_cfgfree(sc->cfg);
if (sc->cs)
bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
if (sc->mt)
bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
mtx_destroy(&sc->lock);
free(sc, M_ENVY24HT);
return err;
}
static int
envy24ht_pci_detach(device_t dev)
{
struct sc_info *sc;
int r;
int i;
#if(0)
device_printf(dev, "envy24ht_pci_detach()\n");
#endif
sc = pcm_getdevinfo(dev);
if (sc == NULL)
return 0;
r = pcm_unregister(dev);
if (r)
return r;
envy24ht_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]);
}
envy24ht_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->mtid, sc->mt);
mtx_destroy(&sc->lock);
free(sc, M_ENVY24HT);
return 0;
}
static device_method_t envy24ht_methods[] = {
DEVMETHOD(device_probe, envy24ht_pci_probe),
DEVMETHOD(device_attach, envy24ht_pci_attach),
DEVMETHOD(device_detach, envy24ht_pci_detach),
DEVMETHOD_END
};
static driver_t envy24ht_driver = {
"pcm",
envy24ht_methods,
PCM_SOFTC_SIZE,
};
DRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, 0, 0);
MODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
MODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1);
MODULE_VERSION(snd_envy24ht, 1);