More es1371 bij Pieter Hijma.
This commit is contained in:
parent
c67a56708e
commit
b79b305ba1
6 changed files with 944 additions and 0 deletions
502
drivers/audio/es1371/AC97.c
Normal file
502
drivers/audio/es1371/AC97.c
Normal file
|
@ -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;
|
||||
}
|
98
drivers/audio/es1371/AC97.h
Normal file
98
drivers/audio/es1371/AC97.h
Normal file
|
@ -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 */
|
65
drivers/audio/es1371/pci_helper.c
Normal file
65
drivers/audio/es1371/pci_helper.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
12
drivers/audio/es1371/pci_helper.h
Normal file
12
drivers/audio/es1371/pci_helper.h
Normal file
|
@ -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
|
241
drivers/audio/es1371/sample_rate_converter.c
Normal file
241
drivers/audio/es1371/sample_rate_converter.c
Normal file
|
@ -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;
|
||||
}
|
26
drivers/audio/es1371/sample_rate_converter.h
Normal file
26
drivers/audio/es1371/sample_rate_converter.h
Normal file
|
@ -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 */
|
Loading…
Reference in a new issue