2007-11-23 12:40:33 +01:00
|
|
|
/* Best viewed with tabsize 4 */
|
|
|
|
|
|
|
|
/* Original:
|
2007-11-23 12:30:50 +01:00
|
|
|
* November 2005 ES1371 driver (Laurens Bronwasser)
|
|
|
|
*/
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
/* Ensoniq ES1371 driver
|
|
|
|
*
|
|
|
|
* aka AudioPCI '97
|
|
|
|
*
|
|
|
|
* 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 AC97 codec is delegated AC97.c.
|
|
|
|
*
|
|
|
|
* October 2007 ES1371 driver (Pieter Hijma),
|
|
|
|
* based on ES1370 driver which is based on the ES1371 driver
|
|
|
|
* by Laurens Bronwasser
|
|
|
|
*/
|
|
|
|
|
2010-03-08 12:04:59 +01:00
|
|
|
#include <machine/pci.h>
|
2007-11-23 12:30:50 +01:00
|
|
|
|
2010-08-25 13:03:53 +02:00
|
|
|
#include <minix/audio_fw.h>
|
2007-11-23 12:30:50 +01:00
|
|
|
#include "es1371.h"
|
2007-11-23 12:40:33 +01:00
|
|
|
#include "AC97.h"
|
|
|
|
#include "sample_rate_converter.h"
|
|
|
|
#include "pci_helper.h"
|
2007-11-23 12:30:50 +01:00
|
|
|
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
/* reg(n) will be the device specific addresses */
|
2007-11-23 12:30:50 +01:00
|
|
|
#define reg(n) dev.base + n
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
|
|
|
|
/* prototypes of private functions */
|
2012-03-25 20:25:53 +02:00
|
|
|
static int detect_hw(void);
|
|
|
|
static int disable_int(int sub_dev);
|
|
|
|
static int set_stereo(u32_t stereo, int sub_dev);
|
|
|
|
static int set_bits(u32_t nr_of_bits, int sub_dev);
|
|
|
|
static int set_sample_rate(u32_t rate, int sub_dev);
|
|
|
|
static int set_sign(u32_t val, int sub_dev);
|
|
|
|
static int get_max_frag_size(u32_t * val, int *len, int sub_dev);
|
|
|
|
static int set_frag_size(u32_t fragment_size, int sub_dev);
|
|
|
|
static int set_int_cnt(int sub_dev);
|
|
|
|
static int free_buf(u32_t *val, int *len, int sub_dev);
|
|
|
|
static int get_samples_in_buf(u32_t *val, int *len, int sub_dev);
|
|
|
|
static int get_set_volume(struct volume_level *level, int *len, int
|
2012-03-24 16:16:34 +01:00
|
|
|
sub_dev, int flag);
|
2012-03-25 20:25:53 +02:00
|
|
|
static int reset(int sub_dev);
|
2007-11-23 12:40:33 +01:00
|
|
|
|
2007-11-23 12:30:50 +01:00
|
|
|
|
|
|
|
DEV_STRUCT dev;
|
|
|
|
aud_sub_dev_conf_t aud_conf[4];
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
sub_dev_t sub_dev[4];
|
|
|
|
special_file_t special_file[4];
|
|
|
|
drv_t drv;
|
2007-11-23 12:30:50 +01:00
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
int drv_init(void) {
|
2007-11-23 12:40:33 +01:00
|
|
|
drv.DriverName = DRIVER_NAME;
|
2007-11-23 12:30:50 +01:00
|
|
|
drv.NrOfSubDevices = 4;
|
|
|
|
drv.NrOfSpecialFiles = 4;
|
2007-11-23 12:40:33 +01:00
|
|
|
|
2007-11-23 12:30:50 +01:00
|
|
|
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;
|
2007-11-23 12:40:33 +01:00
|
|
|
|
2007-11-23 12:30:50 +01:00
|
|
|
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;
|
2010-01-11 15:22:29 +01:00
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
int drv_init_hw (void) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u16_t i, j;
|
|
|
|
|
|
|
|
/* First, detect the hardware */
|
|
|
|
if (detect_hw() != OK) {
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PCI command register
|
|
|
|
* enable the SERR# driver, PCI bus mastering and I/O access
|
|
|
|
*/
|
|
|
|
pci_attr_w16 (dev.devind, PCI_CR, SERR_EN|PCI_MASTER|IO_ACCESS);
|
|
|
|
|
|
|
|
/* turn everything off */
|
|
|
|
pci_outl(reg(CHIP_SEL_CTRL), 0x0UL);
|
|
|
|
|
|
|
|
/* turn off legacy (legacy control is undocumented) */
|
|
|
|
pci_outl(reg(LEGACY), 0x0UL);
|
|
|
|
pci_outl(reg(LEGACY+4), 0x0UL);
|
|
|
|
|
|
|
|
/* turn off serial interface */
|
|
|
|
pci_outl(reg(SERIAL_INTERFACE_CTRL), 0x0UL);
|
|
|
|
/*pci_outl(reg(SERIAL_INTERFACE_CTRL), 0x3UL);*/
|
|
|
|
|
|
|
|
|
|
|
|
/* clear all the memory */
|
|
|
|
for (i = 0; i < 0x10; ++i) {
|
|
|
|
pci_outb(reg(MEM_PAGE), i);
|
|
|
|
for (j = 0; j < 0x10; j += 4) {
|
|
|
|
pci_outl (reg(MEMORY) + j, 0x0UL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sample Rate Converter initialization */
|
|
|
|
if (src_init(&dev) != OK) {
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
if (AC97_init(&dev) != OK) {
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int detect_hw(void) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u32_t device;
|
|
|
|
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 */
|
|
|
|
device = pci_first_dev(&devind, &v_id, &d_id);
|
|
|
|
while( device > 0 ) {
|
|
|
|
/* if we have a match...break */
|
|
|
|
if (v_id == VENDOR_ID && d_id == DEVICE_ID) break;
|
|
|
|
device = pci_next_dev(&devind, &v_id, &d_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* did we find anything? */
|
|
|
|
if (v_id != VENDOR_ID || d_id != DEVICE_ID) {
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_reserve(devind);
|
|
|
|
|
|
|
|
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, PCI_REV);
|
|
|
|
dev.d_id = d_id;
|
|
|
|
dev.v_id = v_id;
|
|
|
|
dev.devind = devind; /* pci device identifier */
|
2007-11-23 12:30:50 +01:00
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int reset(int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
drv_stop(chan);
|
|
|
|
sub_dev[chan].OutOfData = 1;
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
int drv_reset() {
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Split block/character protocols and libdriver
This patch separates the character and block driver communication
protocols. The old character protocol remains the same, but a new
block protocol is introduced. The libdriver library is replaced by
two new libraries: libchardriver and libblockdriver. Their exposed
API, and drivers that use them, have been updated accordingly.
Together, libbdev and libblockdriver now completely abstract away
the message format used by the block protocol. As the memory driver
is both a character and a block device driver, it now implements its
own message loop.
The most important semantic change made to the block protocol is that
it is no longer possible to return both partial results and an error
for a single transfer. This simplifies the interaction between the
caller and the driver, as the I/O vector no longer needs to be copied
back. Also, drivers are now no longer supposed to decide based on the
layout of the I/O vector when a transfer should be cut short. Put
simply, transfers are now supposed to either succeed completely, or
result in an error.
After this patch, the state of the various pieces is as follows:
- block protocol: stable
- libbdev API: stable for synchronous communication
- libblockdriver API: needs slight revision (the drvlib/partition API
in particular; the threading API will also change shortly)
- character protocol: needs cleanup
- libchardriver API: needs cleanup accordingly
- driver restarts: largely unsupported until endpoint changes are
reintroduced
As a side effect, this patch eliminates several bugs, hacks, and gcc
-Wall and -W warnings all over the place. It probably introduces a
few new ones, too.
Update warning: this patch changes the protocol between MFS and disk
drivers, so in order to use old/new images, the MFS from the ramdisk
must be used to mount all file systems.
2011-11-22 13:27:53 +01:00
|
|
|
int drv_start(int sub_dev, int UNUSED(DmaMode)) {
|
2007-11-23 12:40:33 +01:00
|
|
|
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;break;
|
|
|
|
case DAC1_CHAN: enable_bit = DAC1_EN;break;
|
|
|
|
case DAC2_CHAN: enable_bit = DAC2_EN;break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable interrupts from 'sub device' */
|
|
|
|
drv_reenable_int(sub_dev);
|
|
|
|
|
|
|
|
/* this means play!!! */
|
|
|
|
pci_outw(reg(CHIP_SEL_CTRL), pci_inw(reg(CHIP_SEL_CTRL)) | enable_bit);
|
|
|
|
|
|
|
|
aud_conf[sub_dev].busy = 1;
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_stop(int sub_dev)
|
|
|
|
{
|
2007-11-23 12:40:33 +01:00
|
|
|
u32_t enable_bit;
|
|
|
|
|
|
|
|
switch(sub_dev) {
|
|
|
|
case ADC1_CHAN: enable_bit = ADC1_EN;break;
|
|
|
|
case DAC1_CHAN: enable_bit = DAC1_EN;break;
|
|
|
|
case DAC2_CHAN: enable_bit = DAC2_EN;break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* stop the specified channel */
|
|
|
|
pci_outw(reg(CHIP_SEL_CTRL),
|
|
|
|
pci_inw(reg(CHIP_SEL_CTRL)) & ~enable_bit);
|
|
|
|
aud_conf[sub_dev].busy = 0;
|
|
|
|
disable_int(sub_dev);
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* all IO-ctl's sent to the upper driver are passed to this function */
|
2013-09-03 01:59:20 +02:00
|
|
|
int drv_io_ctl(unsigned long request, void * val, int * len, int sub_dev) {
|
2007-11-23 12:30:50 +01:00
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
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 = reset(sub_dev); break;
|
|
|
|
case DSPIOFREEBUF:
|
|
|
|
status = free_buf(val, len, sub_dev); break;
|
|
|
|
case DSPIOSAMPLESINBUF:
|
|
|
|
status = get_samples_in_buf(val, len, sub_dev); break;
|
|
|
|
case DSPIOPAUSE:
|
|
|
|
status = drv_pause(sub_dev); break;
|
|
|
|
case DSPIORESUME:
|
|
|
|
status = drv_resume(sub_dev); break;
|
|
|
|
case MIXIOGETVOLUME:
|
|
|
|
status = get_set_volume(val, len, sub_dev, 0); break;
|
|
|
|
case MIXIOSETVOLUME:
|
|
|
|
status = get_set_volume(val, len, sub_dev, 1); break;
|
|
|
|
default:
|
|
|
|
status = EINVAL; break;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
2007-11-23 12:40:33 +01:00
|
|
|
|
2010-01-11 15:22:29 +01:00
|
|
|
return status;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_get_irq(char *irq) {
|
2007-11-23 12:40:33 +01:00
|
|
|
*irq = dev.irq;
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_get_frag_size(u32_t *frag_size, int sub_dev) {
|
2007-11-23 12:40:33 +01:00
|
|
|
*frag_size = aud_conf[sub_dev].fragment_size;
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_set_dma(u32_t dma, u32_t length, int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
/* 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 = ADC_MEM_PAGE;
|
|
|
|
frame_count_reg = ADC_BUFFER_SIZE;
|
|
|
|
dma_add_reg = ADC_PCI_ADDRESS;
|
|
|
|
break;
|
|
|
|
case DAC1_CHAN: page = DAC_MEM_PAGE;
|
|
|
|
frame_count_reg = DAC1_BUFFER_SIZE;
|
|
|
|
dma_add_reg = DAC1_PCI_ADDRESS;
|
|
|
|
break;;
|
|
|
|
case DAC2_CHAN: page = DAC_MEM_PAGE;
|
|
|
|
frame_count_reg = DAC2_BUFFER_SIZE;
|
|
|
|
dma_add_reg = DAC2_PCI_ADDRESS;
|
|
|
|
break;;
|
|
|
|
default: return EIO;
|
|
|
|
}
|
|
|
|
pci_outb(reg(MEM_PAGE), page);
|
|
|
|
pci_outl(reg(dma_add_reg), dma);
|
|
|
|
|
|
|
|
/* device expects long word count in stead of bytes */
|
|
|
|
length /= 4;
|
|
|
|
|
|
|
|
/* It seems that register _CURRENT_COUNT is overwritten, but this is
|
|
|
|
* the way to go. The register frame_count_reg is only longword
|
|
|
|
* addressable.
|
|
|
|
* It expects length -1
|
|
|
|
*/
|
|
|
|
pci_outl(reg(frame_count_reg), (u32_t) (length - 1));
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* return status of the interrupt summary bit */
|
|
|
|
int drv_int_sum(void) {
|
2007-11-23 12:40:33 +01:00
|
|
|
return pci_inl(reg(INTERRUPT_STATUS)) & INTR;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_int(int sub_dev) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u32_t int_status;
|
|
|
|
u32_t bit;
|
|
|
|
|
|
|
|
/* return status of interrupt bit of specified channel*/
|
|
|
|
switch (sub_dev) {
|
|
|
|
case DAC1_CHAN: bit = DAC1;break;
|
|
|
|
case DAC2_CHAN: bit = DAC2;break;
|
|
|
|
case ADC1_CHAN: bit = ADC;break;
|
2010-01-18 22:37:24 +01:00
|
|
|
default: return EINVAL;
|
2007-11-23 12:40:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int_status = pci_inl(reg(INTERRUPT_STATUS)) & bit;
|
|
|
|
|
|
|
|
return int_status;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int drv_reenable_int(int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u16_t ser_interface, int_en_bit;
|
|
|
|
|
|
|
|
switch(chan) {
|
|
|
|
case ADC1_CHAN: int_en_bit = R1_INT_EN; break;
|
|
|
|
case DAC1_CHAN: int_en_bit = P1_INTR_EN; break;
|
|
|
|
case DAC2_CHAN: int_en_bit = P2_INTR_EN; break;
|
2010-01-18 22:37:24 +01:00
|
|
|
default: return EINVAL;
|
2007-11-23 12:40:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* clear and reenable an interrupt */
|
|
|
|
ser_interface = pci_inw(reg(SERIAL_INTERFACE_CTRL));
|
|
|
|
pci_outw(reg(SERIAL_INTERFACE_CTRL), ser_interface & ~int_en_bit);
|
|
|
|
pci_outw(reg(SERIAL_INTERFACE_CTRL), ser_interface | int_en_bit);
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
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 = P1_PAUSE;break;
|
|
|
|
case DAC2_CHAN: pause_bit = P2_PAUSE;break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pause */
|
|
|
|
pci_outl(reg(SERIAL_INTERFACE_CTRL),
|
|
|
|
pci_inl(reg(SERIAL_INTERFACE_CTRL)) | pause_bit);
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
int drv_resume(int sub_dev) {
|
|
|
|
u32_t pause_bit = 0;
|
|
|
|
|
|
|
|
drv_reenable_int(sub_dev); /* enable interrupts */
|
|
|
|
|
|
|
|
switch(sub_dev) {
|
|
|
|
case DAC1_CHAN: pause_bit = P1_PAUSE;break;
|
|
|
|
case DAC2_CHAN: pause_bit = P2_PAUSE;break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear pause bit */
|
|
|
|
pci_outl(reg(SERIAL_INTERFACE_CTRL),
|
|
|
|
pci_inl(reg(SERIAL_INTERFACE_CTRL)) & ~pause_bit);
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_bits(u32_t nr_of_bits, int sub_dev) {
|
2007-11-23 12:40:33 +01:00
|
|
|
/* set format bits for specified channel. */
|
|
|
|
u16_t size_16_bit, ser_interface;
|
|
|
|
|
|
|
|
switch(sub_dev) {
|
|
|
|
case ADC1_CHAN: size_16_bit = R1_S_EB; break;
|
|
|
|
case DAC1_CHAN: size_16_bit = P1_S_EB; break;
|
|
|
|
case DAC2_CHAN: size_16_bit = P2_S_EB; break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ser_interface = pci_inw(reg(SERIAL_INTERFACE_CTRL));
|
|
|
|
ser_interface &= ~size_16_bit;
|
|
|
|
switch(nr_of_bits) {
|
|
|
|
case 16: ser_interface |= size_16_bit;break;
|
|
|
|
case 8: break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
pci_outw(reg(SERIAL_INTERFACE_CTRL), ser_interface);
|
|
|
|
aud_conf[sub_dev].nr_of_bits = nr_of_bits;
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_stereo(u32_t stereo, int sub_dev) {
|
2007-11-23 12:40:33 +01:00
|
|
|
/* set format bits for specified channel. */
|
|
|
|
u16_t stereo_bit, ser_interface;
|
|
|
|
|
|
|
|
switch(sub_dev) {
|
|
|
|
case ADC1_CHAN: stereo_bit = R1_S_MB; break;
|
|
|
|
case DAC1_CHAN: stereo_bit = P1_S_MB; break;
|
|
|
|
case DAC2_CHAN: stereo_bit = P2_S_MB; break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
ser_interface = pci_inw(reg(SERIAL_INTERFACE_CTRL));
|
|
|
|
ser_interface &= ~stereo_bit;
|
|
|
|
if (stereo) {
|
|
|
|
ser_interface |= stereo_bit;
|
|
|
|
}
|
|
|
|
pci_outw(reg(SERIAL_INTERFACE_CTRL), ser_interface);
|
|
|
|
aud_conf[sub_dev].stereo = stereo;
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_sign(u32_t UNUSED(val), int UNUSED(sub_dev)) {
|
2007-11-23 12:40:33 +01:00
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_frag_size(u32_t fragment_size, int sub_dev_nr) {
|
2007-11-23 12:40:33 +01:00
|
|
|
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;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_sample_rate(u32_t rate, int sub_dev) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u32_t src_base_reg;
|
|
|
|
|
|
|
|
if (rate > MAX_RATE || rate < MIN_RATE) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
/* set the sample rate for the specified channel*/
|
|
|
|
switch(sub_dev) {
|
|
|
|
case ADC1_CHAN: src_base_reg = SRC_ADC_BASE;break;
|
|
|
|
case DAC1_CHAN: src_base_reg = SRC_SYNTH_BASE;break;
|
|
|
|
case DAC2_CHAN: src_base_reg = SRC_DAC_BASE;break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
src_set_rate(&dev, src_base_reg, rate);
|
|
|
|
aud_conf[sub_dev].sample_rate = rate;
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int set_int_cnt(int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
/* 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 = ADC_SAMP_CT; break;
|
|
|
|
case DAC1_CHAN: int_cnt_reg = DAC1_SAMP_CT; break;
|
|
|
|
case DAC2_CHAN: int_cnt_reg = DAC2_SAMP_CT; 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;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int get_max_frag_size(u32_t * val, int * len, int sub_dev_nr) {
|
2007-11-23 12:40:33 +01:00
|
|
|
*len = sizeof(*val);
|
|
|
|
*val = (sub_dev[sub_dev_nr].DmaSize /
|
|
|
|
sub_dev[sub_dev_nr].NrOfDmaFragments);
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int disable_int(int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u16_t ser_interface, int_en_bit;
|
|
|
|
|
|
|
|
switch(chan) {
|
|
|
|
case ADC1_CHAN: int_en_bit = R1_INT_EN; break;
|
|
|
|
case DAC1_CHAN: int_en_bit = P1_INTR_EN; break;
|
|
|
|
case DAC2_CHAN: int_en_bit = P2_INTR_EN; break;
|
2010-01-18 22:37:24 +01:00
|
|
|
default: return EINVAL;
|
2007-11-23 12:40:33 +01:00
|
|
|
}
|
|
|
|
/* clear the interrupt */
|
|
|
|
ser_interface = pci_inw(reg(SERIAL_INTERFACE_CTRL));
|
|
|
|
pci_outw(reg(SERIAL_INTERFACE_CTRL), ser_interface & ~int_en_bit);
|
2010-01-11 15:22:29 +01:00
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int get_samples_in_buf (u32_t *samples_in_buf, int *len, int chan) {
|
2007-11-23 12:40:33 +01:00
|
|
|
u16_t samp_ct_reg;
|
|
|
|
u16_t curr_samp_ct_reg;
|
|
|
|
u16_t curr_samp_ct; /* counts back from SAMP_CT till 0 */
|
|
|
|
|
|
|
|
*len = sizeof(*samples_in_buf);
|
|
|
|
|
|
|
|
switch(chan) {
|
|
|
|
case ADC1_CHAN:
|
|
|
|
curr_samp_ct_reg = ADC_CURR_SAMP_CT;
|
|
|
|
samp_ct_reg = ADC_SAMP_CT; break;
|
|
|
|
case DAC1_CHAN:
|
|
|
|
curr_samp_ct_reg = DAC1_CURR_SAMP_CT;
|
|
|
|
samp_ct_reg = DAC1_SAMP_CT; break;
|
|
|
|
case DAC2_CHAN:
|
|
|
|
curr_samp_ct_reg = DAC2_CURR_SAMP_CT;
|
|
|
|
samp_ct_reg = DAC2_SAMP_CT; break;
|
|
|
|
default: return EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-04-15 20:49:36 +02:00
|
|
|
/* TODO: is this statement useful? */
|
|
|
|
(void) pci_inw(reg(samp_ct_reg));
|
2007-11-23 12:40:33 +01:00
|
|
|
curr_samp_ct = pci_inw(reg(curr_samp_ct_reg));
|
|
|
|
|
|
|
|
*samples_in_buf = (u32_t) (sub_dev[chan].BufLength * 8192) +
|
|
|
|
curr_samp_ct;
|
|
|
|
|
|
|
|
return OK;
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 12:40:33 +01:00
|
|
|
/* returns 1 if there are free buffers */
|
2012-03-25 20:25:53 +02:00
|
|
|
static int free_buf (u32_t *val, int *len, int sub_dev_nr) {
|
2007-11-23 12:40:33 +01:00
|
|
|
*len = sizeof(*val);
|
|
|
|
if (sub_dev[sub_dev_nr].BufLength ==
|
|
|
|
sub_dev[sub_dev_nr].NrOfExtraBuffers) {
|
|
|
|
*val = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*val = 1;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
2007-11-23 12:30:50 +01:00
|
|
|
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int get_set_volume(struct volume_level *level, int *len, int sub_dev,
|
2007-11-23 12:40:33 +01:00
|
|
|
int flag) {
|
|
|
|
*len = sizeof(struct volume_level);
|
|
|
|
if (sub_dev == MIXER) {
|
|
|
|
return AC97_get_set_volume(level, flag);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2007-11-23 12:30:50 +01:00
|
|
|
}
|