📄 ac97_sport.c
字号:
/* theory of operation: we have a rx and a tx buffer * which is filled/emptied in dma autobuffer mode * we generate an interrupt each 'fragment' * commands are copied to/from the cmd fifo to the * cmd channel in the next fragment * audio data is copied to/from the buffer by the user in an async way * this is no problem if we read/write sufficiently large fragments at a time * the copy to/from user routine will refuse to copy data if there is not enough * room. optimum and max fragment size should be about half a buffer. *//* * note: currently we only handle stereo reception and transmission * we also assume that we run in variable frame rate, i.e.: frames * come with the sample frequency, and each received/transmitted frame * contains valid pcm audio data!! */#include <linux/types.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/errno.h>#include <asm/blackfin.h>#include <asm/dma.h>#include "bf53x_structs.h"#include "ac97_sport.h"// bufsize and fragsize in units of ac97 frames!#define WORDS_PER_FRAME (sizeof(struct ac97_frame)/sizeof(__u16))#define BYTES_PER_FRAME (sizeof(struct ac97_frame))#define LOG_BYTES_PER_FRAME 5 /* this better be true: 2 << LOG_BYTES_PER_FRAME == BYTES_PER_FRAME */ #define bzero(buf, size) memset(buf, 0, size)/* * one global device struct * get rid of all the dev arguments and let the__attribute__((aligned(4))) unsigned int linker do the indirection work */ struct ac97_sport_dev_t dev;dma_addr_t addr;_ac97_cmd_queue_t cmd_queue;struct dma_descriptor_block *ac97_tx_desc;struct dma_descriptor_block *ac97_rx_desc; int ac97_tx_lastfrag; // fragment last handled by irqint ac97_rx_lastfrag;short txbuf[AC97_BUFFER_SIZE];short rxbuf[AC97_BUFFER_SIZE];int txHead,txTail;int rxHead,rxTail;unsigned char bWriteWaiting = 0;unsigned char bReadWaiting = 0;unsigned char bStereo = 0;extern unsigned int *pBitmap;extern short BitmapLen;extern short curBitmapRow,curBitmapCol;//2009.04.06 Tony.Liu addtypedef struct _ac97_codec_reg { unsigned char addr; unsigned short value;}t_ac97_reg;#define ALL_WM9707_REG 28t_ac97_reg tst_ac97_reg[ALL_WM9707_REG];const unsigned char gc_wm9707_reg_addr[] = {#if 10x00,0x02,0x04,0x06,0x0A,0x0C,0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C,0x20,0x22,0x24,0x26,0x28,0x2A,0x2C,0x32,0x5C,0x72,0x74,0x7A,0x7C,0x7E#elseAC97_RESET,AC97_INT_PAGING,AC97_POWERDOWN,AC97_EXTENDED_STATUS,AC97_VENDOR_ID1,AC97_VENDOR_ID2#endif};static int wm9707_read_all_reg(struct snd_soc_codec *codec,t_ac97_reg *p_reg,int regs,const unsigned char *p_reg_addr){ int ret,i; unsigned short read_reg; t_ac97_reg *p_now; //Setp 1 config all value reg address p_now = p_reg; memset(p_reg,0,sizeof(t_ac97_reg)*ALL_WM9707_REG); for (i = 0; i < regs; i++){ p_reg->addr = *p_reg_addr; p_reg += 1; p_reg_addr += 1; } //Setp 2 read all register & print p_reg = p_now; printk(KERN_INFO "*******Read all AC97 Codec register*************\n"); for (i = 0; i < regs; i++){ //read_reg = ac97_sport_get_register(p_reg->addr); p_reg->value = read_reg; printk(KERN_INFO "REG 0x%02x 0x%04x ",p_reg->addr,read_reg); if (0x00 == (i % 3)) { printk(KERN_INFO "\n"); } p_reg += 1; } printk(KERN_INFO "\n"); return (ret);}/* * setup sport and dma for communication with ac97 device */static void sport_init(void){ bfin_write_SPORT1_RCR1(0x0000); bfin_write_SPORT1_TCR1(0x0000); bfin_write_SPORT1_RCR2(0x0000); bfin_write_SPORT1_TCR2(0x0000); bfin_write_SPORT1_MTCS0(0x0000003f); bfin_write_SPORT1_MRCS0(0x0000003f); bfin_write_SPORT1_MTCS1(0x00000000); bfin_write_SPORT1_MRCS1(0x00000000); bfin_write_SPORT1_MTCS2(0x00000000); bfin_write_SPORT1_MRCS2(0x00000000); bfin_write_SPORT1_MTCS3(0x00000000); bfin_write_SPORT1_MRCS3(0x00000000); bfin_write_SPORT1_RFSDIV(0x00ff); bfin_write_SPORT1_TFSDIV(0x00ff); bfin_write_SPORT1_RCR2(0x000f); bfin_write_SPORT1_TCR2(0x000f); bfin_write_SPORT1_RCR1(IRFS); bfin_write_SPORT1_TCR1(ITFS); bfin_write_SPORT1_MCMC1(0x0000); bfin_write_SPORT1_MCMC2(0x1000 | MCMEN | MCDTXPE | MCDRXPE); }_ac97_cmd_t *outqueue_cmd(){ unsigned char tmptail; if(cmd_queue.tail == cmd_queue.head) { return NULL; } tmptail = cmd_queue.tail; cmd_queue.tail++; if(cmd_queue.tail >= CMDQUEUE_LEN) { cmd_queue.tail = 0; } return &cmd_queue.cmd_buffer[tmptail];}static void enqueue_cmd(__u16 addr, __u16 data){ //printk("enqueue_cmd\n"); cmd_queue.cmd_buffer[cmd_queue.head].reg = addr; cmd_queue.cmd_buffer[cmd_queue.head].value = data; cmd_queue.head++; if(cmd_queue.head >= CMDQUEUE_LEN) { cmd_queue.head = 0; } if(cmd_queue.head == cmd_queue.tail) { cmd_queue.tail++; if(cmd_queue.tail >= CMDQUEUE_LEN) { cmd_queue.tail = 0; } }}// enqueue to send 'set register <reg> to <val>' command, // followed by 'read <reg>' command// marking the register cache 'dirty'// the receive routine will update the register cache when the answer// arrivesint ac97_sport_set_register(__u16 reg, __u16 val){ if( (reg > 127) || (reg & 0x1) ) return -EINVAL;//printk("ac97_sport_set_register\n"); enqueue_cmd( (reg << 8) , val ); // write return 0; // ok!}int ac97_sport_get_register(__u16 reg){ if( (reg > 127) || (reg & 0x1) ) return -EINVAL; enqueue_cmd( (reg << 8) | 0x8000, 0x0000 ); // write return 0; // ok!}void ac97_sport_silence(void){ int i,j; //printk("ac97_sport_silence\n"); for (i = 0; i < FRAG_NUM ; i++) { for(j=0; j<FRAG_SIZE; j++) { dev.txbuf[i*FRAG_SIZE+j].ac97_tag = TAG_VALID; dev.txbuf[i*FRAG_SIZE+j].ac97_cmddataL8_pcmleftH8 = 0; dev.txbuf[i*FRAG_SIZE+j].ac97_pcmleft12_pcmright4 = 0; dev.txbuf[i*FRAG_SIZE+j].ac97_pcmright16 = 0; } } }//init_ac97 must be call in interrupt function,or call spin_lock before//it and call spin_unlock after itstatic void init_ac97(void){ if(dev.codec_initialized) { return; } dev.codec_initialized = 1; /* Initialize the AC97 */ //ac97_sport_set_register(0x74, 0x2000); //ac97_sport_set_register(AC97_RESET, 0x0000); // reset ac97(write any data to reset) ac97_sport_set_register(AC97_RESET, 0x0000); // reset ac97(write any data to reset) printk(KERN_INFO "AC97 Reset all\n"); wm9707_read_all_reg(0x00,&tst_ac97_reg[0],28,&gc_wm9707_reg_addr[0]); ac97_sport_set_register(AC97_POWER_CONTROL, AC97_PWR_D0); // power on //ac97_sport_set_register(CODEC_MISC_CONTROL_BITS, AD1985_MCB_AC97NC | AD1985_MCB_HPSEL); ac97_sport_set_register(AC97_EXT_AUDIO_CTRL, 0x0009); // ac97_set_sample_rate(AC97_SAMPLERATE_48000); ac97_sport_get_register(AC97_VENDOR_ID1); ac97_sport_get_register(AC97_VENDOR_ID2); ac97_sport_set_register(AC97_GENERAL_PURPOSE,AC97_GP_3D | AC97_GP_MIX); ac97_sport_set_register(AC97_MIXER_VOL,0x0404); ac97_sport_set_register(AC97_PCMOUT_VOL,0x0808); //out ac97_sport_set_register(AC97_HEADPHONE_VOL,0x3f00); ac97_sport_set_register(AC97_MASTER_VOL_STEREO,0x0808); ac97_sport_set_register(AC97_RECORD_SELECT,0x0404 ); ac97_sport_set_register(AC97_RECORD_GAIN,0x0000); //in ac97_sport_set_register(AC97_MIC_VOL,AC97_MUTE); //printk("init_ac97\n"); printk(KERN_INFO "AC97 codec init end\n"); wm9707_read_all_reg(0x00,&tst_ac97_reg[0],28,&gc_wm9707_reg_addr[0]);}static void dma_init_xmit() { int i; /* this better be an integer */ ac97_tx_desc = dma_alloc_coherent(NULL,sizeof(struct dma_descriptor_block) * FRAG_NUM, &addr, 0); for (i = 0; i < FRAG_NUM; i++) { ac97_tx_desc[i].next = (unsigned long)&ac97_tx_desc[i+1]; ac97_tx_desc[i].start_addr = (unsigned long)&dev.txbuf[i*FRAG_SIZE]; ac97_tx_desc[i].x_count = WORDS_PER_FRAME; ac97_tx_desc[i].x_modify = sizeof(__u16); ac97_tx_desc[i].y_count = FRAG_SIZE; ac97_tx_desc[i].y_modify = sizeof(__u16); ac97_tx_desc[i].dma_config = 0x7995; } /* Close the circle */ ac97_tx_desc[FRAG_NUM - 1].next = (unsigned long)&ac97_tx_desc[0]; ac97_tx_lastfrag = 0; // fragment last handled by irq bfin_write_DMA4_NEXT_DESC_PTR((unsigned long *)&ac97_tx_desc[0]); bfin_write_DMA4_CONFIG((unsigned short)(0x7994));#if 0 __builtin_bfin_csync(); *pDMA4_NEXT_DESC_PTR = (unsigned long *)&ac97_tx_desc[0]; *pDMA4_CONFIG = (unsigned short)(ac97_tx_desc[0].cfg) ; __builtin_bfin_ssync();#endif }static void dma_init_recv(){ int i; /* this better be an integer */ ac97_rx_desc = dma_alloc_coherent(NULL,sizeof(struct dma_descriptor_block) * FRAG_NUM, &addr, 0); for (i = 0; i < FRAG_NUM; i++) { ac97_rx_desc[i].next = (unsigned long)&ac97_rx_desc[i+1]; ac97_rx_desc[i].start_addr = (unsigned long)&dev.rxbuf[i*FRAG_SIZE]; ac97_rx_desc[i].x_count = WORDS_PER_FRAME; ac97_rx_desc[i].x_modify = sizeof(__u16); ac97_rx_desc[i].y_count = FRAG_SIZE; ac97_rx_desc[i].y_modify = sizeof(__u16); ac97_rx_desc[i].dma_config = 0x7997; } /* Close the circle */ ac97_rx_desc[FRAG_NUM - 1].next = (unsigned long)&ac97_rx_desc[0]; ac97_rx_lastfrag = 0; bfin_write_DMA3_NEXT_DESC_PTR((unsigned long *)&ac97_rx_desc[0]); bfin_write_DMA3_CONFIG((unsigned short)(0x7996));#if 0 __builtin_bfin_csync(); *pDMA3_NEXT_DESC_PTR = (unsigned long *)&ac97_rx_desc[0]; *pDMA3_CONFIG = (unsigned short)(ac97_rx_desc[0].cfg); __builtin_bfin_ssync();#endif }// 1 = enable, -1 = disable, other = don't changestatic void sport_enable(int tx, int rx){ if(rx == 1) { bfin_write_DMA3_CONFIG(0x7997); bfin_write_SPORT1_RCR1(bfin_read_SPORT1_RCR1() | RSPEN); } if(rx == -1) { bfin_write_SPORT1_RCR1(bfin_read_SPORT1_RCR1() & (~RSPEN)); bfin_write_DMA3_CONFIG(0x7996); } if(tx == 1) { bfin_write_DMA4_CONFIG(0x7995); bfin_write_SPORT1_TCR1(bfin_read_SPORT1_TCR1() | TSPEN); } if(tx == -1) { bfin_write_SPORT1_TCR1(bfin_read_SPORT1_TCR1() & (~TSPEN)); bfin_write_DMA4_CONFIG(0x7994); } #if 0 __builtin_bfin_csync(); if(rx == 1) *pSPORT1_RCR1 |= RSPEN; if(rx == -1) *pSPORT1_RCR1 &= ~RSPEN; if(tx == 1) *pSPORT1_TCR1 |= TSPEN; if(tx == -1) *pSPORT1_TCR1 &= ~TSPEN; __builtin_bfin_ssync();#endif }int ac97_sport_open() { int i,j; printk("ac97_sport_open\n"); if(dev.codec_initialized) { return 0; } bzero(&dev,sizeof(dev)); dev.rxbuf = dma_alloc_coherent(NULL, FRAG_NUM*FRAG_SIZE * sizeof(struct ac97_frame), &addr, 0); dev.txbuf = dma_alloc_coherent(NULL, FRAG_NUM*FRAG_SIZE * sizeof(struct ac97_frame), &addr, 0); /* mark the register cache all clean, even if they are dirty we'd rather mark everything dirty, but chances are openwe won't get an answer for every register query. TODO: figure out which ones to mark dirty */ for (i = 0; i < FRAG_NUM ; i++) { for(j=0; j<FRAG_SIZE; j++) { dev.txbuf[i*FRAG_SIZE+j].ac97_tag = TAG_VALID; } } dma_init_recv( ); dma_init_xmit( ); sport_init(); init_waitqueue_head(&dev.audio_in_wait); init_waitqueue_head(&dev.audio_out_wait); dev.codec_initialized = 0; //for test only cmd_queue.head = 0; cmd_queue.tail = 0; txHead = 0; txTail = 0; rxHead = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -