diff options
author | roger <roger@FreeBSD.org> | 1999-11-19 07:29:10 +0000 |
---|---|---|
committer | roger <roger@FreeBSD.org> | 1999-11-19 07:29:10 +0000 |
commit | 839422ecb689fe2783226672bbe0724d38288048 (patch) | |
tree | 93ea08518545ce6862e3072a7f449a3309286d27 /sys/dev/sound/pci | |
parent | 391b6293c54f0f8ae00845dc50cf50a42cccabd7 (diff) | |
download | FreeBSD-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.c | 407 |
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 */ |