www.pudn.com > 2410audio.rar > 2410audio.c
/* * Philips UDA1380 Audio Device Driver for S3C2410 Linux * * Copyright (C) 2002 MIZI Research, Inc. * */ #include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "khead.h" #include "2410audio.h" #define UDA1380 #define PDEBUG #undef DEBUG //#define DEBUG #ifdef DEBUG #define DPRINTK( x... ) printk( ##x ) #else #define DPRINTK( x... ) #endif /* UDA1380 Register bits */ #define UDA1380_ADDR 0x14 #define UDA1380_REG_DATA 0x0 #define UDA1380_REG_STATUS 0x2 #define SC_512fs (0x0 << 4) #define SC_384fs (0x1 << 4) #define SC_256fs (0x2 << 4) #define IF_IIS (0x0 << 1) #define IF_LSB_16 (0x1 << 1) #define IF_LSB_18 (0x2 << 1) #define IF_LSB_20 (0x3 << 1) #define IF_MSB (0x4 << 1) #define MUTE (0x1 << 2) #define NO_DE_EMPHASIS (0x10 << 3) #define DE_EMPHASIS_32 (0x11 << 3) #define DE_EMPHASIS_441 (0x12 << 3) #define DE_EMPHASIS_48 (0x13 << 3) #define GPIO_L3CLOCK (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B4) #define GPIO_L3DATA (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B3) #define GPIO_L3MODE (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B2) #define AUDIO_NAME "UDA1380" #define AUDIO_NAME_VERBOSE "UDA1380 audio driver" #define AUDIO_FMT_MASK (AFMT_S16_LE) #define AUDIO_FMT_DEFAULT (AFMT_S16_LE) #define AUDIO_CHANNELS_DEFAULT 2 #define AUDIO_RATE_DEFAULT 22050 //8000 #define AUDIO_NBFRAGS_DEFAULT 8 //16 #define AUDIO_FRAGSIZE_DEFAULT 8192 #define S_CLOCK_FREQ 384 #define PCM_ABS(a) (a < 0 ? -a : a) #define IICBUFSIZE 0x20 #define BIT_IIC (0x1<<27) #define rGPECON (*(volatile U32 *)r_GPECON) //Port E control #define rGPEUP (*(volatile U32 *)r_GPEUP) //Pull-up control E #define rIICCON (*(volatile unsigned char *)r_IICCON) //IIC control #define rIICSTAT (*(volatile unsigned char *)r_IICSTAT) //IIC status #define rIICADD (*(volatile unsigned char *)r_IICADD) //IIC address #define rIICDS (*(volatile unsigned char *)r_IICDS) //IIC data shift #define WRDATA (1) #define POLLACK (2) #define RDDATA (3) #define SETRDADDR (4) #define Uart_Printf PDEBUG extern int uda1380_init(void); #define U8 unsigned char #define U32 unsigned int static unsigned char _iicData[IICBUFSIZE]; static volatile int _iicDataCount; static volatile int _iicStatus; static volatile int _iicMode; static int _iicPt; typedef struct { int size; /* buffer size */ char *start; /* point to actual buffer */ dma_addr_t dma_addr; /* physical buffer address */ struct semaphore sem; /* down before touching the buffer */ int master; /* owner for buffer allocation, contain size when true */ } audio_buf_t; typedef struct { audio_buf_t *buffers; /* pointer to audio buffer structures */ audio_buf_t *buf; /* current buffer used by read/write */ u_int buf_idx; /* index for the pointer above */ u_int fragsize; /* fragment i.e. buffer size */ u_int nbfrags; /* nbr of fragments */ dmach_t dma_ch; /* DMA channel (channel2 for audio) */ } audio_stream_t; static audio_stream_t output_stream; static audio_stream_t input_stream; #define NEXT_BUF(_s_,_b_) { \ (_s_)->_b_##_idx++; \ (_s_)->_b_##_idx %= (_s_)->nbfrags; \ (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; } extern int uda1380_init(void); static u_int audio_rate; static int audio_channels; static int audio_fmt; static u_int audio_fragsize; static u_int audio_nbfrags; static int audio_rd_refcount; static int audio_wr_refcount; #define audio_active (audio_rd_refcount | audio_wr_refcount) static int audio_dev_dsp; static int audio_dev_mixer; static int audio_mix_modcnt; static int uda1380_volume; static u8 uda_sampling; U32 r_GPECON,r_GPEUP; U32 r_IICCON,r_IICSTAT,r_IICADD,r_IICDS; // SMDK2410 IIC configuration // GPE15=IICSDA, GPE14=IICSCL // "Interrupt mode" for IIC block //=================================================================== int address_map(void) { r_GPECON =__ioremap(0x56000040,4,0); r_GPEUP =__ioremap(0x56000048,4,0); r_IICCON =__ioremap(0x54000000,4,0); r_IICSTAT =__ioremap(0x54000004,4,0); r_IICADD =__ioremap(0x54000008,4,0); r_IICDS =__ioremap(0x5400000c,4,0); return 0; } void Delay(int times) { udelay(1000); } int Test_Iic2(void) { unsigned int i,j,save_E,save_PE; unsigned int upll, uclk, camclk,camclk_div; static U32 data; static U32 rdata; static U32 wdata; address_map(); save_E = rGPECON; save_PE = rGPEUP; rGPEUP |= 0xc000; //Pull-up disable rGPECON |= 0xa00000; //GPE15:IICSDA , GPE14:IICSCL //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16 rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); rIICADD = 0x10; //2410 slave address = [7:1] rIICSTAT = 0x10; //IIC bus data output enable(Rx/Tx) #if 1 wdata = 0xaa55; i=0x11; _Wr24C080(0x30,(U8)i,wdata); printk("the date write is: %x\n",wdata); _Rd24C080(0x31,(U8)i,&(rdata)); printk("the read data is: %x \n",rdata); #endif rGPEUP = save_PE; rGPECON = save_E; return 0; } //**************[ _Wr24C080 ]***************************************** void _Wr24C080(U32 slvAddr,U32 addr,U32 wdata) { _iicMode = WRDATA; _iicPt = 0; _iicData[0] = (U8)addr; _iicData[1] = wdata/256; _iicData[2] = wdata; _iicDataCount = 3; Uart_Printf("MSB is: %2x \n",_iicData[1]); Uart_Printf("LSB is: %2x \n",_iicData[2]); rIICDS = slvAddr&0xfe; //0x30 //Master Tx mode, Start(Write), IIC-bus data output enable //Bus arbitration sucessful, Address as slave status flag Cleared, //Address zero status flag cleared, Last received bit is 0 rIICSTAT = 0xf0; //Clearing the pending bit isn't needed because the pending bit has been cleared. while(_iicDataCount!=-1) Run_IicPoll(); _iicMode = POLLACK; while(1) { rIICDS =slvAddr&0xfe; _iicStatus = 0x100; //To check if _iicStatus is changed rIICSTAT = 0xf0; //Master Tx, Start, Output Enable, Sucessful, Cleared, Cleared, 0 rIICCON = 0xaf; //Resumes IIC operation. while(_iicStatus==0x100) Run_IicPoll(); if(!(_iicStatus & 0x1)) PDEBUG("ACK is received\n"); break; //When ACK is received } rIICSTAT = 0xd0; //Master Tx condition, Stop(Write), Output Enable rIICCON = 0xaf; //Resumes IIC operation. Delay(1); //Wait until stop condtion is in effect. //Write is completed. } //************************[ _Rd24C080 ]******************************** void _Rd24C080(U32 slvAddr,U32 addr,U32 *rdata) { PDEBUG("read---->0\n"); _iicMode = SETRDADDR; _iicPt = 0; _iicData[0] = (U8)addr; _iicDataCount = 1; PDEBUG("read---->1\n"); rIICDS = slvAddr&0xfe; //0x31 rIICSTAT = 0xf0; //MasTx,Start //Clearing the pending bit isn't needed because the pending bit has been cleared. PDEBUG("read---->2\n"); while(_iicDataCount!=-1) Run_IicPoll(); _iicMode = RDDATA; _iicPt = 0; _iicDataCount = 2; rIICDS = slvAddr|0x01; rIICSTAT = 0xb0; //Master Rx,Start rIICCON = 0xaf; //Resumes IIC operation. while(_iicDataCount!=-1) Run_IicPoll(); *rdata = _iicData[1]*256+_iicData[2]; // Uart_Printf("the read date is: %2x \n",_iicData[1]); // Uart_Printf("the read date is: %2x \n",_iicData[2]); } //**********************[ Run_IicPoll ]********************************* void Run_IicPoll(void) { if(rIICCON & 0x10) //Tx/Rx Interrupt Enable IicPoll(); } //**********************[IicPoll ]************************************** void IicPoll(void) { U32 iicSt,i; iicSt = rIICSTAT; if(iicSt & 0x8){ PDEBUG("bus arbitration is failed\n"); } //When bus arbitration is failed. if(iicSt & 0x4){ PDEBUG("matched\n"); } //When a slave address is matched with IICADD if(iicSt & 0x2){ PDEBUG("slave address 0000000b\n"); } //When a slave address is 0000000b if(iicSt & 0x1){ PDEBUG("Ack isn't received\n"); } //When ACK isn't received switch(_iicMode) { case POLLACK: PDEBUG("poll--ACK\n"); _iicStatus = iicSt; break; case RDDATA: // PDEBUG("read--ACK\n"); if((_iicDataCount--)==0) { _iicData[_iicPt++] = rIICDS; rIICSTAT = 0x90; //Stop MasRx condition rIICCON = 0xaf; //Resumes IIC operation. Delay(1); //Wait until stop condtion is in effect. //Too long time... //The pending bit will not be set after issuing stop condition. break; } _iicData[_iicPt++] = rIICDS; //The last data has to be read with no ack. if((_iicDataCount)==0) rIICCON = 0x2f; //Resumes IIC operation with NOACK. else rIICCON = 0xaf; //Resumes IIC operation with ACK break; case WRDATA: // PDEBUG("write--ACK\n"); if((_iicDataCount--)==0) { rIICSTAT = 0xd0; //stop MasTx condition rIICCON = 0xaf; //resumes IIC operation. Delay(1); //wait until stop condtion is in effect. //The pending bit will not be set after issuing stop condition. break; } PDEBUG("poll--->write\n"); rIICDS = _iicData[_iicPt++]; //_iicData[0] has dummy. for(i=0;i<10;i++); //for setup time until rising edge of IICSCL rIICCON = 0xaf; //resumes IIC operation. break; case SETRDADDR: // Uart_Printf("IicPoll() SETRADDR [S%d]\n",_iicDataCount); // PDEBUG("SETRDADDR\n"); if((_iicDataCount--)==0) { break; //IIC operation is stopped because of IICCON[4] } rIICDS = _iicData[_iicPt++]; for(i=0;i<10;i++); //for setup time until rising edge of IICSCL rIICCON = 0xaf; //resumes IIC operation. break; default: break; } } unsigned char command[] = { 0x00, 0x0F, 0x02, //Sysclock //0x00, 0x0F, 0x32, //WSPLL 44.1KHz 0x01, 0x00, 0x00, 0x02, 0xaf, 0xff, //line in 0x03, 0x00, 0x00, 0x04, 0x02, 0x02, //default 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, //0x22, 0x00, 0x00, //line-in 0x22, 0x00, 0x0C, //mic 0x23, 0x00, 0x00 }; int uda1380_init(void) { unsigned int i,j,save_E,save_PE; static U32 data; static U32 rdata; static U32 wdata; address_map(); save_E = rGPECON; save_PE = rGPEUP; rGPEUP |= 0xc000; //Pull-up disable rGPECON |= 0xa0000000; //GPE15:IICSDA , GPE14:IICSCL //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16 rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); rIICADD = 0x10; //2410 slave address = [7:1] rIICSTAT = 0x10; Test_Iic2(); //IIC bus data output enable(Rx/Tx) #if 0 wdata = 0xba65; i=0x10; _Wr24C080(0x30,(U8)i,wdata); printk("the date write is: %x\n",wdata); _Rd24C080(0x31,(U8)i,&(rdata)); printk("the read data is: %x \n",rdata); #endif { int ic; unsigned int msb, lsb, data, reg; for (ic = 0; ic < 42; ic += 3) { reg = command[ic]; msb = command[ic + 1]; lsb = command[ic + 2]; data = msb << 8 | lsb; _Wr24C080(0x30,(U8)reg,data); } printk("Init uda1380 finished.\n"); } rGPEUP = save_PE; rGPECON = save_E; return 0; } static void audio_clear_buf(audio_stream_t * s) { DPRINTK("audio_clear_buf\n"); s3c2440_dma_flush_all(s->dma_ch); if (s->buffers) { int frag; for (frag = 0; frag < s->nbfrags; frag++) { if (!s->buffers[frag].master) continue; consistent_free(s->buffers[frag].start, s->buffers[frag].master, s->buffers[frag].dma_addr); } kfree(s->buffers); s->buffers = NULL; } s->buf_idx = 0; s->buf = NULL; } static int audio_setup_buf(audio_stream_t * s) { int frag; int dmasize = 0; char *dmabuf = 0; dma_addr_t dmaphys = 0; if (s->buffers) return -EBUSY; s->nbfrags = audio_nbfrags; s->fragsize = audio_fragsize; 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]; 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; } b->start = dmabuf; b->dma_addr = dmaphys; 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]; return 0; err: printk(AUDIO_NAME ": unable to allocate audio memory\n "); audio_clear_buf(s); return -ENOMEM; } static void audio_dmaout_done_callback(void *buf_id, int size) { audio_buf_t *b = (audio_buf_t *) buf_id; DPRINTK("DMA-OUT%d\n", size); up(&b->sem); wake_up(&b->sem.wait); } static void audio_dmain_done_callback(void *buf_id, int size) { audio_buf_t *b = (audio_buf_t *) buf_id; DPRINTK("DMA-IN%d\n", size); b->size = size; up(&b->sem); wake_up(&b->sem.wait); } static int audio_sync(struct file *file) { audio_stream_t *s = &output_stream; audio_buf_t *b = s->buf; DPRINTK("audio_sync\n"); if (!s->buffers) return 0; if (b->size != 0) { down(&b->sem); s3c2440_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, b->size, DMA_BUF_WR); b->size = 0; NEXT_BUF(s, buf); } b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags); if (down_interruptible(&b->sem)) return -EINTR; up(&b->sem); return 0; } static inline int copy_from_user_mono_stereo(char *to, const char *from, int count) { u_int *dst = (u_int *)to; const char *end = from + count; if (verify_area(VERIFY_READ, from, count)) return -EFAULT; if ((int)from & 0x2) { u_int v; __get_user(v, (const u_short *)from); from += 2; *dst++ = v | (v << 16); } while (from < end-2) { u_int v, x, y; __get_user(v, (const u_int *)from); from += 4; x = v << 16; x |= x >> 16; y = v >> 16; y |= y << 16; *dst++ = x; *dst++ = y; } if (from < end) { u_int v; __get_user(v, (const u_short *)from); *dst = v | (v << 16); } return 0; } static ssize_t smdk2440_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { const char *buffer0 = buffer; audio_stream_t *s = &output_stream; int chunksize, ret = 0; DPRINTK("audio_write : start count=%d\n", count); switch (file->f_flags & O_ACCMODE) { case O_WRONLY: case O_RDWR: break; default: return -EPERM; } if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; count &= ~0x03; while (count > 0) { audio_buf_t *b = s->buf; if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } if (audio_channels == 2) { chunksize = s->fragsize - b->size; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize, s->buf_idx); if (copy_from_user(b->start + b->size, buffer, chunksize)) { up(&b->sem); return -EFAULT; } b->size += chunksize; } else { chunksize = (s->fragsize - b->size) >> 1; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize*2, s->buf_idx); if (copy_from_user_mono_stereo(b->start + b->size, buffer, chunksize)) { up(&b->sem); return -EFAULT; } b->size += chunksize*2; } buffer += chunksize; count -= chunksize; if (b->size < s->fragsize) { up(&b->sem); break; } s3c2440_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, b->size, DMA_BUF_WR); b->size = 0; NEXT_BUF(s, buf); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write : end count=%d\n\n", ret); return ret; } static ssize_t smdk2440_mixer_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_stream_t *s = &input_stream; int chunksize, ret = 0; DPRINTK("audio_read: count=%d\n", count); switch (file->f_flags & O_ACCMODE) { case O_RDONLY: case O_RDWR: break; default: return -EPERM; } if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; count &= ~0x03; while (count > 0) { audio_buf_t *b = s->buf; /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } /* Grab data from the current buffer */ chunksize = b->size; if (chunksize > count) chunksize = count;//************add******* DPRINTK("read %d from %d\n", chunksize, s->buf_idx); if (copy_to_user (buffer, b->start + s->fragsize - b->size, chunksize)) { up(&b->sem); return -EFAULT; } b->size -= chunksize; buffer += chunksize; count -= chunksize; if (b->size > 0) { up(&b->sem); break; } /* Make current buffer available for DMA again */ DPRINTK("b->dma_addr %x\n",b->dma_addr); s3c2440_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, s->fragsize, DMA_BUF_RD); NEXT_BUF(s, buf); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_read: return=%d\n", ret); return ret; } static unsigned int smdk2440_audio_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; int i; DPRINTK("audio_poll(): mode=%s\n", (file->f_mode & FMODE_WRITE) ? "w" : ""); if (file->f_mode & FMODE_WRITE) { if (!output_stream.buffers && audio_setup_buf(&output_stream)) return -ENOMEM; poll_wait(file, &output_stream.buf->sem.wait, wait); for (i = 0; i < output_stream.nbfrags; i++) { if (atomic_read(&output_stream.buffers[i].sem.count) > 0) mask |= POLLOUT | POLLWRNORM; } } DPRINTK("audio_poll() returned mask of %s\n", (mask & POLLOUT) ? "w" : ""); return mask; } static loff_t smdk2440_audio_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } static int smdk2440_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, U32 arg) { int ret; long val = 0; switch (cmd) { case SOUND_MIXER_INFO: { mixer_info info; strncpy(info.id, "UDA1380", sizeof(info.id)); strncpy(info.name,"Philips UDA1380", sizeof(info.name)); info.modify_counter = audio_mix_modcnt; return copy_to_user((void *)arg, &info, sizeof(info)); } case SOUND_OLD_MIXER_INFO: { _old_mixer_info info; strncpy(info.id, "UDA1380", sizeof(info.id)); strncpy(info.name,"Philips UDA1380", sizeof(info.name)); return copy_to_user((void *)arg, &info, sizeof(info)); } case SOUND_MIXER_READ_STEREODEVS: return put_user(0, (long *) arg); case SOUND_MIXER_READ_CAPS: val = SOUND_CAP_EXCL_INPUT; return put_user(val, (long *) arg); case SOUND_MIXER_WRITE_VOLUME: ret = get_user(val, (long *) arg); if (ret) return ret; val &= 0xFF; uda1380_volume = (val << 8) | val; _Wr24C080(0x30,0x10,(unsigned int)uda1380_volume); break; case SOUND_MIXER_READ_VOLUME: val = uda1380_volume; val &= 0xFF; return put_user(val, (long *) arg); case SOUND_MIXER_VOLUME: ret = get_user(val, (long *) arg); if (ret) return ret; _Wr24C080(0x30,0x10,(unsigned int)val); break; default: DPRINTK("mixer ioctl %u unknown\n", cmd); return -ENOSYS; } audio_mix_modcnt++; return 0; } static int iispsr_value(int s_bit_clock, int sample_rate) { int i, prescaler = 0; U32 tmpval; U32 tmpval384; U32 tmpval384min = 0xffff; tmpval384 = s3c2440_get_bus_clk(GET_PCLK) / s_bit_clock; for (i = 0; i < 32; i++) { tmpval = tmpval384/(i+1); if (PCM_ABS((sample_rate - tmpval)) < tmpval384min) { tmpval384min = PCM_ABS((sample_rate - tmpval)); prescaler = i; } } DPRINTK("prescaler = %d\n", prescaler); return prescaler; } static long audio_set_dsp_speed(long val) { printk("val = %ld\n",val); switch (val) { case 48000: case 44100: IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100)) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100))); break; case 22050: IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 22050)) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 22050))); break; case 11025: IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 11025)) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 11025))); break; case 8000: IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 8000)) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 8000))); break; default: return -1; } audio_rate = val; return audio_rate; } static int smdk2440_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) { long val; switch (cmd) { case SNDCTL_DSP_SETFMT: get_user(val, (long *) arg); if (val & AUDIO_FMT_MASK) { audio_fmt = val; break; } else return -EINVAL; case SNDCTL_DSP_CHANNELS: case SNDCTL_DSP_STEREO: get_user(val, (long *) arg); if (cmd == SNDCTL_DSP_STEREO) val = val ? 2 : 1; if (val != 1 && val != 2) return -EINVAL; audio_channels = val; break; case SOUND_PCM_READ_CHANNELS: put_user(audio_channels, (long *) arg); break; case SNDCTL_DSP_SPEED: get_user(val, (long *) arg); val = audio_set_dsp_speed(val); if (val < 0) return -EINVAL; put_user(val, (long *) arg); break; case SOUND_PCM_READ_RATE: put_user(audio_rate, (long *) arg); break; case SNDCTL_DSP_GETFMTS: put_user(AUDIO_FMT_MASK, (long *) arg); break; case SNDCTL_DSP_GETBLKSIZE: put_user(audio_fragsize, (long *) arg); break; case SNDCTL_DSP_SETFRAGMENT: if (output_stream.buffers) return -EBUSY; get_user(val, (long *) arg); audio_fragsize = 1 << (val & 0xFFFF); if (audio_fragsize < 16) audio_fragsize = 16; if (audio_fragsize > 16384) audio_fragsize = 16384; audio_nbfrags = (val >> 16) & 0x7FFF; if (audio_nbfrags < 2) audio_nbfrags = 2; if (audio_nbfrags * audio_fragsize > 128 * 1024) audio_nbfrags = 128 * 1024 / audio_fragsize; if (audio_setup_buf(&output_stream)) return -ENOMEM; break; case SNDCTL_DSP_SYNC: return audio_sync(file); case SNDCTL_DSP_GETOSPACE: { audio_stream_t *s = &output_stream; audio_buf_info *inf = (audio_buf_info *) arg; int err = verify_area(VERIFY_WRITE, inf, sizeof(*inf)); int i; int frags = 0, bytes = 0; if (err) return err; for (i = 0; i < s->nbfrags; i++) { if (atomic_read(&s->buffers[i].sem.count) > 0) { if (s->buffers[i].size == 0) frags++; bytes += s->fragsize - s->buffers[i].size; } } put_user(frags, &inf->fragments); put_user(s->nbfrags, &inf->fragstotal); put_user(s->fragsize, &inf->fragsize); put_user(bytes, &inf->bytes); break; } case SNDCTL_DSP_RESET: switch (file->f_flags & O_ACCMODE) { case O_RDONLY: case O_RDWR: } switch (file->f_flags & O_ACCMODE) { case O_WRONLY: case O_RDWR: audio_clear_buf(&output_stream); } return 0; case SNDCTL_DSP_POST: case SNDCTL_DSP_SUBDIVIDE: case SNDCTL_DSP_NONBLOCK: case SNDCTL_DSP_GETCAPS: case SNDCTL_DSP_GETTRIGGER: case SNDCTL_DSP_SETTRIGGER: case SNDCTL_DSP_GETIPTR: case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SNDCTL_DSP_SETDUPLEX: return -ENOSYS; default: return smdk2440_mixer_ioctl(inode, file, cmd, arg); } return 0; } static int smdk2440_audio_open(struct inode *inode, struct file *file) { #if 0 int cold = !audio_active; DPRINTK("audio_open\n"); if ((file->f_flags & O_ACCMODE) == O_RDONLY) { if (audio_rd_refcount || audio_wr_refcount) return -EBUSY; audio_rd_refcount++; } else if ((file->f_flags & O_ACCMODE) == O_WRONLY) { if (audio_wr_refcount) return -EBUSY; audio_wr_refcount++; } else if ((file->f_flags & O_ACCMODE) == O_RDWR) { if (audio_rd_refcount || audio_wr_refcount) return -EBUSY; audio_rd_refcount++; audio_wr_refcount++; } else return -EINVAL; if (cold) { audio_rate = AUDIO_RATE_DEFAULT; audio_channels = AUDIO_CHANNELS_DEFAULT; audio_fragsize = AUDIO_FRAGSIZE_DEFAULT; audio_nbfrags = AUDIO_NBFRAGS_DEFAULT; audio_clear_buf(&output_stream); audio_clear_buf(&input_stream); // audio_power_on(); } #endif MOD_INC_USE_COUNT; return 0; } static int smdk2440_mixer_open(struct inode *inode, struct file *file) { MOD_INC_USE_COUNT; return 0; } static int smdk2440_audio_release(struct inode *inode, struct file *file) { DPRINTK("audio_release\n"); switch (file->f_flags & O_ACCMODE) { case O_RDONLY: case O_RDWR: if (audio_rd_refcount == 1) audio_rd_refcount = 0; } switch (file->f_flags & O_ACCMODE) { case O_WRONLY: case O_RDWR: if (audio_wr_refcount == 1) { audio_sync(file); audio_clear_buf(&output_stream); audio_wr_refcount = 0; } } #if 0 if (!audio_active) audio_power_off(); #endif MOD_DEC_USE_COUNT; return 0; } static int smdk2440_mixer_release(struct inode *inode, struct file *file) { MOD_DEC_USE_COUNT; return 0; } static struct file_operations smdk2440_audio_fops = { llseek: smdk2440_audio_llseek, write: smdk2440_audio_write, poll: smdk2440_audio_poll, ioctl: smdk2440_audio_ioctl, open: smdk2440_audio_open, release: smdk2440_audio_release }; static struct file_operations smdk2440_mixer_fops = { ioctl: smdk2440_mixer_ioctl, open: smdk2440_mixer_open, read: smdk2440_mixer_read, release: smdk2440_mixer_release }; static void init_s3c2440_iis_bus(void) { IISCON = 0; IISMOD = 0; IISFIFOC = 0; /* 44 KHz , 384fs */ IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100)) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100))); IISCON = (IISCON_TX_DMA /* Transmit DMA service request */ |IISCON_RX_DMA /* Receive Channel idle */ |IISCON_PRESCALE); /* IIS Prescaler Enable */ IISMOD = (IISMOD_SEL_MA /* Master mode */ | IISMOD_SEL_TX /* Transmit */ | IISMOD_SEL_RX /* Transmit */ | IISMOD_CH_RIGHT /* Low for left channel */ | IISMOD_FMT_IIS /* MSB-justified format */ | IISMOD_BIT_16 /* Serial data bit/channel is 16 bit */ | IISMOD_FREQ_384 /* Master clock freq = 384 fs */ | IISMOD_SFREQ_32 ); /* 32 fs */ IISFIFOC = (IISFCON_TX_DMA /* Transmit FIFO access mode: DMA */ | IISFCON_TX_EN | IISFCON_RX_DMA | IISFCON_RX_EN ); /* Transmit FIFO enable */ IISCON |= IISCON_EN; /* IIS enable(start) */ } static int __init audio_init_dma(audio_stream_t * s, char *desc) { if(s->dma_ch == 1) { // printk("s->dma_ch = %d\n",s->dma_ch); return s3c2440_request_dma("I2SSDI", s->dma_ch, NULL, audio_dmain_done_callback); } else { // printk("s->dma_ch = %d\n",s->dma_ch); return s3c2440_request_dma("I2SSDO", s->dma_ch, audio_dmaout_done_callback, NULL); } } static int audio_clear_dma(audio_stream_t * s) { s3c2440_free_dma(s->dma_ch); return 0; } int init_module1380(void) { U32 flags; int cold = !audio_active; local_irq_save(flags); /* GPE 3: I2SSDI */ set_gpio_ctrl(GPIO_E3 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); /* GPE 0: I2SLRCK */ set_gpio_ctrl(GPIO_E0 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); /* GPE 1: I2SSCLK */ set_gpio_ctrl(GPIO_E1 | GPIO_PULLUP_EN | GPIO_MODE_I2SSCLK); /* GPE 2: CDCLK */ set_gpio_ctrl(GPIO_E2 | GPIO_PULLUP_EN | GPIO_MODE_CDCLK); /* GPE 4: I2SSDO */ set_gpio_ctrl(GPIO_E4 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDO); local_irq_restore(flags); uda1380_init(); init_s3c2440_iis_bus(); output_stream.dma_ch = DMA_CH2; input_stream.dma_ch = DMA_CH1; if (audio_init_dma(&output_stream, "UDA1380 out")) { audio_clear_dma(&output_stream); printk( KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels\n" ); return -EBUSY; } if (audio_init_dma(&input_stream, "UDA1380 in")) { audio_clear_dma(&input_stream); printk( KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels\n" ); return -EBUSY; } audio_dev_dsp = register_sound_dsp(&smdk2440_audio_fops, -1); audio_dev_mixer = register_sound_mixer(&smdk2440_mixer_fops, -1); printk(AUDIO_NAME_VERBOSE " initialized\n"); if (cold) { audio_rate = AUDIO_RATE_DEFAULT; audio_channels = AUDIO_CHANNELS_DEFAULT; audio_fragsize = AUDIO_FRAGSIZE_DEFAULT; audio_nbfrags = AUDIO_NBFRAGS_DEFAULT; audio_clear_buf(&output_stream); audio_clear_buf(&input_stream); } return 0; } void cleanup_module(void) { unregister_sound_dsp(audio_dev_dsp); unregister_sound_mixer(audio_dev_mixer); audio_clear_dma(&input_stream); audio_clear_dma(&output_stream); printk(AUDIO_NAME_VERBOSE " unloaded\n"); } module_init(init_module1380); module_exit(cleanup_module);