OSS驱动

最常用的音频接口:蓝色:音频输入端口,可将MP3、录音机、音响等的音频输出端通过双头3.5mm的音频线连接到电脑,通过电脑再进行处理或者录制。蓝色接口在四声道/六声道音效设置下,还可以连接后置环绕喇叭,在8声道输出时,仍为音频输入端口。

绿色:音频输出端口,用于连接耳机或2.0、2.1音箱。

粉色:麦克风端口,用于连接到麦克风

黑色:后置环绕喇叭接头,在四声道,六声道,八声道音效设置下,用于可以连接后置环绕喇叭。

橙色:中置重低音喇叭接头,在六声道八声道音效设置下,用于可以连接中置重低音喇叭。

灰色:侧边环绕喇叭接头,在八声道音效设置下,用于可以连接侧边环绕喇叭。

Linux音频OSSbuffer分析

整体使用框图

Buff分配流程

首先在probe JZ mixer设备的时候进行初始化DMA及缓冲buff. 在函数init_jz_i2s中调

audio_init_endpoint进行实始化。

fragsize= JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;

fragstotal= JZCODEC_RW_BUFFER_TOTAL;

audio_init_endpoint(&out_endpoint,fragsize, fragstotal);

audio_init_endpoint(&in_endpoint,fragsize, fragstotal);

进入audio_init_endpoint函数,在这里先表一下in_endpointout_ endpoint结构体, 初始化为0.

static audio_pipe out_endpoint = {

.mem = 0,

.savenode = 0,

.fragsize = 0,

.fragstotal = 0,

.trans_state = 0,

};

static audio_pipe in_endpoint= {

.mem = 0,

.savenode = 0,

.fragsize = 0,

.fragstotal = 0,

.trans_state = 0,

};

我们进入audio_resizemem_endpoint分析内存的分配其中调用init_audio_node进行实质的

分配。

JZCODEC_RW_BUFFER_SIZE // 1 * 4096

JZCODEC_RW_BUFFER_TOTAL; // 4buff

unsignedint fact; //分配物理页的order

audio_node *pbuff; //代表1audio node

audio_head *phead; //代表整个audiobuff 链表

unsignedint *mem; //分配得到整个audiobuff的虚拟地址

structlist_head *audio_wfree; //freebuff 链表

structlist_head *audio_wuse; //usebuff 链表

int memsize; //链表头+节点+audiobuff占用总空间

int datasize; //audiobuff占用的总空间

int headlistsize; //链表头+4个节点占用总空间

//Alloc memory first, to avail fail

datasize = ALIGN_PAGE_SIZE(pagesize * count);

headlistsize = ALIGN_PAGE_SIZE(count *sizeof(audio_node) + sizeof(audio_head));

memsize = headlistsize + datasize;

fact = get_order(memsize);

mem= (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, fact);

首先获得整个audiobuff的大小 datasize, 再获得list_head + list_node * 4的大小

headlistsize, 然后计算总大小memsize, __get_free_pages分配连续的物理地址空间,这里

注意一下,返回的是物理地址。

//Free old buffer

if(*memory) {

phead = (audio_head *)*memory;

fact = phead->fact;

free_pages((unsignedlong)*memory, fact);

*memory = NULL;

}

*memory= mem;

这里检查一下是不是有分配audiobuff, 如果有就释放掉,再将新分配的地址赋值给memory. 

mixerioctlSNDCTL_DSP_SETFRAGMENT也会重新分配大小的。

phead = (audio_head *)*memory;

phead->fact = fact;

phead->listsize = headlistsize;

phead->datasize = datasize;

audio_wuse = &(phead->use);

audio_wfree = &(phead->free);

接下来就是给listhead赋值,然后初始化audio_wuseaudio_wfree链表。

pbuff= (audio_node *)((unsigned int)*memory + sizeof(audio_head));

*fragmem_start= (int)((unsigned int)*memory + headlistsize);

for(i = 0; i < count; i++) {

pbuff->pBuf = (unsigned int)*memory + headlistsize +pagesize * i;

pbuff->phyaddr = (unsigned int)virt_to_phys((void*)pbuff->pBuf);

pbuff->start = 0;

pbuff->end = 0;

DEBUG

pbuff->pBufID = i;

DPRINT_Q("audio_notebuffer[%d] = %x\n", i, (unsigned int)pbuff->pBuf);

list_add(&pbuff->list,audio_wfree);

pbuff++;

}

在这里将audio_wfree 链表全部初始化,注意此时wfree链表中有4个结点,wuse中没有结点。

if (ret) {

endpoint->fragsize =pagesize;

endpoint->fragstotal =count;

endpoint->memsize = ret;

}

接下来返回到audio_resizemem_endpoint, 将endpoint结构赋值。

Audio buff分配好后,再返回到audio_init_endpointDMA进行初始化, 这里根据传

入的是play或者record进行初始化,大致上是雷同的,这里只分析 replay的初始化

if ((ch =jz_request_aic_dma(DMA_ID_I2S_TX,"audio dac", jz_i2s_dma_irq,IRQF_DISABLED,

endpoint)) < 0) {

printk(KERN_ERR "%s:can't reqeust DMA DAC channel.\n", __FUNCTION__);

return -1;

}

chan->io = i;

chan->dev_id = dev_id;

chan->dev_str = dev_str;

chan->fifo_addr = CPHYSADDR(AIC_DR);

switch (dev_id) {

case DMA_ID_AIC_TX:

chan->mode = DMA_AIC_TX_CMD_UNPACK | DMA_MODE_WRITE;

chan->source = DMAC_DRSR_RS_AICOUT;

break;

case DMA_ID_AIC_RX:

chan->mode = DMA_32BIT_RX_CMD | DMA_MODE_READ;

chan->source = DMAC_DRSR_RS_AICIN;

break;

default:

printk("JZ AIC: %s:%d,need fix !!!\n", __FUNCTION__, __LINE__);

BUG_ON(1);

}

调用jz_request_aic_dma函数注册DMA中断,填写chan的模式与地址填充,最后启动

AIC DMA的时钟,再返回到init_audio_replaydma中,初始化寄存器,DMA就初始化OK了。

至此整个AUDIO BUFF就初始化完成了。

播放流程分析

先上图来说话:

首先进入jz_audio_write函数, 里边有两个分支,根据模式进行区分的,第1种是使用

mmap方式传输。下边就直接进入audiobuff 方式的

jz_audio_write_data函数分析。

while(count >= pout_endpoint->fragsize) {

bat_cnt= endpoint_put_userdata(pout_endpoint,

&(buffer[usecount]),

pout_endpoint->fragsize);

//Prepare data success.

if(bat_cnt > 0) {

usecount+= bat_cnt;

count-= bat_cnt;

DPRINT("bat_cnt= %d\n", bat_cnt);

}

//Perhaps non node is avialable.

elseif (bat_cnt == 0) {

DPRINT("bat_cnt== 0\n");

break;

}

//Error occured.

else{

//break and handle prepared data.

if(usecount > 0) {

DPRINT("bat_cnt< 0, usecount > 0\n");

break;

}

//Has not prepared any data and return error when prepared data.

else{

DPRINT("bat_cnt< 0, usecount == 0\n");

returnbat_cnt;

首先检查传入音频块的大小,必须大于fragsize(4096),先来整体分析这个函数,然后

再进入内部分析细节,bat_cnt 根据返回值进行处理,返回0表明没有wfree节点,就直接

跳出,进行播放录音了。

DPRINT("<<<<put_userdata\n");

node= endpoint_get_outnode(endpoint);

if(!node)

return0;

if(copy_from_user((void *)node->pBuf, buffer, count)) {

printk("JZI2S: copy_from_user failed !\n");

return-EFAULT;

}

LEAVE();

returnendpoint_post_outnode(endpoint,node,count);

endpoint_get_outnode获得一个wfreenode,获得一个后就从wfree list上删除掉当

前节点, 播放完成后就返回结点,这样就形成了一个环形缓冲区。

最后返回到jz_audio_write_data, 最后如果当前while 循环结束后还有剩余的数据,就

重新做一次播放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值