在调试Linux声音相关驱动时,有时只使用i2s等接口对接其他芯片而非codec,此时也需要按照alsa框架配置codec等信息。本文给出了一个虚拟codec,即dummy-codec的配置源码。
1,在dts中配置i2s、dummy-codec和声卡相关节点
i2s0: i2s@ff400000 {
compatible = "test,rk3399-i2s", "test,test-i2s";
reg = <0x0 0xff400000 0x0 0x1000>;
test,grf = <&grf>;
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH 0>;
dmas = <&dmac_bus 0>, <&dmac_bus 1>;
dma-names = "tx", "rx";
clock-names = "i2s_clk", "i2s_hclk";
clocks = <&cru SCLK_I2S0_8CH>, <&cru HCLK_I2S0_8CH>;
resets = <&cru SRST_I2S0_8CH>, <&cru SRST_H_I2S0_8CH>;
reset-names = "reset-m", "reset-h";
pinctrl-names = "default";
pinctrl-0 = <&i2s0_8ch_bus>;
power-domains = <&power RK3399_PD_SDIOAUDIO>;
test,clk-trcm = <1>;
status = "disabled";
};
dummy_codec: dummy-codec {
status = "okay";
compatible = "test,dummy-codec";
#sound-dai-cells = <0>;
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "mclk";
pinctrl-names = "default";
pinctrl-0 = <&i2s_8ch_mclk>;
};
test_i2s_sound: test-i2s-sound {
status = "okay";
compatible = "simple-audio-card";
simple-audio-card,name = "radia,radia-i2s-sound";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <256>;
simple-audio-card,cpu {
sound-dai = <&i2s0>;
};
simple-audio-card,codec {
sound-dai = <&dummy_codec>;
};
};
2,添加源代码linux/sound/soc/codecs/dummy-codec.c
/* * dummy_codec.c -- dummy audio codec * * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include <linux/clk.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/pcm.h> #include <sound/initval.h> struct dummy_codec_priv { struct snd_soc_codec *codec; struct clk *mclk; }; static int dummy_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct dummy_codec_priv *dummy_codec = snd_soc_codec_get_drvdata(codec); if (!IS_ERR(dummy_codec->mclk)) clk_prepare_enable(dummy_codec->mclk); return 0; } static void dummy_codec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct dummy_codec_priv *dummy_codec = snd_soc_codec_get_drvdata(codec); if (!IS_ERR(dummy_codec->mclk)) clk_disable_unprepare(dummy_codec->mclk); } static struct snd_soc_dai_ops dummy_codec_dai_ops = { .startup = dummy_codec_startup, .shutdown = dummy_codec_shutdown, }; struct snd_soc_dai_driver dummy_dai = { .name = "dummy_codec", .playback = { .stream_name = "Dummy Playback", .channels_min = 2, .channels_max = 384, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE), }, .capture = { .stream_name = "Dummy Capture", .channels_min = 2, .channels_max = 384, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE), }, .ops = &dummy_codec_dai_ops, }; static struct snd_soc_codec_driver soc_dummy_codec; static int radia_dummy_codec_probe(struct platform_device *pdev) { struct dummy_codec_priv *codec_priv; codec_priv = devm_kzalloc(&pdev->dev, sizeof(*codec_priv), GFP_KERNEL); if (!codec_priv) return -ENOMEM; platform_set_drvdata(pdev, codec_priv); codec_priv->mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(codec_priv->mclk)) { /* some devices may not need mclk,so warnnig */ dev_warn(&pdev->dev, "Unable to get mclk\n"); if (PTR_ERR(codec_priv->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; else if (PTR_ERR(codec_priv->mclk) != -ENOENT) return -EINVAL; } else { dev_info(&pdev->dev, "get mclk success\n"); } return snd_soc_register_codec(&pdev->dev, &soc_dummy_codec, &dummy_dai, 1); } static int radia_dummy_codec_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; } static const struct of_device_id radia_dummy_codec_of_match[] = { { .compatible = "test,dummy-codec", }, {}, }; MODULE_DEVICE_TABLE(of, radia_dummy_codec_of_match); static struct platform_driver radia_dummy_codec_driver = { .driver = { .name = "dummy_codec", .of_match_table = of_match_ptr(radia_dummy_codec_of_match), }, .probe = radia_dummy_codec_probe, .remove = radia_dummy_codec_remove, }; module_platform_driver(radia_dummy_codec_driver); MODULE_AUTHOR("radia <radia@stackdump.cn>"); MODULE_DESCRIPTION("radia Dummy Codec Driver"); MODULE_LICENSE("GPL v2");
7+