/*
|
/*
|
* sound/vidc_audio.c
|
* sound/vidc_audio.c
|
*
|
*
|
* Audio routines for the VIDC
|
* Audio routines for the VIDC
|
*
|
*
|
* Copyright (C) 1997 Russell King
|
* Copyright (C) 1997 Russell King
|
*/
|
*/
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include "sound_config.h"
|
#include "sound_config.h"
|
|
|
#include "vidc.h"
|
#include "vidc.h"
|
|
|
/*
|
/*
|
* VIDC sound
|
* VIDC sound
|
*
|
*
|
* When using SERIAL SOUND mode (external DAC), the number of physical
|
* When using SERIAL SOUND mode (external DAC), the number of physical
|
* channels is fixed at 2. Therefore, the sample rate = vidc sample rate.
|
* channels is fixed at 2. Therefore, the sample rate = vidc sample rate.
|
*/
|
*/
|
|
|
#if defined(CONFIG_VIDC)
|
#if defined(CONFIG_VIDC)
|
|
|
static int vidc_adev;
|
static int vidc_adev;
|
|
|
static int vidc_audio_volume;
|
static int vidc_audio_volume;
|
static int vidc_audio_rate;
|
static int vidc_audio_rate;
|
static char vidc_audio_bits;
|
static char vidc_audio_bits;
|
static char vidc_audio_channels;
|
static char vidc_audio_channels;
|
|
|
extern void vidc_update_filler (int bits, int channels);
|
extern void vidc_update_filler (int bits, int channels);
|
|
|
int vidc_audio_get_volume (void)
|
int vidc_audio_get_volume (void)
|
{
|
{
|
return vidc_audio_volume;
|
return vidc_audio_volume;
|
}
|
}
|
|
|
int vidc_audio_set_volume (int newvol)
|
int vidc_audio_set_volume (int newvol)
|
{
|
{
|
vidc_audio_volume = newvol;
|
vidc_audio_volume = newvol;
|
return vidc_audio_volume;
|
return vidc_audio_volume;
|
}
|
}
|
|
|
static int vidc_audio_set_bits (int bits)
|
static int vidc_audio_set_bits (int bits)
|
{
|
{
|
switch (bits) {
|
switch (bits) {
|
case AFMT_QUERY:
|
case AFMT_QUERY:
|
break;
|
break;
|
case AFMT_U8:
|
case AFMT_U8:
|
case AFMT_S8:
|
case AFMT_S8:
|
case AFMT_S16_LE:
|
case AFMT_S16_LE:
|
vidc_audio_bits = bits;
|
vidc_audio_bits = bits;
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
break;
|
break;
|
default:
|
default:
|
vidc_audio_bits = AFMT_S16_LE;
|
vidc_audio_bits = AFMT_S16_LE;
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
break;
|
break;
|
}
|
}
|
return vidc_audio_bits;
|
return vidc_audio_bits;
|
}
|
}
|
|
|
static int vidc_audio_set_rate (int rate)
|
static int vidc_audio_set_rate (int rate)
|
{
|
{
|
if (rate) {
|
if (rate) {
|
int newsize, new2size;
|
int newsize, new2size;
|
vidc_audio_rate = ((500000 / rate) + 1) >> 1;
|
vidc_audio_rate = ((500000 / rate) + 1) >> 1;
|
if (vidc_audio_rate < 3)
|
if (vidc_audio_rate < 3)
|
vidc_audio_rate = 3;
|
vidc_audio_rate = 3;
|
if (vidc_audio_rate > 255)
|
if (vidc_audio_rate > 255)
|
vidc_audio_rate = 255;
|
vidc_audio_rate = 255;
|
outl ((vidc_audio_rate - 2) | 0xb0000000, IO_VIDC_BASE);
|
outl ((vidc_audio_rate - 2) | 0xb0000000, IO_VIDC_BASE);
|
outl (0xb1000003, IO_VIDC_BASE);
|
outl (0xb1000003, IO_VIDC_BASE);
|
newsize = (10000 / vidc_audio_rate) & ~3;
|
newsize = (10000 / vidc_audio_rate) & ~3;
|
if (newsize< 208)
|
if (newsize< 208)
|
newsize = 208;
|
newsize = 208;
|
if (newsize > 4096)
|
if (newsize > 4096)
|
newsize = 4096;
|
newsize = 4096;
|
for (new2size = 128; new2size < newsize; new2size <<= 1);
|
for (new2size = 128; new2size < newsize; new2size <<= 1);
|
if (new2size - newsize > newsize - (new2size >> 1))
|
if (new2size - newsize > newsize - (new2size >> 1))
|
new2size >>= 1;
|
new2size >>= 1;
|
dma_bufsize = new2size;
|
dma_bufsize = new2size;
|
}
|
}
|
return 250000 / vidc_audio_rate;
|
return 250000 / vidc_audio_rate;
|
}
|
}
|
|
|
static int vidc_audio_set_channels (int channels)
|
static int vidc_audio_set_channels (int channels)
|
{
|
{
|
switch (channels) {
|
switch (channels) {
|
case 0: break;
|
case 0: break;
|
case 1:
|
case 1:
|
case 2:
|
case 2:
|
vidc_audio_channels = channels;
|
vidc_audio_channels = channels;
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
break;
|
break;
|
default:
|
default:
|
vidc_audio_channels = 2;
|
vidc_audio_channels = 2;
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
vidc_update_filler (vidc_audio_bits, vidc_audio_channels);
|
break;
|
break;
|
}
|
}
|
return vidc_audio_channels;
|
return vidc_audio_channels;
|
}
|
}
|
|
|
/*
|
/*
|
* Open the device
|
* Open the device
|
*
|
*
|
* dev - device
|
* dev - device
|
* mode - mode to open device (logical OR of OPEN_READ and OPEN_WRITE)
|
* mode - mode to open device (logical OR of OPEN_READ and OPEN_WRITE)
|
*
|
*
|
* Called when opening the DMAbuf (dmabuf.c:259)
|
* Called when opening the DMAbuf (dmabuf.c:259)
|
*/
|
*/
|
static int
|
static int
|
vidc_audio_open (int dev, int mode)
|
vidc_audio_open (int dev, int mode)
|
{
|
{
|
if (vidc_busy)
|
if (vidc_busy)
|
return -EBUSY;
|
return -EBUSY;
|
|
|
if (mode & OPEN_READ && !mode & OPEN_WRITE) {
|
if (mode & OPEN_READ && !mode & OPEN_WRITE) {
|
printk ("VIDCsound: This audio device doesn't have recording capability\n");
|
printk ("VIDCsound: This audio device doesn't have recording capability\n");
|
return -EIO;
|
return -EIO;
|
}
|
}
|
vidc_busy = 1;
|
vidc_busy = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Close the device
|
* Close the device
|
*
|
*
|
* dev - device
|
* dev - device
|
*
|
*
|
* Called when closing the DMAbuf (dmabuf.c:477)
|
* Called when closing the DMAbuf (dmabuf.c:477)
|
* after halt_xfer
|
* after halt_xfer
|
*/
|
*/
|
static void
|
static void
|
vidc_audio_close (int dev)
|
vidc_audio_close (int dev)
|
{
|
{
|
vidc_busy = 0;
|
vidc_busy = 0;
|
}
|
}
|
|
|
static int
|
static int
|
vidc_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
|
vidc_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
|
{
|
{
|
int ret;
|
int ret;
|
|
|
switch (cmd) {
|
switch (cmd) {
|
case SOUND_PCM_WRITE_RATE:
|
case SOUND_PCM_WRITE_RATE:
|
if (local)
|
if (local)
|
ret = vidc_audio_set_rate ((int) arg);
|
ret = vidc_audio_set_rate ((int) arg);
|
else
|
else
|
ret = vidc_audio_set_rate (get_user ((int *)arg));
|
ret = vidc_audio_set_rate (get_user ((int *)arg));
|
break;
|
break;
|
|
|
case SOUND_PCM_READ_RATE:
|
case SOUND_PCM_READ_RATE:
|
ret = vidc_audio_set_rate (0);
|
ret = vidc_audio_set_rate (0);
|
break;
|
break;
|
|
|
case SNDCTL_DSP_STEREO:
|
case SNDCTL_DSP_STEREO:
|
if (local)
|
if (local)
|
ret = vidc_audio_set_channels ((int) arg + 1) - 1;
|
ret = vidc_audio_set_channels ((int) arg + 1) - 1;
|
else
|
else
|
ret = vidc_audio_set_channels (get_user ((int *)arg) + 1) - 1;
|
ret = vidc_audio_set_channels (get_user ((int *)arg) + 1) - 1;
|
break;
|
break;
|
|
|
case SOUND_PCM_WRITE_CHANNELS:
|
case SOUND_PCM_WRITE_CHANNELS:
|
if (local)
|
if (local)
|
ret = vidc_audio_set_channels ((int) arg);
|
ret = vidc_audio_set_channels ((int) arg);
|
else
|
else
|
ret = vidc_audio_set_channels (get_user ((int *)arg));
|
ret = vidc_audio_set_channels (get_user ((int *)arg));
|
break;
|
break;
|
|
|
case SOUND_PCM_READ_CHANNELS:
|
case SOUND_PCM_READ_CHANNELS:
|
ret = vidc_audio_set_channels (0);
|
ret = vidc_audio_set_channels (0);
|
break;
|
break;
|
|
|
case SNDCTL_DSP_SETFMT:
|
case SNDCTL_DSP_SETFMT:
|
if (local)
|
if (local)
|
ret = vidc_audio_set_bits ((int) arg);
|
ret = vidc_audio_set_bits ((int) arg);
|
else
|
else
|
ret = vidc_audio_set_bits (get_user ((int *)arg));
|
ret = vidc_audio_set_bits (get_user ((int *)arg));
|
break;
|
break;
|
|
|
case SOUND_PCM_READ_BITS:
|
case SOUND_PCM_READ_BITS:
|
ret = vidc_audio_set_bits (0);
|
ret = vidc_audio_set_bits (0);
|
break;
|
break;
|
|
|
case SOUND_PCM_WRITE_FILTER:
|
case SOUND_PCM_WRITE_FILTER:
|
case SOUND_PCM_READ_FILTER:
|
case SOUND_PCM_READ_FILTER:
|
ret = -EINVAL;
|
ret = -EINVAL;
|
break;
|
break;
|
|
|
default:
|
default:
|
ret = -EINVAL;
|
ret = -EINVAL;
|
break;
|
break;
|
}
|
}
|
if (local)
|
if (local)
|
return ret;
|
return ret;
|
else
|
else
|
return snd_ioctl_return ((int *)arg, ret);
|
return snd_ioctl_return ((int *)arg, ret);
|
}
|
}
|
|
|
/*
|
/*
|
* Output a block via DMA to sound device
|
* Output a block via DMA to sound device
|
*
|
*
|
* dev - device number
|
* dev - device number
|
* buf - physical address of buffer
|
* buf - physical address of buffer
|
* total_count - total byte count in buffer
|
* total_count - total byte count in buffer
|
* intrflag - set if this has been called from an interrupt (via DMAbuf_outputintr)
|
* intrflag - set if this has been called from an interrupt (via DMAbuf_outputintr)
|
* restart_dma - set if DMA needs to be re-initialised
|
* restart_dma - set if DMA needs to be re-initialised
|
*
|
*
|
* Called when:
|
* Called when:
|
* 1. Starting output (dmabuf.c:1327)
|
* 1. Starting output (dmabuf.c:1327)
|
* 2. (dmabuf.c:1504)
|
* 2. (dmabuf.c:1504)
|
* 3. A new buffer needs to be sent to the device (dmabuf.c:1579)
|
* 3. A new buffer needs to be sent to the device (dmabuf.c:1579)
|
*/
|
*/
|
static void
|
static void
|
vidc_audio_dma_interrupt (void)
|
vidc_audio_dma_interrupt (void)
|
{
|
{
|
DMAbuf_outputintr (vidc_adev, 1);
|
DMAbuf_outputintr (vidc_adev, 1);
|
}
|
}
|
|
|
static void
|
static void
|
vidc_audio_output_block (int dev, unsigned long buf, int total_count,
|
vidc_audio_output_block (int dev, unsigned long buf, int total_count,
|
int intrflag, int restart_dma)
|
int intrflag, int restart_dma)
|
{
|
{
|
dma_start = (unsigned long)bus_to_virt(buf);
|
dma_start = (unsigned long)bus_to_virt(buf);
|
dma_count = total_count;
|
dma_count = total_count;
|
|
|
if (restart_dma && !intrflag) {
|
if (restart_dma && !intrflag) {
|
dma_interrupt = vidc_audio_dma_interrupt;
|
dma_interrupt = vidc_audio_dma_interrupt;
|
vidc_sound_dma_irq (0, NULL, NULL);
|
vidc_sound_dma_irq (0, NULL, NULL);
|
outb (0x30, IOMD_SD0CR);
|
outb (0x30, IOMD_SD0CR);
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
vidc_audio_start_input (int dev, unsigned long buf, int count,
|
vidc_audio_start_input (int dev, unsigned long buf, int count,
|
int intrflag, int restart_dma)
|
int intrflag, int restart_dma)
|
{
|
{
|
}
|
}
|
|
|
static int
|
static int
|
vidc_audio_prepare_for_input (int dev, int bsize, int bcount)
|
vidc_audio_prepare_for_input (int dev, int bsize, int bcount)
|
{
|
{
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
/*
|
/*
|
* Prepare for outputting samples to `dev'
|
* Prepare for outputting samples to `dev'
|
*
|
*
|
* Each buffer that will be passed will be `bsize' bytes long,
|
* Each buffer that will be passed will be `bsize' bytes long,
|
* with a total of `bcount' buffers.
|
* with a total of `bcount' buffers.
|
*
|
*
|
* Called when:
|
* Called when:
|
* 1. A trigger enables audio output (dmabuf.c:978)
|
* 1. A trigger enables audio output (dmabuf.c:978)
|
* 2. We get a write buffer without dma_mode setup (dmabuf.c:1152)
|
* 2. We get a write buffer without dma_mode setup (dmabuf.c:1152)
|
* 3. We restart a transfer (dmabuf.c:1324)
|
* 3. We restart a transfer (dmabuf.c:1324)
|
*/
|
*/
|
static int
|
static int
|
vidc_audio_prepare_for_output (int dev, int bsize, int bcount)
|
vidc_audio_prepare_for_output (int dev, int bsize, int bcount)
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static void
|
static void
|
vidc_audio_reset (int dev)
|
vidc_audio_reset (int dev)
|
{
|
{
|
}
|
}
|
|
|
/*
|
/*
|
* Halt a DMA transfer to `dev'
|
* Halt a DMA transfer to `dev'
|
*
|
*
|
* Called when:
|
* Called when:
|
* 1. We close the DMAbuf (dmabuf.c:476)
|
* 1. We close the DMAbuf (dmabuf.c:476)
|
* 2. We run out of output buffers to output to the device. (dmabuf.c:1456)
|
* 2. We run out of output buffers to output to the device. (dmabuf.c:1456)
|
* 3. We run out of output buffers and we're closing down. (dmabuf.c:1546)
|
* 3. We run out of output buffers and we're closing down. (dmabuf.c:1546)
|
* 4. We run out of input buffers in AUTOMODE. (dmabuf.c:1651)
|
* 4. We run out of input buffers in AUTOMODE. (dmabuf.c:1651)
|
*/
|
*/
|
static void
|
static void
|
vidc_audio_halt_xfer (int dev)
|
vidc_audio_halt_xfer (int dev)
|
{
|
{
|
dma_count = 0;
|
dma_count = 0;
|
}
|
}
|
|
|
static int
|
static int
|
vidc_audio_local_qlen (int dev)
|
vidc_audio_local_qlen (int dev)
|
{
|
{
|
return dma_count != 0;
|
return dma_count != 0;
|
}
|
}
|
|
|
static struct audio_driver vidc_audio_driver = {
|
static struct audio_driver vidc_audio_driver = {
|
vidc_audio_open, /* open */
|
vidc_audio_open, /* open */
|
vidc_audio_close, /* close */
|
vidc_audio_close, /* close */
|
vidc_audio_output_block, /* output_block */
|
vidc_audio_output_block, /* output_block */
|
vidc_audio_start_input, /* start_input */
|
vidc_audio_start_input, /* start_input */
|
vidc_audio_ioctl, /* ioctl */
|
vidc_audio_ioctl, /* ioctl */
|
vidc_audio_prepare_for_input, /* prepare_for_input */
|
vidc_audio_prepare_for_input, /* prepare_for_input */
|
vidc_audio_prepare_for_output, /* prepare_for_output */
|
vidc_audio_prepare_for_output, /* prepare_for_output */
|
vidc_audio_reset, /* reset */
|
vidc_audio_reset, /* reset */
|
vidc_audio_halt_xfer, /* halt_xfer */
|
vidc_audio_halt_xfer, /* halt_xfer */
|
vidc_audio_local_qlen, /*+local_qlen */
|
vidc_audio_local_qlen, /*+local_qlen */
|
NULL, /*+copy_from_user */
|
NULL, /*+copy_from_user */
|
NULL, /*+halt_input */
|
NULL, /*+halt_input */
|
NULL, /*+halt_output */
|
NULL, /*+halt_output */
|
NULL, /*+trigger */
|
NULL, /*+trigger */
|
NULL, /*+set_speed */
|
NULL, /*+set_speed */
|
NULL, /*+set_bits */
|
NULL, /*+set_bits */
|
NULL, /*+set_channels */
|
NULL, /*+set_channels */
|
};
|
};
|
|
|
static struct audio_operations vidc_audio_operations = {
|
static struct audio_operations vidc_audio_operations = {
|
"VIDCsound",
|
"VIDCsound",
|
0,
|
0,
|
AFMT_U8 | AFMT_S16_LE,
|
AFMT_U8 | AFMT_S16_LE,
|
NULL,
|
NULL,
|
&vidc_audio_driver
|
&vidc_audio_driver
|
};
|
};
|
|
|
void vidc_audio_init (struct address_info *hw_config)
|
void vidc_audio_init (struct address_info *hw_config)
|
{
|
{
|
vidc_audio_volume = 100 | (100 << 8);
|
vidc_audio_volume = 100 | (100 << 8);
|
if (num_audiodevs < MAX_AUDIO_DEV) {
|
if (num_audiodevs < MAX_AUDIO_DEV) {
|
audio_devs[vidc_adev = num_audiodevs++] = &vidc_audio_operations;
|
audio_devs[vidc_adev = num_audiodevs++] = &vidc_audio_operations;
|
audio_devs[vidc_adev]->dmachan1 = hw_config->dma;
|
audio_devs[vidc_adev]->dmachan1 = hw_config->dma;
|
/* size of the total DMA buffer */
|
/* size of the total DMA buffer */
|
audio_devs[vidc_adev]->buffsize = DSP_BUFFSIZE;
|
audio_devs[vidc_adev]->buffsize = DSP_BUFFSIZE;
|
audio_devs[vidc_adev]->min_fragment = 10; /* 1024 bytes => 64 buffers */
|
audio_devs[vidc_adev]->min_fragment = 10; /* 1024 bytes => 64 buffers */
|
audio_devs[vidc_adev]->mixer_dev = num_mixers;
|
audio_devs[vidc_adev]->mixer_dev = num_mixers;
|
audio_devs[vidc_adev]->flags |= 0;
|
audio_devs[vidc_adev]->flags |= 0;
|
} else
|
} else
|
printk ("VIDCsound: Too many PCM devices available\n");
|
printk ("VIDCsound: Too many PCM devices available\n");
|
}
|
}
|
#endif
|
#endif
|
|
|