📄 at91_audio.c
字号:
/*----------------------------------------------------------** linux/drivers/at91/at91_audio.c** **** Copyright (C) 2006 Hyesco Technology Co.,Ltd**** Author: casiawu <wujh@hyesco.com>**** History:**** 2006.4 casiawu <wujh@hyesco.com>** Original version**---------------------------------------------------------*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/ioport.h>//#include <sound_config.h>//#include <dev_table.h>#include <linux/soundcard.h>#include <linux/sound.h>#include <linux/interrupt.h>#include <asm/arch/AT91RM9200_SSC.h>#include <asm/arch/board.h>#include "at91_audio.h"#include "i2c.h"#define I2S_ASY_MASTER_TX_SETTING(nb_bit_by_slot, nb_slot_by_frame)( +\AT91C_SSC_CKS_DIV +\AT91C_SSC_CKO_CONTINOUS +\AT91C_SSC_START_FALL_RF +\((1<<16) & AT91C_SSC_STTDLY) +\((((nb_bit_by_slot*nb_slot_by_frame)/2)-1) <<24))#define I2S_ASY_TX_FRAME_SETTING(nb_bit_by_slot, nb_slot_by_frame)( +\(nb_bit_by_slot-1) +\AT91C_SSC_MSBF +\(((nb_slot_by_frame-1)<<8) & AT91C_SSC_DATNB) +\(((nb_bit_by_slot-1)<<16) & AT91C_SSC_FSLEN) +\AT91C_SSC_FSOS_NEGATIVE)#define NEXT_BUF(_s_,_b_) { \ (_s_)->_b_##_idx++; \ (_s_)->_b_##_idx %= (_s_)->nbfrags; \ (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; } #define AUDIO_MODULE_NAME "AT91_AUDIO" /* global definitions */#define AUDIO_NBFRAGS_DEFAULT 8#define AUDIO_FRAGSIZE_DEFAULT (8*1024) #define SLOT_BY_FRAME 2#define BITS_BY_SLOT 16static int audio_dev_dsp; static unsigned int a9200_stereo;static unsigned int a9200_bits;AT91PS_SSC ssc_regs;AT91PS_PDC pdc_regs;unsigned char uda1380_rx_init[] = { 0x00, 0x0f, 0x3f, 0x01, 0x00, 0x88, 0x02, 0xaf, 0xff, 0x03, 0x00, 0x00, 0x04, 0x02, 0x02, 0x10, 0x00, 0x00, 0x11, 0xb0, 0xb0, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x08, 0x08, 0x22, 0x16, 0x0C, 0x23, 0x00, 0x00}; /*----------------------------------------------------- * Name : codec_init() * Function: Init the code chip * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/ void codec_init(unsigned char *command){ int ic; unsigned int msb, lsb, data, reg; DPRINTK("Testing I2C\n"); for (ic = 0; ic < 42; ic += 3) { reg = command[ic]; lsb = command[ic + 1]; msb = command[ic + 2]; data = msb << 8 | lsb; I2C_write(reg, data, 2); }} /*----------------------------------------------------- * Name : AT91F_SSC_SetBaudrate() * Function: Set the baudrate according to the CPU clock * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/__inline void AT91F_SSC_SetBaudrate ( AT91PS_SSC pSSC, // \arg pointer to a SSC controller unsigned int mainClock, // \arg peripheral clock unsigned int speed) // \arg SSC baudrate{ unsigned int baud_value; //* Define the baud rate divisor register if (speed == 0) baud_value = 0; else { baud_value = (unsigned int) (mainClock * 10)/(2*speed); if ((baud_value % 10) >= 5) baud_value = (baud_value / 10) + 1; else baud_value /= 10; } pSSC->SSC_CMR = baud_value;} /*----------------------------------------------------- * Name : SSC_init() * Function: Init the PIO and SSC REG * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/ void SSC_init(void){ DPRINTK("Init SSC...\n"); //init the global variables a9200_bits = 16; a9200_stereo = 1; ssc_regs = (AT91PS_SSC) AT91C_VA_BASE_SSC1; pdc_regs = (AT91PS_PDC) AT91C_VA_BASE_PDC_SSC1; // Configure SSC1 PIOs TF/TK/TD AT91_SYS->PIOB_PDR = AT91C_PB6_TF1 | AT91C_PB7_TK1 | AT91C_PB8_TD1 | AT91C_PB9_RD1 | AT91C_PB10_RK1; AT91_SYS->PIOB_ASR = AT91C_PB6_TF1 | AT91C_PB7_TK1 | AT91C_PB8_TD1 | AT91C_PB9_RD1 | AT91C_PB10_RK1; AT91_SYS->PMC_PCER = 1 << AT91C_ID_SSC1;/* Peripheral Clock Enable */ /*soft reset */ ssc_regs->SSC_CR = AT91C_SSC_SWRST; AT91F_SSC_SetBaudrate(ssc_regs, at91_master_clock,FILE_SAMPLING_FREQ *(BITS_BY_SLOT * SLOT_BY_FRAME)); ssc_regs->SSC_IDR = 0xCFF; //disabled all /* SSC Transmit Clock Mode Register */ ssc_regs->SSC_TCMR = I2S_ASY_MASTER_TX_SETTING(BITS_BY_SLOT,SLOT_BY_FRAME); /* SSC Transmit Frame Mode Register */ ssc_regs->SSC_TFMR = I2S_ASY_TX_FRAME_SETTING(BITS_BY_SLOT,SLOT_BY_FRAME); /* SSC_RFMR */ ssc_regs->SSC_RFMR = I2S_ASY_TX_FRAME_SETTING(BITS_BY_SLOT,SLOT_BY_FRAME); /* SSC_RCMR */ ssc_regs->SSC_RCMR = I2S_ASY_MASTER_TX_SETTING(BITS_BY_SLOT,SLOT_BY_FRAME); ssc_regs->SSC_CR |= (AT91C_SSC_RXDIS | AT91C_SSC_TXEN) ; // ssc_regs->SSC_IER |= (AT91C_SSC_ENDTX | AT91C_SSC_TXBUFE | AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF); pdc_regs->PDC_PTCR |= (AT91C_PDC_RXTDIS | AT91C_PDC_TXTEN ); }/*----------------------------------------------------- * Name : at91_audio_open() * Function: Open aduio dev * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/ int at91_audio_open(struct inode * inode, struct file * filp){ // MOD_INC_USE_COUNT; return 0;}/*----------------------------------------------------- * Name : at91_audio_release() * Function: Release audio dev * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/int at91_audio_release(struct inode * inode,struct file * filp){ // MOD_DEC_USE_COUNT; return 0;} /*----------------------------------------------------- * Name : at91_audio_ioctl() * Function: Audio IO Control * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/ int at91_audio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ return 0;} /*----------------------------------------------------- * Name : at91_audio_clear_buf() * Function: frees all buffers * Last rework data: 06-04-14 wujh@hyesco.com *----------------------------------------------------*/static void at91_audio_clear_buf(audio_stream_t * s){ DPRINTK("aduio_clear_buf\n"); s->active = 0; at91rm9200_dma_flush_all(s->dma); if (s->buffers) { int frag; for (frag = 0; frag < s->nbfrags; frag++) { if (!s->buffers[frag].master) continue; dma_free_coherent(NULL, s->buffers[frag].master, s->buffers[frag].start, s->buffers[frag].dma_addr); } kfree(s->buffers); s->buffers = NULL; } s->buf_idx = 0; s->buf = NULL;}/*----------------------------------------------------- * Name : at91_audio_setup_buf() * Function: This function allocates the buffer structure array * and buffer data space according to the current number of * fragments and fragment size. * Last rework data: 06-04-14 wujh@hyesco.com *----------------------------------------------------*/static int at91_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 = dma_alloc_coherent(NULL, dmasize, &dmaphys, GFP_KERNEL); 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]; return 0;err: printk(" unable to allocate audio memory\n "); at91_audio_clear_buf(s); return -ENOMEM;}/*----------------------------------------------------- * Name : at91_audio_prime_dma() * Function: Send data buffer to dma queue * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/static void at91_audio_prime_dma(audio_stream_t *s){ int i; s->active = 1; for (i = 0; i < s->nbfrags; i++) { audio_buf_t *b = s->buf; down(&b->sem); at91rm9200_dma_queue_buffer(s->dma, (void *) b, b->dma_addr, s->fragsize); NEXT_BUF(s, buf); }}/*----------------------------------------------------- * Name : at91_audio_read() * Function: Read Data from Audio and Send to user * Last rework data: 06-04-13 wujh@hyesco.com *----------------------------------------------------*/static ssize_t at91_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos){ audio_stream_t *s = &input_stream; unsigned long bufcnt,chunksize; unsigned long ret = 0,i=0,tmpint=0,tmpint2=0; unsigned short *buflp; unsigned long *appbuflp; audio_buf_t *b; bufcnt = count ; appbuflp = (unsigned long *)buffer; ret = -ERESTARTSYS; DPRINTK("audio_read: count=%d\n", count); if(a9200_stereo == 1) bufcnt >>= 1; if(a9200_bits == 16) bufcnt >>= 1; if (!s->active) { if (!s->buffers && at91_audio_setup_buf(s)) return -ENOMEM; DPRINTK("audio_read: audio_prime_dma(s)\n"); at91_audio_prime_dma(s); } wmb(); b = s->buf; ssc_regs->SSC_CR |= AT91C_SSC_RXEN ; pdc_regs->PDC_PTCR |= AT91C_PDC_RXTEN; wmb(); while (bufcnt) { if (down_interruptible(&b->sem)) break; /* Grab data from the current buffer */ chunksize = b->size; if (chunksize > 2*bufcnt) chunksize = 2*bufcnt; DPRINTK("read %d from %d\n", chunksize, s->buf_idx);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -