int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; DCSR(prtd->dma_ch) = DCSR_RUN; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: DCSR(prtd->dma_ch) &= ~DCSR_RUN; break; case SNDRV_PCM_TRIGGER_RESUME: DCSR(prtd->dma_ch) |= DCSR_RUN; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; DCSR(prtd->dma_ch) |= DCSR_RUN; break; default: ret = -EINVAL; } return ret; }
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; pxa_dma_desc *final_desc; u_long dcmd_save = 0; DECLARE_WAITQUEUE(wait, current); 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. */ final_desc = NULL; b = &s->buffers[s->usr_frag]; if (b->offset &= ~3) { final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE]; b->offset &= (MAX_DMA_SIZE-1); dcmd_save = final_desc->dcmd; final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN; final_desc->ddadr |= DDADR_STOP; b->offset = 0; b->dma_desc->ddadr &= ~DDADR_STOP; if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { DDADR(s->dma_ch) = b->dma_desc->ddadr; DCSR(s->dma_ch) = DCSR_RUN; } } /* Wait for DMA to complete. */ set_current_state(TASK_INTERRUPTIBLE); #if 0 /* * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set * along wotj DCSR_RUN. Silicon bug? */ add_wait_queue(&s->stop_wq, &wait); DCSR(s->dma_ch) |= DCSR_STOPIRQEN; schedule(); #else add_wait_queue(&s->frag_wq, &wait); while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } #endif set_current_state(TASK_RUNNING); remove_wait_queue(&s->frag_wq, &wait); /* Restore the descriptor chain. */ if (final_desc) { final_desc->dcmd = dcmd_save; final_desc->ddadr &= ~DDADR_STOP; b->dma_desc->ddadr |= DDADR_STOP; } return 0; }
/* * Prepare taskfile for submission. */ static void pxa_qc_prep(struct ata_queued_cmd *qc) { struct pata_pxa_data *pd = qc->ap->private_data; int si = 0; struct scatterlist *sg; if (!(qc->flags & ATA_QCFLAG_DMAMAP)) return; pd->dma_desc_id = 0; DCSR(pd->dma_channel) = 0; DALGN &= ~(1 << pd->dma_dreq); for_each_sg(qc->sg, sg, qc->n_elem, si) pxa_load_dmac(sg, qc); pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP; /* Fire IRQ only at the end of last block */ pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN; DDADR(pd->dma_channel) = pd->dma_desc_addr; DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel; }
static int pxa95x_pcm_ssp_trigger(struct snd_pcm_substream *substream, int cmd) { struct pxa95x_runtime_data *prtd = substream->runtime->private_data; int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: dvfm_disable_lowpower(pxa95x_ssp_dvfm_idx); DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; DCSR(prtd->dma_ch) |= DCSR_RUN; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: DCSR(prtd->dma_ch) &= ~DCSR_RUN; dvfm_enable_lowpower(pxa95x_ssp_dvfm_idx); break; default: ret = -EINVAL; } return ret; }
static unsigned int audio_poll(struct file *file, struct poll_table_struct *wait) { audio_state_t *state = file->private_data; audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; unsigned int mask = 0; if (file->f_mode & FMODE_READ) { /* Start audio input if not already active */ if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; if (DCSR(is->dma_ch) & DCSR_STOPSTATE) { DDADR(is->dma_ch) = is->buffers[is->dma_frag].dma_desc->ddadr; DCSR(is->dma_ch) = DCSR_RUN; } poll_wait(file, &is->frag_wq, wait); } if (file->f_mode & FMODE_WRITE) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; poll_wait(file, &os->frag_wq, wait); } if (file->f_mode & FMODE_READ) if (( is->mapped && is->bytecount > 0) || (!is->mapped && atomic_read(&is->sem.count) > 0)) mask |= POLLIN | POLLRDNORM; if (file->f_mode & FMODE_WRITE) if (( os->mapped && os->bytecount > 0) || (!os->mapped && atomic_read(&os->sem.count) > 0)) mask |= POLLOUT | POLLWRNORM; return mask; }
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; 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 && DCSR(is->dma_ch) & DCSR_RUN) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN) 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) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; if (!(DCSR(is->dma_ch) & DCSR_RUN)) { audio_buf_t *b = &is->buffers[is->dma_frag]; DDADR(is->dma_ch) = b->dma_desc->ddadr; DCSR(is->dma_ch) = DCSR_RUN; } } else { DCSR(is->dma_ch) = 0; } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; if (!(DCSR(os->dma_ch) & DCSR_RUN)) { audio_buf_t *b = &os->buffers[os->dma_frag]; DDADR(os->dma_ch) = b->dma_desc->ddadr; DCSR(os->dma_ch) = DCSR_RUN; } } else { DCSR(os->dma_ch) = 0; } } return 0; 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_frag].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_GETOPTR: case SNDCTL_DSP_GETIPTR: { count_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; dma_addr_t ptr; int bytecount, offset, flags; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (DCSR(s->dma_ch) & DCSR_RUN) { audio_buf_t *b; save_flags_cli(flags); ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch); b = &s->buffers[s->dma_frag]; offset = ptr - b->dma_desc->dsadr; if (offset >= s->fragsize) offset = s->fragsize - 4; } else { save_flags(flags); offset = 0; } inf.ptr = s->dma_frag * s->fragsize + offset; bytecount = s->bytecount + offset; s->bytecount = -offset; inf.blocks = s->fragcount; s->fragcount = 0; restore_flags(flags); if (bytecount < 0) bytecount = 0; inf.bytes = bytecount; 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_WRITE) audio_clear_buf(os); if (file->f_mode & FMODE_READ) audio_clear_buf(is); return 0; default: return state->client_ioctl(inode, file, cmd, arg); } 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; if (ppos != &file->f_pos) return -ESPIPE; 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_frag]; /* prime DMA */ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { DDADR(s->dma_ch) = s->buffers[s->dma_frag].dma_desc->ddadr; DCSR(s->dma_ch) = DCSR_RUN; } /* 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 current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; if (copy_to_user(buffer, b->data + b->offset, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; buffer += chunksize; count -= chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* * Make this buffer available for DMA again. * We unlock this fragment's checkpoint descriptor and * kick DMA if it is idle. Using checkpoint descriptors * allows for control operations without the need for * stopping the DMA channel if it is already running. */ b->offset = 0; b->dma_desc->ddadr &= ~DDADR_STOP; /* move the index to the next fragment */ if (++s->usr_frag >= s->nbfrags) s->usr_frag = 0; } if ((buffer - buffer0)) ret = buffer - buffer0; return ret; }
/* * Our DMA interrupt handler */ static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs) { audio_stream_t *s = dev_id; u_int dcsr; dcsr = DCSR(ch); DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; if (!s->buffers) { printk("AC97 DMA: wow... received IRQ for channel %d but no buffer exists\n", ch); return; } if (dcsr & DCSR_BUSERR) printk("AC97 DMA: bus error interrupt on channel %d\n", ch); if (dcsr & DCSR_ENDINTR) { u_long cur_dma_desc; u_int cur_dma_frag; /* * Find out which DMA desc is current. Note that DDADR * points to the next desc, not the current one. */ cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE; /* * Let the compiler nicely optimize constant divisors into * multiplications for the common cases which is much faster. * Common cases: x = 1 + (1 << y) for y = [0..3] */ switch (s->descs_per_frag) { case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break; case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break; case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break; case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break; default: cur_dma_frag = cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE); } /* Account for possible wrap back of cur_dma_desc above */ if (cur_dma_frag >= s->nbfrags) cur_dma_frag = s->nbfrags - 1; while (s->dma_frag != cur_dma_frag) { if (!s->mapped) { /* * This fragment is done - set the checkpoint * descriptor to STOP until it is gets * processed by the read or write function. */ s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP; up(&s->sem); } if (++s->dma_frag >= s->nbfrags) s->dma_frag = 0; /* Accounting */ s->bytecount += s->fragsize; s->fragcount++; } /* ... and for polling processes */ wake_up(&s->frag_wq); } if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE)) wake_up(&s->stop_wq); }