]> Zhao Yanbai Git Server - minix.git/commitdiff
More es1371 bij Pieter Hijma.
authorBen Gras <ben@minix3.org>
Fri, 23 Nov 2007 11:52:34 +0000 (11:52 +0000)
committerBen Gras <ben@minix3.org>
Fri, 23 Nov 2007 11:52:34 +0000 (11:52 +0000)
drivers/audio/es1371/AC97.c [new file with mode: 0644]
drivers/audio/es1371/AC97.h [new file with mode: 0644]
drivers/audio/es1371/pci_helper.c [new file with mode: 0644]
drivers/audio/es1371/pci_helper.h [new file with mode: 0644]
drivers/audio/es1371/sample_rate_converter.c [new file with mode: 0644]
drivers/audio/es1371/sample_rate_converter.h [new file with mode: 0644]

diff --git a/drivers/audio/es1371/AC97.c b/drivers/audio/es1371/AC97.c
new file mode 100644 (file)
index 0000000..c6ac88d
--- /dev/null
@@ -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 (file)
index 0000000..e2bce84
--- /dev/null
@@ -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 (file)
index 0000000..f462211
--- /dev/null
@@ -0,0 +1,65 @@
+/* best viewed with tabsize 4 */
+
+#include "../../drivers.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <minix/sysutil.h>
+#include <errno.h>
+
+
+#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 (file)
index 0000000..5c91c69
--- /dev/null
@@ -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 (file)
index 0000000..8622983
--- /dev/null
@@ -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 (file)
index 0000000..c093c25
--- /dev/null
@@ -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 */