多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全IT社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當前位置:首頁 > php開源 > 綜合技術 > alsa 驅(qū)動介紹

alsa 驅(qū)動介紹

來源:程序員人生   發(fā)布時間:2016-07-02 13:40:22 閱讀次數(shù):10075次
Machine
以裝配有CS4270的1款android 智能電視的為例
/sound/soc/samsung/exynos.c


Platform
以Samsung cpu exynos4412為例
/sound/soc/samsung/


Codec
以wolfson的Codec芯片cs4270為例
/sound/soc/codecs/cs4270.c


ALSA 框架介紹



Alsa 太多太雜,很難整理的規(guī)整,只能看到哪里寫到哪里

ASoC被分為Machine,Platform和Codec3大部件,


Platform驅(qū)動的主要作用是完成音頻數(shù)據(jù)的管理,終究通過CPU的數(shù)字音頻接口(DAI)把音頻數(shù)據(jù)傳送

給Codec進行處理,終究由Codec輸出驅(qū)動耳機或是喇叭的音信信號。在具體實現(xiàn)上,ASoC有把Platform驅(qū)動分為兩個部份:snd_soc_platform_driver
和snd_soc_dai_driver。其中,platform_driver負責管理音頻數(shù)據(jù),把音頻數(shù)據(jù)通過dma或其他操作傳送至cpu dai中,dai_driver則主要完成cpu1側(cè)的
dai的參數(shù)配置,同時也會通過1定的途徑把必要的dma等參數(shù)與snd_soc_platform_driver進行交互。


Machine  是指某1款機器,可以是某款裝備,某款開發(fā)板,又或是某款智能手機,由此可以看出Machine幾近是不可重用的,每一個Machine上的硬件實

現(xiàn)可能都不1樣,CPU不1樣,Codec不1樣,音頻的輸入、輸出裝備也不1樣,Machine為CPU、Codec、輸入輸出裝備提供了1個載體。
Platform  1般是指某1個SoC平臺,比如pxaxxx,s3cxxxx,omapxxx等等,與音頻相干的通常包括該SoC中的時鐘、DMA、I2S、PCM等等,只要指定了SoC,那末我們可以認為它會有1個對應的Platform,它只與SoC相干,與Machine無關,這樣我們就能夠把Platform抽象出來,使得同1款SoC不用做任何的改動,就能夠用在不同的Machine中。實際上,把Platform認為是某個SoC更好理解。


Codec  字面上的意思就是編解碼器,Codec里面包括了I2S接口、D/A、A/D、Mixer、PA(功放),通常包括多種輸入(Mic、Line-in、I2S、PCM)和多個

輸出(耳機、喇叭、聽筒,Line-out),Codec和Platform1樣,是可重用的部件,同1個Codec可以被不同的Machine使用。嵌入式Codec通常通過I2C對
內(nèi)部的寄存器進行控制。 

Machine驅(qū)動的初始化,codec和dai的注冊,都會調(diào)用snd_soc_instantiate_cards()進行1次聲卡和codec,dai,platform的匹配綁定進程,這里所說的
綁定,正如Machine驅(qū)動1文中所描寫,就是通過3個全局鏈表,按名字進行匹配,把匹配的codec,dai和platform實例賦值給聲卡每對dai的snd_soc_pcm_runtime變量中。1旦綁定成功,將會使得codec和dai驅(qū)動的probe回調(diào)被調(diào)用

alsa架構的數(shù)據(jù)交互,是通過對PCM裝備的操作來完成的, PCM裝備分成playback和capture兩個stream, 每一個stream底下有N個substream

alsa驅(qū)動最底層需要調(diào)試的有3塊: DMA部份,IIS驅(qū)動部份,codec部份










IIS介紹

A)I2S有4根線,
1.串行時鐘SCLK,也叫位時鐘(BCLK),即對應數(shù)字音頻的每位數(shù)據(jù),SCLK都有1個脈沖。SCLK的頻率=2×采樣頻率×采樣位數(shù)。
2. 幀時鐘LRCK,(也稱WS),用于切換左右聲道的數(shù)據(jù)。LRCK為“1”表示正在傳輸?shù)氖怯衣暤赖臄?shù)據(jù),為“0”則表示正在傳輸?shù)氖亲舐暤赖臄?shù)據(jù)。LRCK
的頻率等于采樣頻率。
3.串行數(shù)據(jù)SDATA,就是用2進制補碼表示的音頻數(shù)據(jù)。
4.有時為了使系統(tǒng)間能夠更好地同步,還需要另外傳輸1個信號MCLK,稱為主時鐘,也叫系統(tǒng)時鐘(Sys Clock),是采樣頻率的256倍或384倍。


B)聲音數(shù)據(jù)DAT1般在CLK的上升沿進行采樣,有些DAC也是可以調(diào)的。每一個聲道里面可以容納的CLK數(shù)必須多于數(shù)據(jù)的位數(shù),多出來的時鐘和數(shù)據(jù)DAC會丟棄不用,比如16bit采樣的聲音數(shù)據(jù)當1個聲道是32個CLK且left-justify的時候,后面106個時鐘的數(shù)據(jù)會被DAC丟掉,不影響的。



C)I2S數(shù)據(jù)的格式分I2S, Left-justify, Right-justify。3種格式的區(qū)分在于聲音數(shù)據(jù)與WS的對應關系:
1 .  I2S模式DAT的MSB在WS變化后的第2個上升沿開始傳輸
2.  Left-justify模式DAT的MSB在WS變化后的第1個上升沿開始傳輸
3.   Right-justify模式DAT的LSB在WS行將變換到下1聲道前的最后1個時鐘傳輸


I2S部份觸及的幾個頻率:
  * 輸出采樣頻率 fs = 44.1KHz.  (也有其它fs的音源, 但加了resampler后, 都變成44.1KHz輸出了). 這是個關鍵頻率.
  * LRCLK, 就等于fs. (L/R聲道信號)
  * BCLK = 32倍fs = 1411.2KHz = 1.4112MHz. (bit clock). 2聲道16bit, 故32倍fs. 若2聲道24bit, 則48倍fs.
  * MCLK是全部audio模塊的工作頻率, 通常選fs的256, 384, 512倍. 比如: 256倍fs = 11289.6KHz = 11.2896MHz.

從頻率設置來講, MCLK是個主要頻率, 它是全部audio模塊的工作頻率.

那末, 從軟件來講要設置兩個方面的寄存器: 1是該PLL從晶振頻率如何得到PLLout頻率(比如P/M/S/k). 2是PLLout如何分頻得到audio部份的MCLK.


IIS驅(qū)動部份最重要的就是注冊以下鉤子函數(shù),掛到了alsa驅(qū)動上
static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { .trigger = i2s_trigger, .hw_params = i2s_hw_params, .set_fmt = i2s_set_fmt, .set_clkdiv = i2s_set_clkdiv, .set_sysclk = i2s_set_sysclk, .startup = i2s_startup, .shutdown = i2s_shutdown, .delay = i2s_delay, };



codec芯片介紹
cs4270的驅(qū)動要設置的參數(shù)有:
靜音,傳輸模式,比特位長度,時鐘主從模式,音量大小
cs4270驅(qū)動里面定義了snd_soc_dai_driver結(jié)構成員,里面定義了playback和capture兩個substream,同事也掛了1個snd_soc_dai_ops結(jié)構體,里面全
是操作函數(shù)指針。
alsa上面1層層的終究會調(diào)用到這些指針

static const struct snd_soc_dai_ops cs4270_dai_ops = { .hw_params = cs4270_hw_params, .set_sysclk = cs4270_set_dai_sysclk, .set_fmt = cs4270_set_dai_fmt, .digital_mute = cs4270_dai_mute, }; static struct snd_soc_dai_driver cs4270_dai = { .name = "cs4270-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 4000, .rate_max = 216000, .formats = CS4270_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 4000, .rate_max = 216000, .formats = CS4270_FORMATS, }, .ops = &cs4270_dai_ops, };




DMA介紹

IIS總線是慢速總線,相對CPU來講,太慢。所以采取DMA的方式最能節(jié)省CPU性能。
PCM playback的時候,DMA目的地址是IIS FIFO寄存器。源地址是寄存PCM數(shù)據(jù)的內(nèi)存。
DMA的驅(qū)動采取了linux pl330的驅(qū)動架構,采取中斷的方式來觸發(fā)后續(xù)DMA。
IIS中通過DMA的方式寫入FIFO寄存器,在DMA的驅(qū)動中掛接了1個回調(diào)函數(shù)audio_buffdone。DMA完成后,回函數(shù)調(diào)用,刷新alsa的環(huán),便于下1次DMA
DMA的目的地址,就是IIS發(fā)送寄存器的地址。源地址,就是申請的DMA buffer,只不過DMAbuffer被映照成了1個環(huán)

static void dma_enqueue(struct snd_pcm_substream *substream) { struct runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; unsigned int limit; struct samsung_dma_prep dma_info; pr_debug("Entered %s\n", __func__); limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit); dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE); dma_info.direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); dma_info.fp = audio_buffdone; //回調(diào)函數(shù) dma_info.fp_param = substream; dma_info.period = prtd->dma_period; dma_info.len = prtd->dma_period*limit; while (prtd->dma_loaded < limit) { pr_debug("dma_loaded: %d\n", prtd->dma_loaded); if ((pos + dma_info.period) > prtd->dma_end) { dma_info.period = prtd->dma_end - pos; pr_debug("%s: corrected dma len %ld\n", __func__, dma_info.period); } dma_info.buf = pos; prtd->params->ops->prepare(prtd->params->ch, &dma_info); //DMA注冊 prtd->dma_loaded++; pos += prtd->dma_period; if (pos >= prtd->dma_end) pos = prtd->dma_start; } prtd->dma_pos = pos; } static void audio_buffdone(void *data) { struct snd_pcm_substream *substream = data; struct runtime_data *prtd = substream->runtime->private_data; pr_debug("Entered %s\n", __func__); if (prtd->state & ST_RUNNING) { prtd->dma_pos += prtd->dma_period; if (prtd->dma_pos >= prtd->dma_end) prtd->dma_pos = prtd->dma_start; if (substream) snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); if (!samsung_dma_has_circular()) { prtd->dma_loaded--; dma_enqueue(substream); } spin_unlock(&prtd->lock); } }



DMA部份主要通過注冊以下鉤子函數(shù)來掛到alsa驅(qū)動里面

static struct snd_pcm_ops dma_ops = { .open = dma_open, .close = dma_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = dma_hw_params, .hw_free = dma_hw_free, .prepare = dma_prepare, .trigger = dma_trigger, .pointer = dma_pointer, .mmap = dma_mmap, };


alsa數(shù)據(jù)讀寫簡介

播放時,利用程序把音頻數(shù)據(jù)源源不斷地寫入dma buffer中,然后相應platform的dma操作則不停地從該buffer中取出數(shù)據(jù),經(jīng)dai送往codec中。錄音時
則正好相反,codec源源不斷地把A/D轉(zhuǎn)換好的音頻數(shù)據(jù)經(jīng)過dai送入dma buffer中,而利用程序則不斷地從該buffer中讀走音頻數(shù)據(jù)

以播放(playback)為例,我現(xiàn)在知道最少有3個途徑可以完成對dma buffer的寫入:
利用程序調(diào)用alsa-lib的snd_pcm_writei、snd_pcm_writen函數(shù);
利用程序使用ioctl:SNDRV_PCM_IOCTL_WRITEI_FRAMES或SNDRV_PCM_IOCTL_WRITEN_FRAMES;
利用程序使用alsa-lib的snd_pcm_mmap_begin/snd_pcm_mmap_commit;

以上幾種方式終究把數(shù)據(jù)寫入dma buffer中,然后修改runtime->control->appl_ptr的值。
播放進程中,通常會配置成每個period size生成1個dma中斷,中斷處理函數(shù)最重要的任務就是:
更新dma的硬件確當前位置,該數(shù)值通常保存在runtime->private_data中;
調(diào)用snd_pcm_period_elapsed函數(shù),該函數(shù)會進1步調(diào)用snd_pcm_update_hw_ptr0函數(shù)更新上述所說的4個緩沖區(qū)管理字段,然后喚醒相應的等待進程;
這個中斷實際上在DMA驅(qū)動內(nèi)部,給DMA驅(qū)動1個回調(diào)函數(shù)就能夠了。就是我們前面說的audio_buffdone

Playback時數(shù)據(jù)流向

/sound/soc/samsung/里面,寫入到DMA源buffer時,
用的是mmap的寫入方式,不是采取的snd_pcm_hw_writei

幾個關鍵點:
1,Pos計算方式
dma_pointer里面, res = prtd->dma_pos - prtd->dma_start;
此pos就是在DMA 源buffer中的位置
2,dma的初始化
dma_enqueue里面,把DMA源buffer切成period_size大小,掛到DMA隊列里
每次period_size傳輸完了,就會調(diào)用audio_buffdone終端處理函數(shù),更新dma_pos
同時audio_buffdone也會調(diào)用snd_pcm_update_hw_ptr0重新計算hw_ptr,從而計算是否是有足夠的可用空間,來喚醒等待的poll

從alsalib來看,要先調(diào)用snd_pcm_start,觸發(fā)DMA操作開始
snd_pcm_start---SNDRV_PCM_IOCTL_START---snd_pcm_action_lock_irq--snd_pcm_do_start----dma_trigger
每次寫入mmap buffer之前,要先snd_pcm_wait,等待有足夠可用的空間.
snd_pcm_wait----snd_pcm_wait_nocheck----poll-----snd_pcm_playback_poll---poll_wait
然后調(diào)用snd_pcm_mmap_begin獲得mmap 內(nèi)存
然后寫入,然后調(diào)用snd_pcm_mmap_commit做1下alsalib和驅(qū)動里面的環(huán)同步

DMA實際上是在不停的DMA的。有空閑的了,上層就不用wait了,就會寫入了


幾個典型調(diào)用流程

設置hw_param參數(shù)時,調(diào)用流程 snd_pcm_hw_params _snd_pcm_hw_params pcm->ops->hw_params----snd_pcm_hw_hw_params SNDRV_PCM_IOCTL_HW_PARAMS snd_pcm_common_ioctl1 snd_pcm_hw_params_user snd_pcm_hw_params substream->ops->hw_params soc_pcm_hw_params codec_dai->driver->ops->hw_params cpu_dai->driver->ops->hw_params cs4270_hw_params 設置mixer 參數(shù)時,volume為例,調(diào)用流程 snd_mixer_selem_set_playback_volume_all snd_mixer_selem_set_playback_volume set_volume_ops _snd_mixer_selem_set_volume---selem_write selem_write_main elem_write_volume snd_hctl_elem_write snd_ctl_elem_write snd_ctl_hw_elem_write snd_ctl_elem_write_user snd_ctl_elem_write snd_soc_put_volsw snd_soc_update_bits_locked regmap_update_bits_check IIS clk 設置流程 snd_pcm_common_ioctl1 snd_pcm_hw_params_user snd_pcm_hw_params substream->ops->hw_params soc_pcm_hw_params rtd->dai_link->ops->hw_params smdk_wm8994_pcm_hw_params snd_soc_dai_set_sysclk dai->driver->ops->set_sysclk i2s_set_sysclk













生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 毛片999| 最近中文字幕视频完整 | 国产精品福利在线观看 | 欧美精品v欧洲精品 | 中文字幕一区二区在线观看 | 精品午夜国产在线观看不卡 | h在线观看视频 | 精品国产三级v | 亚洲全网成人资源在线观看 | 国内成人精品亚洲日本语音 | 欧美在线一二三区 | 国产精品欧美日韩精品 | 国产毛片久久久久久国产毛片 | 亚洲欧美日本综合 | jizz亚洲日本 | 毛片一区 | 爆操网站 | av中文字幕网免费观看 | 韩国三级做爰中文字幕 | 日本亚洲精品成人 | 亚洲成a人在线观看 | 吃奶跟添下面特舒服 | 麻豆高清视频在线观看 | 色综合一本到久久亚洲91 | 亚洲精品一区最新 | 亚洲综合精品一二三区在线 | 性欧美高清极品猛交 | 在线免费观看www | 国产福利在线观看永久免费 | 欧美性bbbbbxxxxxxx| 亚洲第一色在线 | 午夜私人福利影院 | h视频免费观看 | 精品一区二区三区免费视频 | 国产成人综合久久精品红 | 久久欧美精品 | 国产在线观看一区二区三区 | 欧美黑人xxxx猛牲大交 | 暖暖在线精品日本中文 | 大片免费在线观看网址 | 欧美成人全部免费观看1314色 |