tag 标签: SAI

相关博文
  • 热度 12
    2023-4-19 17:50
    1371 次阅读|
    0 个评论
    B y Toradex 胡珊逢 Apalis iMX8 计算机模块的数字音频接口 SAI ( Synchronous Audio Interface )可以配置为 AC97 、 I2S 格式,用于连接外部音频编解码器。文章接下来将介绍在 Linux BSP v6 上如何扩展第二路 SGTL5000 。 iMX8 处理器具有多路 SAI 通道, SAI1 已经被模块片上的 SGTL5000 使用, SAI0 通道引出到模块金手指上,并且是兼容 Apalis 标准数字音频接口, 该 通道在基于其他 CPU 的 Apalis 模块也可以直接使用。因此,我们选择 SAI0 扩展外部 SGTL5000 。 SGTL5000 面向 iMX8 处理器的接口主要是 I2S 和时钟信号。 Apalis iMX8 的 SAI0 通道可以直接连接 SGTL5000 的 I2S 接口。但是 MXM3_194 引脚上没有时钟信号。对于外部音频编解码器如果需要时钟输入,如 SGTL5000 的 SYS_MCLK ,可以选择 MXM3_215 引脚,或者采用外部时钟源,如晶振。 SGTL5000 在 master mode 时可以向 Apalis iMX8 输出 I2S_LRCLK 和 I2S_SCLK 信号。 当 SYS_MCLK = SupportedRates * Fs , I2S_LRCLK 可以和 SYS_MCLK 同步。如果无法满足, SGTL5000 则会使用内部的 PLL 产生符合音源的 I2S_LRCLK 频率。 PLL 的使用会增加 额外的功耗。 PLL 的时钟输入也来自 SYS_MCLK ,可以支持 8.0 到 27MHz 频率。 外部 SGTL5000 原理图如下,其中使用一个 12.28MHz 的外部有源晶振。 接下来修改 device tree 添加 SGTL5000 的相关配置。 imx8-apalis-v1.1.dtsi 的 I2C3 节点添加 SGTL5000 的 I2C 配置。 --------------------------------------- /* Apalis I2C3 (CAM) */ &i2c3 { #address-cells = ; #size-cells = ; clock-frequency = ; pinctrl-names = "default"; pinctrl-0 = ; status = "okay"; external_sgtl5000: audio-codec@a { #sound-dai-cells = ; compatible = "fsl,sgtl5000"; reg = ; //micbias-resistor-k-ohms = ; micbias-voltage-m-volts = ; VDDA-supply = ; VDDIO-supply = ; VDDD-supply = ; clocks = ; }; }; --------------------------------------- SGTL5000 使用外部的电源供电,所以也需要添加 VDDA-supply , VDDIO-supply 和 VDDD-supply 。 --------------------------------------- reg_ex_auido_3v: regulator-ex-audio-3v { compatible = "regulator-fixed"; regulator-name = "ex-audio-3V"; regulator-min-microvolt = ; regulator-max-microvolt = ; regulator-always-on; }; reg_ex_auido_1v8: regulatorex-audio-1v8 { compatible = "regulator-fixed"; regulator-name = "+V1.8"; regulator-min-microvolt = ; regulator-max-microvolt = ; }; --------------------------------------- 以及外部时钟定义。 --------------------------------------- ex_audio_clk: sgtl5000_12M { compatible = "fixed-clock"; #clock-cells = ; clock-frequency = ; }; --------------------------------------- 在原有的 sound 节点后再增加一个 外部 SGTL5000 的 sound_external 。 --------------------------------------- sound_external { compatible = "simple-audio-card"; simple-audio-card,bitclock-master = ; simple-audio-card,format = "i2s"; simple-audio-card,frame-master = ; simple-audio-card,name = "external-sgtl5000"; simple-audio-card,cpu { sound-dai = ; }; dailink_master_external: simple-audio-card,codec { sound-dai = ; system-clock-frequency = ; }; }; --------------------------------------- 配置 sai0 节点。 --------------------------------------- &sai0 { assigned-clocks = , , , ; assigned-clock-rates = , , , ; pinctrl-names = "default"; pinctrl-0 = ; #sound-dai-cells = ; //fsl,txm-rxs; }; --------------------------------------- 最后在 imx8-apalis-ixora-v1.2.dtsi 里启用 SAI0 。 --------------------------------------- &sai0 { status = "okay"; }; --------------------------------------- 参考 这里 重新编译和部署 device tree 。上面提到的 patch 文件下载 。重新启动后检查外部 SGTL5000 挂载情况。在 I2C-5 通道上已经发现地址为 0x0a 的设备。 --------------------------------------- root@apalis-imx8-06852111:/tmp# i2cdetect -y -r 5 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- UU -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --------------------------------------- 相应的驱动也加载。 --------------------------------------- root@apalis-imx8-06852111:/tmp# dmesg|grep sgtl sgtl5000 3-000a: sgtl5000 revision 0x11 sgtl5000 5-000a: sgtl5000 revision 0x11 --------------------------------------- 检查声卡设备,发现 sysdefault:CARD=externalsgtl500 。 --------------------------------------- root@apalis-imx8-06852111:/tmp# aplay -L null Discard all samples (playback) or generate zero samples (capture) sysdefault:CARD=externalsgtl500 external-sgtl5000, 59040000.sai-sgtl5000 sgtl5000-0 Default Audio Device sysdefault:CARD=apalisimx8qmsgt apalis-imx8qm-sgtl5000, 59050000.sai-sgtl5000 sgtl5000-0 Default Audio Device sysdefault:CARD=imxspdif imx-spdif, S/PDIF PCM snd-soc-dummy-dai-0 Default Audio Device --------------------------------------- 使用 BSP 默认的音频文件播放。 --------------------------------------- root@apalis-imx8-06852111:/tmp# aplay -D sysdefault:CARD=externalsgtl500 ~/sound/Gong.wav --------------------------------------- 如果音量小的话,使用 alsamixer 调整。按 F6 选择外部 SGTL5000 。 Gong.wav 是一个采样率 44.1KHz , 16bit 双声道文件,比特率 =16*2*44.1K=1.4MHz 。如下面示波器 测量 显示, CH1 是 LRCLK , CH2 是 SCLK 。 SGTL5000 在播放时会根据音源文件调整工作参数,例如时钟, PLL 工作状态。可以通过 cat /sys/kernel/debug/regmap/5-000a/registers 命令查看寄存器配置。由于 SGTL5000 的已经被其驱动占用,所以无法使用 i2cget 读取寄存器。 例如寄存器 CHIP_CLK_CTRL 0x0004 的值为 0007 。 bit3:2=0x01 ,表示采样频率为 44.1KHz 。 Bit 1:0 = 0x3 ,表示使用 PLL 。这是由于 44.1KHz 信号无法通过 12.28MHz 的 256 、 384 或 512 倍分频得到,所以只能使用 PLL 产生。 外部 SGTL5000 的录音功能可以使用下面命令。 --------------------------------------- root@apalis-imx8-06852111:/tmp# arecord -D hw:0,0 -V mono -c 2 -f S16_LE -r 44100 -t wav mic.wav --------------------------------------- 其中 hw:0,0 可以 aplay -l 命令查看对应声卡的 card, subdevice 序号。 --------------------------------------- root@apalis-imx8-06852111:/tmp# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: externalsgtl500 , device 0: 59040000.sai-sgtl5000 sgtl5000-0 Subdevices: 1/1 Subdevice #0: subdevice #0 --------------------------------------- 总结 Apalis iMX8 的数字音频接口可以非常方便地扩展音频编解码器,具体的引脚分配、 device tree 配置等需要结合所使用的音频编解码器,有些可能会十分简单,如 MAX98357A ,其甚至不需要 I2C 和 SYS_MCLK 就可以直接工作。 Apalis iMX8 所使用的 Linux kernel 源码中有非常多的示例可供用户参考。
  • 热度 12
    2022-1-7 10:52
    1718 次阅读|
    0 个评论
    B y Toradex 胡珊逢 Colibri iMX6ULL 是 Toradex 面向低成本设备推出的 Arm 计算机模块。该产品没有音频编解码器,无法直接输出模拟音频信号。本文将介绍如何使用模块的数字音频 I2S 接口扩展 MAX98357A , 包括如何配置 device tree 和时钟。 在 Colibri iMX6ULL 模块上的 i.MX 6ULL SoC 通过 synchronous audio interfaces ( SAI ) 接口提供数字音频接口 , 可以支持 AC97 或者 I2S 以连接外部音频编解码器。 MAX98357A 是一款易于使用的音频解码器,片上带有 D 类功放。无需 I2C 配置和外部 MCLK 时钟,进一步简化电路设计。接下来我们使用 Colibri iMX6ULL 搭配 Colibri Evaluation Board , 安装 Linux BSP 5.4 为例进行说明。 Colibri iMX6ULL 总共有三个 SAI 接口 , 这里使用 SAI2 连接 MAX98357A 。 根据上面的连接关系 , 对应修改 device tree 。在 imx6ull-colibri-eval-v3.dtsi 中首先增加一个 codec_ext 节点。 --------------------------------------------- codec_ext: max98357a@0 { compatible = "maxim,max98357a"; #sound-dai-cells = ; }; --------------------------------------------- 然后再添加一个 simple audio card 的节点 sound 。其中的 sound-dai 引用了上面的定义的 codec_ext 。 --------------------------------------------- sound { compatible = "simple-audio-card"; status = "okay"; simple-audio-card,name = "max98357a"; simple-audio-card,format = "i2s"; simple-audio-card,bitclock-master = ; simple-audio-card,frame-master = ; simple-audio-card,codec { sound-dai = ; }; dailink_master_cpu: simple-audio-card,cpu { sound-dai = ; }; }; --------------------------------------------- 上面的 sound 节点中使用了 sai2 , 因此接下来需要对 sai2 进行初始化。 --------------------------------------------- &sai2 { pinctrl-names = "default"; pinctrl-0 = ; assigned-clocks = , ; assigned-clock-parents = ; assigned-clock-rates = , ; fsl,sai-asynchronous; /*fsl,sai-mclk-direction-output;*/ status = "okay"; }; --------------------------------------------- 这里有几个关键的参数。 pinctrl_sai2 配置了引脚复用关系。对于 MAX98357A 实际上只需要前面三个引脚 , 如前面表格中列出的连接关系。 MX6UL_PAD_JTAG_TMS__SAI2_MCLK 和 MX6UL_PAD_JTAG_TMS__CCM_CLKO1 为了方便测试相关时钟信号。而这两个其实是同一个引脚,只是复用功能选择不同。 sound 节点中 fsl,sai-mclk-direction-output; 参数同样也是为了测试 SAI 的 MCLK 输出 , 配合 MX6UL_PAD_JTAG_TMS__SAI2_MCLK 输出 SAI MCLK , 但该信号并不用于 MAX98357A 。 --------------------------------------------- pinctrl_sai2: sai2grp { fsl,pins = < MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x1F089 MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088 /*MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088*/ MX6UL_PAD_JTAG_TMS__CCM_CLKO1 0x17088 ; }; }; --------------------------------------------- sound 节点中还有三个重要属性 , assigned-clocks , assigned-clock-parents 和 assigned-clock-rates 。在介绍这些属性前 , 我们简单了解下 iMX6ULL 的 Clock Controller Module ( CCM ) 。 CCM 负责产生和控制 iMX6ULL 上每个模块运行所需的时钟信号,包括从 Arm CPU 核心到各种外设如 USB 、 Audio 、网络等。由于每个模块需要的时钟各不相同,从高频到低频,因此 CCM 内部有多个 PLL 以及分频器提供多种频率的时钟信号。对于本次使用的 SAI 模块 , 根据 iMX6ULL 芯片手册 Figure 18-2. Clock Tree - Part 1 , 其时钟关系如下。 SAI2_CLK_ROOT 通过 MUX 可以选择三个来源 , 分别是 PLL3 , PLL4 , PLL5 。中间会有多个不同位数的分频器,将 PLL 输出较高的时钟逐级降为较低的频率。这些分频器并不需要手动设置, CCM 驱动会根据期望输出的频率自动计算合适的分频比例。 SAI2_CLK_ROOT 最终为 SAI2 模块提供运行时钟信号。在 SAI 内部 , 时钟信号经过内部的分频器后产生 I2S 的 bit block 以及帧同步信号 sync 。 对于音频文件,可以计算出所需的 bit clock 。例如对于一个双通道, 16bit , 48K 采样率的音频文件, bit clock = 16x2x48000 = 1536000 。这个数值是我们所需的最终目标时钟。根据上面的图示 , bit clock 源自于 PLL4 。根据 iMX6ULL 芯片手册 18.5.1.3.4 Audio/video PLL 章节 , 该 PLL 的频率范围从 650MHz 到 1.3GHz 。以 1536000 为基数,取其整数 512 倍,得到 786432000 。即 786432000 经过总共 512 倍分频后可以产生 1536000 bit clock 信号。这时我们再回顾 sai2 节点中的一些时钟属性。 --------------------------------------------- assigned-clocks = , ; assigned-clock-parents = ; assigned-clock-rates = , ; --------------------------------------------- IMX6UL_CLK_SAI2_SEL 设置的 CCM_CSCMR1 寄存器中的 SAI2_CLK_SEL 位。这里设置为 2, 选择 PLL4 作为时钟源。 IMX6UL_CLK_PLL4_AUDIO_DIV 为 PLL4 分频后的时钟,设置为 19660800 ,这个数值是 1536000 的 24 倍。而 PLL4 在分频前为 786432000 , 这是 IMX6UL_CLK_PLL4_AUDIO_DIV 的 4 倍 , 在其 650MHz 到 1.3GHz 的频率范围内。 完成上面 device tree 修改后 , 重新编译 , 并更新到 Colibri iMX6ULL 。下面命令可以直接在模块上覆盖原来的 dtb 文件。 --------------------------------------------- root@colibri-imx6ull:~# ubiupdatevol /dev/ubi0_1 imx6ull-colibri-eval-v3.dtb --------------------------------------------- 重启后可以看到 max98357a 音频设备。 --------------------------------------------- root@colibri-imx6ull:~# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: max98357a , device 0: 202c000.sai-HiFi HiFi-0 fsl-sai 202c000.sai: clk_rate 0 Hz, mclk_clk id 0 fsl-sai 202c000.sai: clk_rate 24576000 Hz, mclk_clk id 1 fsl-sai 202c000.sai: ratio 16 for freq 1536000Hz based on clock 24576000Hz fsl-sai 202c000.sai: best fit: clock id=1, ratio=16, deviation=0 --------------------------------------------- 1536000Hz 的 bit clock 是通过 24576000Hz 进行 16 分频得到的。这是因为在 SAI 内部还有一个分频器可以对 sai2_clk_root 再次分频从而输出合适的 bit clock 。 I2S2_TCR2 寄存器 ( 地址 0x0202c008 ) 的最后 7 位 DIV 可以配置该分频器。分频数值为 (DIV + 1)x2 。读取该寄存器, DIV 值为 7, 进行 16 分频。对应上面 SAI 驱动调试日志的 “ratio 16 for freq 1536000Hz based on clock 24576000Hz” 。 --------------------------------------------- root@colibri-imx6ull:~# devmem2 0x0202c008 w /dev/mem opened. Memory mapped at address 0x76f4b000. Read at address 0x0202C008 (0x76f4b008): 0x07000007 --------------------------------------------- 最后接上 MAX98357A 和扬声器就可以听到播放的音频文件。 https://v.youku.com/v_show/id_XNTgzMjUxMTg2NA==.html 总结 通过扩展 MAX98357A 我们介绍了 iMX6 ULL SoC 音频驱动 SAI 的工作原理,以及如何配置 device tree 和测试方法。借鉴该方法,用户也可以扩展其他基于 I2S 的音频编解码器。