1、添加codec设备节点
kernel-imx/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
+ sound-mic {
+ compatible = "fsl,imx6q-sabresd-mic1388",
+ "fsl,imx-audio-mic1388";
+ model = "mic1388-audio";
+ cpu-dai = <&ssi1>;
+ audio-codec = <&codec_mic1388>;
+ mux-int-port = <1>;
+ mux-ext-port = <5>;
+ };
+
+ codec_mic1388: mic1388{
+ compatible = "wlf,mic1388";
+ };
2、pinctrl添加对应管脚配置
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_KEY_ROW1__AUD5_RXD 0x130b0
+ MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0
+ MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0
+ MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0
3、iis1时钟配置
+&ssi1 {
+/*
+ assigned-clocks = <&clks IMX6QDL_CLK_SSI1_SEL>;
+ assigned-clock-parents = <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>;
+ assigned-clock-rates = <24576000>;
+*/
+ assigned-clocks = <&clks IMX6QDL_PLL4_BYPASS_SRC>,
+ <&clks IMX6QDL_CLK_PLL4>,
+ <&clks IMX6QDL_PLL4_BYPASS>,
+ <&clks IMX6QDL_CLK_PLL4_AUDIO>,
+ <&clks IMX6QDL_CLK_PLL4_POST_DIV>,
+ <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>,
+ <&clks IMX6QDL_CLK_SSI1_SEL>,
+ <&clks IMX6QDL_CLK_SSI1_PRED>,
+ <&clks IMX6QDL_CLK_SSI1_PODF>,
+ <&clks IMX6QDL_CLK_SSI1>,
+ <&clks IMX6QDL_CLK_CKO2_SEL>,
+ <&clks IMX6QDL_CLK_CKO2_PODF>,
+ <&clks IMX6QDL_CLK_CKO2>,
+ <&clks IMX6QDL_CLK_CKO>;
+ assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>,
+ <&clks IMX6QDL_PLL4_BYPASS_SRC>,
+ <&clks IMX6QDL_CLK_PLL4>,
+ <&clks IMX6QDL_PLL4_BYPASS>,
+ <&clks IMX6QDL_CLK_PLL4_AUDIO>,
+ <&clks IMX6QDL_CLK_PLL4_POST_DIV>,
+ <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>,
+ <&clks IMX6QDL_CLK_SSI1_SEL>,
+ <&clks IMX6QDL_CLK_SSI1_PRED>,
+ <&clks IMX6QDL_CLK_SSI1_PODF>,
+ <&clks IMX6QDL_CLK_SSI1>,
+ <&clks IMX6QDL_CLK_CKO2_SEL>,
+ <&clks IMX6QDL_CLK_CKO2_PODF>,
+ <&clks IMX6QDL_CLK_CKO2>;
+ assigned-clock-rates = <24000000>,
+ <1179648000>,
+ <1179648000>,
+ <1179648000>,
+ <589824000>,
+ <589824000>,
+ <589824000>,
+ <147456000>,
+ <24576000>,
+ <24576000>,
+ <24576000>,
+ <24576000>,
+ <24576000>,
+ <24576000>;
+ fsl,mode = "i2s-master";
+ status = "okay";
+};
/kernel-imx/arch/arm/mach-imx/clk-imx6q.c
+ //add for ssi1
+ imx_clk_set_parent(clk[IMX6QDL_CLK_SSI1_SEL], clk[IMX6QDL_CLK_PLL4_AUDIO_DIV]);//johnli add
+ imx_clk_set_rate(clk[IMX6QDL_CLK_SSI1_PRED], 147456000); //johnli add 48000X512=24576000 22579200
+ imx_clk_set_rate(clk[IMX6QDL_CLK_SSI1_PODF], 24576000); //johnli add 48000X512=24576000 22579200
+ imx_clk_set_rate(clk[IMX6QDL_CLK_SSI1], 24576000); //johnli add 48000X512=24576000 22579200
4、添加code_dai driver驱动
kernel-imx/sound/soc/codecs/mic1388.c
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is\r
+ * determined automatically by examining the Master clock and Bit clock ratios\r
+ */
+#if 0
+#define WM8524_RATES (SNDRV_PCM_RATE_48000)\r
+#else
+#define WM8524_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+#endif
+static struct snd_soc_dai_driver wm8524_dai = {
+ .name = "mic1388",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+
+};
+static struct snd_soc_codec_driver soc_codec_dev_wm8524;
+static int mic1388_probe(struct platform_device *pdev)
+{
+ printk(KERN_CRIT "{CKS} wm8524_probe\n");
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm8524, &wm8524_dai, 1);
+}
+static int mic1388_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id mic1388_of_match[] = {
+ { .compatible = "wlf,mic1388", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mic1388_of_match);
+static struct platform_driver mic1388_codec_driver = {
+ .driver = {
+ .name = "mic1388-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = mic1388_of_match,
+ },
+ .probe = mic1388_probe,
+ .remove = mic1388_remove,
+};
+module_platform_driver(mic1388_codec_driver);
+MODULE_DESCRIPTION("ASoC wm8524 driver");
+MODULE_LICENSE("GPL");
5、添加到kconfig和makefile
kernel-imx/sound/soc/codecs/Kconfig
+ select SND_SOC_MIC1388
+ +config SND_SOC_MIC1388
+ tristate
kernel-imx/sound/soc/codecs/Makefile
+obj-$(CONFIG_SND_SOC_MIC1388) += snd-soc-mic1388.o
6、添加link_dai driver 即machine driver
kernel-imx/sound/soc/fsl/imx-mic1388.c
+//johnli
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/control.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/pinctrl/consumer.h>
+#include "imx-audmux.h"
+#include "imx-ssi.h"
+#define DAI_NAME_SIZE 32
+struct imx_priv {
+ int sysclk; /*mclk from the outside*/
+ int codec_sysclk;
+ int dai_hifi;
+ int hp_irq;
+ int hp_status;
+ int amic_irq;
+ int amic_status;
+ struct platform_device *pdev;
+};
+struct imx_wm8524_data {
+ struct snd_soc_dai_link dai;
+ struct snd_soc_card card;
+ char codec_dai_name[DAI_NAME_SIZE];
+ char platform_name[DAI_NAME_SIZE];
+ unsigned int clk_frequency;
+};
+static unsigned int sample_format = SNDRV_PCM_FMTBIT_S16_LE;
+static struct imx_priv card_priv;
+static struct snd_soc_card snd_soc_card_imx;
+static int imx_hifi_startup(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+static void imx_hifi_shutdown(struct snd_pcm_substream *substream)
+{
+ return;
+}
+static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int channels = params_channels(params);
+ int ret = 0;
+ u32 dai_format;
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ 2, 32);
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS;
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+static struct snd_soc_ops imx_hifi_ops = {
+ .startup = imx_hifi_startup,
+ .shutdown = imx_hifi_shutdown,
+ .hw_params = imx_hifi_hw_params,
+};
+static struct platform_device *imx_snd_device; //johnli
+static int imx_wm8524_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *cpu_np, *codec_np;
+ struct platform_device *cpu_pdev;
+ struct imx_priv *priv = &card_priv;
+ struct imx_wm8524_data *data;
+ struct clk *codec_clk = NULL;
+ int int_port, ext_port;
+ int ret;
+ priv->pdev = pdev;
+ cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (!strstr(cpu_np->name, "ssi"))
+ goto audmux_bypass;
+ ret = of_property_read_u32(np, "mux-int-port", &int_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
+ return ret;
+ }
+/*
+ * The port numbering in the hardware manual starts at 1, while\r
+ * the audmux API expects it starts at 0.\r
+ */
+ int_port--;
+ ext_port--;
+ ret = imx_audmux_v2_configure_port(ext_port,IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR |
+ IMX_AUDMUX_V2_PTCR_RFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_RCSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+ if (ret) {
+ dev_err(&pdev->dev, "audmux internal port setup failed\n");
+ return ret;
+ }
+ imx_audmux_v2_configure_port(int_port,
+ IMX_AUDMUX_V2_PTCR_SYN,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+ if (ret) {
+ dev_err(&pdev->dev, "audmux external port setup failed\n");
+ return ret;
+ }
+ audmux_bypass:
+ codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ cpu_pdev = of_find_device_by_node(cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SSI platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ data->dai.name = "HiFi";
+ data->dai.stream_name = "HiFi";
+ data->dai.codec_dai_name = "mic1388";
+ data->dai.codec_of_node = codec_np;
+ data->dai.cpu_dai_name = dev_name(&cpu_pdev->dev);
+ data->dai.platform_of_node = cpu_np;
+ data->dai.ops = &imx_hifi_ops;
+ data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS;
+ data->card.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+ data->card.num_links = 1;
+ data->card.dai_link = &data->dai;
+ platform_set_drvdata(pdev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto fail;
+ }
+ goto fail;
+fail:
+ if (cpu_np)
+ of_node_put(cpu_np);
+ if (codec_np)
+ of_node_put(codec_np);
+ return ret;
+}
+static int imx_wm8524_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+static const struct of_device_id imx_wm8524_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-mic1388", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_wm8524_dt_ids);
+static struct platform_driver imx_wm8524_driver = {
+ .driver = {
+ .name = "imx-mic1388",
+ .owner = THIS_MODULE,
+ //.pm = &snd_soc_pm_ops,
+ .of_match_table = imx_wm8524_dt_ids,
+ },
+ .probe = imx_wm8524_probe,
+ .remove = imx_wm8524_remove,
+};
+module_platform_driver(imx_wm8524_driver);
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC imx mic1388");
+MODULE_LICENSE("GPL");
7、添加到kconfig和Makefile
kernel-imx/sound/soc/fsl/Kconfig
+config SND_SOC_IMX_MIC1388
+ tristate "SoC Audio support for i.MX boards with mic1388"
+ select SND_SOC_MIC1388
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_FSL_SSI
+ help
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a MIC1388 codec.
kernel-imx/sound/soc/fsl/Makefile
+snd-soc-imx-mic1388-objs := imx-mic1388.o
+obj-$(CONFIG_SND_SOC_IMX_MIC1388) += snd-soc-imx-mic1388.o
8、menuconfig 开启编译
kernel-imx/arch/arm/configs/imx_v7_defconfig
+CONFIG_SND_SOC_MIC1388=y
+CONFIG_SND_SOC_IMX_MIC1388=y
以上是整个添加虚拟声卡的流程。