From: Ben Gras Date: Fri, 23 Nov 2007 11:52:34 +0000 (+0000) Subject: More es1371 bij Pieter Hijma. X-Git-Tag: v3.1.4~309 X-Git-Url: http://zhaoyanbai.com/repos/Bv9ARM.ch06.html?a=commitdiff_plain;h=b79b305ba1780329af2767a746e2a3a20575fdf0;p=minix.git More es1371 bij Pieter Hijma. --- diff --git a/drivers/audio/es1371/AC97.c b/drivers/audio/es1371/AC97.c new file mode 100644 index 000000000..c6ac88da4 --- /dev/null +++ b/drivers/audio/es1371/AC97.c @@ -0,0 +1,502 @@ +#include "AC97.h" + + + + + +/* AC97 Mixer and Mode control function prototypes */ + +FORWARD _PROTOTYPE( int AC97_read, + (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) ); +FORWARD _PROTOTYPE( int AC97_write, + (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) ); +FORWARD _PROTOTYPE( void set_src_sync_state, (int state) ); +FORWARD _PROTOTYPE( int AC97_write_unsynced, + (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) ); +FORWARD _PROTOTYPE( int AC97_read_unsynced, + (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) ); +FORWARD _PROTOTYPE( void set_nice_volume, (void) ); +FORWARD _PROTOTYPE( int AC97_get_volume, (struct volume_level *level) ); +FORWARD _PROTOTYPE( int AC97_set_volume, (struct volume_level *level) ); + + + +#define AC97_0DB_GAIN 0x0008 +#define AC97_MAX_ATTN 0x003f +#define AC97_MUTE 0x8000U + + +/* Control function defines */ +#define AC97_CTL_4SPKR 0x00U /* 4-spkr output mode enable */ +#define AC97_CTL_MICBOOST 0x01U /* Mic boost (+30 dB) enable */ +#define AC97_CTL_PWRDOWN 0x02U /* power-down mode */ +#define AC97_CTL_DOSMODE 0x03U /* A/D sync to DAC1 */ + + /* Timeout waiting for: */ +#define AC97_ERR_WIP_TIMEOUT -1 /* write in progress complete */ +#define AC97_ERR_DATA_TIMEOUT -2 /* data ready */ +#define AC97_ERR_SRC_NOT_BUSY_TIMEOUT -3 /* SRC not busy */ +#define AC97_ERR_SRC_SYNC_TIMEOUT -4 /* state #1 */ + + + + + + +/* Timeouts in milliseconds */ +#define WIP_TIMEOUT 250UL +#define DRDY_TIMEOUT 250UL + +/* The default SRC syncronization state number is 1. This state occurs + just after de-assertion of SYNC. This is supposed to be the safest + state for accessing the codec with an ES1371 Rev 1. Later versions + of the chip allegedly don't require syncronization. Be very careful + if you change this ! */ + +#define SRC_UNSYNCED 0xffffffffUL +static u32_t SrcSyncState = 0x00010000UL; +static DEV_STRUCT *dev; + + +PRIVATE void set_src_sync_state (int state) +{ + if (state < 0) + SrcSyncState = SRC_UNSYNCED; + else { + SrcSyncState = (u32_t)state << 16; + SrcSyncState &= 0x00070000Ul; + } +} + + +PRIVATE int AC97_write (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) +{ +u32_t dtemp, i; +u16_t wBaseAddr = pCC->base; + + /* wait for WIP bit (Write In Progress) to go away */ + /* remember, register CODEC_READ (0x14) + is a pseudo read-write register */ + if (WaitBitd (wBaseAddr + CODEC_READ, 30, 0, WIP_TIMEOUT)){ + printf("AC97_ERR_WIP_TIMEOUT\n"); + return (AC97_ERR_WIP_TIMEOUT); + } + if (SRC_UNSYNCED != SrcSyncState) + { + /* enable SRC state data in SRC mux */ + if (WaitBitd (wBaseAddr + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000)) + return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT); + + /* todo: why are we writing an undefined register? */ + dtemp = pci_inl(wBaseAddr + SAMPLE_RATE_CONV); + pci_outl(wBaseAddr + SAMPLE_RATE_CONV, (dtemp & SRC_CTLMASK) | + 0x00010000UL); + + /* wait for a SAFE time to write addr/data and then do it */ + /*_disable(); */ + for( i = 0; i < 0x1000UL; ++i ) + if( (pci_inl(wBaseAddr + SAMPLE_RATE_CONV) & 0x00070000UL) == + SrcSyncState ) + break; + + if (i >= 0x1000UL) { + /* _enable(); */ + return (AC97_ERR_SRC_SYNC_TIMEOUT); + } + } + + /* A test for 5880 - prime the PCI data bus */ + { + u32_t dat = ((u32_t) wAddr << 16) | wData; + char page = pci_inb(wBaseAddr + MEM_PAGE); + + pci_outl (wBaseAddr + MEM_PAGE, dat); + + /* write addr and data */ + pci_outl(wBaseAddr + CODEC_READ, dat); + + pci_outb(wBaseAddr + MEM_PAGE, page); /* restore page reg */ + } + + if (SRC_UNSYNCED != SrcSyncState) + { + /* _enable(); */ + + /* restore SRC reg */ + if (WaitBitd (wBaseAddr + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000)) + return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT); + + pci_outl(wBaseAddr + SAMPLE_RATE_CONV, dtemp & 0xfff8ffffUL); + } + + return 0; +} + + +PRIVATE int AC97_read (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) +{ +u32_t dtemp, i; +u16_t base = pCC->base; + + /* wait for WIP to go away */ + if (WaitBitd (base + CODEC_READ, 30, 0, WIP_TIMEOUT)) + return (AC97_ERR_WIP_TIMEOUT); + + if (SRC_UNSYNCED != SrcSyncState) + { + /* enable SRC state data in SRC mux */ + if (WaitBitd (base + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000)) + return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(base + SAMPLE_RATE_CONV); + pci_outl(base + SAMPLE_RATE_CONV, (dtemp & SRC_CTLMASK) | + 0x00010000UL); + + /* wait for a SAFE time to write a read request and then do it */ + /* todo: how do we solve the lock() problem? */ + /* _disable(); */ + for( i = 0; i < 0x1000UL; ++i ) + if( (pci_inl(base + SAMPLE_RATE_CONV) & 0x00070000UL) == + SrcSyncState ) + break; + + if (i >= 0x1000UL) { + /*_enable();*/ + return (AC97_ERR_SRC_SYNC_TIMEOUT); + } + } + + /* A test for 5880 - prime the PCI data bus */ + { + /* set bit 23, this means read in stead of write. */ + u32_t dat = ((u32_t) wAddr << 16) | (1UL << 23); + char page = pci_inb(base + MEM_PAGE); + + /* todo: why are we putting data in the mem page register??? */ + pci_outl(base + MEM_PAGE, dat); + + /* write addr w/data=0 and assert read request */ + pci_outl(base + CODEC_READ, dat); + + pci_outb(base + MEM_PAGE, page); /* restore page reg */ + + } + if (SRC_UNSYNCED != SrcSyncState) + { + + /*_enable();*/ + + /* restore SRC reg */ + if (WaitBitd (base + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000)) + return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT); + + pci_outl(base + SAMPLE_RATE_CONV, dtemp & 0xfff8ffffUL); + } + + /* now wait for the stinkin' data (DRDY = data ready) */ + if (WaitBitd (base + CODEC_READ, 31, 1, DRDY_TIMEOUT)) + return (AC97_ERR_DATA_TIMEOUT); + + dtemp = pci_inl(base + CODEC_READ); + + if (data) + *data = (u16_t) dtemp; + + return 0; +} + + +PRIVATE int AC97_write_unsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) +{ + /* wait for WIP to go away */ + if (WaitBitd (pCC->base + CODEC_READ, 30, 0, WIP_TIMEOUT)) + return (AC97_ERR_WIP_TIMEOUT); + + /* write addr and data */ + pci_outl(pCC->base + CODEC_READ, ((u32_t) wAddr << 16) | wData); + return 0; +} + + +PRIVATE int AC97_read_unsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) +{ +u32_t dtemp; + + /* wait for WIP to go away */ + if (WaitBitd (pCC->base + CODEC_READ, 30, 0, WIP_TIMEOUT)) + return (AC97_ERR_WIP_TIMEOUT); + + /* write addr w/data=0 and assert read request */ + pci_outl(pCC->base + CODEC_READ, ((u32_t) wAddr << 16) | (1UL << 23)); + + /* now wait for the stinkin' data (RDY) */ + if (WaitBitd (pCC->base + CODEC_READ, 31, 1, DRDY_TIMEOUT)) + return (AC97_ERR_DATA_TIMEOUT); + + dtemp = pci_inl(pCC->base + CODEC_READ); + + if (data) + *data = (u16_t) dtemp; + + return 0; +} + + +int AC97_init( DEV_STRUCT * pCC ) { + int retVal; + /* All powerdown modes: off */ + + dev = pCC; + + retVal = AC97_write (pCC, AC97_POWERDOWN_CONTROL_STAT, 0x0000U); + if (OK != retVal) + return (retVal); + + /* Mute Line Out & set to 0dB attenuation */ + + retVal = AC97_write (pCC, AC97_MASTER_VOLUME, 0x0000U); + if (OK != retVal) + return (retVal); + + + retVal = AC97_write (pCC, AC97_MONO_VOLUME, 0x8000U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_PHONE_VOLUME, 0x8008U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_MIC_VOLUME, 0x0008U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_LINE_IN_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_CD_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_AUX_IN_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_PCM_OUT_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_RECORD_GAIN_VOLUME, 0x0000U); + if (OK != retVal) + return (retVal); + + /* Connect Line In to ADC */ + retVal = AC97_write (pCC, AC97_RECORD_SELECT, 0x0404U); + if (OK != retVal) + return (retVal); + + retVal = AC97_write (pCC, AC97_GENERAL_PURPOSE, 0x0000U); + if (OK != retVal) + return (retVal); + + set_nice_volume(); + + return OK; +} + + +PRIVATE void set_nice_volume(void) { + /* goofy code to set the DAC1 channel to an audibe volume + to be able to test it without using the mixer */ + + AC97_write_unsynced(dev, AC97_PCM_OUT_VOLUME, 0x0808);/* the higher, + the softer */ + AC97_write_unsynced(dev, AC97_MASTER_VOLUME, 0x0101); + AC97_write_unsynced(dev, 0x38, 0); /* not crucial */ + + AC97_write_unsynced(dev, AC97_LINE_IN_VOLUME, 0x0303); + AC97_write_unsynced(dev, AC97_MIC_VOLUME, 0x005f); + + /* mute record gain */ + AC97_write_unsynced(dev, AC97_RECORD_GAIN_VOLUME, 0xFFFF); + /* mic record volume high */ + AC97_write_unsynced(dev, AC97_RECORD_GAIN_MIC_VOL, 0x0000); + + /* Also, to be able test recording without mixer: + select ONE channel as input below. */ + + /* select LINE IN */ + /*AC97_write_unsynced(dev, AC97_RECORD_SELECT, 0x0404);*/ + + /* select MIC */ + AC97_write_unsynced(dev, AC97_RECORD_SELECT, 0x0000); + + /* unmute record gain */ + AC97_write_unsynced(dev, AC97_RECORD_GAIN_VOLUME, 0x0000); +} + + +PRIVATE int get_volume(u8_t *left, u8_t *right, int cmd) { + u16_t value; + + AC97_read_unsynced(dev, (u16_t)cmd, &value); + + *left = value>>8; + *right = value&0xff; + + return OK; +} + + +PRIVATE int set_volume(int left, int right, int cmd) { + u16_t waarde; + + waarde = (u16_t)((left<<8)|right); + + AC97_write_unsynced(dev, (u16_t)cmd, waarde); + + return OK; +} + + +void convert(int left_in, int right_in, int max_in, int *left_out, + int *right_out, int max_out, int swaplr) { + int tmp; + + if(left_in < 0) left_in = 0; + else if(left_in > max_in) left_in = max_in; + if(right_in < 0) right_in = 0; + else if(right_in > max_in) right_in = max_in; + + if (swaplr) { + tmp = left_in; + left_in = right_in; + right_in = tmp; + } + + *left_out = (-left_in) + max_out; + *right_out = (-right_in) + max_out; +} + + +int AC97_get_set_volume(struct volume_level *level, int flag) { + if (flag) { + return AC97_set_volume(level); + } + else { + return AC97_get_volume(level); + } +} + + +PRIVATE int AC97_get_volume(struct volume_level *level) { + int cmd; + u8_t left; + u8_t right; + + switch(level->device) { + case Master: + cmd = AC97_MASTER_VOLUME; + get_volume(&left, &right, cmd); + convert(left, right, 0x1f, + &(level->left), &(level->right), 0x1f, 0); + break; + case Dac: + return EINVAL; + break; + case Fm: + cmd = AC97_PCM_OUT_VOLUME; + get_volume(&left, &right, cmd); + convert(left, right, 0x1f, + &(level->left), &(level->right), 0x1f, 0); + break; + case Cd: + cmd = AC97_CD_VOLUME; + get_volume(&left, &right, cmd); + convert(left, right, 0x1f, + &(level->left), &(level->right), 0x1f, 0); + break; + case Line: + cmd = AC97_LINE_IN_VOLUME; + get_volume(&left, &right, cmd); + convert(left, right, 0x1f, + &(level->left), &(level->right), 0x1f, 0); + break; + case Mic: + cmd = AC97_MIC_VOLUME; + get_volume(&left, &right, cmd); + convert(left, right, 0x1f, + &(level->left), &(level->right), 0x1f, 1); + break; + case Speaker: + cmd = AC97_PC_BEEP_VOLUME; + return EINVAL; + break; + case Treble: + cmd = AC97_MASTER_TONE; + get_volume(&left, &right, cmd); + convert(left, right, 0xf, + &(level->left), &(level->right), 0xf, 1); + break; + case Bass: + cmd = AC97_MASTER_TONE; + get_volume(&left, &right, cmd); + convert(left, right, 0xf, + &(level->left), &(level->right), 0xf, 1); + break; + default: + return EINVAL; + } + return OK; +} + + +PRIVATE int AC97_set_volume(struct volume_level *level) { + int cmd; + int left; + int right; + + switch(level->device) { + case Master: + cmd = AC97_MASTER_VOLUME; + convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0); + break; + case Dac: + return EINVAL; + break; + case Fm: + cmd = AC97_PCM_OUT_VOLUME; + convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0); + break; + case Cd: + cmd = AC97_CD_VOLUME; + convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0); + break; + case Line: + cmd = AC97_LINE_IN_VOLUME; + convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0); + break; + case Mic: + cmd = AC97_MIC_VOLUME; + convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 1); + break; + case Speaker: + cmd = AC97_PC_BEEP_VOLUME; + return EINVAL; + break; + case Treble: + cmd = AC97_MASTER_TONE; + return EINVAL; + break; + case Bass: + cmd = AC97_MASTER_TONE; + return EINVAL; + break; + default: + return EINVAL; + } + set_volume(left, right, cmd); + + return OK; +} diff --git a/drivers/audio/es1371/AC97.h b/drivers/audio/es1371/AC97.h new file mode 100644 index 000000000..e2bce8476 --- /dev/null +++ b/drivers/audio/es1371/AC97.h @@ -0,0 +1,98 @@ +#ifndef AC97_H +#define AC97_H + + +#include "es1371.h" +#include "wait.h" +#include "pci_helper.h" +#include "sample_rate_converter.h" + + +/* + This function initializes the AC97 to a default mode. +*/ +int AC97_init( DEV_STRUCT * pCC ); + +int AC97_get_set_volume(struct volume_level *level, int flag); + + + +/* This is a main memory cache copy of the codec's ac97 configuration + registers. See Intel's Audio Codec 97 standard (rev2.3) for info. */ + +typedef struct ac97_struct { + u16_t Reset; /* 0x00 */ + u16_t MasterVolume; /* 0x02 */ + u16_t AUXOutVolume; /* 0x04 */ + u16_t MonoVolume; /* 0x06 */ + u16_t MasterTone; /* 0x08 */ + u16_t PCBeepVolume; /* 0x0A */ + u16_t PhoneVolume; /* 0x0C */ + u16_t MicVolume; /* 0x0E */ + u16_t LineInVolume; /* 0x10 */ + u16_t CDVolume; /* 0x12 */ + u16_t VideoVolume; /* 0x14 */ + u16_t AUXInVolume; /* 0x16 */ + u16_t PCMOutVolume; /* 0x18 */ + u16_t RecordSelect; /* 0x1A */ + u16_t RecordGain; /* 0x1C */ + u16_t RecordGainMic; /* 0x1E */ + u16_t GeneralPurpose; /* 0x20 */ + u16_t Control3D; /* 0x22 */ + u16_t AudioIntAndPaging; /* 0x24 */ + u16_t PowerdownControlAndStat; /* 0x26 */ + u16_t ExtendedAudio1; /* 0x28 */ + u16_t ExtendedAudio2; /* 0x2A */ + /* ... */ + u16_t VendorID1; /* 0x7C */ + u16_t VendorID2; /* 0x7E */ +} ac97_t; + + + +/* Source and output volume control register defines */ +#define AC97_MASTER_VOLUME 0x02U /* Master out */ +#define AC97_AUX_OUT_VOLUME 0x04U /* Auxiliary out volume */ +#define AC97_MONO_VOLUME 0x06U /* Mono out volume */ +#define AC97_MASTER_TONE 0x08U /* high byte= bass, low byte= treble*/ +#define AC97_PC_BEEP_VOLUME 0x0aU /* PC speaker volume */ +#define AC97_PHONE_VOLUME 0x0cU /* Phone volume */ +#define AC97_MIC_VOLUME 0x0eU /* Mic, mono */ +#define AC97_LINE_IN_VOLUME 0x10U /* Line volume */ +#define AC97_CD_VOLUME 0x12U /* CD audio volume */ +#define AC97_VIDEO_VOLUME 0x14U /* Video (TV) volume */ +#define AC97_AUX_IN_VOLUME 0x16U /* Aux line source, left */ +#define AC97_PCM_OUT_VOLUME 0x18U /* The DACs - wav+synth */ +#define AC97_RECORD_GAIN_VOLUME 0x1cU /* Record input level */ +#define AC97_RECORD_GAIN_MIC_VOL 0x1eU /* Record input level */ + +/* Other AC97 control register defines */ +#define AC97_RESET 0x00U /* any write here to reset AC97 */ +#define AC97_GENERAL_PURPOSE 0x20U /* */ +#define AC97_POWERDOWN_CONTROL_STAT 0x26U /* */ +#define AC97_RECORD_SELECT 0x1aU /* record mux select */ +#define AC97_VENDOR_ID1 0x7cU /* 1st two Vendor ID bytes */ +#define AC97_VENDOR_ID2 0x7eU /* last Vendor ID byte plus rev. + number */ + +/* Record Select defines */ +#define AC97_RECORD_MIC 0 +#define AC97_RECORD_CD 1 +#define AC97_RECORD_VIDEO 2 +#define AC97_RECORD_AUX 3 +#define AC97_RECORD_LINE 4 +#define AC97_RECORD_STEREO_MIX 5 +#define AC97_RECORD_MONO_MIX 6 +#define AC97_RECORD_PHONE 7 + +#define MASTER_VOL_MASK 0x1F +#define DAC_VOL_MASK 0x1F +#define AUX_IN_VOL_MASK 0x1F +#define MUTE_MASK 0x8000 + + + + + + +#endif /* AC97_H */ diff --git a/drivers/audio/es1371/pci_helper.c b/drivers/audio/es1371/pci_helper.c new file mode 100644 index 000000000..f462211f1 --- /dev/null +++ b/drivers/audio/es1371/pci_helper.c @@ -0,0 +1,65 @@ +/* best viewed with tabsize 4 */ + +#include "../../drivers.h" + +#include +#include +#include +#include +#include + + +#include "pci_helper.h" + +#include "es1371.h" + +/*===========================================================================* + * helper functions for I/O * + *===========================================================================*/ +PUBLIC unsigned pci_inb(U16_t port) { + u32_t value; + int s; + if ((s=sys_inb(port, &value)) !=OK) + printf("%s: warning, sys_inb failed: %d\n", DRIVER_NAME, s); + return value; +} + + +PUBLIC unsigned pci_inw(U16_t port) { + u32_t value; + int s; + if ((s=sys_inw(port, &value)) !=OK) + printf("%s: warning, sys_inw failed: %d\n", DRIVER_NAME, s); + return value; +} + + +PUBLIC unsigned pci_inl(U16_t port) { + U32_t value; + int s; + if ((s=sys_inl(port, &value)) !=OK) + printf("%s: warning, sys_inl failed: %d\n", DRIVER_NAME, s); + return value; +} + + +PUBLIC void pci_outb(U16_t port, U8_t value) { + int s; + if ((s=sys_outb(port, value)) !=OK) + printf("%s: warning, sys_outb failed: %d\n", DRIVER_NAME, s); +} + + +PUBLIC void pci_outw(U16_t port, U16_t value) { + int s; + if ((s=sys_outw(port, value)) !=OK) + printf("%s: warning, sys_outw failed: %d\n", DRIVER_NAME, s); +} + + +PUBLIC void pci_outl(U16_t port, U32_t value) { + int s; + if ((s=sys_outl(port, value)) !=OK) + printf("%s: warning, sys_outl failed: %d\n", DRIVER_NAME, s); +} + diff --git a/drivers/audio/es1371/pci_helper.h b/drivers/audio/es1371/pci_helper.h new file mode 100644 index 000000000..5c91c6993 --- /dev/null +++ b/drivers/audio/es1371/pci_helper.h @@ -0,0 +1,12 @@ +#ifndef PCI_HELPER +#define PCI_HELPER + +_PROTOTYPE( unsigned pci_inb, (U16_t port) ); +_PROTOTYPE( unsigned pci_inw, (U16_t port) ); +_PROTOTYPE( unsigned pci_inl, (U16_t port) ); + +_PROTOTYPE( void pci_outb, (U16_t port, U8_t value) ); +_PROTOTYPE( void pci_outw, (U16_t port, U16_t value) ); +_PROTOTYPE( void pci_outl, (U16_t port, U32_t value) ); + +#endif diff --git a/drivers/audio/es1371/sample_rate_converter.c b/drivers/audio/es1371/sample_rate_converter.c new file mode 100644 index 000000000..8622983c9 --- /dev/null +++ b/drivers/audio/es1371/sample_rate_converter.c @@ -0,0 +1,241 @@ +#include "sample_rate_converter.h" + + + + +#define SRC_RATE 48000U +#define reg(n) DSP->base + n + + +/* register/base and control equates for the SRC RAM */ +#define SRC_SYNTH_FIFO 0x00 +#define SRC_DAC_FIFO 0x20 +#define SRC_ADC_FIFO 0x40 + +#define SRC_SYNTH_LVOL 0x7c +#define SRC_SYNTH_RVOL 0x7d +#define SRC_DAC_LVOL 0x7e +#define SRC_DAC_RVOL 0x7f +#define SRC_ADC_LVOL 0x6c +#define SRC_ADC_RVOL 0x6d + +#define SRC_TRUNC_N_OFF 0x00 +#define SRC_INT_REGS_OFF 0x01 +#define SRC_ACCUM_FRAC_OFF 0x02 +#define SRC_VFREQ_FRAC_OFF 0x03 + +/* miscellaneous control defines */ +#define SRC_IOPOLL_COUNT 0x1000UL + +#define SRC_SYNTHFREEZE (1UL << 21) +#define SRC_DACFREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) + + + + +FORWARD _PROTOTYPE( int src_reg_read, (DEV_STRUCT * DSP, + u16_t reg, u16_t *data) ); +FORWARD _PROTOTYPE( int src_reg_write, (DEV_STRUCT * DSP, + u16_t reg, u16_t val) ); + + +int src_init ( DEV_STRUCT * DSP ) { + u32_t i; + int retVal; + + /* Clear all SRC RAM then init - keep SRC disabled until done */ + /* Wait till SRC_RAM_BUSY is 0 */ + if (WaitBitd (reg(SAMPLE_RATE_CONV), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + pci_outl(reg(SAMPLE_RATE_CONV), SRC_DISABLE); + + /* from the opensound system driver, no idea where the specification is */ + /* there are indeed 7 bits for the addresses of the SRC */ + for( i = 0; i < 0x80; ++i ) { + if (SRC_SUCCESS != (retVal = src_reg_write(DSP, (u16_t)i, 0U))) + return (retVal); + } + + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_SYNTH_BASE + SRC_TRUNC_N_OFF, 16 << 4))) + return (retVal); + if (SRC_SUCCESS != (retVal = src_reg_write(DSP, + SRC_SYNTH_BASE + SRC_INT_REGS_OFF, 16 << 10))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_DAC_BASE + SRC_TRUNC_N_OFF, 16 << 4))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_DAC_BASE + SRC_INT_REGS_OFF, 16 << 10))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_SYNTH_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_SYNTH_RVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_DAC_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_DAC_RVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_ADC_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = + src_reg_write(DSP, SRC_ADC_RVOL, 1 << 12))) + return (retVal); + + /* default some rates */ + src_set_rate(DSP, SRC_SYNTH_BASE, SRC_RATE); + src_set_rate(DSP, SRC_DAC_BASE, SRC_RATE); + src_set_rate(DSP, SRC_ADC_BASE, SRC_RATE); + + /* now enable the whole deal */ + if (WaitBitd (reg(SAMPLE_RATE_CONV), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + pci_outl(reg(SAMPLE_RATE_CONV), 0UL); + + return 0; +} + + +PRIVATE int src_reg_read(DEV_STRUCT * DSP, u16_t reg, u16_t *data) { + u32_t dtemp; + + /* wait for ready */ + if (WaitBitd (reg(SAMPLE_RATE_CONV), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(reg(SAMPLE_RATE_CONV)); + + /* assert a read request */ + /*pci_outl(reg(SAMPLE_RATE_CONV), + (dtemp & SRC_CTLMASK) | ((u32_t) reg << 25));*/ + pci_outl(reg(SAMPLE_RATE_CONV), (dtemp & + (DIS_REC|DIS_P2|DIS_P1|SRC_DISABLE)) | ((u32_t) reg << 25)); + + /* now wait for the data */ + if (WaitBitd (reg(SAMPLE_RATE_CONV), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + if (NULL != data) + *data = (u16_t) pci_inl(reg(SAMPLE_RATE_CONV)); + + return 0; +} + + +PRIVATE int src_reg_write(DEV_STRUCT * DSP, u16_t reg, u16_t val) { + u32_t dtemp; + + /* wait for ready */ + if (WaitBitd (reg(SAMPLE_RATE_CONV), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(reg(SAMPLE_RATE_CONV)); + + /* assert the write request */ + pci_outl(reg(SAMPLE_RATE_CONV), + (dtemp & SRC_CTLMASK) | SRC_RAM_WE | ((u32_t) reg << 25) | val); + + return 0; +} + + +void src_set_rate(DEV_STRUCT * DSP, char base, u16_t rate) { + u32_t freq, dtemp, i; + u16_t N, truncM, truncStart, wtemp; + + + if( base != SRC_ADC_BASE ) + { + /* freeze the channel */ + dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE; + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_RAM_BUSY) ) + break; + pci_outl(reg(SAMPLE_RATE_CONV), + (pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_CTLMASK) | + dtemp); + + /* calculate new frequency and write it - preserve accum */ + /* please don't try to understand. */ + freq = ((u32_t) rate << 16) / 3000U; + src_reg_read(DSP, base + SRC_INT_REGS_OFF, &wtemp); + + src_reg_write(DSP, base + SRC_INT_REGS_OFF, + (wtemp & 0x00ffU) | + (u16_t) (freq >> 6) & 0xfc00); + + src_reg_write(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1); + + /* un-freeze the channel */ + dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE; + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_RAM_BUSY) ) + break; + pci_outl(reg(SAMPLE_RATE_CONV), + (pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_CTLMASK) & + ~dtemp); + } + else /* setting ADC rate */ + { + /* freeze the channel */ + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_RAM_BUSY) ) + break; + pci_outl(reg(SAMPLE_RATE_CONV), + (pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_CTLMASK) | + SRC_ADCFREEZE); + + + + /* derive oversample ratio */ + N = rate/3000U; + if( N == 15 || N == 13 || N == 11 || N == 9 ) + --N; + src_reg_write(DSP, SRC_ADC_LVOL, N << 8); + src_reg_write(DSP, SRC_ADC_RVOL, N << 8); + + /* truncate the filter and write n/trunc_start */ + truncM = (21*N - 1) | 1; + if( rate >= 24000U ) + { + if( truncM > 239 ) + truncM = 239; + truncStart = (239 - truncM) >> 1; + src_reg_write(DSP, base + SRC_TRUNC_N_OFF, + (truncStart << 9) | (N << 4)); + } + else + { + if( truncM > 119 ) + truncM = 119; + truncStart = (119 - truncM) >> 1; + src_reg_write(DSP, base + SRC_TRUNC_N_OFF, + 0x8000U | (truncStart << 9) | (N << 4)); + } + + /* calculate new frequency and write it - preserve accum */ + freq = ((48000UL << 16) / rate) * N; + src_reg_read(DSP, base + SRC_INT_REGS_OFF, &wtemp); + src_reg_write(DSP, base + SRC_INT_REGS_OFF, + (wtemp & 0x00ffU) | + (u16_t) (freq >> 6) & 0xfc00); + src_reg_write(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1); + + /* un-freeze the channel */ + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_RAM_BUSY) ) + break; + pci_outl(reg(SAMPLE_RATE_CONV), + (pci_inl(reg(SAMPLE_RATE_CONV)) & SRC_CTLMASK) & + ~SRC_ADCFREEZE); + } + return; +} diff --git a/drivers/audio/es1371/sample_rate_converter.h b/drivers/audio/es1371/sample_rate_converter.h new file mode 100644 index 000000000..c093c25bf --- /dev/null +++ b/drivers/audio/es1371/sample_rate_converter.h @@ -0,0 +1,26 @@ +#ifndef SRC_H +#define SRC_H + +#include "es1371.h" +#include "wait.h" +#include "pci_helper.h" + +_PROTOTYPE( int src_init, (DEV_STRUCT * DSP) ); +_PROTOTYPE( void src_set_rate, (DEV_STRUCT * DSP, char src_base, u16_t rate) ); + +#define SRC_SYNTH_BASE 0x70 +#define SRC_DAC_BASE 0x74 +#define SRC_ADC_BASE 0x78 + +#define SRC_BUSY_BIT 23 + +#define SRC_RAM_WE 0x01000000 +#define SRC_RAM_BUSY 0x00800000 +#define SRC_DISABLE 0x00400000 +#define DIS_P1 0x00200000 +#define DIS_P2 0x00100000 +#define DIS_REC 0x00080000 + +#define SRC_CTLMASK (DIS_REC|DIS_P2|DIS_P1|SRC_DISABLE) + +#endif /* SRC_H */