/*************************************************************************************** * * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive, * we need to prime it * **************************************************************************************/ void audio_prime_rx(audio_state_t * state) { audio_stream_t *is = state->input_stream; FN_IN; if (state->need_tx_for_rx) { /* * With some codecs like the Philips UDA1341 we must ensure * there is an output stream at any time while recording since * this is how the UDA1341 gets its clock from the SA1100. * So while there is no playback data to send, the output DMA * will spin with all zeroes. We use the cache flush special * area for that. */ state->output_stream->spin_idle = 1; audio_process_dma(state->output_stream); } is->pending_frags = is->nbfrags; init_completion(&is->wfc); is->wfc.done = 0; is->active = 1; audio_process_dma(is); FN_OUT(0); return; }
static snd_pcm_uframes_t wmt_pdm_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct audio_stream_a *prtd = runtime->private_data; int stream_id = substream->pstr->stream; struct audio_stream_a *s = &prtd[stream_id]; dma_addr_t ptr; snd_pcm_uframes_t offset = 0; //DBG_DETAIL(); ptr = wmt_get_dma_pos(s->dmach); /* if (((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) || ((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_U8))) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) >> 1); } else if ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_U8)) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) >> 2); } else if ((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_FLOAT)) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) << 1); } else if ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_FLOAT)) { offset = bytes_to_frames(runtime, ptr - dump_buf[stream_id].addr); } else offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); */ if (((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) || ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE))) { offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); } if (offset >= runtime->buffer_size) offset = 0; spin_lock(&s->dma_lock); if (s->periods > 0 && s->periods < 2) { if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_pcm_playback_hw_avail(runtime) >= 2 * runtime->period_size) audio_process_dma(s); } else { if (snd_pcm_capture_hw_avail(runtime) >= 2* runtime->period_size) audio_process_dma(s); } } spin_unlock(&s->dma_lock); //DPRINTK("offset = %x", (unsigned int)offset); return offset; }
/* The call back that handles buffer stuff */ static void audio_dma_callback(int lch, u16 ch_status, void *data) { audio_stream_t *s = data; audio_buf_t *b = &s->buffers[s->dma_tail]; FN_IN; if (s->dma_spinref > 0) { s->dma_spinref--; } else if (!s->buffers) { printk(KERN_CRIT "omap_audio: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; s->dma_spinref = -s->dma_spinref; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) complete(&s->wfc); else s->pending_frags++; wake_up(&s->wq); } audio_process_dma(s); FN_OUT(0); return; }
static void audio_dma_callback(void *data) { audio_stream_t *s = data; audio_buf_t *b = &s->buffers[s->dma_tail]; DPRINTK(__FUNCTION__ ": b->dma_ref = %d, b->offset = %d\n", b->dma_ref, b->offset); if (s->dma_spinref > 0) { s->dma_spinref--; } else if (!s->buffers) { printk(KERN_CRIT "omap_audio: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; s->dma_spinref = -s->dma_spinref; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) up(&s->sem); else s->pending_frags++; wake_up(&s->wq); } audio_process_dma(s); }
static int wmt_pdm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; int stream_id = substream->pstr->stream; struct audio_stream_a *prtd = runtime->private_data; struct audio_stream_a *s = &prtd[stream_id]; int ret = 0; DBG_DETAIL(); DPRINTK("Enter, cmd=%d", cmd); spin_lock(&s->dma_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: s->active = 1; audio_process_dma(s); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: s->active = 0; audio_stop_dma(s); /*prtd->period_index = -1;*/ break; default: ret = -EINVAL; } spin_unlock(&s->dma_lock); return ret; }
void audio_dma_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id, int size, enum s3c2410_dma_buffresult result) { audio_stream_t *s = (audio_stream_t *)buf_id; audio_buf_t *b = &s->buffers[s->dma_tail]; if (!s->buffers) { printk(KERN_CRIT "elfin: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) up(&s->sem); else s->pending_frags++; wake_up(&s->wq); } audio_process_dma(s); }
int s3c_audio_resume(audio_state_t *s, u32 level) { if (level == RESUME_ENABLE) { #if 0 audio_stream_t *is = s->input_stream; audio_stream_t *os = s->output_stream; if (AUDIO_ACTIVE(s) && s->hw_init) s->hw_init(s->data); if (os && os->dma_regs) { //DMA_RESET(os); audio_process_dma(os); } if (is && is->dma_regs) { //DMA_RESET(is); audio_process_dma(is); } #endif } return 0; }
static int audio_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) { audio_state_t *state = pm_dev->data; audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; int stopstate; switch (req) { case PM_SUSPEND: /* enter D1-D3 */ if (is && is->dma_regs) { stopstate = is->stopped; audio_stop_dma(is); DMA_CLEAR(is); is->dma_spinref = 0; is->stopped = stopstate; } if (os && os->dma_regs) { stopstate = os->stopped; audio_stop_dma(os); DMA_CLEAR(os); os->dma_spinref = 0; os->stopped = stopstate; } if (AUDIO_ACTIVE(state) && state->hw_shutdown) state->hw_shutdown(state->data); break; case PM_RESUME: /* enter D0 */ if (AUDIO_ACTIVE(state) && state->hw_init) state->hw_init(state->data); if (os && os->dma_regs) { DMA_RESET(os); audio_process_dma(os); } if (is && is->dma_regs) { DMA_RESET(is); audio_process_dma(is); } break; } return 0; }
static void audio_prime_rx(audio_state_t *state) { audio_stream_t *is = state->input_stream; unsigned long flags; local_irq_save(flags); is->pending_frags = is->nbfrags; sema_init(&is->sem, 0); is->active = 1; audio_process_dma(is); local_irq_restore(flags); }
static void audio_prime_rx(audio_state_t *state) { audio_stream_t *is = state->input_stream; unsigned long flags; local_irq_save(flags); if (state->need_tx_for_rx) { /* * With some codecs like the Philips UDA1341 we must ensure * there is an output stream at any time while recording since * this is how the UDA1341 gets its clock from the SA1100. * So while there is no playback data to send, the output DMA * will spin with all zeroes. We use the cache flush special * area for that. */ state->output_stream->spin_idle = 1; audio_process_dma(state->output_stream); } is->pending_frags = is->nbfrags; sema_init(&is->sem, 0); is->active = 1; audio_process_dma(is); local_irq_restore(flags); }
/* * This is called when dma IRQ occurs at the end of each transmited block */ static void audio_dma_callback(void *data) { struct audio_stream_a *s = data; //DBG_DETAIL(); /* * If we are getting a callback for an active stream then we inform * the PCM middle layer we've finished a period */ if (s->active) snd_pcm_period_elapsed(s->stream); spin_lock(&s->dma_lock); if (s->periods > 0) s->periods--; audio_process_dma(s); spin_unlock(&s->dma_lock); }
/*************************************************************************************** * * Sync up the buffers before we shutdown, else under-run errors will happen * **************************************************************************************/ int audio_sync(struct file *file) { audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; audio_buf_t *b; u_int shiftval = 0; unsigned long flags; DECLARE_WAITQUEUE(wait, current); FN_IN; if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) { FN_OUT(1); return 0; } /* * Send current buffer if it contains data. Be sure to send * a full sample count. */ b = &s->buffers[s->usr_head]; if (b->offset &= ~3) { /* Wait for a buffer to become free */ if (wait_for_completion_interruptible(&s->wfc)) return 0; /* * HACK ALERT ! * To avoid increased complexity in the rest of the code * where full fragment sizes are assumed, we cheat a little * with the start pointer here and don't forget to restore * it later. */ /* As this is a last frag we need only one dma channel * to complete. So it's need to unlink dma channels * to avoid empty dma work. */ if (!cpu_is_omap1510() && AUDIO_QUEUE_EMPTY(s)) omap_sound_dma_unlink_lch(s); shiftval = s->fragsize - b->offset; b->offset = shiftval; b->dma_addr -= shiftval; b->data -= shiftval; local_irq_save(flags); s->bytecount -= shiftval; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; s->pending_frags++; audio_process_dma(s); local_irq_restore(flags); } /* Let's wait for all buffers to complete */ set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->wq, &wait); while ((s->pending_frags || (s->wfc.done < s->nbfrags)) && !signal_pending(current)) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&s->wq, &wait); /* undo the pointer hack above */ if (shiftval) { local_irq_save(flags); b->dma_addr += shiftval; b->data += shiftval; /* ensure sane DMA code behavior if not yet processed */ if (b->offset != 0) b->offset = s->fragsize; local_irq_restore(flags); } FN_OUT(0); return 0; }
static int audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) { audio_state_t *state = file->private_data; audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; long val; DPRINTK(__FILE__ " audio_ioctl 0x%08x\n", cmd); /* dispatch based on command */ switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) return put_user(os->fragsize, (int *)arg); else return put_user(is->fragsize, (int *)arg); case SNDCTL_DSP_GETCAPS: val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; if (is && os) val |= DSP_CAP_DUPLEX; return put_user(val, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (long *) arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { int ret = audio_set_fragments(is, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } if (file->f_mode & FMODE_WRITE) { int ret = audio_set_fragments(os, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } return 0; case SNDCTL_DSP_SYNC: return audio_sync(file); case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && is->active && !is->stopped) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { unsigned long flags; if (!is->active) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; audio_prime_rx(state); } local_irq_save(flags); is->stopped = 0; audio_process_dma(is); local_irq_restore(flags); } else { audio_stop_dma(is); } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { unsigned long flags; if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; local_irq_save(flags); if (os->mapped && !os->pending_frags) { os->pending_frags = os->nbfrags; sema_init(&os->sem, 0); os->active = 1; } os->stopped = 0; audio_process_dma(os); local_irq_restore(flags); } else { audio_stop_dma(os); } } return 0; case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_GETIPTR: { count_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; int bytecount, offset; unsigned long flags; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (s->active) { local_irq_save(flags); offset = audio_get_dma_pos(s); inf.ptr = s->dma_tail * s->fragsize + offset; bytecount = s->bytecount + offset; s->bytecount = -offset; inf.blocks = s->fragcount; s->fragcount = 0; local_irq_restore(flags); if (bytecount < 0) bytecount = 0; inf.bytes = bytecount; } return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_GETOSPACE: case SNDCTL_DSP_GETISPACE: { audio_buf_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; inf.bytes = atomic_read(&s->sem.count) * s->fragsize; /* inf.bytes -= s->buffers[s->usr_head].offset; */ inf.fragments = inf.bytes / s->fragsize; inf.fragsize = s->fragsize; inf.fragstotal = s->nbfrags; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_READ) { audio_reset(is); if (state->need_tx_for_rx) { unsigned long flags; local_irq_save(flags); os->spin_idle = 0; local_irq_restore(flags); } } if (file->f_mode & FMODE_WRITE) { audio_reset(os); } return 0; default: /* * Let the client of this module handle the * non generic ioctls */ return state->client_ioctl(inode, file, cmd, arg); } return 0; }
static int audio_sync(struct file *file) { audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; audio_buf_t *b; u_int shiftval = 0; unsigned long flags; DECLARE_WAITQUEUE(wait, current); DPRINTK("audio_sync\n"); if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) return 0; /* * Send current buffer if it contains data. Be sure to send * a full sample count. */ b = &s->buffers[s->usr_head]; if (b->offset &= ~3) { down(&s->sem); /* * HACK ALERT ! * To avoid increased complexity in the rest of the code * where full fragment sizes are assumed, we cheat a little * with the start pointer here and don't forget to restore * it later. */ shiftval = s->fragsize - b->offset; b->offset = shiftval; b->dma_addr -= shiftval; b->data -= shiftval; s->bytecount -= shiftval; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); s->pending_frags++; audio_process_dma(s); local_irq_restore(flags); } /* Let's wait for all buffers to complete */ set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->wq, &wait); while ((s->pending_frags || (atomic_read(&s->sem.count) < s->nbfrags)) && !signal_pending(current)) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&s->wq, &wait); /* undo the pointer hack above */ if (shiftval) { local_irq_save(flags); b->dma_addr += shiftval; b->data += shiftval; /* ensure sane DMA code behavior if not yet processed */ if (b->offset != 0) b->offset = s->fragsize; local_irq_restore(flags); } return 0; }
static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->input_stream; int chunksize, ret = 0; unsigned long flags; DPRINTK("audio_read: count=%d\n", count); if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->active) { if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; audio_prime_rx(state); } while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_head]; /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Grab data from the current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; DPRINTK("read %d from %d\n", chunksize, s->usr_head); if (copy_to_user(buffer, b->data+ b->offset, chunksize)) { up(&s->sem); return -EFAULT; } buffer += chunksize; count -= chunksize; b->offset += chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* Update pointers and return current fragment to DMA */ b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); s->pending_frags++; audio_process_dma(s); local_irq_restore(flags); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_read: return=%d\n", ret); return ret; }
/* * Driver interface functions */ static int audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { const char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; int chunksize, ret = 0; unsigned long flags; DPRINTK("audio_write: count=%d\n", count); if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_head]; /* Wait for a buffer to become free */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Feed the current buffer */ if(state->sound_mode == MONO) { /* mono mode play back */ if(count > s->fragsize/2) chunksize = s->fragsize/2; else chunksize = count; if (copy_from_user_mono_stereo(b->data + b->offset, buffer, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize*2; } else { /* stereo mode play back */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize, s->usr_head); if (copy_from_user(b->data + b->offset, buffer, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; } count -= chunksize; buffer += chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* Update pointers and send current fragment to DMA */ b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); s->pending_frags++; s->active = 1; audio_process_dma(s); local_irq_restore(flags); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write: return=%d\n", ret); return ret; }