0d2d8c6db2
sb16: port of isa sb16 driver to user-space. Port by Peter Boonstoppel. es1371: By Laurens Bronwasser.
588 lines
17 KiB
C
Executable file
588 lines
17 KiB
C
Executable file
/* This is the main file of the ES1371 sound driver
|
|
* There is no main function over here, instead the main function
|
|
* is located in the generic dma driver. All this driver does is
|
|
* implement the interface audio/audio_fw.h. All functions having the
|
|
* prefix 'drv_' are dictated by audio/audio_fw.h. The function
|
|
* prototypes you see below define a set of private helper functions.
|
|
* Control over the sample rate converter and the codec is delegated
|
|
* to SRC.c and codec.c respectively.
|
|
*
|
|
* November 2005 ES1371 driver (Laurens Bronwasser)
|
|
*/
|
|
|
|
|
|
#include "../framework/audio_fw.h"
|
|
#include "es1371.h"
|
|
#include "codec.h"
|
|
#include "SRC.h"
|
|
#include "../AC97.h"
|
|
|
|
|
|
#define reg(n) dev.base + n
|
|
|
|
FORWARD _PROTOTYPE( int detect_hw, (void) );
|
|
FORWARD _PROTOTYPE( int disable_int, (int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_stereo, (u32_t stereo, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_bits, (u32_t nr_of_bits, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_sample_rate, (u32_t rate, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_sign, (u32_t val, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int get_max_frag_size, (u32_t * val, int *len, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_frag_size, (u32_t fragment_size, int sub_dev) );
|
|
FORWARD _PROTOTYPE( int set_int_cnt, (int sub_dev) );
|
|
FORWARD _PROTOTYPE( int AC97Write, (u16_t wAddr, u16_t wData));
|
|
FORWARD _PROTOTYPE( int AC97Read, (u16_t wAddr, u16_t *data));
|
|
FORWARD _PROTOTYPE( void set_nice_volume, (void) );
|
|
|
|
DEV_STRUCT dev;
|
|
u32_t base = 0;
|
|
aud_sub_dev_conf_t aud_conf[4];
|
|
|
|
|
|
PUBLIC sub_dev_t sub_dev[4];
|
|
PUBLIC special_file_t special_file[4];
|
|
PUBLIC drv_t drv;
|
|
|
|
|
|
PUBLIC int drv_init(void) {
|
|
drv.DriverName = "ES1371";
|
|
drv.NrOfSubDevices = 4;
|
|
drv.NrOfSpecialFiles = 4;
|
|
|
|
sub_dev[DAC1_CHAN].readable = 0;
|
|
sub_dev[DAC1_CHAN].writable = 1;
|
|
sub_dev[DAC1_CHAN].DmaSize = 64 * 1024;
|
|
sub_dev[DAC1_CHAN].NrOfDmaFragments = 2;
|
|
sub_dev[DAC1_CHAN].MinFragmentSize = 1024;
|
|
sub_dev[DAC1_CHAN].NrOfExtraBuffers = 4;
|
|
|
|
sub_dev[ADC1_CHAN].readable = 1;
|
|
sub_dev[ADC1_CHAN].writable = 0;
|
|
sub_dev[ADC1_CHAN].DmaSize = 64 * 1024;
|
|
sub_dev[ADC1_CHAN].NrOfDmaFragments = 2;
|
|
sub_dev[ADC1_CHAN].MinFragmentSize = 1024;
|
|
sub_dev[ADC1_CHAN].NrOfExtraBuffers = 4;
|
|
|
|
sub_dev[MIXER].writable = 0;
|
|
sub_dev[MIXER].readable = 0;
|
|
|
|
sub_dev[DAC2_CHAN].readable = 0;
|
|
sub_dev[DAC2_CHAN].writable = 1;
|
|
sub_dev[DAC2_CHAN].DmaSize = 64 * 1024;
|
|
sub_dev[DAC2_CHAN].NrOfDmaFragments = 2;
|
|
sub_dev[DAC2_CHAN].MinFragmentSize = 1024;
|
|
sub_dev[DAC2_CHAN].NrOfExtraBuffers = 4;
|
|
|
|
special_file[0].minor_dev_nr = 0;
|
|
special_file[0].write_chan = DAC1_CHAN;
|
|
special_file[0].read_chan = NO_CHANNEL;
|
|
special_file[0].io_ctl = DAC1_CHAN;
|
|
|
|
special_file[1].minor_dev_nr = 1;
|
|
special_file[1].write_chan = NO_CHANNEL;
|
|
special_file[1].read_chan = ADC1_CHAN;
|
|
special_file[1].io_ctl = ADC1_CHAN;
|
|
|
|
special_file[2].minor_dev_nr = 2;
|
|
special_file[2].write_chan = NO_CHANNEL;
|
|
special_file[2].read_chan = NO_CHANNEL;
|
|
special_file[2].io_ctl = MIXER;
|
|
|
|
special_file[3].minor_dev_nr = 3;
|
|
special_file[3].write_chan = DAC2_CHAN;
|
|
special_file[3].read_chan = NO_CHANNEL;
|
|
special_file[3].io_ctl = DAC2_CHAN;
|
|
}
|
|
|
|
int drv_init_hw (void)
|
|
{
|
|
u16_t i, j;
|
|
|
|
/* First, detect the hardware */
|
|
if (detect_hw() != OK) {
|
|
return EIO;
|
|
}
|
|
/*
|
|
Put HW in a nice state ... all devices enabled except joystick,
|
|
NMI enables off, clear pending NMIs if any */
|
|
|
|
/* PCI command register */
|
|
pci_attr_w16 (dev.devind, PCI_CR, 0x0105);
|
|
/* set power management control/status register */
|
|
pci_attr_w16 (dev.devind, 0xE0, 0x0000);
|
|
|
|
pci_outb(reg(CONC_bDEVCTL_OFF), 0x00);
|
|
pci_outb(reg(CONC_bMISCCTL_OFF), 0x00);
|
|
pci_outb(reg(CONC_b4SPKR_OFF), 0x00);
|
|
pci_outb(reg(CONC_bNMIENA_OFF), 0x00);
|
|
pci_outb(reg(CONC_bNMICTL_OFF), 0x08);
|
|
pci_outw(reg(CONC_wNMISTAT_OFF), 0x0000);
|
|
pci_outb(reg(CONC_bSERCTL_OFF), 0x00);
|
|
|
|
/* clear all cache RAM */
|
|
for( i = 0; i < 0x10; ++i )
|
|
{
|
|
pci_outb(reg(CONC_bMEMPAGE_OFF), i);
|
|
for( j = 0; j < 0x10; j += 4 )
|
|
pci_outl (reg(CONC_MEMBASE_OFF) + j, 0UL);
|
|
}
|
|
/* DO NOT SWITCH THE ORDER OF SRCInit and CODECInit function calls!!! */
|
|
/* The effect is only noticable after a cold reset (reboot) */
|
|
if (SRCInit(&dev) != OK) {
|
|
return EIO;
|
|
}
|
|
if (CODECInit(&dev) != OK) {
|
|
return EIO;
|
|
}
|
|
set_nice_volume(); /* of course we need a nice mixer to do this */
|
|
|
|
/* initialize variables for each sub_device */
|
|
for (i = 0; i < drv.NrOfSubDevices; i++) {
|
|
if(i != MIXER) {
|
|
aud_conf[i].busy = 0;
|
|
aud_conf[i].stereo = DEFAULT_STEREO;
|
|
aud_conf[i].sample_rate = DEFAULT_RATE;
|
|
aud_conf[i].nr_of_bits = DEFAULT_NR_OF_BITS;
|
|
aud_conf[i].sign = DEFAULT_SIGNED;
|
|
aud_conf[i].fragment_size = sub_dev[i].DmaSize / sub_dev[i].NrOfDmaFragments;
|
|
}
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int detect_hw(void) {
|
|
|
|
u32_t r;
|
|
int devind;
|
|
u16_t v_id, d_id;
|
|
|
|
/* detect_hw tries to find device and get IRQ and base address
|
|
with a little (much) help from the PCI library.
|
|
This code is quite device independent and you can copy it.
|
|
(just make sure to get the bugs out first)*/
|
|
|
|
pci_init();
|
|
/* get first device and then search through the list */
|
|
r = pci_first_dev(&devind, &v_id, &d_id);
|
|
while( r > 0 ) {
|
|
/* if we have a match...break */
|
|
if (v_id == VENDOR_ID && d_id == DEVICE_ID) break;
|
|
r = pci_next_dev(&devind, &v_id, &d_id);
|
|
}
|
|
|
|
/* did we find anything? */
|
|
if (v_id != VENDOR_ID || d_id != DEVICE_ID) {
|
|
return EIO;
|
|
}
|
|
/* right here we should reserve the device, but the pci library
|
|
doesn't support global reservation of devices yet. This would
|
|
be a problem if more ES1371's were installed on this system. */
|
|
|
|
dev.name = pci_dev_name(v_id, d_id);
|
|
/* get base address of our device, ignore least signif. bit
|
|
this last bit thing could be device dependent, i don't know */
|
|
dev.base = pci_attr_r32(devind, PCI_BAR) & 0xfffffffe;
|
|
/* get IRQ */
|
|
dev.irq = pci_attr_r8(devind, PCI_ILR);
|
|
dev.revision = pci_attr_r8(devind, 0x08);
|
|
dev.d_id = d_id;
|
|
dev.v_id = v_id;
|
|
dev.devind = devind; /* pci device identifier */
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_reset(void)
|
|
{
|
|
/* make a WARM reset */
|
|
u16_t i;
|
|
|
|
/* set SYNC_RES bit */
|
|
pci_outl(reg(CONC_bDEVCTL_OFF),
|
|
pci_inl(reg(CONC_bDEVCTL_OFF)) | SYNC_RES_BIT);
|
|
|
|
/* got to delay at least 1 usec, try 18 usec */
|
|
for (i=0; i<100; i++) {
|
|
pci_inb(reg(0));
|
|
}
|
|
/* clear SYNC_RES bit */
|
|
pci_outl(reg(CONC_bDEVCTL_OFF),
|
|
pci_inl(reg(CONC_bDEVCTL_OFF)) & ~SYNC_RES_BIT);
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_start(int sub_dev, int DmaMode)
|
|
{
|
|
u32_t enable_bit, result = 0;
|
|
|
|
/* Write default values to device in case user failed to configure.
|
|
If user did configure properly, everything is written twice.
|
|
please raise your hand if you object against to this strategy...*/
|
|
result |= set_sample_rate(aud_conf[sub_dev].sample_rate, sub_dev);
|
|
result |= set_stereo(aud_conf[sub_dev].stereo, sub_dev);
|
|
result |= set_bits(aud_conf[sub_dev].nr_of_bits, sub_dev);
|
|
result |= set_sign(aud_conf[sub_dev].sign, sub_dev);
|
|
|
|
/* set the interrupt count */
|
|
result |= set_int_cnt(sub_dev);
|
|
|
|
if (result) {
|
|
return EIO;
|
|
}
|
|
|
|
/* if device currently paused, resume */
|
|
drv_resume(sub_dev);
|
|
|
|
switch(sub_dev) {
|
|
case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break;
|
|
case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break;
|
|
case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
/* enable interrupts from 'sub device' */
|
|
drv_reenable_int(sub_dev);
|
|
|
|
/* this means GO!!! */
|
|
pci_outl(reg(CONC_bDEVCTL_OFF),
|
|
pci_inl(reg(CONC_bDEVCTL_OFF)) | enable_bit);
|
|
|
|
aud_conf[sub_dev].busy = 1;
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_stop(int sub_dev)
|
|
{
|
|
u32_t enable_bit;
|
|
|
|
switch(sub_dev) {
|
|
case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break;
|
|
case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break;
|
|
case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
/* stop the codec */
|
|
pci_outl(reg(CONC_bDEVCTL_OFF),
|
|
pci_inl(reg(CONC_bDEVCTL_OFF)) & ~enable_bit);
|
|
|
|
aud_conf[sub_dev].busy = 0;
|
|
disable_int(sub_dev);
|
|
return OK;
|
|
}
|
|
|
|
|
|
/* all IO-ctl's sent to the upper driver are passed to this function */
|
|
int drv_io_ctl(int request, void * val, int * len, int sub_dev) {
|
|
|
|
int status;
|
|
|
|
switch(request) {
|
|
case DSPIORATE: status = set_sample_rate(*((u32_t *) val), sub_dev); break;
|
|
case DSPIOSTEREO: status = set_stereo(*((u32_t *) val), sub_dev); break;
|
|
case DSPIOBITS: status = set_bits(*((u32_t *) val), sub_dev); break;
|
|
case DSPIOSIZE: status = set_frag_size(*((u32_t *) val), sub_dev); break;
|
|
case DSPIOSIGN: status = set_sign(*((u32_t *) val), sub_dev); break;
|
|
case DSPIOMAX: status = get_max_frag_size(val, len, sub_dev);break;
|
|
case DSPIORESET: status = drv_reset(); break;
|
|
case AC97READ: status = AC97Read (*((u16_t *)val), ((u16_t *) val+2));break;
|
|
case AC97WRITE: status = AC97Write(*((u16_t *)val), *((u16_t *) val+2));break;
|
|
default: status = EINVAL; break;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_get_irq(char *irq) {
|
|
*irq = dev.irq;
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_get_frag_size(u32_t *frag_size, int sub_dev) {
|
|
*frag_size = aud_conf[sub_dev].fragment_size;
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_set_dma(u32_t dma, u32_t length, int chan) {
|
|
/* dma length in bytes,
|
|
max is 64k long words for es1371 = 256k bytes */
|
|
u32_t page, frame_count_reg, dma_add_reg;
|
|
|
|
switch(chan) {
|
|
case ADC1_CHAN: page = CONC_ADCCTL_PAGE;
|
|
frame_count_reg = CONC_wADCFC_OFF;
|
|
dma_add_reg = CONC_dADCPADDR_OFF;
|
|
break;
|
|
case DAC1_CHAN: page = CONC_SYNCTL_PAGE;
|
|
frame_count_reg = CONC_wSYNFC_OFF;
|
|
dma_add_reg = CONC_dSYNPADDR_OFF;
|
|
break;;
|
|
case DAC2_CHAN: page = CONC_DACCTL_PAGE;
|
|
frame_count_reg = CONC_wDACFC_OFF;
|
|
dma_add_reg = CONC_dDACPADDR_OFF;
|
|
break;;
|
|
default: return EIO;
|
|
}
|
|
pci_outb(reg(CONC_bMEMPAGE_OFF), page);
|
|
pci_outl(reg(dma_add_reg), dma);
|
|
/* device expects long word count in stead of bytes */
|
|
length /= 4;
|
|
/* device expects length -1 */
|
|
pci_outl(reg(frame_count_reg), (u32_t) (length - 1));
|
|
}
|
|
|
|
|
|
/* return status of the interrupt summary bit */
|
|
int drv_int_sum(void) {
|
|
u32_t int_status;
|
|
int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & 0x80000000UL;
|
|
return int_status;
|
|
}
|
|
|
|
|
|
int drv_int(int sub_dev) {
|
|
u32_t int_status;
|
|
char bit;
|
|
|
|
/* return status of interrupt bit of specified channel*/
|
|
|
|
switch (sub_dev) {
|
|
case DAC1_CHAN: bit = DAC1_INT_STATUS_BIT;break;
|
|
case DAC2_CHAN: bit = DAC2_INT_STATUS_BIT;break;
|
|
case ADC1_CHAN: bit = ADC1_INT_STATUS_BIT;break;
|
|
}
|
|
int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & bit;
|
|
return int_status;
|
|
}
|
|
|
|
|
|
int drv_reenable_int(int chan) {
|
|
u32_t i, int_en_bit;
|
|
|
|
switch(chan) {
|
|
case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break;
|
|
case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break;
|
|
case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break;
|
|
default: EINVAL;
|
|
}
|
|
/* clear and reenable an interrupt */
|
|
i = pci_inl(reg(CONC_bSERFMT_OFF));
|
|
pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit);
|
|
pci_outl(reg(CONC_bSERFMT_OFF), i | int_en_bit);
|
|
}
|
|
|
|
|
|
int drv_pause(int sub_dev)
|
|
{
|
|
u32_t pause_bit;
|
|
|
|
disable_int(sub_dev); /* don't send interrupts */
|
|
|
|
switch(sub_dev) {
|
|
case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break;
|
|
case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
/* pause */
|
|
pci_outl(reg(CONC_bSERFMT_OFF),
|
|
pci_inl(reg(CONC_bSERFMT_OFF)) | pause_bit);
|
|
return OK;
|
|
}
|
|
|
|
|
|
int drv_resume(int sub_dev)
|
|
{
|
|
u32_t pause_bit = 0;
|
|
|
|
/* todo: drv_reenable_int(sub_dev); *//* enable interrupts */
|
|
|
|
switch(sub_dev) {
|
|
case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break;
|
|
case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
/* clear pause bit */
|
|
pci_outl(reg(CONC_bSERFMT_OFF),
|
|
pci_inl(reg(CONC_bSERFMT_OFF)) & ~pause_bit);
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_bits(u32_t nr_of_bits, int sub_dev) {
|
|
|
|
/* set format bits for specified channel. */
|
|
u32_t size_16_bit, i;
|
|
|
|
switch(sub_dev) {
|
|
case ADC1_CHAN: size_16_bit = ADC1_16_8_BIT;break;
|
|
case DAC1_CHAN: size_16_bit = DAC1_16_8_BIT;break;
|
|
case DAC2_CHAN: size_16_bit = DAC2_16_8_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
i = pci_inb(reg(CONC_bSERFMT_OFF));
|
|
i &= ~size_16_bit;
|
|
switch(nr_of_bits) {
|
|
case 16: i |= size_16_bit;break;
|
|
case 8: break;
|
|
default: return EINVAL;
|
|
}
|
|
pci_outb(reg(CONC_bSERFMT_OFF), i);
|
|
aud_conf[sub_dev].nr_of_bits = nr_of_bits;
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_stereo(u32_t stereo, int sub_dev) {
|
|
|
|
/* set format bits for specified channel. */
|
|
u32_t stereo_bit, i;
|
|
switch(sub_dev) {
|
|
case ADC1_CHAN: stereo_bit = ADC1_STEREO_BIT;break;
|
|
case DAC1_CHAN: stereo_bit = DAC1_STEREO_BIT;break;
|
|
case DAC2_CHAN: stereo_bit = DAC2_STEREO_BIT;break;
|
|
default: return EINVAL;
|
|
}
|
|
i = pci_inb(reg(CONC_bSERFMT_OFF));
|
|
i &= ~stereo_bit;
|
|
if( stereo == TRUE ) {
|
|
i |= stereo_bit;
|
|
}
|
|
pci_outb(reg(CONC_bSERFMT_OFF), i);
|
|
aud_conf[sub_dev].stereo = stereo;
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_sign(u32_t val, int sub_dev) {
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_frag_size(u32_t fragment_size, int sub_dev_nr) {
|
|
if (fragment_size > (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments) || fragment_size < sub_dev[sub_dev_nr].MinFragmentSize) {
|
|
return EINVAL;
|
|
}
|
|
aud_conf[sub_dev_nr].fragment_size = fragment_size;
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_sample_rate(u32_t rate, int sub_dev) {
|
|
u32_t SRCBaseReg;
|
|
|
|
if (rate > MAX_RATE || rate < MIN_RATE) {
|
|
return EINVAL;
|
|
}
|
|
/* set the sample rate for the specified channel*/
|
|
switch(sub_dev) {
|
|
case ADC1_CHAN: SRCBaseReg = SRC_ADC_BASE;break;
|
|
case DAC1_CHAN: SRCBaseReg = SRC_SYNTH_BASE;break;
|
|
case DAC2_CHAN: SRCBaseReg = SRC_DAC_BASE;break;
|
|
default: return EINVAL;
|
|
}
|
|
SRCSetRate(&dev, SRCBaseReg, rate);
|
|
aud_conf[sub_dev].sample_rate = rate;
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int set_int_cnt(int chan) {
|
|
/* Write interrupt count for specified channel.
|
|
After <DspFragmentSize> bytes, an interrupt will be generated */
|
|
|
|
int sample_count; u16_t int_cnt_reg;
|
|
|
|
if (aud_conf[chan].fragment_size > (sub_dev[chan].DmaSize / sub_dev[chan].NrOfDmaFragments)
|
|
|| aud_conf[chan].fragment_size < sub_dev[chan].MinFragmentSize) {
|
|
return EINVAL;
|
|
}
|
|
|
|
switch(chan) {
|
|
case ADC1_CHAN: int_cnt_reg = CONC_wADCIC_OFF;break;
|
|
case DAC1_CHAN: int_cnt_reg = CONC_wSYNIC_OFF;break;
|
|
case DAC2_CHAN: int_cnt_reg = CONC_wDACIC_OFF;break;
|
|
default: return EINVAL;
|
|
}
|
|
|
|
sample_count = aud_conf[chan].fragment_size;
|
|
|
|
/* adjust sample count according to sample format */
|
|
if( aud_conf[chan].stereo == TRUE ) sample_count >>= 1;
|
|
switch(aud_conf[chan].nr_of_bits) {
|
|
case 16: sample_count >>= 1;break;
|
|
case 8: break;
|
|
default: return EINVAL;
|
|
}
|
|
/* set the sample count - 1 for the specified channel. */
|
|
pci_outw(reg(int_cnt_reg), sample_count - 1);
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int get_max_frag_size(u32_t * val, int * len, int sub_dev_nr) {
|
|
*len = sizeof(*val);
|
|
*val = (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments);
|
|
return OK;
|
|
}
|
|
|
|
|
|
PRIVATE int disable_int(int chan) {
|
|
u32_t i, int_en_bit;
|
|
|
|
switch(chan) {
|
|
case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break;
|
|
case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break;
|
|
case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break;
|
|
default: EINVAL;
|
|
}
|
|
/* clear the interrupt */
|
|
i = pci_inl(reg(CONC_bSERFMT_OFF));
|
|
pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit);
|
|
}
|
|
|
|
|
|
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 */
|
|
|
|
AC97Write(AC97_PCM_OUT_VOLUME, 0x0808);/* the higher, the softer */
|
|
AC97Write(AC97_MASTER_VOLUME, 0x0101);
|
|
AC97Write(0x38, 0); /* not crucial */
|
|
|
|
AC97Write(AC97_LINE_IN_VOLUME, 0x0303);
|
|
AC97Write(AC97_MIC_VOLUME, 0x0303);
|
|
|
|
/* mute record gain */
|
|
AC97Write(AC97_RECORD_GAIN_VOLUME, 0xFFFF);
|
|
|
|
/* Also, to be able test recording without mixer:
|
|
select ONE channel as input below. */
|
|
|
|
/* select LINE IN */
|
|
/*CodecWrite(AC97_RECORD_SELECT, 0x0404);*/
|
|
|
|
/* select MIC */
|
|
AC97Write(AC97_RECORD_SELECT, 0x0000);
|
|
|
|
/* unmute record gain */
|
|
AC97Write(AC97_RECORD_GAIN_VOLUME, 0x0000);
|
|
}
|
|
|
|
|
|
/* The following two functions can be used by the mixer to
|
|
control and read volume settings. */
|
|
PRIVATE int AC97Write (u16_t addr, u16_t data)
|
|
{
|
|
/* todo: only allow volume control,
|
|
no serial data or dev ctl please*/
|
|
return CodecWriteUnsynced(&dev, addr, data);
|
|
}
|
|
|
|
|
|
PRIVATE int AC97Read (u16_t addr, u16_t *data)
|
|
{
|
|
return CodecReadUnsynced(&dev, addr, data);
|
|
}
|