⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ac97_sport.c

📁 Blackfin AC97 Interface driver
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 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 + -