static int audio_release(struct inode *inode, struct file *file) { audio_state_t *state = (audio_state_t *)file->private_data; DPRINTK("audio_release\n"); down(&state->sem); if (file->f_mode & FMODE_READ) { if (state->tx_spinning) { sa1100_dma_set_spin(state->output_stream->dma_ch, 0, 0); state->tx_spinning = 0; } audio_clear_buf(state->input_stream); if (!state->skip_dma_init) { sa1100_free_dma(state->input_stream->dma_ch); if (state->need_tx_for_rx && !state->wr_ref) sa1100_free_dma(state->output_stream->dma_ch); } state->rd_ref = 0; } if (file->f_mode & FMODE_WRITE) { audio_sync(file); audio_clear_buf(state->output_stream); if (!state->skip_dma_init) if (!state->need_tx_for_rx || !state->rd_ref) sa1100_free_dma(state->output_stream->dma_ch); state->wr_ref = 0; } if (!AUDIO_ACTIVE(state)) { if (state->hw_shutdown) state->hw_shutdown(state->data); #ifdef CONFIG_PM pm_unregister(state->pm_dev); #endif } up(&state->sem); return 0; }
static int audio_release(struct inode *inode, struct file *file) { audio_state_t *state = file->private_data; down(&state->sem); if (file->f_mode & FMODE_READ) { audio_clear_buf(state->input_stream); DRCMRRXPCDR = 0; pxa_free_dma(state->input_stream->dma_ch); state->rd_ref = 0; } if (file->f_mode & FMODE_WRITE) { audio_sync(file); audio_clear_buf(state->output_stream); DRCMRTXPCDR = 0; pxa_free_dma(state->output_stream->dma_ch); state->wr_ref = 0; } up(&state->sem); return 0; }
/* * Validate and sets up buffer fragments, etc. */ static int audio_set_fragments(audio_stream_t *s, int val) { if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) return -EBUSY; if (s->buffers) audio_clear_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xffff; if (val < 5) val = 5; if (val > 15) val = 15; s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 256 * 1024) s->nbfrags = 256 * 1024 / s->fragsize; if (audio_setup_buf(s)) return -ENOMEM; return val|(s->nbfrags << 16); }
static int audio_set_fragments(audio_stream_t *s, int val) { if (s->active) return -EBUSY; if (s->buffers) audio_clear_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xffff; if (val < 4) val = 4; if (val > 15) val = 15; s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 128 * 1024) s->nbfrags = 128 * 1024 / s->fragsize; if (audio_setup_buf(s)) return -ENOMEM; return val|(s->nbfrags << 16); }
/* * This function allocates the DMA descriptor array and buffer data space * according to the current number of fragments and fragment size. */ static int audio_setup_buf(audio_stream_t * s) { pxa_dma_desc *dma_desc; dma_addr_t dma_desc_phys; int nb_desc, frag, i, buf_size = 0; char *dma_buf = NULL; dma_addr_t dma_buf_phys = 0; if (s->buffers) return -EBUSY; /* Our buffer structure array */ s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); if (!s->buffers) goto err; memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags); /* * Our DMA descriptor array: * for Each fragment we have one checkpoint descriptor plus one * descriptor per MAX_DMA_SIZE byte data blocks. */ nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags; dma_desc = consistent_alloc(GFP_KERNEL, nb_desc * DMA_DESC_SIZE, &dma_desc_phys); if (!dma_desc) goto err; s->descs_per_frag = nb_desc / s->nbfrags; s->buffers->dma_desc = dma_desc; s->dma_desc_phys = dma_desc_phys; for (i = 0; i < nb_desc - 1; i++) dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE; dma_desc[i].ddadr = dma_desc_phys; /* Our actual DMA buffers */ for (frag = 0; frag < s->nbfrags; frag++) { audio_buf_t *b = &s->buffers[frag]; /* * Let's allocate non-cached memory for DMA buffers. * We try to allocate all memory at once. * If this fails (a common reason is memory fragmentation), * then we'll try allocating smaller buffers. */ if (!buf_size) { buf_size = (s->nbfrags - frag) * s->fragsize; do { dma_buf = consistent_alloc(GFP_KERNEL, buf_size, &dma_buf_phys); if (!dma_buf) buf_size -= s->fragsize; } while (!dma_buf && buf_size); if (!dma_buf) goto err; b->master = buf_size; memzero(dma_buf, buf_size); } /* * Set up our checkpoint descriptor. Since the count * is always zero, we'll abuse the dsadr and dtadr fields * just in case this one is picked up by the hardware * while processing SOUND_DSP_GETPTR. */ dma_desc->dsadr = dma_buf_phys; dma_desc->dtadr = dma_buf_phys; dma_desc->dcmd = DCMD_ENDIRQEN; if (s->output && !s->mapped) dma_desc->ddadr |= DDADR_STOP; b->dma_desc = dma_desc++; /* set up the actual data descriptors */ for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) { dma_desc[i].dsadr = (s->output) ? (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr; dma_desc[i].dtadr = (s->output) ? s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE); dma_desc[i].dcmd = s->dcmd | ((s->fragsize < MAX_DMA_SIZE) ? s->fragsize : MAX_DMA_SIZE); } dma_desc += i; /* handle buffer pointers */ b->data = dma_buf; dma_buf += s->fragsize; dma_buf_phys += s->fragsize; buf_size -= s->fragsize; } s->usr_frag = s->dma_frag = 0; s->bytecount = 0; s->fragcount = 0; sema_init(&s->sem, (s->output) ? s->nbfrags : 0); return 0; err: printk("pxa-audio: unable to allocate audio memory\n "); audio_clear_buf(s); return -ENOMEM; }
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; }
int sa1100_audio_attach(struct inode *inode, struct file *file, audio_state_t *state) { int err, need_tx_dma; DPRINTK("audio_open\n"); down(&state->sem); /* access control */ err = -ENODEV; if ((file->f_mode & FMODE_WRITE) && !state->output_stream) goto out; if ((file->f_mode & FMODE_READ) && !state->input_stream) goto out; err = -EBUSY; if ((file->f_mode & FMODE_WRITE) && state->wr_ref) goto out; if ((file->f_mode & FMODE_READ) && state->rd_ref) goto out; err = -EINVAL; if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !state->output_stream) goto out; /* request DMA channels */ if (state->skip_dma_init) goto skip_dma; need_tx_dma = ((file->f_mode & FMODE_WRITE) || ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) need_tx_dma = 0; if (need_tx_dma) { err = sa1100_request_dma(&state->output_stream->dma_ch, state->output_id, state->output_dma); if (err) goto out; } if (file->f_mode & FMODE_READ) { err = sa1100_request_dma(&state->input_stream->dma_ch, state->input_id, state->input_dma); if (err) { if (need_tx_dma) sa1100_free_dma(state->output_stream->dma_ch); goto out; } } skip_dma: /* now complete initialisation */ if (!AUDIO_ACTIVE(state)) { if (state->hw_init) state->hw_init(state->data); #ifdef CONFIG_PM state->pm_dev = pm_register(PM_SYS_DEV, 0, audio_pm_callback); if (state->pm_dev) state->pm_dev->data = state; #endif } if ((file->f_mode & FMODE_WRITE)) { state->wr_ref = 1; audio_clear_buf(state->output_stream); state->output_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; state->output_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; state->output_stream->mapped = 0; sa1100_dma_set_callback(state->output_stream->dma_ch, audio_dmaout_done_callback); init_waitqueue_head(&state->output_stream->wq); } if (file->f_mode & FMODE_READ) { state->rd_ref = 1; audio_clear_buf(state->input_stream); state->input_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; state->input_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; state->input_stream->mapped = 0; sa1100_dma_set_callback(state->input_stream->dma_ch, audio_dmain_done_callback); init_waitqueue_head(&state->input_stream->wq); } file->private_data = state; file->f_op->release = audio_release; file->f_op->write = audio_write; file->f_op->read = audio_read; file->f_op->mmap = audio_mmap; file->f_op->poll = audio_poll; file->f_op->ioctl = audio_ioctl; file->f_op->llseek = audio_llseek; err = 0; out: up(&state->sem); return err; }
static int audio_setup_buf(audio_stream_t * s) { int frag; int dmasize = 0; char *dmabuf = NULL; dma_addr_t dmaphys = 0; if (s->buffers) return -EBUSY; s->buffers = (audio_buf_t *) kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); if (!s->buffers) goto err; memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); for (frag = 0; frag < s->nbfrags; frag++) { audio_buf_t *b = &s->buffers[frag]; /* * Let's allocate non-cached memory for DMA buffers. * We try to allocate all memory at once. * If this fails (a common reason is memory fragmentation), * then we allocate more smaller buffers. */ if (!dmasize) { dmasize = (s->nbfrags - frag) * s->fragsize; do { dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA, dmasize, &dmaphys); if (!dmabuf) dmasize -= s->fragsize; } while (!dmabuf && dmasize); if (!dmabuf) goto err; b->master = dmasize; memzero(dmabuf, dmasize); } b->start = dmabuf; b->dma_addr = dmaphys; b->stream = s; sema_init(&b->sem, 1); DPRINTK("buf %d: start %p dma %p\n", frag, b->start, b->dma_addr); dmabuf += s->fragsize; dmaphys += s->fragsize; dmasize -= s->fragsize; } s->buf_idx = 0; s->buf = &s->buffers[0]; s->bytecount = 0; s->fragcount = 0; return 0; err: printk(AUDIO_NAME ": unable to allocate audio memory\n "); audio_clear_buf(s); return -ENOMEM; }