1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-19 15:33:56 +00:00

Bring in a bunch of bug fixes and some code to support more chipsets.

Neither me nor Ariff have access to any of this hardware, so all tests
have been made by Konstantin and Artem.  Commit message mostly written
by Konstantin.

envy24:
- Add test code to support rear line-in input on 'Terratec DMX 6fire'
  audio card.  This code is also intended to be used in the future for
  support of cards, that have I2C-to-GPIO expanders wired between the
  control line of the audio codec and the Envy24, however such cards
  are too complex and i can't add that support without hardware sample
  of such board, i've already tried and failed.

envy24ht:
- Add support for 'AudioTrak Prodigy HD2'.
- Add support for 'AudioTrak Prodigy 7.1 XT'.
- Add support for 'ESI Juli@' (Works ok, DAC volume is hard-coded for
  the time being, so 'mixer vol ...' doesn't work, only 'mixer pcm
  ...' works). [1]
- Fix bug in the init data for M-Audio Revolution 5.1, that
  results in distorted sound.
- Add software volume control (now 'mixer pcm' works, thanks to Ariff).
- Add support for more samples rates - 176.4kHz and 192kHz.
- Fix problem with the 192kHz samples rate playback when 24.576MHz
  crystal is used on the board instead of 49.152MHz crystal.

spicds:
- Add support for Asahi Kasei flagship DAC - AK4396 (used in AudioTrak
  Prodigy HD2).

Submitted by:	Konstantin Dimitrov <kosio.dimitrov@gmail.com>
Tested by:	Artem Antonov [1]
Reviewed by:	ariff
This commit is contained in:
Joel Dahl 2007-05-27 19:58:39 +00:00
parent e487a5e2a0
commit ccb43d8d2c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=170031
6 changed files with 246 additions and 19 deletions

View File

@ -55,6 +55,9 @@ struct sc_info;
#define ENVY24_NAMELEN 32
#define SDA_GPIO 0x10
#define SCL_GPIO 0x20
struct envy24_sample {
volatile u_int32_t buffer;
};
@ -740,7 +743,7 @@ envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
/* -------------------------------------------------------------------- */
/* M-Audio Delta series AK4524 access interface routine */
/* Envy24 I2C through GPIO bit-banging */
struct envy24_delta_ak4524_codec {
struct spicds_info *info;
@ -750,6 +753,110 @@ struct envy24_delta_ak4524_codec {
int cs, cclk, cdti;
};
static void
envy24_gpio_i2c_ctl(void *codec, unsigned int scl, unsigned int sda)
{
u_int32_t data = 0;
struct envy24_delta_ak4524_codec *ptr = codec;
#if(0)
device_printf(ptr->parent->dev, "--> %d, %d\n", scl, sda);
#endif
data = envy24_gpiord(ptr->parent);
data &= ~(SDA_GPIO | SCL_GPIO);
if (scl) data += SCL_GPIO;
if (sda) data += SDA_GPIO;
envy24_gpiowr(ptr->parent, data);
return;
}
static void
i2c_wrbit(void *codec, void (*ctrl)(void*, unsigned int, unsigned int), int bit)
{
struct envy24_delta_ak4524_codec *ptr = codec;
unsigned int sda;
if (bit)
sda = 1;
else
sda = 0;
ctrl(ptr, 0, sda);
DELAY(I2C_DELAY);
ctrl(ptr, 1, sda);
DELAY(I2C_DELAY);
ctrl(ptr, 0, sda);
DELAY(I2C_DELAY);
}
static void
i2c_start(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
{
struct envy24_delta_ak4524_codec *ptr = codec;
ctrl(ptr, 1, 1);
DELAY(I2C_DELAY);
ctrl(ptr, 1, 0);
DELAY(I2C_DELAY);
ctrl(ptr, 0, 0);
DELAY(I2C_DELAY);
}
static void
i2c_stop(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
{
struct envy24_delta_ak4524_codec *ptr = codec;
ctrl(ptr, 0, 0);
DELAY(I2C_DELAY);
ctrl(ptr, 1, 0);
DELAY(I2C_DELAY);
ctrl(ptr, 1, 1);
DELAY(I2C_DELAY);
}
static void
i2c_ack(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
{
struct envy24_delta_ak4524_codec *ptr = codec;
ctrl(ptr, 0, 1);
DELAY(I2C_DELAY);
ctrl(ptr, 1, 1);
DELAY(I2C_DELAY);
/* dummy, need routine to change gpio direction */
ctrl(ptr, 0, 1);
DELAY(I2C_DELAY);
}
static void
i2c_wr(void *codec, void (*ctrl)(void*, unsigned int, unsigned int), u_int32_t dev, int reg, u_int8_t val)
{
struct envy24_delta_ak4524_codec *ptr = codec;
int mask;
i2c_start(ptr, ctrl);
for (mask = 0x80; mask != 0; mask >>= 1)
i2c_wrbit(ptr, ctrl, dev & mask);
i2c_ack(ptr, ctrl);
if (reg != 0xff) {
for (mask = 0x80; mask != 0; mask >>= 1)
i2c_wrbit(ptr, ctrl, reg & mask);
i2c_ack(ptr, ctrl);
}
for (mask = 0x80; mask != 0; mask >>= 1)
i2c_wrbit(ptr, ctrl, val & mask);
i2c_ack(ptr, ctrl);
i2c_stop(ptr, ctrl);
}
/* -------------------------------------------------------------------- */
/* M-Audio Delta series AK4524 access interface routine */
static void
envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
{
@ -863,6 +970,18 @@ envy24_delta_ak4524_init(void *codec)
/* for the time being, init only first codec */
if (ptr->num == 0)
spicds_init(ptr->info);
/* 6fire rear input init test, set ptr->num to 1 for test */
if (ptr->parent->cfg->subvendor == 0x153b && \
ptr->parent->cfg->subdevice == 0x1138 && ptr->num == 100) {
ptr->cs = 0x02;
spicds_init(ptr->info);
device_printf(ptr->parent->dev, "6fire rear input init\n");
i2c_wr(ptr, envy24_gpio_i2c_ctl, \
PCA9554_I2CDEV, PCA9554_DIR, 0x80);
i2c_wr(ptr, envy24_gpio_i2c_ctl, \
PCA9554_I2CDEV, PCA9554_OUT, 0x02);
}
}
static void
@ -1219,6 +1338,9 @@ envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev)
device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg);
#endif
envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4);
/* 6fire rear input init test */
envy24_wrmt(sc, ENVY24_MT_RECORD, 0x00, 4);
}
return 0;
@ -1664,6 +1786,7 @@ envy24chan_trigger(kobj_t obj, void *data, int go)
ch->emldma(ch);
break;
case PCMTRIG_ABORT:
if (ch->run) {
#if(0)
device_printf(sc->dev, "envy24chan_trigger(): abort\n");
#endif
@ -1688,6 +1811,7 @@ envy24chan_trigger(kobj_t obj, void *data, int go)
envy24_updintr(sc, ch->dir);
}
#endif
}
break;
}
snd_mtxunlock(sc->lock);
@ -2411,16 +2535,15 @@ envy24_pci_attach(device_t dev)
mixer_init(dev, &envy24mixer_class, sc);
/* set channel information */
err = pcm_register(dev, sc, sc->dacn, sc->adcn);
err = pcm_register(dev, sc, 5, 2 + sc->adcn);
if (err)
goto bad;
sc->chnum = ENVY24_CHAN_PLAY_DAC1;
for (i = 0; i < sc->dacn; i++) {
sc->chnum = 0;
for (i = 0; i < 5; i++) {
pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
sc->chnum++;
}
sc->chnum = ENVY24_CHAN_REC_ADC1;
for (i = 0; i < sc->adcn; i++) {
for (i = 0; i < 2 + sc->adcn; i++) {
pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc);
sc->chnum++;
}

View File

@ -479,4 +479,17 @@
/* M-Audio Delta series parameter */
#define ENVY24_DELTA_AK4524_CIF 0
#define I2C_DELAY 1000
/* PCA9554 registers */
#define PCA9554_I2CDEV 0x40 /* I2C device address */
#define PCA9554_IN 0x00 /* input port */
#define PCA9554_OUT 0x01 /* output port */
#define PCA9554_INVERT 0x02 /* polarity invert */
#define PCA9554_DIR 0x03 /* port directions */
/* PCF8574 registers */
#define PCF8574_I2CDEV_DAC 0x48
#define PCF8574_SENSE_MASK 0x40
/* end of file */

View File

@ -26,6 +26,17 @@
*
*/
/*
* Konstantin Dimitrov's thanks list:
*
* A huge thanks goes to Spas Filipov for his friendship, support and his
* generous gift - an 'Audiotrak Prodigy HD2' audio card! I also want to
* thank Keiichi Iwasaki and his parents, because they helped Spas to get
* the card from Japan! Having hardware sample of Prodigy HD2 made adding
* support for that great card very easy and real fun and pleasure.
*
*/
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pci/spicds.h>
@ -247,7 +258,7 @@ static int envy24ht_mixmap[] = {
/* variable rate audio */
static u_int32_t envy24ht_speed[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
12000, 11025, 9600, 8000, 0
};
@ -319,12 +330,21 @@ static struct cfg_info cfg_table[] = {
{
"Envy24HT audio (AudioTrak Prodigy 7.1 LT)",
0x3132, 0x4154,
0x0b, 0x80, 0xfc, 0xc3,
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,
@ -339,7 +359,7 @@ static struct cfg_info cfg_table[] = {
0x1412, 0x3631,
0x42, 0x80, 0xf8, 0xc1,
0x3fff85, 0x72, 0x4000fa,
0x08, 0x02, 0x20, 0x00, 0x03,
0x08, 0x02, 0x10, 0x00, 0x03,
0,
&spi_codec,
},
@ -352,6 +372,24 @@ static struct cfg_info cfg_table[] = {
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 audio (Generic)",
0, 0,
@ -377,7 +415,7 @@ static u_int32_t envy24ht_playfmt[] = {
0
};
static struct pcmchan_caps envy24ht_playcaps = {8000, 96000, envy24ht_playfmt, 0};
static struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0};
struct envy24ht_emldma {
u_int32_t format;
@ -505,7 +543,6 @@ envy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
return (int)data;
}
#if 0
static int
envy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
{
@ -540,7 +577,6 @@ envy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data
return 0;
}
#endif
static int
envy24ht_rdrom(struct sc_info *sc, u_int32_t addr)
@ -572,7 +608,7 @@ envy24ht_rom2cfg(struct sc_info *sc)
device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n");
#endif
size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE);
if (size < ENVY24HT_E2PROM_GPIOSTATE + 3) {
if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) {
#if(0)
device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size);
#endif
@ -816,7 +852,7 @@ envy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask)
static u_int32_t
envy24ht_gpiogetdir(struct sc_info *sc)
{
return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4;
return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4);
}
#endif
@ -982,17 +1018,19 @@ static struct {
{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 int
envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
u_int32_t code;
u_int32_t code, i2sfmt;
int i = 0;
#if(0)
@ -1014,6 +1052,17 @@ envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
#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++) {
@ -1684,6 +1733,7 @@ envy24htchan_trigger(kobj_t obj, void *data, int go)
ch->emldma(ch);
break;
case PCMTRIG_ABORT:
if (ch->run) {
#if(0)
device_printf(sc->dev, "envy24htchan_trigger(): abort\n");
#endif
@ -1706,6 +1756,7 @@ envy24htchan_trigger(kobj_t obj, void *data, int go)
if (ch->blk != sc->blk[slot])
envy24ht_updintr(sc, ch->dir);
}*/
}
break;
}
snd_mtxunlock(sc->lock);
@ -1800,6 +1851,12 @@ envy24htmixer_init(struct snd_mixer *m)
mix_setdevs(m, ENVY24HT_MIX_MASK);
mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK);
struct snddev_info *d = NULL;
d = device_get_softc(sc->dev);
if (d != NULL)
d->flags |= SD_F_SOFTPCMVOL;
snd_mtxunlock(sc->lock);
return 0;
@ -2289,6 +2346,12 @@ envy24ht_init(struct sc_info *sc)
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);

View File

@ -108,13 +108,18 @@
#define ENVY24HT_MT_RATE_16000 0x05
#define ENVY24HT_MT_RATE_8000 0x06
#define ENVY24HT_MT_RATE_96000 0x07
#define ENVY24HT_MT_RATE_192000 0x0e
#define ENVY24HT_MT_RATE_64000 0x0f
#define ENVY24HT_MT_RATE_44100 0x08
#define ENVY24HT_MT_RATE_22050 0x09
#define ENVY24HT_MT_RATE_11025 0x0a
#define ENVY24HT_MT_RATE_88200 0x0b
#define ENVY24HT_MT_RATE_176400 0x0c
#define ENVY24HT_MT_RATE_MASK 0x0f
#define ENVY24HT_MT_I2S 0x02 /* I2S Data Format Register */
#define ENVY24HT_MT_I2S_MLR128 0x08 /* MCLK/LRCLK ratio 128x (or 256x) */
#define ENVY24HT_MT_PADDR 0x10 /* Playback DMA Current/Base Address Register */
#define ENVY24HT_MT_PCNT 0x14 /* Playback DMA Current/Base Count Register */
#define ENVY24HT_MT_PTERM 0x1C /* Playback Current/Base Terminal Count Register */

View File

@ -88,6 +88,12 @@ spicds_wrcd(struct spicds_info *codec, int reg, u_int16_t val)
spicds_wrbit(codec, 0);
spicds_wrbit(codec, 1);
}
else if (codec->type == SPICDS_TYPE_AK4396)
{
/* AK4396 chip address */
spicds_wrbit(codec, 0);
spicds_wrbit(codec, 0);
}
else {
/* chip address */
spicds_wrbit(codec, 1);
@ -229,6 +235,8 @@ spicds_init(struct spicds_info *codec)
spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */
if (codec->type == SPICDS_TYPE_AK4381)
spicds_wrcd(codec, 0x00, 0x0f); /* I2S, 24bit, power-up */
if (codec->type == SPICDS_TYPE_AK4396)
spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */
snd_mtxunlock(codec->lock);
}
@ -261,7 +269,8 @@ spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int r
#endif
snd_mtxlock(codec->lock);
if (left >= 100)
if (codec->type == SPICDS_TYPE_AK4381)
if ((codec->type == SPICDS_TYPE_AK4381) || \
(codec->type == SPICDS_TYPE_AK4396))
left = 255;
else
left = 127;
@ -270,14 +279,15 @@ spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int r
case SPICDS_TYPE_WM8770:
left = left + 27;
break;
case SPICDS_TYPE_AK4381:
case SPICDS_TYPE_AK4381 || SPICDS_TYPE_AK4396:
left = left * 255 / 100;
break;
default:
left = left * 127 / 100;
}
if (right >= 100)
if (codec->type == SPICDS_TYPE_AK4381)
if ((codec->type == SPICDS_TYPE_AK4381) || \
(codec->type == SPICDS_TYPE_AK4396))
right = 255;
else
right = 127;
@ -286,7 +296,7 @@ spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int r
case SPICDS_TYPE_WM8770:
right = right + 27;
break;
case SPICDS_TYPE_AK4381:
case SPICDS_TYPE_AK4381 || SPICDS_TYPE_AK4396:
right = right * 255 / 100;
break;
default:
@ -335,6 +345,14 @@ spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int r
spicds_wrcd(codec, AK4381_ROATT, right);
}
if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4396) {
#if(0)
device_printf(codec->dev, "spicds_set(): AK4396(PLAY) %d/%d\n", left, right);
#endif
spicds_wrcd(codec, AK4396_LOATT, left);
spicds_wrcd(codec, AK4396_ROATT, right);
}
snd_mtxunlock(codec->lock);
}

View File

@ -33,6 +33,7 @@
#define SPICDS_TYPE_WM8770 2
#define SPICDS_TYPE_AK4358 3
#define SPICDS_TYPE_AK4381 4
#define SPICDS_TYPE_AK4396 5
/* AK4524/AK4528 control registers */
#define AK4524_POWER 0x00
@ -101,6 +102,10 @@
#define AK4381_LOATT 0x03
#define AK4381_ROATT 0x04
/* AK4396 control registers */
#define AK4396_LOATT 0x03
#define AK4396_ROATT 0x04
struct spicds_info;
typedef void (*spicds_ctrl)(void *, unsigned int, unsigned int, unsigned int);