diff options
Diffstat (limited to 'drivers/sound/dmabuf.c')
-rw-r--r-- | drivers/sound/dmabuf.c | 1464 |
1 files changed, 465 insertions, 999 deletions
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c index 9cc363b60..6be1cfe64 100644 --- a/drivers/sound/dmabuf.c +++ b/drivers/sound/dmabuf.c @@ -4,7 +4,7 @@ * The DMA buffer manager for digitized voice applications */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software @@ -12,6 +12,7 @@ */ #include <linux/config.h> +#undef BE_CONSERVATIVE #include "sound_config.h" @@ -28,9 +29,6 @@ static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = { {0}}; -#define NEUTRAL8 0x80 -#define NEUTRAL16 0x00 - static int ndmaps = 0; #define MAX_DMAP (MAX_AUDIO_DEV*2) @@ -39,130 +37,9 @@ static struct dma_buffparms dmaps[MAX_DMAP] = { {0}}; -static int space_in_queue (int dev); - static void dma_reset_output (int dev); static void dma_reset_input (int dev); -static int dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact); - -static void -reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording) -{ - /* - * This routine breaks the physical device buffers to logical ones. - */ - - struct audio_operations *dsp_dev = audio_devs[dev]; - - unsigned i, n; - unsigned sr, nc, sz, bsz; - - sr = dsp_dev->d->set_speed (dev, 0); - nc = dsp_dev->d->set_channels (dev, 0); - sz = dsp_dev->d->set_bits (dev, 0); - - if (sz == 8) - dmap->neutral_byte = NEUTRAL8; - else - dmap->neutral_byte = NEUTRAL16; - - if (sr < 1 || nc < 1 || sz < 1) - { - printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", - dev, sr, nc, sz); - sr = DSP_DEFAULT_SPEED; - nc = 1; - sz = 8; - } - - sz = sr * nc * sz; - - sz /= 8; /* #bits -> #bytes */ - dmap->data_rate = sz; - - if (dmap->fragment_size == 0) - { /* Compute the fragment size using the default algorithm */ - - /* - * Compute a buffer size for time not exceeding 1 second. - * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds - * of sound (using the current speed, sample size and #channels). - */ - - bsz = dsp_dev->buffsize; - while (bsz > sz) - bsz /= 2; - - if (bsz == dsp_dev->buffsize) - bsz /= 2; /* Needs at least 2 buffers */ - -/* - * Split the computed fragment to smaller parts. After 3.5a9 - * the default subdivision is 4 which should give better - * results when recording. - */ - - if (dmap->subdivision == 0) /* Not already set */ - { - dmap->subdivision = 4; /* Init to the default value */ - - if ((bsz / dmap->subdivision) > 4096) - dmap->subdivision *= 2; - if ((bsz / dmap->subdivision) < 4096) - dmap->subdivision = 1; - } - - bsz /= dmap->subdivision; - - if (bsz < 16) - bsz = 16; /* Just a sanity check */ - - dmap->fragment_size = bsz; - } - else - { - /* - * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or - * the buffer size computation has already been done. - */ - if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2)) - dmap->fragment_size = (audio_devs[dev]->buffsize / 2); - bsz = dmap->fragment_size; - } - - bsz &= ~0x03; /* Force size which is multiple of 4 bytes */ -#ifdef OS_DMA_ALIGN_CHECK - OS_DMA_ALIGN_CHECK (bsz); -#endif - - n = dsp_dev->buffsize / bsz; - if (n > MAX_SUB_BUFFERS) - n = MAX_SUB_BUFFERS; - if (n > dmap->max_fragments) - n = dmap->max_fragments; - - if (n < 2) - { - n = 2; - bsz /= 2; - } - - dmap->nbufs = n; - dmap->bytes_in_use = n * bsz; - dmap->fragment_size = bsz; - - if (dmap->raw_buf) - memset (dmap->raw_buf, - dmap->neutral_byte, - dmap->bytes_in_use); - - for (i = 0; i < dmap->nbufs; i++) - { - dmap->counts[i] = 0; - } - - dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; -} +static int local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); static void dma_init_buffers (int dev, struct dma_buffparms *dmap) @@ -176,17 +53,18 @@ dma_init_buffers (int dev, struct dma_buffparms *dmap) in_sleep_flag[dev].opts = WK_NONE; } - dmap->flags = DMA_BUSY; /* Other flags off */ - dmap->qlen = dmap->qhead = dmap->qtail = 0; - dmap->nbufs = 1; + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; dmap->bytes_in_use = audio_devs[dev]->buffsize; dmap->dma_mode = DMODE_NONE; dmap->mapping_flags = 0; - dmap->neutral_byte = NEUTRAL8; + dmap->neutral_byte = 0x80; dmap->data_rate = 8000; dmap->cfrag = -1; dmap->closing = 0; + dmap->nbufs = 1; + dmap->flags = DMA_BUSY; /* Other flags off */ } static int @@ -219,6 +97,8 @@ open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan) dmap->fragment_size = 0; dmap->max_fragments = 65536; /* Just a large value */ dmap->byte_counter = 0; + dmap->applic_profile = APF_NORMAL; + dmap->needs_reorg = 1; dma_init_buffers (dev, dmap); @@ -235,20 +115,20 @@ close_dmap (int dev, struct dma_buffparms *dmap, int chan) dmap->dma_mode = DMODE_NONE; dmap->flags &= ~DMA_BUSY; - disable_dma (chan); - sound_free_dmap (dev, dmap, chan); + disable_dma (dmap->dma); } + static unsigned int default_set_bits (int dev, unsigned int bits) { - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) bits, 1); + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits); } static int default_set_speed (int dev, int speed) { - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) speed, 1); + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed); } static short @@ -256,7 +136,7 @@ default_set_channels (int dev, short channels) { int c = channels; - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) c, 1); + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c); } static void @@ -279,50 +159,66 @@ DMAbuf_open (int dev, int mode) if (dev >= num_audiodevs) { - /* printk ("PCM device %d not installed.\n", dev); */ return -ENXIO; } if (!audio_devs[dev]) { - /* printk ("PCM device %d not initialized\n", dev); */ return -ENXIO; } if (!(audio_devs[dev]->flags & DMA_DUPLEX)) { audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; - audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1; + audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma; } + check_driver (audio_devs[dev]->d); + if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0) return retval; - check_driver (audio_devs[dev]->d); - dmap_out = audio_devs[dev]->dmap_out; dmap_in = audio_devs[dev]->dmap_in; - if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0) + if (dmap_in == dmap_out) + audio_devs[dev]->flags &= ~DMA_DUPLEX; + + if (mode & OPEN_WRITE) { - audio_devs[dev]->d->close (dev); - return retval; + if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0) + { + audio_devs[dev]->d->close (dev); + return retval; + } } audio_devs[dev]->enable_bits = mode; - if (mode & OPEN_READ && - audio_devs[dev]->flags & DMA_DUPLEX && - dmap_out != dmap_in) - if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0) - { - audio_devs[dev]->d->close (dev); - close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1); - return retval; - } + + if (mode == OPEN_READ || (mode != OPEN_WRITE && + audio_devs[dev]->flags & DMA_DUPLEX)) + { + if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0) + { + audio_devs[dev]->d->close (dev); + + if (mode & OPEN_WRITE) + { + close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma); + } + + return retval; + } + } + audio_devs[dev]->open_mode = mode; audio_devs[dev]->go = 1; - in_sleep_flag[dev].opts = WK_NONE; - out_sleep_flag[dev].opts = WK_NONE; + + if (mode & OPEN_READ) + in_sleep_flag[dev].opts = WK_NONE; + + if (mode & OPEN_WRITE) + out_sleep_flag[dev].opts = WK_NONE; audio_devs[dev]->d->set_bits (dev, 8); audio_devs[dev]->d->set_channels (dev, 1); @@ -334,17 +230,10 @@ DMAbuf_open (int dev, int mode) void DMAbuf_reset (int dev) { - unsigned long flags; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + dma_reset_output (dev); - save_flags (flags); - cli (); - audio_devs[dev]->d->reset (dev); - restore_flags (flags); - - dma_reset_output (dev); - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) + if (audio_devs[dev]->open_mode & OPEN_READ) dma_reset_input (dev); } @@ -356,8 +245,6 @@ dma_reset_output (int dev) struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - if (dmap->dma_mode != DMODE_OUTPUT) - return; if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ return; @@ -404,7 +291,6 @@ dma_reset_output (int dev) }; } audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - audio_devs[dev]->dmap_out->flags |= DMA_RESTART; /* * Finally shut the device off @@ -412,68 +298,74 @@ dma_reset_output (int dev) if (!(audio_devs[dev]->flags & DMA_DUPLEX) || !audio_devs[dev]->d->halt_output) - audio_devs[dev]->d->reset (dev); + audio_devs[dev]->d->halt_io (dev); else audio_devs[dev]->d->halt_output (dev); audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED; restore_flags (flags); - dma_init_buffers (dev, audio_devs[dev]->dmap_out); + clear_dma_ff (dmap->dma); + disable_dma (dmap->dma); + dmap->byte_counter = 0; reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; } static void dma_reset_input (int dev) { unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; save_flags (flags); cli (); if (!(audio_devs[dev]->flags & DMA_DUPLEX) || !audio_devs[dev]->d->halt_input) - audio_devs[dev]->d->reset (dev); + audio_devs[dev]->d->halt_io (dev); else audio_devs[dev]->d->halt_input (dev); audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED; restore_flags (flags); - dma_init_buffers (dev, audio_devs[dev]->dmap_in); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1); } -static void -launch_output (int dev, struct dma_buffparms *dmap) +void +DMAbuf_launch_output (int dev, struct dma_buffparms *dmap) { - dmap->flags |= DMA_ACTIVE; - - if (dmap->dma_mode == DMODE_NONE || dmap->flags & DMA_RESTART) + if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE)) { - reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0); - audio_devs[dev]->d->prepare_for_output (dev, - dmap->fragment_size, dmap->nbufs); - } + if (!(dmap->flags & DMA_STARTED)) + { + reorganize_buffers (dev, dmap, 0); - dmap->dma_mode |= DMODE_OUTPUT; + if (audio_devs[dev]->d->prepare_for_output (dev, + dmap->fragment_size, dmap->nbufs)) + return; - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_WRITE); + } + if (dmap->counts[dmap->qhead] == 0) + dmap->counts[dmap->qhead] = dmap->fragment_size; - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 0, - !(audio_devs[dev]->flags & DMA_AUTOMODE) || - !(dmap->flags & DMA_STARTED)); - dmap->flags |= DMA_STARTED; - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + dmap->flags |= DMA_ACTIVE; } int DMAbuf_sync (int dev) { unsigned long flags; - int tmout; + int tmout, n = 0; if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) return 0; @@ -499,13 +391,14 @@ DMAbuf_sync (int dev) ; if (dmap->qlen > 0) if (!(dmap->flags & DMA_ACTIVE)) - launch_output (dev, dmap); + DMAbuf_launch_output (dev, dmap); ; audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; audio_devs[dev]->dmap_out->underrun_count = 0; while (!(current->signal & ~current->blocked) + && n++ <= audio_devs[dev]->dmap_out->nbufs && audio_devs[dev]->dmap_out->qlen && audio_devs[dev]->dmap_out->underrun_count == 0) { @@ -534,7 +427,6 @@ DMAbuf_sync (int dev) } } audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - audio_devs[dev]->dmap_out->flags |= DMA_RESTART; restore_flags (flags); /* * Some devices such as GUS have huge amount of on board RAM for the @@ -578,32 +470,39 @@ DMAbuf_release (int dev, int mode) { unsigned long flags; - audio_devs[dev]->dmap_out->closing = 1; - audio_devs[dev]->dmap_in->closing = 1; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + audio_devs[dev]->dmap_out->closing = 1; + if (audio_devs[dev]->open_mode & OPEN_READ) + audio_devs[dev]->dmap_in->closing = 1; - if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)) - if (!((current->signal & ~current->blocked)) - && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) - { - DMAbuf_sync (dev); - } + if (audio_devs[dev]->open_mode & OPEN_WRITE) + if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)) + if (!((current->signal & ~current->blocked)) + && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) + { + DMAbuf_sync (dev); + } if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) - memset (audio_devs[dev]->dmap_out->raw_buf, - audio_devs[dev]->dmap_out->neutral_byte, - audio_devs[dev]->dmap_out->bytes_in_use); + { + memset (audio_devs[dev]->dmap_out->raw_buf, + audio_devs[dev]->dmap_out->neutral_byte, + audio_devs[dev]->dmap_out->bytes_in_use); + } save_flags (flags); cli (); - audio_devs[dev]->d->halt_xfer (dev); + DMAbuf_reset (dev); audio_devs[dev]->d->close (dev); - close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + if (audio_devs[dev]->open_mode & OPEN_WRITE) + close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); - if (audio_devs[dev]->open_mode & OPEN_READ && - audio_devs[dev]->flags & DMA_DUPLEX) - close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->open_mode != OPEN_WRITE && + audio_devs[dev]->flags & DMA_DUPLEX)) + close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma); audio_devs[dev]->open_mode = 0; restore_flags (flags); @@ -611,21 +510,15 @@ DMAbuf_release (int dev, int mode) return 0; } -static int -activate_recording (int dev, struct dma_buffparms *dmap) +int +DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap) { - int prepare = 0; + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) return 0; - if (dmap->flags & DMA_RESTART) - { - dma_reset_input (dev); - dmap->flags &= ~DMA_RESTART; - prepare = 1; - } - if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ { DMAbuf_sync (dev); @@ -633,8 +526,7 @@ activate_recording (int dev, struct dma_buffparms *dmap) dmap->dma_mode = DMODE_NONE; } - - if (prepare || !dmap->dma_mode) + if (!dmap->dma_mode) { int err; @@ -649,12 +541,14 @@ activate_recording (int dev, struct dma_buffparms *dmap) if (!(dmap->flags & DMA_ACTIVE)) { + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 0, - !(audio_devs[dev]->flags & DMA_AUTOMODE) || - !(dmap->flags & DMA_STARTED)); - dmap->flags |= DMA_ACTIVE | DMA_STARTED; + dmap->fragment_size, 0); + dmap->flags |= DMA_ACTIVE; if (audio_devs[dev]->d->trigger) audio_devs[dev]->d->trigger (dev, audio_devs[dev]->enable_bits * audio_devs[dev]->go); @@ -669,6 +563,11 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) int err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EIO; + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + save_flags (flags); cli (); if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) @@ -687,7 +586,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) return -EAGAIN; } - if ((err = activate_recording (dev, dmap)) < 0) + if ((err = DMAbuf_activate_recording (dev, dmap)) < 0) { restore_flags (flags); return err; @@ -737,7 +636,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) { printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); err = EIO; - audio_devs[dev]->d->reset (dev); + dma_reset_input (dev); ; } else @@ -781,83 +680,8 @@ DMAbuf_rmchars (int dev, int buff_no, int c) return 0; } -static int -dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) -{ - if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return ioctl_out (arg, fact); - } - - if (dmap->subdivision != 0 || - dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - if (fact > MAX_REALTIME_FACTOR) - return -EINVAL; - - if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) - return -EINVAL; - - dmap->subdivision = fact; - return ioctl_out (arg, fact); -} - -static int -dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) -{ - int bytes, count; - - if (fact == 0) - return -EIO; - - if (dmap->subdivision != 0 || - dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - bytes = fact & 0xffff; - count = (fact >> 16) & 0x7fff; - - if (count == 0) - count = MAX_SUB_BUFFERS; - - if (bytes < 4 || bytes > 17) /* <16 || > 512k */ - return -EINVAL; - - if (count < 2) - return -EINVAL; - - if (audio_devs[dev]->min_fragment > 0) - if (bytes < audio_devs[dev]->min_fragment) - bytes = audio_devs[dev]->min_fragment; - -#ifdef OS_DMA_MINBITS - if (bytes < OS_DMA_MINBITS) - bytes = OS_DMA_MINBITS; -#endif - - dmap->fragment_size = (1 << bytes); - dmap->max_fragments = count; - - if (dmap->fragment_size > audio_devs[dev]->buffsize) - dmap->fragment_size = audio_devs[dev]->buffsize; - - if (dmap->fragment_size == audio_devs[dev]->buffsize && - audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->fragment_size /= 2; /* Needs at least 2 buffers */ - - dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ - if (arg) - return ioctl_out (arg, bytes | (count << 16)); - else - return 0; -} - -static int -get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap) +int +DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap) { /* * Try to approximate the active byte position of the DMA pointer within the @@ -865,6 +689,7 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap) */ int pos; unsigned long flags; + int chan = dmap->dma; save_flags (flags); cli (); @@ -873,298 +698,38 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap) else { clear_dma_ff (chan); - disable_dma (chan); + disable_dma (dmap->dma); pos = get_dma_residue (chan); - if (chan > 3) /* Word count */ - pos *= 2; - pos = dmap->bytes_in_use - pos - 1; - if (pos < 0) - pos = 0; - if (pos > dmap->bytes_in_use) - pos = dmap->bytes_in_use; - enable_dma (chan); - } - restore_flags (flags); - /* printk ("%04x ", pos); */ - - return pos; -} - + pos = dmap->bytes_in_use - pos; -int -DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) -{ - struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; - struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; - - switch (cmd) - { - case SNDCTL_DSP_GETBLKSIZE: - if (!(dmap_out->flags & DMA_ALLOC_DONE)) + if (dmap->flags & DMODE_OUTPUT) { - reorganize_buffers (dev, dmap_out, - (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - reorganize_buffers (dev, dmap_in, - (audio_devs[dev]->open_mode == OPEN_READ)); + if (dmap->qhead == 0) + pos %= dmap->bytes_in_use; } - - if (local) - return dmap_out->fragment_size; - else - return ioctl_out (arg, dmap_out->fragment_size); - break; - - case SNDCTL_DSP_SUBDIVIDE: - { - int fact; - int ret; - - get_user (fact, (int *) arg); - - ret = dma_subdivide (dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - ret = dma_subdivide (dev, dmap_in, arg, fact); - - return ret; - } - break; - - case SNDCTL_DSP_SETFRAGMENT: - { - int fact; - int ret; - - get_user (fact, (int *) arg); - ret = dma_set_fragment (dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - ret = dma_set_fragment (dev, dmap_in, arg, fact); - - return ret; - } - break; - - case SNDCTL_DSP_GETISPACE: - case SNDCTL_DSP_GETOSPACE: - if (!local) - return -EINVAL; else { - struct dma_buffparms *dmap = dmap_out; - - audio_buf_info *info = (audio_buf_info *) arg; - - if (cmd == SNDCTL_DSP_GETISPACE && - !(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - - if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) - dmap = dmap_in; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return -EINVAL; - - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); - - info->fragstotal = dmap->nbufs; - - if (cmd == SNDCTL_DSP_GETISPACE) - info->fragments = dmap->qlen; - else - { - if (!space_in_queue (dev)) - info->fragments = 0; - else - { - info->fragments = dmap->nbufs - dmap->qlen; - if (audio_devs[dev]->d->local_qlen) - { - int tmp = audio_devs[dev]->d->local_qlen (dev); - - if (tmp && info->fragments) - tmp--; /* - * This buffer has been counted twice - */ - info->fragments -= tmp; - } - } - } - - if (info->fragments < 0) - info->fragments = 0; - else if (info->fragments > dmap->nbufs) - info->fragments = dmap->nbufs; - - info->fragsize = dmap->fragment_size; - info->bytes = info->fragments * dmap->fragment_size; - - if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) - info->bytes -= dmap->counts[dmap->qhead]; + if (dmap->qtail == 0) + pos %= dmap->bytes_in_use; } - return 0; - - case SNDCTL_DSP_SETTRIGGER: - { - unsigned long flags; - - int bits; - int changed; - - get_user (bits, (int *) arg); - bits &= audio_devs[dev]->open_mode; - - if (audio_devs[dev]->d->trigger == NULL) - return -EINVAL; - - if (!(audio_devs[dev]->flags & DMA_DUPLEX)) - if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) - { - printk ("Sound: Device doesn't have full duplex capability\n"); - return -EINVAL; - } - - save_flags (flags); - cli (); - changed = audio_devs[dev]->enable_bits ^ bits; - - if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) - { - int err; - - reorganize_buffers (dev, dmap_in, 1); - - if ((err = audio_devs[dev]->d->prepare_for_input (dev, - dmap_in->fragment_size, dmap_in->nbufs)) < 0) - return -err; - - audio_devs[dev]->enable_bits = bits; - activate_recording (dev, dmap_in); - } - - - if ((changed & bits) & PCM_ENABLE_OUTPUT && - dmap_out->mapping_flags & DMA_MAP_MAPPED && - audio_devs[dev]->go) - { - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - reorganize_buffers (dev, dmap_out, 0); - } - - ; - dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; - launch_output (dev, dmap_out); - ; - } - - audio_devs[dev]->enable_bits = bits; - if (changed && audio_devs[dev]->d->trigger) - { - audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go); - } - restore_flags (flags); - } - case SNDCTL_DSP_GETTRIGGER: - return ioctl_out (arg, audio_devs[dev]->enable_bits); - break; - - case SNDCTL_DSP_SETSYNCRO: - - if (!audio_devs[dev]->d->trigger) - return -EINVAL; - - audio_devs[dev]->d->trigger (dev, 0); - audio_devs[dev]->go = 0; - return 0; - break; - - case SNDCTL_DSP_GETIPTR: - { - count_info info; - unsigned long flags; - - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - - save_flags (flags); - cli (); - info.bytes = audio_devs[dev]->dmap_in->byte_counter; - info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in); - info.blocks = audio_devs[dev]->dmap_in->qlen; - info.bytes += info.ptr; - { - char *fixit = (char *) &info; - - copy_to_user (&((char *) arg)[0], fixit, sizeof (info)); - }; - - if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) - audio_devs[dev]->dmap_in->qlen = 0; /* Acknowledge interrupts */ - restore_flags (flags); - return 0; - } - break; - - case SNDCTL_DSP_GETOPTR: - { - count_info info; - unsigned long flags; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - - save_flags (flags); - cli (); - info.bytes = audio_devs[dev]->dmap_out->byte_counter; - info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out); - info.blocks = audio_devs[dev]->dmap_out->qlen; - info.bytes += info.ptr; - { - char *fixit = (char *) &info; - - copy_to_user (&((char *) arg)[0], fixit, sizeof (info)); - }; - - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) - audio_devs[dev]->dmap_out->qlen = 0; /* Acknowledge interrupts */ - restore_flags (flags); - return 0; - } - break; - - - case SNDCTL_DSP_POST: - ; - if (audio_devs[dev]->dmap_out->qlen > 0) - if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) - launch_output (dev, audio_devs[dev]->dmap_out); - ; - return 0; - break; - - default: - return audio_devs[dev]->d->ioctl (dev, cmd, arg, local); + if (pos < 0) + pos = 0; + if (pos > dmap->bytes_in_use) + pos = dmap->bytes_in_use; + enable_dma (dmap->dma); } + restore_flags (flags); + /* printk ("%04x ", pos); */ + return pos; } /* * DMAbuf_start_devices() is called by the /dev/music driver to start * one or more audio devices at desired moment. */ - -void +static void DMAbuf_start_device (int dev) { if (audio_devs[dev]->open_mode != 0) @@ -1189,8 +754,8 @@ DMAbuf_start_devices (unsigned int devmask) DMAbuf_start_device (dev); } -static int -space_in_queue (int dev) +int +DMAbuf_space_in_queue (int dev) { int len, max, tmp; struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; @@ -1227,224 +792,214 @@ space_in_queue (int dev) return 1; } -int -DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) +static int +output_sleep (int dev, int dontblock) { - unsigned long flags; - int abort, err = EIO; + int tmout; + int err = 0; struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - dmap->flags &= ~DMA_CLEAN; + if (dontblock) + { + return -EAGAIN; + } - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) { - printk ("Sound: Can't write to mmapped device (3)\n"); - return -EINVAL; + return -EAGAIN; } - if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ + /* + * Wait for free space + */ + if (!audio_devs[dev]->go) + tmout = 0; + else { - DMAbuf_reset (dev); - dmap->dma_mode = DMODE_NONE; + tmout = + (dmap->fragment_size * HZ) / dmap->data_rate; + + tmout += HZ / 10; /* Some safety distance */ + + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; } - else if (dmap->flags & DMA_RESTART) /* Restart buffering */ + + if ((current->signal & ~current->blocked)) + return -EIO; + + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + out_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&out_sleeper[dev]); + if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + out_sleep_flag[dev].opts |= WK_TIMEOUT; + } + out_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + if ((out_sleep_flag[dev].opts & WK_TIMEOUT)) { - DMAbuf_sync (dev); + printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + err = EIO; + ; dma_reset_output (dev); - dmap->dma_mode = DMODE_NONE; + } + else if ((current->signal & ~current->blocked)) + { + err = EINTR; } - dmap->flags &= ~(DMA_RESTART | DMA_EMPTY); + return err; +} - if (!dmap->dma_mode) - { - int err; +static int +find_output_space (int dev, char **buf, int *size) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + unsigned long flags; + unsigned long offs, active_offs; + long len; - reorganize_buffers (dev, dmap, 0); - dmap->dma_mode = DMODE_OUTPUT; - if ((err = audio_devs[dev]->d->prepare_for_output (dev, - dmap->fragment_size, dmap->nbufs)) < 0) - return err; - } + if (!DMAbuf_space_in_queue (dev)) + return 0; save_flags (flags); cli (); - abort = 0; - while (!space_in_queue (dev) && - !abort) - { - int tmout; +#ifdef BE_CONSERVATIVE + active_offs = dmap->byte_counter + (dmap->qhead + 1) * dmap->fragment_size; +#else + active_offs = ((dmap->byte_counter + DMAbuf_get_buffer_pointer (dev, dmap))); + /* / dmap->fragment_size) * dmap->fragment_size; */ - if (dontblock) - { - restore_flags (flags); - return -EAGAIN; - } +#endif - if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) - { - restore_flags (flags); - return -EAGAIN; - } + offs = (dmap->user_counter % dmap->bytes_in_use) & ~3; + *buf = dmap->raw_buf + offs; - /* - * Wait for free space - */ - if (!audio_devs[dev]->go) - tmout = 0; - else - { - tmout = - (dmap->fragment_size * HZ) / dmap->data_rate; + len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ - tmout += HZ / 10; /* Some safety distance */ + if ((offs + len) > dmap->bytes_in_use) + len = dmap->bytes_in_use - offs; - if (tmout < (HZ / 2)) - tmout = HZ / 2; - if (tmout > 20 * HZ) - tmout = 20 * HZ; - } + if (len < 0) + { + restore_flags (flags); + return 0; + } + if ((offs + len) > dmap->bytes_in_use) + len = dmap->bytes_in_use - offs; - { - unsigned long tlimit; + *size = len & ~3; - if (tmout) - current->timeout = tlimit = jiffies + (tmout); - else - tlimit = (unsigned long) -1; - out_sleep_flag[dev].opts = WK_SLEEP; - interruptible_sleep_on (&out_sleeper[dev]); - if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) - { - if (jiffies >= tlimit) - out_sleep_flag[dev].opts |= WK_TIMEOUT; - } - out_sleep_flag[dev].opts &= ~WK_SLEEP; - }; - if ((out_sleep_flag[dev].opts & WK_TIMEOUT)) - { - printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); - err = EIO; - abort = 1; - ; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; - audio_devs[dev]->d->reset (dev); - } - else if ((current->signal & ~current->blocked)) - { - err = EINTR; - abort = 1; - } - } restore_flags (flags); - if (!space_in_queue (dev)) - { - return -err; /* Caught a signal ? */ - } - - *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size; - *size = dmap->fragment_size; - dmap->counts[dmap->qtail] = 0; - - return dmap->qtail; + return (len > 0); } int -DMAbuf_get_curr_buffer (int dev, int *buf_no, char **dma_buf, int *buf_ptr, int *buf_size) +DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) { + unsigned long flags; + int err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - if (dmap->cfrag < 0) - return -1; + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); - *dma_buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size; - *buf_ptr = dmap->counts[dmap->qtail]; - *buf_size = dmap->fragment_size; - return *buf_no = dmap->cfrag; -} + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + printk ("Sound: Can't write to mmapped device (3)\n"); + return -EINVAL; + } -int -DMAbuf_set_count (int dev, int buff_no, int l) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ + { + DMAbuf_reset (dev); + dmap->dma_mode = DMODE_NONE; + } + + dmap->dma_mode = DMODE_OUTPUT; + + save_flags (flags); + cli (); - if (buff_no == dmap->qtail) + while (!find_output_space (dev, buf, size)) { - dmap->cfrag = buff_no; - dmap->counts[buff_no] = l; + if ((err = output_sleep (dev, dontblock)) < 0) + { + restore_flags (flags); + return err; + } } - else - dmap->cfrag = -1; + + if (!find_output_space (dev, buf, size)) + { + restore_flags (flags); + return -EIO; /* Caught a signal ? */ + } + restore_flags (flags); + + dmap->flags |= DMA_DIRTY; return 0; } int -DMAbuf_start_output (int dev, int buff_no, int l) +DMAbuf_move_wrpointer (int dev, int l) { struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - int restart = 0; - int post = dmap->flags & DMA_POST; + unsigned long ptr = (dmap->user_counter / dmap->fragment_size) + * dmap->fragment_size; + + unsigned long end_ptr, p, prev_count; + int post = (dmap->flags & DMA_POST); ; dmap->flags &= ~DMA_POST; dmap->cfrag = -1; - if (dmap->flags & DMA_RESTART) - restart = 1; -/* - * Bypass buffering if using mmapped access - */ + prev_count = dmap->user_counter; + dmap->user_counter += l; - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) - { - l = dmap->fragment_size; - dmap->counts[dmap->qtail] = l; - dmap->flags &= ~DMA_RESTART; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->user_counter < prev_count) /* Wrap? */ + { /* Wrap the device counter too */ + dmap->byte_counter %= dmap->bytes_in_use; } - else - { - - dmap->qlen++; - dmap->counts[dmap->qtail] = l; - if (l < dmap->fragment_size) - { - int p = dmap->fragment_size * dmap->qtail; + end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; -#if defined(PPC) || defined(sparc) || defined(HPPA) - dmap->neutral_byte = dmap->raw_buf[p + l - 2]; -#else - dmap->neutral_byte = dmap->raw_buf[p + l - 1]; -#endif - - memset (dmap->raw_buf + p + l, - dmap->neutral_byte, - dmap->fragment_size - l); - } - else - dmap->neutral_byte = - dmap->raw_buf[dmap->fragment_size * dmap->qtail - 1]; + p = (dmap->user_counter - 1) % dmap->bytes_in_use; + dmap->neutral_byte = dmap->raw_buf[p]; + /* Update the fragment based bookkeeping too */ + while (ptr < end_ptr) + { + dmap->counts[dmap->qtail] = dmap->fragment_size; dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + ptr += dmap->fragment_size; } - /* Delay playback until there are at least two fragments (to prevent hiccup) */ - if (dmap->qlen > 1 || post || - (dmap->qlen > 0 && dmap->nbufs <= 2) || - (dmap->qlen > 0 && dmap->flags & DMA_SYNCING) || - restart || l != dmap->fragment_size) - if (!(dmap->flags & DMA_ACTIVE)) + dmap->counts[dmap->qtail] = dmap->user_counter - ptr; + + if (!(dmap->flags & DMA_ACTIVE)) + if (dmap->qlen > 1 || + (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) { - launch_output (dev, dmap); + DMAbuf_launch_output (dev, dmap); } ; @@ -1459,26 +1014,23 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) if (dma_mode == DMA_MODE_WRITE) { - chan = audio_devs[dev]->dmachan1; + chan = audio_devs[dev]->dmap_out->dma; dmap = audio_devs[dev]->dmap_out; } else { - chan = audio_devs[dev]->dmachan2; + chan = audio_devs[dev]->dmap_in->dma; dmap = audio_devs[dev]->dmap_in; } if (dmap->raw_buf == NULL) { - printk ("sound: DMA buffer == NULL\n"); + printk ("sound: DMA buffer(1) == NULL\n"); + printk ("Device %d, chn=%s\n", dev, + (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); return 0; } - /* Handle cards with non automode DMA in new way */ - if (physaddr != dmap->raw_buf_phys) /* Not fragment 0 */ - return count; - count = dmap->bytes_in_use; - if (chan < 0) return 0; @@ -1487,79 +1039,55 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) * set_dma_addr() */ - if (audio_devs[dev]->flags & DMA_AUTOMODE) - { /* - * Auto restart mode. Transfer the whole * - * buffer - */ - unsigned long flags; + sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0); - save_flags (flags); - cli (); - disable_dma (chan); - clear_dma_ff (chan); - set_dma_mode (chan, dma_mode | DMA_AUTOINIT); - set_dma_addr (chan, dmap->raw_buf_phys); - set_dma_count (chan, dmap->bytes_in_use); - enable_dma (chan); - restore_flags (flags); + return count; +} + +static int +local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + int chan; + struct dma_buffparms *dmap; + + if (dma_mode == DMA_MODE_WRITE) + { + chan = audio_devs[dev]->dmap_out->dma; + dmap = audio_devs[dev]->dmap_out; } else { - unsigned long flags; - - save_flags (flags); - cli (); - disable_dma (chan); - clear_dma_ff (chan); - set_dma_mode (chan, dma_mode); - set_dma_addr (chan, physaddr); - set_dma_count (chan, count); - enable_dma (chan); - restore_flags (flags); + chan = audio_devs[dev]->dmap_in->dma; + dmap = audio_devs[dev]->dmap_in; } - return count; -} - -void -DMAbuf_init (void) -{ - int dev; + if (dmap->raw_buf == NULL) + { + printk ("sound: DMA buffer(2) == NULL\n"); + printk ("Device %d, chn=%s\n", dev, + (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); + return 0; + } + if (chan < 0) + return 0; /* - * NOTE! This routine could be called several times. + * The count must be one less than the actual size. This is handled by + * set_dma_addr() */ - for (dev = 0; dev < num_audiodevs; dev++) - if (audio_devs[dev]->dmap_out == NULL) - { - audio_devs[dev]->dmap_out = - audio_devs[dev]->dmap_in = - &dmaps[ndmaps++]; + sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1); + dmap->flags |= DMA_STARTED; - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->dmap_in = - &dmaps[ndmaps++]; - } + return count; } static void -force_restart (int dev, struct dma_buffparms *dmap) +finish_output_interrupt (int dev, struct dma_buffparms *dmap) { unsigned long flags; - if ((audio_devs[dev]->flags & DMA_DUPLEX) && - audio_devs[dev]->d->halt_output) - audio_devs[dev]->d->halt_output (dev); - else - audio_devs[dev]->d->halt_xfer (dev); - - dmap->flags &= ~(DMA_ACTIVE | DMA_STARTED); - dmap->flags |= DMA_RESTART; - dmap->qlen = dmap->qhead = dmap->qtail = 0; - save_flags (flags); cli (); if ((out_sleep_flag[dev].opts & WK_SLEEP)) @@ -1590,111 +1118,98 @@ DMAbuf_outputintr (int dev, int event_type) int this_fragment; #ifdef OS_DMA_INTR - if (audio_devs[dev]->dmachan1 >= 0) - sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + if (audio_devs[dev]->dmap_out->dma >= 0) + sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); #endif if (dmap->raw_buf == NULL) { - printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n"); + printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev); return; } if (dmap->mapping_flags & DMA_MAP_MAPPED) { + unsigned long prev_counter = dmap->byte_counter; + /* mmapped access */ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; if (dmap->qhead == 0) /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - dmap->qlen++; /* Yes increment it (don't decrement) */ - dmap->flags &= ~DMA_ACTIVE; - dmap->counts[dmap->qhead] = dmap->fragment_size; + { + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter < prev_counter) /* Overflow */ + { + dmap->user_counter %= dmap->bytes_in_use; + } + } + dmap->qlen++; /* Yes increment it (don't decrement) */ if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; + dmap->flags &= ~DMA_ACTIVE; + dmap->counts[dmap->qhead] = dmap->fragment_size; - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - dmap->flags |= DMA_ACTIVE; + DMAbuf_launch_output (dev, dmap); + finish_output_interrupt (dev, dmap); + return; } - else if (event_type != 2) + + if (event_type == 2) { - if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) - { - printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", - dev, dmap->qlen, dmap->nbufs); - force_restart (dev, dmap); - return; - } + finish_output_interrupt (dev, dmap); + return; + } - save_flags (flags); - cli (); + if (dmap->qlen > dmap->nbufs) + dmap->qlen = dmap->nbufs; - dmap->qlen--; - this_fragment = dmap->qhead; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + if (dmap->qlen <= 0) + { + finish_output_interrupt (dev, dmap); + return; + } - if (dmap->qhead == 0) /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - dmap->flags &= ~DMA_ACTIVE; + save_flags (flags); + cli (); - if (event_type == 1 && dmap->qlen < 1) - { - dmap->underrun_count++; + dmap->qlen--; + this_fragment = dmap->qhead; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->qlen = 0; - force_restart (dev, dmap); - } + if (dmap->qhead == 0) /* Wrapped */ + { + unsigned long prev_counter = dmap->byte_counter; - if (dmap->qlen) + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter < prev_counter) /* Overflow */ { - if (dmap->flags & DMA_CLEAN) - { - int p = dmap->fragment_size * this_fragment; - - memset (dmap->raw_buf + p, - dmap->neutral_byte, - dmap->fragment_size); - } + dmap->user_counter %= dmap->bytes_in_use; + } + } - if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { + if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; + if (event_type == 1 && dmap->qlen < 1) + { + dmap->underrun_count++; - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - dmap->flags |= DMA_ACTIVE; + dmap->qlen = 0; + if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS) + { + dmap->flags &= ~DMA_DIRTY; + memset (audio_devs[dev]->dmap_out->raw_buf, + audio_devs[dev]->dmap_out->neutral_byte, + audio_devs[dev]->dmap_out->bytes_in_use); } + dmap->user_counter += dmap->fragment_size; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + } - restore_flags (flags); - } /* event_type != 2 */ + if (dmap->qlen > 0) + DMAbuf_launch_output (dev, dmap); - save_flags (flags); - cli (); - if ((out_sleep_flag[dev].opts & WK_SLEEP)) - { - { - out_sleep_flag[dev].opts = WK_WAKEUP; - wake_up (&out_sleeper[dev]); - }; - } restore_flags (flags); + finish_output_interrupt (dev, dmap); } void @@ -1704,8 +1219,8 @@ DMAbuf_inputintr (int dev) struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; #ifdef OS_DMA_INTR - if (audio_devs[dev]->dmachan2 >= 0) - sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); + if (audio_devs[dev]->dmap_in->dma >= 0) + sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma); #endif if (dmap->raw_buf == NULL) @@ -1718,15 +1233,26 @@ DMAbuf_inputintr (int dev) { dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (dmap->qtail == 0) /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; + { + unsigned long prev_counter = dmap->byte_counter; + + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter < prev_counter) /* Overflow */ + { + dmap->user_counter %= dmap->bytes_in_use; + } + } dmap->qlen++; if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) { + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); + dmap->fragment_size, 1); if (audio_devs[dev]->d->trigger) audio_devs[dev]->d->trigger (dev, audio_devs[dev]->enable_bits * audio_devs[dev]->go); @@ -1734,26 +1260,13 @@ DMAbuf_inputintr (int dev) dmap->flags |= DMA_ACTIVE; } - else if (dmap->qlen == (dmap->nbufs - 1)) + else if (dmap->qlen >= (dmap->nbufs - 1)) { /* printk ("Sound: Recording overrun\n"); */ dmap->underrun_count++; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - { - /* Force restart on next read */ - if ((audio_devs[dev]->flags & DMA_DUPLEX) && - audio_devs[dev]->d->halt_input) - audio_devs[dev]->d->halt_input (dev); - else - audio_devs[dev]->d->halt_xfer (dev); - - dmap->flags &= ~DMA_ACTIVE; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; - } + /* Just throw away the oldest fragment but keep the engine running */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; } else { @@ -1763,15 +1276,26 @@ DMAbuf_inputintr (int dev) dev, dmap->qlen, dmap->nbufs); dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (dmap->qtail == 0) /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; + { + unsigned long prev_counter = dmap->byte_counter; + + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter < prev_counter) /* Overflow */ + { + dmap->user_counter %= dmap->bytes_in_use; + } + } } if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) { + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); + dmap->fragment_size, 1); if (audio_devs[dev]->d->trigger) audio_devs[dev]->d->trigger (dev, audio_devs[dev]->enable_bits * audio_devs[dev]->go); @@ -1798,9 +1322,8 @@ DMAbuf_open_dma (int dev) * NOTE! This routine opens only the primary DMA channel (output). */ - int chan = audio_devs[dev]->dmachan1; + int chan = audio_devs[dev]->dmap_out->dma; int err; - unsigned long flags; if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0) { @@ -1812,6 +1335,8 @@ DMAbuf_open_dma (int dev) if (chan >= 0) { + unsigned long flags; + save_flags (flags); cli (); disable_dma (chan); @@ -1825,17 +1350,45 @@ DMAbuf_open_dma (int dev) void DMAbuf_close_dma (int dev) { - DMAbuf_reset_dma (dev); - close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); } void -DMAbuf_reset_dma (int dev) +DMAbuf_init (void) { + int dev; + + /* + * NOTE! This routine could be called several times. + */ + + for (dev = 0; dev < num_audiodevs; dev++) + if (audio_devs[dev]->dmap_out == NULL) + { + if (audio_devs[dev]->d == NULL) + panic ("OSS: audio_devs[%d]->d == NULL\n", dev); + if (audio_devs[dev]->parent_dev) + { /* Use DMA map of the parent dev */ + int parent = audio_devs[dev]->parent_dev - 1; + + audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out; + audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in; + } + else + { + audio_devs[dev]->dmap_out = + audio_devs[dev]->dmap_in = + &dmaps[ndmaps++]; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->dmap_in = + &dmaps[ndmaps++]; + } + } } int -DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait) { struct dma_buffparms *dmap; unsigned long flags; @@ -1857,7 +1410,7 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait cli (); in_sleep_flag[dev].opts = WK_SLEEP; - select_wait (&in_sleeper[dev], wait); + poll_wait (&in_sleeper[dev], wait); restore_flags (flags); return 0; } @@ -1873,7 +1426,7 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait save_flags (flags); cli (); - activate_recording (dev, dmap); + DMAbuf_activate_recording (dev, dmap); restore_flags (flags); } return 0; @@ -1885,7 +1438,7 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait cli (); in_sleep_flag[dev].opts = WK_SLEEP; - select_wait (&in_sleeper[dev], wait); + poll_wait (&in_sleeper[dev], wait); restore_flags (flags); return 0; } @@ -1904,7 +1457,7 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait cli (); out_sleep_flag[dev].opts = WK_SLEEP; - select_wait (&out_sleeper[dev], wait); + poll_wait (&out_sleeper[dev], wait); restore_flags (flags); return 0; } @@ -1919,13 +1472,13 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait return 1; } - if (!space_in_queue (dev)) + if (!DMAbuf_space_in_queue (dev)) { save_flags (flags); cli (); out_sleep_flag[dev].opts = WK_SLEEP; - select_wait (&out_sleeper[dev], wait); + poll_wait (&out_sleeper[dev], wait); restore_flags (flags); return 0; } @@ -1940,91 +1493,4 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait } -#else /* CONFIG_AUDIO */ -/* - * Stub versions if audio services not included - */ - -int -DMAbuf_open (int dev, int mode) -{ - return -ENXIO; -} - -int -DMAbuf_release (int dev, int mode) -{ - return 0; -} - -int -DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) -{ - return -EIO; -} - -int -DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) -{ - return -EIO; -} - -int -DMAbuf_rmchars (int dev, int buff_no, int c) -{ - return -EIO; -} - -int -DMAbuf_start_output (int dev, int buff_no, int l) -{ - return -EIO; -} - -int -DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) -{ - return -EIO; -} - -void -DMAbuf_init (void) -{ -} - -int -DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) -{ - return -EIO; -} - -int -DMAbuf_open_dma (int dev) -{ - return -ENXIO; -} - -void -DMAbuf_close_dma (int dev) -{ - return; -} - -void -DMAbuf_reset_dma (int dev) -{ - return; -} - -void -DMAbuf_inputintr (int dev) -{ - return; -} - -void -DMAbuf_outputintr (int dev, int underrun_flag) -{ - return; -} #endif |