本文记录工作中,在rk3568安卓13的SDK中适配板载外设spdif功能时具体的驱动修改和遇到的问题。
目录
前言
spdif功能,大部分人可能不熟悉,我也是第一次接触。百度后才发现在HDMI未出现之前spdif很火,能无损传输数字音频,仅用一根线就能打包传输Dolby Digital、DTS编码的5.1环绕声,大幅简化家庭影院布线,在CD机、早期DVD播放机、老款AV功放上是标配数字音频接口。本文介绍在rk3568安卓13的SDK中,是如何实现spdif功能的。
一、spdif介绍
SPDIF 严谨写法为 S/PDIF,是 Sony/Philips Digital Interface Format 的缩写,中文称为索尼/飞利浦数字接口格式 。SPDIF 传输的是数字信号而非模拟信号,采用双相标记编码(BMC)进行数据传输,时钟信号嵌入在数据流中 。可传输 PCM 立体声音频及 Dolby Digital、DTS 等多声道压缩音频信号,但不支持杜比 TrueHD 及 EAC-3 格式的 7.1 声道音频 。
SPDIF 分为同轴(电信号)和光纤(光信号)两种物理接口,传输的信号内容相同,仅载体不同 。近距离传输一般选同轴,长距离或电磁环境复杂时选光纤 。
二、驱动适配
1.硬件原理
rk3568-pinctrl.dtsi文件中关于spdif引脚的定义如下,可以看出设备树中需要用spdifm2_tx这个节点
kernel-5.10/arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
spdif {
/omit-if-no-ref/
spdifm0_tx: spdifm0-tx {
rockchip,pins =
/* spdifm0_tx */
<1 RK_PA4 4 &pcfg_pull_none>;
};
/omit-if-no-ref/
spdifm1_tx: spdifm1-tx {
rockchip,pins =
/* spdifm1_tx */
<3 RK_PC5 2 &pcfg_pull_none>;
};
/omit-if-no-ref/
spdifm2_tx: spdifm2-tx {
rockchip,pins =
/* spdifm2_tx */
<4 RK_PC4 2 &pcfg_pull_none>;
};
};
2.设备树修改
rk官方参考文件rk3568-evb.dtsi中已经配置好了spdif,我们只需修改将引脚对应起来就可以了。我们可以先看下官方芯片级设备树文件rk3568.dtsi中关于spdif的节点定义,只有以下节点,可以看出我们需要把spdif_8ch节点下的引脚改成自用的即可,具体引脚名用spdifm2_tx。为保证规范我们需要到自己的板级设备树文件去改。
kernel-5.10/arch/arm64/boot/dts/rockchip/rk3568.dtsi
spdif_8ch: spdif@fe460000 {
compatible = "rockchip,rk3568-spdif";
reg = <0x0 0xfe460000 0x0 0x1000>;
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dmac1 1>;
dma-names = "tx";
clock-names = "mclk", "hclk";
clocks = <&cru MCLK_SPDIF_8CH>, <&cru HCLK_SPDIF_8CH>;
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spdifm0_tx>;
status = "disabled";
};
kernel-5.10/arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi
spdif-sound {
status = "okay";
compatible = "simple-audio-card";
simple-audio-card,name = "ROCKCHIP,SPDIF";
simple-audio-card,mclk-fs = <128>;
simple-audio-card,cpu {
sound-dai = <&spdif_8ch>;
};
simple-audio-card,codec {
sound-dai = <&spdif_out>;
};
};
spdif_out: spdif-out {
status = "okay";
compatible = "linux,spdif-dit";
#sound-dai-cells = <0>;
};
&spdif_8ch {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spdifm2_tx>; //修改为自己板子上对应的引脚
};
3. 内核配置
还需要确保内核中关于SPDIF的驱动编译开关打开,一般默认是打开的,最好核对下。
kernel-5.10/arch/arm64/configs/rockchip_defconfig
CONFIG_SND_SOC_SPDIF=y
4.驱动测试
这个时候打包完SDK测试就能看到驱动适配完了,在安卓板子上执行以下命令,预期输出以下。
rk3568_t:/ # cat /proc/asound/cards
0 [rockchiprk809 ]: rockchip-rk809 - rockchip-rk809
rockchip-rk809
1 [rockchiphdmi ]: rockchip_hdmi - rockchip,hdmi
rockchip,hdmi
2 [ROCKCHIPSPDIF ]: ROCKCHIP_SPDIF - ROCKCHIP,SPDIF
ROCKCHIP,SPDIF
但是用自带的音乐播放器播放音乐spdif还是没有输出,这是因为安卓层音频路由策略的问题,下一章介绍安卓音频路由策略的修改,可以先用tinyplay工具指定音卡去播放测试,tinyplay工具不支持MP3格式,最好用wav格式音频测试,执行以下命令,其中-D 2表示指定序号为2的音卡,上图看出就是对应的spdif,这时能通过spdif正常播放音乐,这说明硬件和驱动都适配完成了。
rk3568_t:/ # tinyplay /data/local/tmp/bhs.wav -D 2 -d 0
Playing sample: 2 ch, 44100 hz, 16 bit 39156752 bytes
三.音频路由策略
1.配置文件修改
音频配置文件修改,因为spdif没有硬件检测功能,软件上配置为常在设备即可,需要修改以下文件
rk3568-sdk/device/rockchip/common/audio_policy_configuration.xml
<attachedDevices>
<item>Speaker</item>
<item>Built-In Mic</item>
<item>SPDIF Out</item> <!--增加spdif,名字可参考文件下文 -->
<item>HDMIIn</item>
</attachedDevices>
2.音频路由策略修改
一般插耳机的话,耳机优先级最高。我这里的需求是耳机或喇叭接入时优先这俩工作,但这俩没插时,就spdif和hdmi同时工作。调试时发现同一个流打开多个 PCM 设备后, route_pcm_card_open 函数接口只允许一个“活动路径”存在,因为spdif不走route_pcm_card_open接口,我这里就没有修改接口以支持多个音频路由。有需求的伙伴可以去研究下如何修改route_pcm_card_open接口以支持多个设备。官方已经写好多设备同时输出的接口了,调用这个函数open_sound_card_policy即可,需要打开相关宏,可以看到start_output_stream接口中调用open_sound_card_policy,如下所示,SUPPORT_MULTIAUDIO这个宏官方已经处理好了,至于BOX_HAL需要修改Android.mk,注释掉BOX_HAL定义前的判断条件即可。还需要修改同文件中的out_write接口以支持音频流写入多设备。
//hardware/rockchip/audio/tinyalsa_hal/audio_hw.c文件中
//start_output_stream函数中,通过宏定义开关调用open_sound_card_policy
#ifndef SUPPORT_MULTIAUDIO
#ifdef BOX_HAL
open_sound_card_policy(out);
#endif
#endif
//在open_sound_card_policy函数中,新增业务逻辑如果耳机或者喇叭接入则返回,只耳机或喇叭响
for (int i = 0; i < out->num_configs; ++i) {
if (audio_is_bluetooth_out_sco_device(out->devices[i]))
return;
//新增代码
if (out->devices[i] == AUDIO_DEVICE_OUT_WIRED_HEADPHONE || out->devices[i] == AUDIO_DEVICE_OUT_WIRED_HEADSET ||
out->devices[i] == AUDIO_DEVICE_OUT_SPEAKER)
return;
}
//out_write函数中,需要在调用写音频流处修改将break注释掉,以支持多设备
for(i = 0; i < SND_OUT_SOUND_CARD_MAX; i++){
if (out->pcm[i]) {
if (i == SND_OUT_SOUND_CARD_BT) {
// HARD CODE FIXME 48000 stereo -> 8000 stereo
size_t inFrameCount = bytes/2/2;
int destRate = out->config.rate;
int srcRate = adev->bt_wb_speech_enabled? 16000:8000;
int coefficient = (destRate/srcRate);
size_t outFrameCount = inFrameCount/coefficient;
int16_t out_buffer[outFrameCount*2];
memset(out_buffer, 0x00, outFrameCount*2);
out->resampler->resample_from_input(out->resampler,
(const int16_t *)buffer,
&inFrameCount,
out_buffer,
&outFrameCount);
ret = pcm_write(out->pcm[i], (void *)out_buffer, outFrameCount*2*2);
if (ret != 0)
break;
} else {
/*
* do not write hdmi/spdif snd sound if they are taken by other
bitstream/multi channle pcm stream
*/
if((i == SND_OUT_SOUND_CARD_HDMI) && (adev->owner[SOUND_CARD_HDMI] != (int*)out) && (adev->owner[SOUND_CARD_HDMI] != NULL)) ||
((i == SND_OUT_SOUND_CARD_SPDIF) && (adev->owner[SOUND_CARD_SPDIF] != (int*)out) && (adev->owner[SOUND_CARD_SPDIF] != NULL))){
continue;
}
ret = pcm_write(out->pcm[i], (void *)buffer, bytes);
if (ret != 0) {
ALOGD("%s:%d pcm_write error, errno = %d, %s",
__FUNCTION__, __LINE__, errno, pcm_get_error(out->pcm[i]));
// break; //新加修改,此处注释掉,以支持多设备
}
}
}
}
hardware/rockchip/audio/tinyalsa_hal/Android.mk
#注释掉BOX_HAL定义前的判断条件
#ifneq ($(filter box atv, $(strip $(TARGET_BOARD_PLATFORM_PRODUCT))), ) //注释掉
commonCFlags += -DBOX_HAL
#endif
总结
本文介绍了本人在rk3568安卓13的SDK中适配spdif功能时,所做的具体修改点和遇到的问题。修改后的驱动代码已经过验证,能实现我的需求,也希望能为其他人提供参考,少走弯路。

155

被折叠的 条评论
为什么被折叠?



