参考:
Android Qcom Audio入门学习
安卓音频学习
qcom SM6125平台 dmic调试
linux音频子系统 - ASoC-PCM之machine
[Linux Audio Driver] 高通平台MI2S总线配置
【Audio driver】mixer_paths.xml文件分析
Qualcomm Audio HAL 音频通路设置
高通8155 音频数据从HAL到DSP
平台:qcm6125
系统:android13
最近做项目需要用到I2S作输入,硬件连接如下:
IO说明
查看当前声卡
trinket:/sys/class/sound # ls -alh card0/
lrwxrwxrwx 1 root root 0 2024-05-23 00:28 device -> ../../../soc:qcom,msm-audio-apr:qcom,q6core-audio:sound
trinket:/sys/class/sound # ls -alh controlC0/
total 0
drwxr-xr-x 3 root root 0 1970-01-01 08:00 .
drwxr-xr-x 126 root root 0 1970-01-01 08:00 ..
-r--r--r-- 1 root root 4.0K 2024-05-23 00:28 dev
lrwxrwxrwx 1 root root 0 2024-05-23 00:28 device -> ../../card0
drwxr-xr-x 2 root root 0 2024-05-23 00:28 power
lrwxrwxrwx 1 root root 0 2024-05-23 00:28 subsystem -> ../../../../../../../../../class/sound
-rw-r--r-- 1 root root 4.0K 2024-05-23 00:28 uevent
查看对应的FE pcm设备是否存在: cat /proc/asound/pcm
Codec是否注册到了声卡
cat /sys/kernel/debug/asoc/codecs
ADSP core driver
./vendor/qcom/opensource/audio-kernel/dsp/q6core.c
Machine driver
./vendor/qcom/opensource/audio-kernel/asoc/sm6150.c
DAI driver
./vendor/qcom/opensource/audio-kernel/asoc/msm-dai-q6-v2.c
音频控件配置:
tinymix : 配置音频路由
tinycap: 录音
tinyplay: 播放
对应的dts配置
&sm6150_snd {
...
// dmic和i2s不能共用
//qcom,cdc-dmic01-gpios = <&cdc_dmic01_gpios>;
//qcom,cdc-dmic23-gpios = <&cdc_dmic23_gpios>;
qcom,sec-mi2s-gpios = <&cdc_sec_i2s_gpios>;
...
};
cdc_sec_i2s_gpios: cdc_sec_i2s_pinctrl {
compatible = "qcom,msm-cdc-pinctrl";
pinctrl-names = "aud_active", "aud_sleep";
pinctrl-0 = <&sec_i2s_sck_active &sec_i2s_ws_active &sec_i2s_sd0_active &sec_i2s_sd1_active>;
pinctrl-1 = <&sec_i2s_sck_sleep &sec_i2s_ws_sleep &sec_i2s_sd0_sleep &sec_i2s_sd1_sleep>;
};
pinctrl DTS配置
sec_i2s_sck_active: sec_i2s_sck_active {
mux {
pins = "gpio125";
function = "sec_mi2s";
};
config {
pins = "gpio125";
drive-strength = <8>;
bias-disable;
output-high;
};
};
sec_i2s_sck_sleep: sec_i2s_sck_sleep {
mux {
pins = "gpio125";
function = "sec_mi2s";
};
config {
pins = "gpio125";
drive-strength = <2>;
bias-pull-down;
};
};
sec_i2s_ws_active: sec_i2s_ws_active {
mux {
pins = "gpio126";
function = "sec_mi2s";
};
config {
pins = "gpio126";
drive-strength = <8>;
bias-disable;
output-high;
};
};
sec_i2s_ws_sleep: sec_i2s_ws_sleep {
mux {
pins = "gpio126";
function = "sec_mi2s";
};
config {
pins = "gpio126";
drive-strength = <2>;
bias-pull-down;
};
};
sec_i2s_sd0_active: sec_i2s_sd0_active {
mux {
pins = "gpio127";
function = "sec_mi2s";
};
config {
pins = "gpio127";
drive-strength = <8>;
bias-disable;
output-high;
};
};
sec_i2s_sd0_sleep: sec_i2s_sd0_sleep {
mux {
pins = "gpio127";
function = "sec_mi2s";
};
config {
pins = "gpio127";
drive-strength = <2>;
bias-pull-down;
input-enable;
};
};
sec_i2s_sd1_active: sec_i2s_sd1_active {
mux {
pins = "gpio128";
function = "sec_mi2s";
};
config {
pins = "gpio128";
drive-strength = <8>;
bias-disable;
input-enable;
};
};
sec_i2s_sd1_sleep: sec_i2s_sd1_sleep {
mux {
pins = "gpio128";
function = "sec_mi2s";
};
config {
pins = "gpio128";
drive-strength = <2>;
bias-pull-down;
input-enable;
};
};
输入命令
tinymix 'MultiMedia1 Mixer SEC_MI2S_TX' 1
tinycap /data/test.wav
Unable to open PCM device
(cannot set hw params: Invalid argument)Captured 0 frames
刚开始毫无头绪,开始查找文档 寻找疑点。发现qcom,msm-mi2s-master默认配置的是master模式,作为输入 应该配置成slave。
/* kernel/msm-4.14/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt */
/*- qcom,msm-mi2s-master: This property is used to inform machine driver
if MSM is the clock master of mi2s. 1 means master and 0 means slave. The
first entry is primary mi2s; the second entry is secondary mi2s, and so on.
*/
/* vendor/qcom/opensource/audio-kernel/asoc/sm6150.c
enum {
PRIM_MI2S = 0,
SEC_MI2S,
TERT_MI2S,
QUAT_MI2S,
QUIN_MI2S,
MI2S_MAX,
};
*/
qcom,msm-mi2s-master = <1>, <0>, <1>, <1>, <1>;
qcom,auxpcm-audio-intf = <0>; //怕会影响也一起关了
编译烧录测试发现还是不行,头大!
后面在Machine 中添加打印继续追踪
static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
...
case 1:
if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0)
goto error_invalid_data;
...
}
static int msm_dai_q6_mi2s_platform_data_validation(
struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
{
struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
struct msm_mi2s_pdata *mi2s_pdata =
(struct msm_mi2s_pdata *) pdev->dev.platform_data;
unsigned int ch_cnt;
int rc = 0;
u16 sd_line;
if (mi2s_pdata == NULL) {
pr_err("%s: mi2s_pdata NULL", __func__);
return -EINVAL;
}
rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines,
&sd_line, &ch_cnt);
if (rc < 0) {
dev_err(&pdev->dev, "invalid MI2S RX sd line config\n");
goto rtn;
}
if (ch_cnt) {
dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
sd_line;
dai_data->rx_dai.pdata_mi2s_lines = sd_line;
dai_driver->playback.channels_min = 1;
dai_driver->playback.channels_max = ch_cnt << 1;
} else {
dai_driver->playback.channels_min = 0;
dai_driver->playback.channels_max = 0;
}
rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines,
&sd_line, &ch_cnt);
if (rc < 0) {
dev_err(&pdev->dev, "invalid MI2S TX sd line config\n");
goto rtn;
}
if (ch_cnt) {
dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
sd_line;
dai_data->tx_dai.pdata_mi2s_lines = sd_line;
dai_driver->capture.channels_min = 1;
dai_driver->capture.channels_max = ch_cnt << 1;
} else {
dai_driver->capture.channels_min = 0;
dai_driver->capture.channels_max = 0;
}
dev_err(&pdev->dev, "%s: playback sdline 0x%x capture sdline 0x%x\n",
__func__, dai_data->rx_dai.pdata_mi2s_lines,
dai_data->tx_dai.pdata_mi2s_lines);
dev_err(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
__func__, dai_driver->playback.channels_max,
dai_driver->capture.channels_max);
rtn:
return rc;
}
static int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev)
{
....
rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines",
&rx_line);
if (rc) {
dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__,
"qcom,msm-mi2s-rx-lines");
goto free_pdata;
}
rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines",
&tx_line);
if (rc) {
dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__,
"qcom,msm-mi2s-tx-lines");
goto free_pdata;
}
dev_err(&pdev->dev, "dev name %s Rx line 0x%x , Tx ine 0x%x\n",
dev_name(&pdev->dev), rx_line, tx_line);
mi2s_pdata->rx_sd_lines = rx_line;
mi2s_pdata->tx_sd_lines = tx_line;
mi2s_pdata->intf_id = mi2s_intf;
....
}
最后发现 msm-mi2s-tx-lines 默认是0 导致这个问题
...
dai_mi2s1: qcom,msm-dai-q6-mi2s-sec {
compatible = "qcom,msm-dai-q6-mi2s";
qcom,msm-dai-q6-mi2s-dev-id = <1>;
qcom,msm-mi2s-rx-lines = <1>;
qcom,msm-mi2s-tx-lines = <0>;
};
...
改成1就能正常录音了,后续再补充
&dai_mi2s1{
compatible = "qcom,msm-dai-q6-mi2s";
qcom,msm-dai-q6-mi2s-dev-id = <1>;
qcom,msm-mi2s-rx-lines = <0>;
qcom,msm-mi2s-tx-lines = <1>;
};