summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pci
diff options
context:
space:
mode:
authorroger <roger@FreeBSD.org>1999-11-19 07:29:10 +0000
committerroger <roger@FreeBSD.org>1999-11-19 07:29:10 +0000
commit839422ecb689fe2783226672bbe0724d38288048 (patch)
tree93ea08518545ce6862e3072a7f449a3309286d27 /sys/dev/sound/pci
parent391b6293c54f0f8ae00845dc50cf50a42cccabd7 (diff)
downloadFreeBSD-src-839422ecb689fe2783226672bbe0724d38288048.zip
FreeBSD-src-839422ecb689fe2783226672bbe0724d38288048.tar.gz
Rename es1370.c to es137x.c and merge in the patches to support
ES1371 and ES1373 PCI Audio Sound Chips. The 1371 and 1373 can be found on newer CreativeLabs/Ensoniq sound cards such as the SoundBlaster PCI 16,64 and 128. Submitted by: Russell Cattelan <cattelan@thebarn.com> Obtained from: Parts obtained from linux, but rewritten by Russell
Diffstat (limited to 'sys/dev/sound/pci')
-rw-r--r--sys/dev/sound/pci/es137x.c407
1 files changed, 390 insertions, 17 deletions
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index 4bd8cd6..2ef00e9 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -1,7 +1,8 @@
/*
- * Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
- * AK4531.
+ * Support the ENSONIQ AudioPCI board and Creative Labs SoundBlaster PCI
+ * boards based on the ES1370, ES1371 and ES1373 chips.
*
+ * Copyright (c) 1999 Russell Cattelan <cattelan@thebarn.com>
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright (c) 1998 by Joachim Kuebart. All rights reserved.
*
@@ -40,34 +41,50 @@
* $FreeBSD$
*/
+/*
+ * Part of this code was heavily inspired by the linux driver from
+ * Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Just about everything has been touched and reworked in some way but
+ * the all the underlying sequences/timing/register values are from
+ * Thomas' code.
+ *
+*/
+
#include "pci.h"
#include "pcm.h"
#include <dev/pcm/sound.h>
-#include <dev/pcm/pci/es1370.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/es137x.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
+#include <sys/sysctl.h>
+
#if NPCI != 0
+static int debug = 0;
+SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, "");
+
#define MEM_MAP_REG 0x14
/* PCI IDs of supported chips */
#define ES1370_PCI_ID 0x50001274
+#define ES1371_PCI_ID 0x13711274
/* device private data */
struct es_info;
-struct es_chinfo {
+typedef struct es_chinfo {
struct es_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
u_int32_t fmt;
-};
+} es_chinfo_t;
-struct es_info {
+typedef struct es_info {
bus_space_tag_t st;
bus_space_handle_t sh;
bus_dma_tag_t parent_dmat;
@@ -76,23 +93,33 @@ struct es_info {
u_long ctrl;
u_long sctrl;
struct es_chinfo pch, rch;
-};
+} es_info_t;
/* -------------------------------------------------------------------- */
-
/* prototypes */
+
+static u_int es1371_wait_src_ready(es_info_t *);
+static void es1371_src_write(es_info_t *, u_short, unsigned short);
+static u_int es1371_adc_rate (es_info_t *, u_int, int);
+static u_int es1371_dac1_rate(es_info_t *, u_int, int);
+static u_int es1371_dac2_rate(es_info_t *, u_int, int);
+static void es1371_wrcodec(void *, int, u_int32_t);
+static u_int32_t es1371_rdcodec(void *, u_int32_t);
+static int es1371_init(es_info_t *es);
+static int eschan1371_setspeed(void *data, u_int32_t speed);
+
static int es_init(struct es_info *);
static void es_intr(void *);
static int write_codec(struct es_info *, u_char, u_char);
/* channel interface */
static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
-static int eschan_setdir(void *data, int dir);
-static int eschan_setformat(void *data, u_int32_t format);
-static int eschan_setspeed(void *data, u_int32_t speed);
-static int eschan_setblocksize(void *data, u_int32_t blocksize);
-static int eschan_trigger(void *data, int go);
-static int eschan_getptr(void *data);
+static int eschan_setdir(void *data, int dir);
+static int eschan_setformat(void *data, u_int32_t format);
+static int eschan_setspeed(void *data, u_int32_t speed);
+static int eschan_setblocksize(void *data, u_int32_t blocksize);
+static int eschan_trigger(void *data, int go);
+static int eschan_getptr(void *data);
static pcmchan_caps *eschan_getcaps(void *data);
static pcmchan_caps es_playcaps = {
@@ -391,6 +418,331 @@ es_intr (void *p)
if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
}
+
+/* ES1371 specific code */
+
+#define CODEC_ID_SESHIFT 10
+#define CODEC_ID_SEMASK 0x1f
+
+#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */
+#define CODEC_PIADD_MASK 0x007f0000
+#define CODEC_PIADD_SHIFT 16
+#define CODEC_PIDAT_MASK 0x0000ffff
+#define CODEC_PIDAT_SHIFT 0
+
+#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */
+#define CODEC_POADD_MASK 0x007f0000
+#define CODEC_POADD_SHIFT 16
+#define CODEC_PODAT_MASK 0x0000ffff
+#define CODEC_PODAT_SHIFT 0
+
+#define CODEC_RDY 0x80000000 /* AC97 read data valid */
+#define CODEC_WIP 0x40000000 /* AC97 write in progress */
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1371_REG_CODEC 0x14
+#define ES1371_REG_LEGACY 0x18 /* W/R: Legacy control/status register */
+#define ES1371_REG_SMPRATE 0x10 /* W/R: Codec rate converter interface register */
+
+#define ES1371_SYNC_RES (1<<14) /* Warm AC97 reset */
+#define ES1371_DIS_R1 (1<<19) /* record channel accumulator update disable */
+#define ES1371_DIS_P2 (1<<20) /* playback channel 2 accumulator update disable */
+#define ES1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */
+#define ES1371_DIS_SRC (1<<22) /* sample rate converter disable */
+#define ES1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */
+#define ES1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */
+#define ES1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */
+#define ES1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */
+#define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */
+
+/*
+ * Sample rate converter addresses
+ */
+
+#define ES_SMPREG_DAC1 0x70
+#define ES_SMPREG_DAC2 0x74
+#define ES_SMPREG_ADC 0x78
+#define ES_SMPREG_TRUNC_N 0x00
+#define ES_SMPREG_INT_REGS 0x01
+#define ES_SMPREG_VFREQ_FRAC 0x03
+#define ES_SMPREG_VOL_ADC 0x6c
+#define ES_SMPREG_VOL_DAC1 0x7c
+#define ES_SMPREG_VOL_DAC2 0x7e
+
+
+int
+es1371_init(struct es_info *es)
+{
+ int idx;
+
+ if(debug > 0) printf("es_init\n");
+
+ es->ctrl = 0;
+ es->sctrl = 0;
+ /* initialize the chips */
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, 0);
+ /* AC'97 warm reset to start the bitclk */
+ bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, es->ctrl | ES1371_SYNC_RES);
+ DELAY(2000);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL,es->ctrl);
+ /* Init the sample rate converter */
+ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC);
+ for (idx = 0; idx < 0x80; idx++)
+ es1371_src_write(es, idx, 0);
+ es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
+ es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
+ es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
+ es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
+ es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12);
+ es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
+ es1371_adc_rate (es, 22050, 1);
+ es1371_dac1_rate(es, 22050, 1);
+ es1371_dac2_rate(es, 22050, 1);
+ /* WARNING:
+ * enabling the sample rate converter without properly programming
+ * its parameters causes the chip to lock up (the SRC busy bit will
+ * be stuck high, and I've found no way to rectify this other than
+ * power cycle)
+ */
+ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, 0);
+
+ return (0);
+}
+
+void
+es1371_wrcodec(void *s, int addr, u_int32_t data)
+{
+ /* unsigned long flags; */
+ int sl;
+ unsigned t, x;
+ struct es_info *es = (struct es_info*)s;
+
+ if(debug > 0) printf("wrcodec addr 0x%x data 0x%x\n",addr,data);
+
+ for (t = 0; t < 0x1000; t++)
+ if(!(bus_space_read_4(es->st, es->sh,(ES1371_REG_CODEC & CODEC_WIP))))
+ break;
+ sl = spltty();
+ /* save the current state for later */
+ x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
+ /* enable SRC state data in SRC mux */
+ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
+ (es1371_wait_src_ready(s) &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
+ /* wait for a SAFE time to write addr/data and then do it, dammit */
+ for (t = 0; t < 0x1000; t++)
+ if (( bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
+ break;
+
+ if(debug > 2) printf("one b_s_w: 0x%x 0x%x 0x%x\n",es->sh,ES1371_REG_CODEC,
+ ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
+ ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
+
+ bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC,
+ ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
+ ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
+ /* restore SRC reg */
+ es1371_wait_src_ready(s);
+ if(debug > 2) printf("two b_s_w: 0x%x 0x%x 0x%x\n",es->sh,ES1371_REG_SMPRATE,x);
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,x);
+ splx(sl);
+}
+
+u_int32_t
+es1371_rdcodec(void *s, u_int32_t addr)
+{
+ /* unsigned long flags; */
+ int sl;
+ unsigned t, x;
+
+ struct es_info *es = (struct es_info *)s;
+
+ if(debug > 0) printf("rdcodec addr 0x%x ... ",addr);
+
+ for (t = 0; t < 0x1000; t++)
+ if (!(x = bus_space_read_4(es->st,es->sh,ES1371_REG_CODEC) & CODEC_WIP))
+ break;
+ if(debug >0) printf("loop 1 t 0x%x x 0x%x ",t,x);
+
+ sl = spltty();
+
+ /* save the current state for later */
+ x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
+ /* enable SRC state data in SRC mux */
+ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
+ (es1371_wait_src_ready(s) &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
+ /* wait for a SAFE time to write addr/data and then do it, dammit */
+ for (t = 0; t < 0x5000; t++)
+ if (( x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
+ break;
+ if(debug >0) printf("loop 2 t 0x%x x 0x%x ",t,x);
+ bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC,
+ ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD);
+
+ /* restore SRC reg */
+ es1371_wait_src_ready(s);
+ bus_space_write_4(es->st,es->sh,ES1371_REG_SMPRATE,x);
+
+ splx(sl);
+
+ /* now wait for the stinkin' data (RDY) */
+ for (t = 0; t < 0x1000; t++)
+ if ((x = bus_space_read_4(es->st,es->sh,ES1371_REG_CODEC)) & CODEC_RDY)
+ break;
+ if(debug > 0) printf("loop 3 t 0x%x 0x%x ret 0x%x\n",t,x,((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT));
+ return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
+}
+
+
+
+
+static u_int
+es1371_src_read(es_info_t *es, u_short reg){
+
+ unsigned int r;
+
+ r = es1371_wait_src_ready(es) &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
+ r |= ES1371_SRC_RAM_ADDRO(reg);
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
+ return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es));
+}
+
+static void
+es1371_src_write(es_info_t *es, u_short reg, u_short data){
+ u_int r;
+
+ r = es1371_wait_src_ready(es) &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
+ r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data);
+ /* printf("es1371_src_write 0x%x 0x%x\n",ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE); */
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE);
+}
+
+static u_int
+es1371_adc_rate(es_info_t *es, u_int rate, int set){
+ u_int n, truncm, freq, result;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 4000)
+ rate = 4000;
+ n = rate / 3000;
+ if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+ n--;
+ truncm = (21 * n - 1) | 1;
+ freq = ((48000UL << 15) / rate) * n;
+ result = (48000UL << 15) / (freq / n);
+ if (set) {
+ if (rate >= 24000) {
+ if (truncm > 239)
+ truncm = 239;
+ es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+ (((239 - truncm) >> 1) << 9) | (n << 4));
+ } else {
+ if (truncm > 119)
+ truncm = 119;
+ es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+ 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+ }
+ es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
+ (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) &
+ 0x00ff) | ((freq >> 5) & 0xfc00));
+ es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ es1371_src_write(es, ES_SMPREG_VOL_ADC, n << 8);
+ es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, n << 8);
+ }
+ return result;
+}
+
+static u_int
+es1371_dac1_rate(es_info_t *es, u_int rate, int set){
+ u_int freq, r, result;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 4000)
+ rate = 4000;
+ freq = (rate << 15) / 3000;
+ result = (freq * 3000) >> 15;
+ if (set) {
+ r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
+ es1371_src_write(es, ES_SMPREG_DAC1 +
+ ES_SMPREG_INT_REGS,
+ (es1371_src_read(es,
+ ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00));
+ es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P2 | ES1371_DIS_R1));
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
+ }
+ return result;
+}
+
+static u_int
+es1371_dac2_rate(es_info_t *es, u_int rate, int set){
+ u_int freq, r, result;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 4000)
+ rate = 4000;
+ freq = (rate << 15) / 3000;
+ result = (freq * 3000) >> 15;
+ if (set) {
+ r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
+ /* if(debug > 0) printf("dac2_rate 0x%x\n",bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)); */
+ es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
+ (es1371_src_read(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) &
+ 0x00ff) | ((freq >> 5) & 0xfc00));
+ es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_R1));
+ bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
+ /* if(debug > 0) printf("dac2_rate 0x%x\n",bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)); */
+ }
+ return result;
+}
+
+
+static u_int
+es1371_wait_src_ready(es_info_t *es){
+ u_int t, r;
+
+ for (t = 0; t < 500; t++) {
+ if (!((r = bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)) & ES1371_SRC_RAM_BUSY)){
+ return r;
+ }
+ DELAY(1000);
+ }
+ printf("es1371: wait source ready timeout 0x%x [0x%x]\n", ES1371_REG_SMPRATE, r);
+ return 0;
+}
+
+
+int
+eschan1371_setspeed(void *data, u_int32_t speed)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ /* rec/play speeds locked together - should indicate in flags */
+ es1371_dac2_rate(es, speed, 1); /* play */
+ es1371_adc_rate (es, speed, 1); /* record */
+
+ return speed; /* XXX calc real speed */
+}
+
+
+
/* -------------------------------------------------------------------- */
/*
@@ -423,6 +775,9 @@ es_pci_probe(device_t dev)
if (pci_get_devid(dev) == ES1370_PCI_ID) {
device_set_desc(dev, "AudioPCI ES1370");
return 0;
+ } else if (pci_get_devid(dev) == ES1371_PCI_ID) {
+ device_set_desc(dev, "AudioPCI ES1371");
+ return 0;
}
return ENXIO;
}
@@ -441,6 +796,7 @@ es_pci_attach(device_t dev)
struct resource *irq = 0;
void *ih = 0;
char status[SND_STATUSLEN];
+ struct ac97_info *codec;
d = device_get_softc(dev);
if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
@@ -477,12 +833,28 @@ es_pci_attach(device_t dev)
device_printf(dev, "unable to map register space\n");
goto bad;
}
-
- if (es_init(es) == -1) {
+
+ if (pci_get_devid(dev) == ES1371_PCI_ID) {
+ if(-1 == es1371_init(es)){
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+ codec = ac97_create(es,(ac97_read *)es1371_rdcodec,(ac97_write *)es1371_wrcodec);
+ if (codec == NULL) goto bad;
+ /* our init routine does everything for us */
+ /* set to NULL; flag mixer_init not to run the ac97_init */
+ /* ac97_mixer.init = NULL; */
+ mixer_init(d, &ac97_mixer, codec);
+ /* change the routine for setting speed */
+ es_chantemplate.setspeed = eschan1371_setspeed;
+ } else if (pci_get_devid(dev) == ES1370_PCI_ID) {
+ if (-1 == es_init(es)){
device_printf(dev, "unable to initialize the card\n");
goto bad;
+ }
+ mixer_init(d, &es_mixer, es);
}
- mixer_init(d, &es_mixer, es);
+
irqid = 0;
irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
@@ -540,4 +912,5 @@ static devclass_t pcm_devclass;
DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
+
#endif /* NPCI != 0 */
OpenPOWER on IntegriCloud