天天看點

rk3399_android7.1音頻通路相關說明Android 7.1/ kernel 4.4 的 的 I2S M/S

關于音頻的輸出通路,可以有多重選擇:HDMI-out,喇叭,耳機,LINE-in,USB聲霸卡,藍牙等,切換不同的通路音頻就從不同的通路輸出或者錄入,這裡主要以HDMIin為例來簡單說一下相關AUDIO通路方面的内容。

RK3399 HDMI IN聲霸卡通路選擇

目前RK3399有三路i2s通道,HDMIOUT音頻通路晶片内置為i2s2。目前RK3399 開發闆上的音頻晶片還有藍牙、rt5651、tc358749,音頻通路配置如下:

RK3399 I2S2 沒有使用來作為藍牙通話,則可以 TC358749 I2S 接口接到 RK3399的單獨一個 I2S 上(I2S/PCM 不能跟其他 I2S 裝置共用,否則造成 I2S 信号的幹擾,聲音有雜音),另外的 I2S 接口接 codec 通過功放輸出 ANDROID 系統聲音。

RK3399 HDMI IN 聲霸卡通路配置

通路1:HDMIIn –> RK3399 I2S1- > RK3399 HDMI TX -> HDMI 電視機

通路2:HDMIIn –> RK3399 I2S1- > RK3399 I2S0 -> CODEC ->hp/Speaker

TC358749 I2S 信号送給 RK3399 錄音,然後 RK3399 在通過播放給 HDMI TX 輸出,需要注冊兩個聲霸卡,TC358749 聲霸卡,以及HDMI audio out 聲霸卡,系統預設已經有HDMI audio out 注冊,需要dts中開啟即可,TC358749 需要再重新寫一個聲霸卡驅動。

rk3399_android7.1音頻通路相關說明Android 7.1/ kernel 4.4 的 的 I2S M/S

RK3399 HDMI IN核心實作方案

該部分的總體思路是,注冊東芝 tc358749x 晶片(約定以下簡稱 749 側)聲霸卡,當聲霸卡成功注冊後,打開 hdmiin apk 時,能用 tinyalsa 工具正常的進行錄(tinycap)播(tinyplay)時,此部分即可調通。

rk3399 具有三組I2S 控制器,是以在硬體上連接配接方式有所不同。根據應用場景的不同,當 749 側的 I2S 連接配接到cpu 或 codec 的 I2S 時,需區分 I2S 的主從模式,一般來說,由于 749 側隻能作為 master 模式,是以當和 749 側連接配接的另一側,則需要配置為 slave 模式

rk3399_android7.1音頻通路相關說明Android 7.1/ kernel 4.4 的 的 I2S M/S

Android 7.1/ kernel 4.4 的 的 I2S M/S

4.4 核心使用了 simple-card 通用的 machine 驅動進行聲霸卡的注冊,為了配置主要的 I2S為 slave 模式。

配置檔案:arch/arm64/boot/dts/rockchip/rk3399-box-rev2-ne4000.dts
        tc358749x_sound:tc358749x-sound {
     		compatible = "simple-audio-card";
    		simple-audio-card,format = "i2s";
    		simple-audio-card,name = "rockchip,tc358749x-codec";
    		simple-audio-card,bitclock-master = <&sound0_master>;
    		simple-audio-card,frame-master = <&sound0_master>;
    
     		simple-audio-card,cpu {
    			sound-dai = <&spdif>;
    			sound-dai = <&i2s1>;
     		};
    		sound0_master: simple-audio-card,codec {
    			sound-dai = <&tc358749x>;
     		};
     	};
     對于rk3399具有多組 I2S 的平台,注冊聲霸卡的方式是把 749 和 codec注冊成一張聲霸卡,壓縮包更新檔預設是用這種方式注冊的。
        rt5651-sound {
                status = "disabled";
            };
    	
    	hdmiin-sound {
    		compatible = "rockchip,rockchip-rt5651-tc358749x-sound";
    		rockchip,cpu = <&i2s0 &i2s1>;
    		rockchip,codec = <&rt5651 &tc358749x>;
     		status = "okay";
     	};
    原dts中未對tc358749對應的i2s做配置,添加i2s1配置
        +&i2s1 {
    		status = "okay";
    		rockchip,i2s-broken-burst-len;
            rockchip,playback-channels = <2>;
            rockchip,capture-channels = <2>;
            #sound-dai-cells = <0>;
        };
    目前tc358749晶片挂在i2c1下面,在i2c1下面配置tc358749
        tc358749x: [email protected]0f {
		    #sound-dai-cells = <0>;
	        compatible = "toshiba,tc358749x";
        	reg = <0x0f>;
	        power-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>; //GPIO4_A7
        	stanby-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; //GPIO3_C0 change to GPIO1_B5
	        reset-gpios = <&gpio3 30 GPIO_ACTIVE_HIGH>; //GPIO3_D6
        	int-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; //GPIO4_A5
   	        pinctrl-names = "default";
        	pinctrl-0 = <&hdmiin_gpios>;
	        status = "okay";
    	};
    添加目前音頻配置方案,需修改音頻驅動代碼,根據RK提供修改方案進行修改

           

#測試驅動是否正常

1、tool & cmds :

mmm external/tinyalsa/ [ tinymix tinyplay tinycap ] //從sdk源碼中編譯出tinyalsa測試工具

目前系統提供tinyalsa音頻測試工具可直接用于音頻測試
        tinymix         音頻通路配置
        tinypcminfo     用于檢視pcm通道的相關資訊
        tinyplay        播放音頻
        tinycap         錄音(預設情況下該工具不安裝,需在external/tinyalsa目錄下編譯才會生成)
    錄音:tinycap 001.wav -D 1 -d 1
        001.wav 音頻檔案(tinycap隻能錄到wav格式的音頻檔案)
        -D  聲霸卡 number
        -d  pcm number
        結束錄音用 ctrl+c 組合鍵結束
播放: tinyplay 001.wav -D 0 -d 0
           

調試注意事項

在測試HDMI IN錄音時出現無法錄音的情況,檢查後發現在i2s1的配置中出現GPIO口複用,需将複用的GPIO口注掉:
           
i2s1 {
                i2s1_2ch_bus: i2s1-2ch-bus {
                        rockchip,pins =
                                <4 3 RK_FUNC_1 &pcfg_pull_none>,
                                <4 4 RK_FUNC_1 &pcfg_pull_none>,
                                //<4 5 RK_FUNC_1 &pcfg_pull_none>,
                                <4 6 RK_FUNC_1 &pcfg_pull_none>;
                                //<4 7 RK_FUNC_1 &pcfg_pull_none>;
                };
        };

           

問題調試排查的一些方法

1、通過cat /proc/asound/cards确認聲霸卡有沒有注冊上

rk3399_mid:/ # cat /proc/asound/cards
             0 [rkhdmidpsound  ]: rk-hdmi-dp-soun - rk-hdmi-dp-sound
                                  rk-hdmi-dp-sound
             1 [realtekrt5651co]: realtekrt5651co - realtekrt5651codec_hdmiin
                                  realtekrt5651codec_hdmiin
           

2、檢視目前聲霸卡裝置:

rk3399_mid:/ # ls /dev/snd/                                              
                controlC0 controlC1 pcmC0D0p pcmC1D0c pcmC1D0p pcmC1D1c timer 
            p   播放裝置
            c   錄音裝置
         HDMI out:pcmC0D0p
         codec : pcmC1D0c pcmC1D0p
         HDMI IN : pcmC1D1c
           

3、tc358749 連接配接到cpu的某組i2s,其對應的pcm裝置在某聲霸卡某pcm下,比如”pcmC1D0c”,說明749對應的pcm裝置在聲霸卡1,pcm号為0,則先打開 hdmiin apk,可以這樣錄音:

tinycap /sdcard/test.wav -D 1 -d 0 -c 2 -r 44100 -b 16
           

-D:聲霸卡 number

-d:pcm number

結束錄音用 ctrl+c 組合鍵結束。 注:不能用 windows 的 cmd 指令視窗進行 ctrl+c 結束 。錄音結束後,拷貝 test.wav 出來看看是否正常錄到音。如果沒有的話,确認聲霸卡注冊成功,用示波器量 tc358749 端的 i2s 信号,SDO 腳是否有信号,沒有的找東芝的 FAE 咨詢。

4、常用檢視聲霸卡狀态和資訊info 的指令操作:

cat /proc/asound/card*/pcm*/sub*/status |grep 'stat|close' -EC1
	cat /proc/asound/card*/pcm*/sub*/info|grep id -C1|grep name -v
	cat /proc/asound/card*/pcm*/sub*/info
           
rk3288:/ $ cat /proc/asound/card*/pcm*/sub*/info                               
card: 0
device: 0 //輸入裝置類型
subdevice: 0
stream: CAPTURE //錄音裝置
id: RT5651 PCM rt5651-aif1-0
name: 
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 1
card: 0
device: 0 //輸出裝置類型
subdevice: 0
stream: PLAYBACK //放音裝置
id: RT5651 PCM rt5651-aif1-0
name: 
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 1
           

RK3399 HDMIIN HAL層實作方案

HAL 層的實作是基于音頻驅動已經調好的情況下來實作的,其基本思路是用 alsa-soc lib的API 來做的,主要API如下:
        1. pcm_open
            打開指定聲霸卡下的 pcm 裝置
        2. pcm_frames_to_bytes
            傳回讀回幀數的總大小用位元組
        3. pcm_read
            讀音頻資料
        4. pcm_wirte
            寫音頻資料
        5. pcm_close
            關閉 pcm 裝置
           

一般的程式設計步驟都是按 linux 的程式設計習慣統一調用界面來的,也即:pcm_open - > pcm_write/

pcm_read -> pcm_close

綜上,HAL 層要做的便是按上面順序進行的,pcm_read 從某聲霸卡 pcm 裝置讀取音頻資料,也即是對 749 聲霸卡進行錄音,pcm_write 把讀到的音頻資料寫到某聲霸卡中去,也即是把得到 749的音頻資料通過這張聲霸卡播放,具體的業務邏輯依據實際需求而定。

HAL層對HDMIIN的音頻通路做了單獨的處理,同時對上層apk進行了修改,根據RK提供的更新檔處理上層音頻通路

分析HAL層log

logcat -s AudioHardwareTiny audio_hw_hdmiin alsa_route
           

RK3399 HDMIIN音頻采樣率

目前HDMIIN可讀取的音頻采樣率為44.1khz,還無法适配其他采樣率,當其他音頻采樣率接入時,錄取的聲音會出現斷斷續續的狀态,根據RK提供的更新檔進行修改

hal部分 相關函數說明

調試階段,單獨編譯mmm hardware/rockchip/audio/tinyalsa_hal/ 得到

audio.primary.rk30board.so,push進機器驗證即可。

out/target/product/rk3288/vendor/lib/hw/audio.primary.rk30board.so

hardware/rockchip/audio/tinyalsa_hal/audio_hw.c檔案中start_output_stream函數為判斷輸出(聲音輸出)裝置類型,選擇音頻輸出通路:

根據out-> device 類型判斷

if (out->device & (AUDIO_DEVICE_OUT_AUX_DIGITAL)

然後選擇使用音頻路由輸出:

card = adev->out_card[SND_OUT_SOUND_CARD_HDMI];

然後對應打開哪張聲霸卡:

out->pcm[SND_OUT_SOUND_CARD_HDMI]=pcm_open(card,PCM_DEVICE_HDMIOUT, PCM_OUT | PCM_MONOTONIC, &out->config);

hardware/rockchip/audio/tinyalsa_hal/audio_hw.c檔案中start_input_stream函數為判斷輸入(聲音錄入)裝置類型,選擇音頻輸入通路。

audio_hw.c檔案中read_in_sound_card 接口 //從節點擷取聲霸卡資訊:

file = fopen(SND_CARDS_NODE,“r”);

while(get_line(file,buf,sizeof(buf)) >= 0){

if(is_mic_in_sound_card(buf)){

device->in_card[SND_IN_SOUND_CARD_MIC] = get_card_number(buf);

}

audio_hw.c檔案的 adev_set_parameters 函數會去擷取一些屬性參數,然後設定走哪個route,route的宏定義在alsa_audio.h檔案。

然後在adev_set_parameters中調用str_parms_get_str擷取對應字元串的屬性(下面的例子是擷取字元串HDMIin_enable的屬性,該屬性在apk中調用原生的接口

AudioManager.setParameters("HDMIin_enable=true")):

3060     /* HDMIin enable/disable */
3061     val = str_parms_get_str(parms, "HDMIin_enable", value, sizeof(value));
3062     if (0 <= val) {
3063         if (strcmp(value, "true") == 0) {
3064              ALOGD("\n##[czd]%s:-------- HDMIin_enable(%s) ---------##\n\n", __func__, value);
3065             adev->hdmiin_state = true;
3066             //route_pcm_open(HDMI_IN_NORMAL_ROUTE);
3067             route_pcm_open(HDMI_IN_CAPTURE_ROUTE);
3068             ALOGD("Enable HDMIin");
3069         } else if (strcmp(value, "false") == 0) {
3070              ALOGD("\n##[czd]%s:-------- HDMIin_disable(%s) ---------##\n\n", __func__, value);
3071             route_pcm_open(HDMI_IN_OFF_ROUTE);
3072             adev->hdmiin_state = false;
3073             ALOGD("Disable HDMIin");
3074         } else {
3075              ALOGD("\n##[czd]%s:-------- HDMIin_enable(%s) ---------##\n\n", __func__, value);
3076             ALOGE("Unknown HDMIin state %s!!!", value);
3077             ret = -EINVAL;
3078         }
3079     }
           

繼續閱讀