123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284 |
- /*
- * Common audio handling for the SA11x0 processor
- *
- * Copyright (C) 2007, Ryu Euiyoul <ryu.real@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- *
- * This module handles the generic buffering/DMA/mmap audio interface for
- * codecs connected to the SA1100 chip. All features depending on specific
- * hardware implementations like supported audio formats or samplerates are
- * relegated to separate specific modules.
- *
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <linux/device.h>
- #include <linux/errno.h>
- #include <linux/sound.h>
- #include <linux/soundcard.h>
- #include <linux/sysrq.h>
- #include <linux/device.h>
- #include <linux/dma-mapping.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
- #include <asm/delay.h>
- #include <asm/hardware.h>
- #include <asm/semaphore.h>
- #include <asm/dma.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/regs-iis.h>
- #include <asm/arch/regs-ac97.h>
- #include "s3c6400_pcm.h"
- #undef DEBUG
- //#define DEBUG
- #ifdef DEBUG
- #define DPRINTK printk
- #else
- #define DPRINTK( x... )
- #endif
- #define AUDIO_NAME "s3c-audio"
- #define AUDIO_NBFRAGS_DEFAULT 8
- #define AUDIO_FRAGSIZE_DEFAULT 8192
- #define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
- #define SPIN_ADDR (dma_addr_t)FLUSH_BASE_PHYS
- #define SPIN_SIZE 2048
- static int write_count = 0;
- void audio_dma_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
- int size, enum s3c2410_dma_buffresult result);
- #ifdef CONFIG_SOUND_S3C6400_AC97
- /* DMA operation
- * Control AC97 global control register to set DMA operation
- * If the DMA transfer is endded you should turn off AC97 DMA
- * otherwise the noise will be heard. -JaeCheol Lee (2007.03.05)
- */
- static int audio_dma_operation(struct s3c2410_dma_chan *subchan, enum s3c2410_chan_op op);
- #endif
- /*
- * DMA processing
- */
- static struct s3c2410_dma_client s3cplay_dma_client = {
- .name = "s3c-play",
- };
- #if defined (CONFIG_AC97_MIC_PATH) || defined (CONFIG_SOUND_S3C6400_I2S)
- static struct s3c2410_dma_client s3cmic_dma_client = {
- .name = "s3c-recmic",
- };
- #endif
- #ifdef CONFIG_SOUND_S3C6400_AC97
- static struct s3c2410_dma_client s3cline_dma_client = {
- .name = "s3c-recline",
- };
- #endif
- #ifdef CONFIG_SOUND_S3C6400_AC97
- static int s3c_ac97_dma_init(audio_stream_t * s, int mode)
- {
- if (mode == 0) { //play
- s3c2410_dma_devconfig(s->subchannel, S3C2410_DMASRC_MEM, 0, AC_PCMDATA_PHYS);
- s3c2410_dma_set_opfn(s->subchannel, audio_dma_operation);
- }
- /* attach capture channel */
- if (mode == 1) {
- s3c2410_dma_devconfig(s->subchannel, S3C2410_DMASRC_HW, 0, AC_PCMDATA_PHYS);
- }
- if (mode == 2) {
- s3c2410_dma_devconfig(s->subchannel, S3C2410_DMASRC_HW, 0, AC_MICDATA_PHYS);
- }
- s3c2410_dma_config(s->subchannel, 4, 4);
- s3c2410_dma_set_buffdone_fn(s->subchannel, audio_dma_callback);
- s3c2410_dma_setflags(s->subchannel, S3C2410_DMAF_AUTOSTART);
- return 0;
- }
- #endif
- #ifdef CONFIG_SOUND_S3C6400_I2S
- static int s3c_iis_dma_init(audio_stream_t *s,int mode)
- {
- if(mode == 0) //play
- {
- s3c2410_dma_devconfig(s->subchannel, S3C2410_DMASRC_MEM, 0, S3C_IIS0TXD_PHYS);
- }
- /* attach capture channel */
- if(mode ==1)
- {
- s3c2410_dma_devconfig(s->subchannel, S3C2410_DMASRC_HW, 0, S3C_IIS0RXD_PHYS);
- }
- s3c2410_dma_config(s->subchannel, 4, 0);
- s3c2410_dma_set_buffdone_fn(s->subchannel, audio_dma_callback);
- s3c2410_dma_setflags(s->subchannel, S3C2410_DMAF_AUTOSTART);
- return 0;
- }
- #endif
- static u_int audio_get_dma_pos(audio_stream_t *s)
- {
- int dma_srcpos,dma_dstpos;
- audio_buf_t *b = &s->buffers[s->dma_tail];
- u_int offset;
- if (b->dma_ref) {
- s3c2410_dma_getposition(s->subchannel, &dma_srcpos, &dma_dstpos);
- offset = dma_srcpos - b->dma_addr;
- if (offset >= s->fragsize)
- offset = s->fragsize - 4;
- } else if (s->pending_frags) {
- offset = b->offset;
- } else {
- offset = 0;
- }
- return offset;
- }
- static void audio_stop_dma(audio_stream_t *s)
- {
- u_int pos;
- unsigned long flags;
- audio_buf_t *b;
- if (!s->buffers)
- return;
- local_irq_save(flags);
- s->stopped = 1;
- pos = audio_get_dma_pos(s);
- s3c2410_dma_ctrl(s->subchannel, S3C2410_DMAOP_FLUSH);
- local_irq_restore(flags);
- /* back up pointers to be ready to restart from the same spot */
- while (s->dma_head != s->dma_tail) {
- b = &s->buffers[s->dma_head];
- if (b->dma_ref) {
- b->dma_ref = 0;
- b->offset = 0;
- }
- s->pending_frags++;
- if (s->dma_head == 0)
- s->dma_head = s->nbfrags;
- s->dma_head--;
- }
- b = &s->buffers[s->dma_head];
- if (b->dma_ref) {
- b->offset = pos;
- b->dma_ref = 0;
- }
- }
- static void audio_reset(audio_stream_t *s)
- {
- if (s->buffers) {
- audio_stop_dma(s);
- s->buffers[s->dma_head].offset = 0;
- s->buffers[s->usr_head].offset = 0;
- s->usr_head = s->dma_head;
- s->pending_frags = 0;
- sema_init(&s->sem, s->nbfrags);
- }
- s->active = 0;
- s->stopped = 0;
- }
- static void audio_process_dma(audio_stream_t *s)
- {
- while (s->pending_frags) {
- audio_buf_t *b = &s->buffers[s->dma_head];
- u_int dma_size = s->fragsize - b->offset;
- s3c2410_dma_enqueue(s->subchannel, (void *) s,
- b->dma_addr+b->offset,dma_size);
- b->dma_ref++;
- b->offset += dma_size;
- if (b->offset >= s->fragsize) {
- s->pending_frags--;
- if (++s->dma_head >= s->nbfrags)
- s->dma_head = 0;
-
- }
- }
- }
- 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);
- }
- #ifdef CONFIG_SOUND_S3C6400_AC97
- /* AC97 DMA operation
- * Control AC97 global control register to set DMA operation
- * If the DMA transfer is endded you should turn off AC97 DMA
- * otherwise the noise will be heard. -JaeCheol Lee (2007.03.05)
- */
- static int audio_dma_operation(struct s3c2410_dma_chan *subchan, enum s3c2410_chan_op op)
- {
- switch (op) {
- case S3C2410_DMAOP_START:
- __raw_writel(__raw_readl(AC_GLBCTRL) | (0x2 << 12), AC_GLBCTRL);
- break;
- case S3C2410_DMAOP_STOP:
- __raw_writel(__raw_readl(AC_GLBCTRL) & ~(0x3 << 12), AC_GLBCTRL);
- break;
- default:
- break;
- }
- return 0;
- }
- #endif
- static int copy_to_user_stereo_mono(char *to, char *from, int count)
- {
- int ind, fact;
- if(!count)
- return 0;
- if(from[6] && from[7] && !from[5] && !from[4]) //check for the nonzero channel
- fact = 0;
- else
- fact = 1;
- ind = 0;
- while(ind < count){
- if(ind%2){
- *(from + ind) = *(from + 2*ind +1);
- *(from + ind) = *(from + 2*ind -1);
- }else{
- *(from + ind) = *(from + 2*ind +2);
- *(from + ind) = *(from + 2*ind -0);
- }
-
- ind++;
- }
- return copy_to_user(to, from, count);
- }
- static int copy_from_user_mono_stereo(char *to, const char *from, int count)
- {
- int ind;
- if(copy_from_user(to, from, count))
- return -EFAULT;
- ind = count - 1;
- while(ind){
- if(ind%2){
- *(to + 2*ind + 1) = *(to + ind);
- *(to + 2*ind - 1) = *(to + ind); // R;
- }else{
- *(to + 2*ind + 2) = *(to + ind);
- *(to + 2*ind + 0) = *(to + ind); // R;
- }
- ind--;
- }
- return 0;
- }
- /*
- * Buffer creation/destruction
- */
- static void audio_discard_buf(audio_stream_t * s)
- {
- DPRINTK("audio_discard_buf\n");
- /* ensure DMA isn't using those buffers */
- audio_reset(s);
- if (s->buffers) {
- int frag;
- for (frag = 0; frag < s->nbfrags; frag++) {
- if (!s->buffers[frag].master)
- continue;
- dma_free_coherent(s->dev,
- s->buffers[frag].master,
- s->buffers[frag].data, s->buffers[frag].dma_addr);
- }
- kfree(s->buffers);
- s->buffers = NULL;
- }
- }
- 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 = 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 = dma_alloc_coherent(s->dev, dmasize, &dmaphys, GFP_KERNEL);
- if (!dmabuf)
- dmasize -= s->fragsize;
- } while (!dmabuf && dmasize);
- if (!dmabuf)
- goto err;
- b->master = dmasize;
- memzero(dmabuf, dmasize);
- }
- b->data = dmabuf;
- b->dma_addr = dmaphys;
- DPRINTK("buf %d: start %p dma %#08x\n", frag, b->data, b->dma_addr);
- dmabuf += s->fragsize;
- dmaphys += s->fragsize;
- dmasize -= s->fragsize;
- }
- s->usr_head = s->dma_head = s->dma_tail = 0;
- s->bytecount = 0;
- s->fragcount = 0;
- sema_init(&s->sem, s->nbfrags);
- return 0;
- err:
- printk(AUDIO_NAME ": unable to allocate audio memory\n ");
- audio_discard_buf(s);
- return -ENOMEM;
- }
- /*
- * 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;
- write_count++;
- audio_process_dma(s);
- local_irq_restore(flags);
- }
- if ((buffer - buffer0))
- ret = buffer - buffer0;
- DPRINTK("audio_write: return=%d\n", ret);
- return ret;
- }
- 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 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 (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 */
- if(state->sound_mode == MONO) {
- chunksize = (s->fragsize - b->offset)/2;
- if(chunksize > count)
- chunksize = count/2;
- if(copy_to_user_stereo_mono(buffer, b->data + b->offset, 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("read %d from %d\n", chunksize, s->usr_head);
- 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;
- }
- /* 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;
- }
- 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;
- 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 && s->dma_tail != s->usr_head && !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;
- /* 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_mmap(struct file *file, struct vm_area_struct *vma)
- {
- audio_state_t *state = file->private_data;
- audio_stream_t *s;
- unsigned long size, vma_addr;
- int i, ret;
- if (vma->vm_pgoff != 0)
- return -EINVAL;
- if (vma->vm_flags & VM_WRITE) {
- if (!state->wr_ref)
- return -EINVAL;;
- s = state->output_stream;
- } else if (vma->vm_flags & VM_READ) {
- if (!state->rd_ref)
- return -EINVAL;
- s = state->input_stream;
- } else
- return -EINVAL;
- if (s->mapped)
- return -EINVAL;
- size = vma->vm_end - vma->vm_start;
- if (size != s->fragsize * s->nbfrags)
- return -EINVAL;
- if (!s->buffers && audio_setup_buf(s))
- return -ENOMEM;
- vma_addr = vma->vm_start;
- for (i = 0; i < s->nbfrags; i++) {
- audio_buf_t *buf = &s->buffers[i];
- if (!buf->master)
- continue;
- ret = remap_pfn_range(vma, vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot);
- if (ret)
- return ret;
- vma_addr += buf->master;
- }
- s->mapped = 1;
- return 0;
- }
- 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;
- DPRINTK("audio_poll(): mode=%s%s\n",
- (file->f_mode & FMODE_READ) ? "r" : "", (file->f_mode & FMODE_WRITE) ? "w" : "");
- if (file->f_mode & FMODE_READ) {
- /* Start audio input if not already active */
- if (!is->active) {
- if (!is->buffers && audio_setup_buf(is))
- return -ENOMEM;
- audio_prime_rx(state);
- }
- poll_wait(file, &is->wq, wait);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (!os->buffers && audio_setup_buf(os))
- return -ENOMEM;
- poll_wait(file, &os->wq, wait);
- }
- if (file->f_mode & FMODE_READ)
- if (( is->mapped && is->bytecount > 0))
- mask |= POLLIN | POLLRDNORM;
- if (file->f_mode & FMODE_WRITE)
- if (( os->mapped && os->bytecount > 0))
- mask |= POLLOUT | POLLWRNORM;
- DPRINTK("audio_poll() returned mask of %s%s\n",
- (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : "");
- return mask;
- }
- static loff_t audio_llseek(struct file *file, loff_t offset, int origin)
- {
- return -ESPIPE;
- }
- static int audio_set_fragments(audio_stream_t *s, int val)
- {
- if (s->active)
- return -EBUSY;
- if (s->buffers)
- audio_discard_buf(s);
- s->nbfrags = (val >> 16) & 0x7FFF;
- val &= 0xffff;
- if (val < 4)
- val = 4;
- if (val > 13)
- val = 13;
- 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);
- }
- 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;
- audio_buf_info inf = { 0, };
- long val;
- /* 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:
- {
- audio_stream_t *s = os;
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if (!s->buffers && audio_setup_buf(s))
- return -ENOMEM;
- inf.bytes = s->fragsize * s->free_bufnum;
- inf.fragments = s->free_bufnum;
- inf.fragsize = s->fragsize;
- inf.fragstotal = s->nbfrags;
- return copy_to_user((void *) arg, &inf, sizeof(inf));
- }
- case SNDCTL_DSP_GETISPACE:
- {
- audio_stream_t *s = is;
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if (!s->buffers && audio_setup_buf(s))
- return -ENOMEM;
- inf.bytes = s->fragsize * s->free_bufnum;
- inf.fragments = s->free_bufnum;
- 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 (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_release(struct inode *inode, struct file *file)
- {
- #ifdef CONFIG_SOUND_S3C6400_AC97
- int i;
- #endif
- audio_state_t *state = file->private_data;
- audio_stream_t *os = state->output_stream;
- audio_stream_t *is = state->input_stream;
- DPRINTK("audio_release\n");
- down(&state->sem);
- if (file->f_mode & FMODE_READ) {
- audio_discard_buf(is);
- #if defined (CONFIG_AC97_MIC_PATH) || defined (CONFIG_SOUND_S3C6400_I2S)
- s3c2410_dma_free(is->subchannel, &s3cmic_dma_client);
- #else
- s3c2410_dma_free(is->subchannel, &s3cline_dma_client);
- #endif
- state->rd_ref = 0;
- }
- if (file->f_mode & FMODE_WRITE) {
- audio_sync(file);
- audio_discard_buf(os);
- s3c2410_dma_free( os->subchannel, &s3cplay_dma_client);
- state->wr_ref = 0;
- }
- if (!AUDIO_ACTIVE(state)) {
- if (state->hw_shutdown)
- state->hw_shutdown(state->data);
- }
- #ifdef CONFIG_SOUND_S3C6400_AC97
- for (i = 0; i < 64; i++)
- writel(0, AC_PCMDATA);
- #endif
- up(&state->sem);
- return 0;
- }
- #ifdef CONFIG_PM
- int s3c64xx_audio_suspend(audio_state_t * s, u32 state, u32 level)
- {
- #ifdef CONFIG_SOUND_S3C6400_I2S
- if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) {
- audio_stream_t *is = s->input_stream;
- audio_stream_t *os = s->output_stream;
- int stopstate;
- if (is) {
- stopstate = is->stopped;
- audio_stop_dma(is);
- s3c_dma_ctrl(is->dma, is->subchannel, S3C_DMAOP_FLUSH);
- is->stopped = stopstate;
- }
- if (os) {
- stopstate = os->stopped;
- audio_stop_dma(os);
- s3c_dma_ctrl(os->dma, os->subchannel, S3C_DMAOP_FLUSH);
- os->stopped = stopstate;
- }
- if (AUDIO_ACTIVE(s) && s->hw_shutdown)
- s->hw_shutdown(s->data);
- }
- #endif
- #ifdef CONFIG_SOUND_S3C6400_AC97
- s3c64xx_ac97_suspend();
- #endif
- return 0;
- }
- int s3c64xx_audio_resume(audio_state_t * s, u32 level)
- {
- printk("%s\n", __FUNCTION__);
- #if 0
- if (level == RESUME_ENABLE) {
- 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
- #ifdef CONFIG_SOUND_S3C6400_AC97
- s3c64xx_ac97_resume();
- #endif
- return 0;
- }
- EXPORT_SYMBOL(s3c64xx_audio_suspend);
- EXPORT_SYMBOL(s3c64xx_audio_resume);
- #else
- #define s3c64xx_audio_suspend() NULL
- #define s3c64xx_audio_resume() NULL
- #endif
- /* structure file_operations changed to const.
- */
- static const struct file_operations s3c_f_ops =
- {
- .release = audio_release,
- .write = audio_write,
- .read = audio_read,
- .mmap = audio_mmap,
- .poll = audio_poll,
- .ioctl = audio_ioctl,
- .llseek = audio_llseek
- };
- #ifdef CONFIG_SOUND_S3C6400_AC97
- extern void codec_reset_settings(int mode);
- int s3c_audio_attach(struct inode *inode, struct file *file, audio_state_t * state)
- {
- audio_stream_t *os = state->output_stream;
- audio_stream_t *is_line = state->input_stream_line;
- audio_stream_t *is_mic = state->input_stream_mic;
- int err, need_tx_dma = 0;
- /*make mic default rec source*/
- state->input_stream = state->input_stream_line;
- DPRINTK("audio_open\n");
- down(&state->sem);
- /* access control */
- err = -ENODEV;
- if ((file->f_mode & FMODE_WRITE) && !os)
- goto out;
- if ((file->f_mode & FMODE_READ) && !is_mic)
- 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 && !os)
- goto out;
- if(file->f_mode & FMODE_READ)
- file->f_mode = FMODE_READ;
- /* request DMA channels */
- if (file->f_mode & FMODE_WRITE) {
- if (s3c2410_dma_request(os->subchannel, &s3cplay_dma_client, NULL)) {
- printk(KERN_WARNING "unable to get DMA channel.\n");
- err = -EBUSY;
- goto out;
- }
- need_tx_dma = 1; //we claimed the play channel
- err = s3c_ac97_dma_init(os, 0);
- }
- if (file->f_mode & FMODE_READ) {
- #ifdef CONFIG_AC97_MIC_PATH
- if (s3c2410_dma_request(is_mic->subchannel, &s3cmic_dma_client, NULL)) {
- printk(KERN_WARNING "unable to get %d DMA %d channel.\n", is_mic->dma,
- is_mic->subchannel);
- err = -EBUSY;
- if (need_tx_dma)
- s3c2410_dma_free(os->subchannel, &s3cplay_dma_client);
- s3c2410_dma_free(is_mic->subchannel, &s3cmic_dma_client);
- goto out;
- }
- if (s3c_ac97_dma_init(is_mic, 2))
- goto out;
- #else
- if (s3c2410_dma_request(is_line->subchannel, &s3cline_dma_client, NULL)) {
- printk(KERN_WARNING "unable to get %d DMA %d channel.\n", is_line->dma,
- is_line->subchannel);
- err = -EBUSY;
- if (need_tx_dma)
- s3c2410_dma_free(os->subchannel, &s3cplay_dma_client);
- s3c2410_dma_free(is_line->subchannel, &s3cline_dma_client);
- goto out;
- }
- if (s3c_ac97_dma_init(is_line, 1))
- goto out;
- #endif
- }
- /* now complete initialisation */
- if (!AUDIO_ACTIVE(state)) {
- if (state->hw_init)
- state->hw_init(state->data);
- }
- if ((file->f_mode & FMODE_WRITE)) {
- state->wr_ref = 1;
- audio_reset(os);
- os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
- os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
- os->free_bufnum = AUDIO_NBFRAGS_DEFAULT;
- os->mapped = 0;
- init_waitqueue_head(&os->wq);
- codec_reset_settings(MODE_PLAY);
- }
- /*only mic or line in will be used at a time ..this can be modified*/
- if (file->f_mode & FMODE_READ) {
- state->rd_ref = 1;
- #ifdef CONFIG_AC97_MIC_PATH
- audio_reset(is_mic);
- #else
- audio_reset(is_line);
- #endif
- is_line->fragsize = is_mic->fragsize = AUDIO_FRAGSIZE_DEFAULT;
- is_line->nbfrags = is_mic->nbfrags = AUDIO_NBFRAGS_DEFAULT;
- is_line->free_bufnum = is_mic->free_bufnum = AUDIO_NBFRAGS_DEFAULT;
- is_line->mapped = is_mic->mapped = 0;
- #ifdef CONFIG_AC97_MIC_PATH
- init_waitqueue_head(&is_mic->wq);
- #else
- init_waitqueue_head(&is_line->wq);
- #endif
- codec_reset_settings(MODE_REC);
- }
- file->private_data = state;
- file->f_op = &s3c_f_ops;
- err = 0;
- out:
- up(&state->sem);
- return err;
- }
- #endif
- #ifdef CONFIG_SOUND_S3C6400_I2S
- int s3c_audio_attach(struct inode *inode, struct file *file,
- audio_state_t *state)
- {
- audio_stream_t *os = state->output_stream;
- audio_stream_t *is = state->input_stream;
- int err, need_tx_dma=0;
- DPRINTK("audio_open\n");
- down(&state->sem);
- /* access control */
- err = -ENODEV;
- if ((file->f_mode & FMODE_WRITE) && !os)
- goto out;
- if ((file->f_mode & FMODE_READ) && !is)
- 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 && !os)
- goto out;
- if(file->f_mode & FMODE_READ)
- file->f_mode = FMODE_READ;
- DPRINTK("%s, f_mode = 0x%x\n", __FUNCTION__, file->f_mode);
- /* request DMA channels */
- if (file->f_mode & FMODE_WRITE) {
- if(s3c2410_dma_request(os->subchannel, &s3cplay_dma_client, NULL)) {
- printk(KERN_WARNING "unable to get DMA channel.\n" );
- err = -EBUSY;
- goto out;
- }
- need_tx_dma = 1;
- err = s3c_iis_dma_init(os,0);
- if(err)
- goto out;
-
- err = wm8753_set_hpout_path();
- if(err)
- goto out;
- }
- if (file->f_mode & FMODE_READ) {
- if(s3c2410_dma_request(is->subchannel, &s3cmic_dma_client, NULL)) {
- printk(KERN_WARNING "unable to get DMA channel.\n" );
- err = -EBUSY;
- if (need_tx_dma)
- s3c2410_dma_free(os->subchannel, &s3cplay_dma_client);
- goto out;
- }
- err = s3c_iis_dma_init(is,1);
- if(err)
- goto out;
- #ifdef CONFIG_MIC_PATH
- err = wm8753_set_mic1_path();
- #else
- err = wm8753_set_linein_path();
- #endif
- if(err)
- goto out;
- }
- if ((file->f_mode & FMODE_WRITE)) {
- state->wr_ref = 1;
- audio_reset(os);
- os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
- os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
- os->free_bufnum = AUDIO_NBFRAGS_DEFAULT;
- os->mapped = 0;
- init_waitqueue_head(&os->wq);
- }
- if (file->f_mode & FMODE_READ) {
- state->rd_ref = 1;
- audio_reset(is);
- is->fragsize = AUDIO_FRAGSIZE_DEFAULT;
- is->nbfrags = AUDIO_NBFRAGS_DEFAULT;
- is->free_bufnum = AUDIO_NBFRAGS_DEFAULT;
- is->mapped = 0;
- init_waitqueue_head(&is->wq);
- }
- file->private_data = state;
- file->f_op = &s3c_f_ops;
- err = 0;
- out:
- up(&state->sem);
- return err;
- }
- #endif
- EXPORT_SYMBOL(s3c_audio_attach);
- MODULE_AUTHOR("Ryu Euiyoul");
- MODULE_DESCRIPTION("Common audio handling for the Samsung AP");
- MODULE_LICENSE("GPL");
|