how to directly copy memory data to SSI(ESAI) fifo //怎样播放自己分配的DMA内存里的数据(数据非由alsa传向底层,而是DMA直接从一块内存里面搬移)

Document created by Lei Ge Employee on Dec 7, 2014
Version 1Show Document
  • View in full screen mode

通常的音乐播放是这样一个流程:
alsa将一段上层的应用空间和底层的物理空间通过mmap映射起来,然后DMA从被映射的物理空间往I2S的fifo传送数据。
现在有个客户想将FEC网络那边收到的数据中的audio数据分离出来,暂存到一段物理内存中,然后通过DMA传输到我们芯片的I2S fifo,从而节省掉数据在上层、底层之间来回调用的时间。
这就需要我们I2S这边支持通过DMA直接从一段物理内存拿数据。
为了较为方便的实现这一功能,通过对ALSA架构的分析,我保留了我们整个ASoC代码的架构,只是不让DMA从原来的mmap的地址拿数据,而是从我自己分配的DMA内存中拿数据。
258         uint8_t *wbuf;
259         uint8_t *index1;
261         wbuf = dma_alloc_coherent(NULL, 0x10000, &wpaddr, GFP_DMA);
265         for (i=0; i<0x10000; i++) {
269                         *(wbuf + i) = wav_data[i];
273         }
333                 iprtd->desc = chan->device->device_prep_dma_cyclic(
334                         chan, wpaddr,
335 //                      chan, dma_addr,
336                         iprtd->period_bytes * iprtd->periods,
337                         iprtd->period_bytes,
338                         substream->stream == SNDRV_PCM_STREAM_PLAYBACK     ?
339                         DMA_TO_DEVICE : DMA_FROM_DEVICE);
代码实现上实际上是比较简单的,可以套用以前做过的dma_m2m的部分代码。

 

现在我们要拿一些wav里面的audio数据。
linux
可以用16进制的方法读wav里面的数据:
hexdump -n 65700 -C audio44k16S.wav > wav_data_44100_s16_stereo.haudio44k16S.wav

里面的audio数据用16进制的形式保存到wav_data_44100_s16_stereo.h这个文件里。

然后我们要编辑这个头文件里的audio数据原数据是这种格式的
00000000  52 49 46 46 12 63 0c 00  57 41 56 45 66 6d 74 20  |RIFF.c..WAVEfmt |
00000010  12 00 00 00 01 00 02 00  44 ac 00 00 10 b1 02 00  |........D.......|
00000020  04 00 10 00 00 00 66 61  63 74 04 00 00 00 b5 18  |......fact......|
00000030  03 00 64 61 74 61 d4 62  0c 00 58 01 f0 00 98 01  |..data.b..X.....|
00000040  56 01 3d 01 1e 01 d0 00  ae 00 81 00 35 00 40 00  |V.=.........5.@.|
00000050  dd ff f7 ff 90 ff 9f ff  1d ff 54 ff bb fe 18 ff  |..........T.....|
00000060  61 fe 1e ff e7 fd 5e ff  e4 fd 23 00 39 fe ee 00  |a.....^...#.9...|

我们要把它转换成符合我们要求的这种形式
,0x63 ,0xf0 ,0x0f ,0xf0 ,0x7f ,0xf1 ,0x21 ,0xf1  ,0x89 ,0xf3 ,0xef ,0xf2 ,0x9d ,0xf5 ,0x8c ,0xf4
,0x5f ,0xf7 ,0xd3 ,0xf5 ,0x8c ,0xf8 ,0x08 ,0xf7  ,0x4c ,0xf9 ,0x01 ,0xf8 ,0x74 ,0xf9 ,0x2e ,0xf8
,0x1c ,0xf9 ,0x15 ,0xf8 ,0x09 ,0xf9 ,0x82 ,0xf7  ,0x45 ,0xf9 ,0xd1 ,0xf6 ,0x0b ,0xf9 ,0x1e ,0xf6
,0xce ,0xf8 ,0xbe ,0xf5 ,0xd7 ,0xf8 ,0x9a ,0xf5  ,0xa8 ,0xf9 ,0xc7 ,0xf5 ,0xe0 ,0xfa ,0x3e ,0xf6
,0x25 ,0xfc ,0x44 ,0xf7 ,0x44 ,0xfd ,0xa2 ,0xf8  ,0x9a ,0xfe ,0xea ,0xf9 ,0x25 ,0x00 ,0x94 ,0xfb
,0x16 ,0x02 ,0xec ,0xfc ,0xcb ,0x03 ,0xfe ,0xfd  ,0xef ,0x04 ,0xa5 ,0xfe ,0x45 ,0x05 ,0x0b ,0xff
,0x3b ,0x05 ,0x24 ,0xff ,0x34 ,0x05 ,0x94 ,0xff  ,0x06 ,0x05 ,0x13 ,0x00 ,0x6c ,0x04 ,0x82 ,0x00

 

 

这里有一个比较好的方法

可以通过VIM

垂直编辑的方法较为轻松的实现。

vim垂直编辑:
ctrl+v 然后上下左右移动选中,D删除选中的。
ctrl+v 选中, shift+i 可以在选中的地方加入想插入的字符。
将编辑过的数据放到uint8_t wav_data[],赋值给wbuf,wbuf对应的物理地址
wpaddr中的数据,就是上面wav中的audio数据。

wbuf = dma_alloc_coherent(NULL, 0x10000, &wpaddr, GFP_DMA);中的0x10000=1024*64是我们分配的dma内纯的大小,我们sdma是根据以下大小传输数据的:
iprtd->period_bytes = 21844, iprtd->periods = 3
//21844*3=65532= 0xfffc
这样实际上DMA是不停的刷我们分配的内存里的数据,因为我们没有更新DMA内存里的数据,所以听到的是不断重复播放的声音片段。
刚开始时,我听这个片段,发现片段之间有较为明显的pop音,这是由于我截取的是一首音乐开始的部分,包含了wav包头数据,这不是音乐数据,将这个去除就可以了。

Attachments

    Outcomes