Loading Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +6 −4 Original line number Diff line number Diff line Loading @@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit from the simplification of a new card support and the capability of the wide sample rates support through ASRC. Note: The card is initially designed for those sound cards who use I2S and PCM DAI formats. However, it'll be also possible to support those non I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long as the driver has been properly upgraded. Note: The card is initially designed for those sound cards who use AC'97, I2S and PCM DAI formats. However, it'll be also possible to support those non AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long as the driver has been properly upgraded. The compatible list for this generic sound card currently: "fsl,imx-audio-ac97" "fsl,imx-audio-cs42888" "fsl,imx-audio-wm8962" Loading include/sound/designware_i2s.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ struct i2s_clk_config_data { struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) #define DW_I2S_SLAVE (1 << 2) #define DW_I2S_MASTER (1 << 3) unsigned int cap; int channel; u32 snd_fmts; Loading sound/soc/codecs/es8328.c +1 −1 Original line number Diff line number Diff line Loading @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); int deemph = ucontrol->value.integer.value[0]; unsigned int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) Loading sound/soc/dwc/designware_i2s.c +86 −37 Original line number Diff line number Diff line Loading @@ -282,6 +282,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); if (dev->capability & DW_I2S_MASTER) { if (dev->i2s_clk_cfg) { ret = dev->i2s_clk_cfg(config); if (ret < 0) { Loading @@ -289,7 +290,8 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return ret; } } else { u32 bitclk = config->sample_rate * config->data_width * 2; u32 bitclk = config->sample_rate * config->data_width * 2; ret = clk_set_rate(dev->clk, bitclk); if (ret) { Loading @@ -298,7 +300,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return ret; } } } return 0; } Loading Loading @@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream, return ret; } static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); int ret = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: if (dev->capability & DW_I2S_SLAVE) ret = 0; else ret = -EINVAL; break; case SND_SOC_DAIFMT_CBS_CFS: if (dev->capability & DW_I2S_MASTER) ret = 0; else ret = -EINVAL; break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFM: ret = -EINVAL; break; default: dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); ret = -EINVAL; break; } return ret; } static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, .set_fmt = dw_i2s_set_fmt, }; static const struct snd_soc_component_driver dw_i2s_component = { Loading @@ -366,6 +399,7 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (dev->capability & DW_I2S_MASTER) clk_disable(dev->clk); return 0; } Loading @@ -374,6 +408,7 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); return 0; } Loading Loading @@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } if (COMP1_MODE_EN(comp1)) { dev_dbg(dev->dev, "designware: i2s master mode supported\n"); dev->capability |= DW_I2S_MASTER; } else { dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); dev->capability |= DW_I2S_SLAVE; } return 0; } Loading Loading @@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev) struct resource *res; int ret; struct snd_soc_dai_driver *dw_i2s_dai; const char *clk_id; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { Loading @@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; if (pdata) { dev->capability = pdata->cap; clk_id = NULL; ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { clk_id = "i2sclk"; ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); } if (ret < 0) return ret; dev->capability = pdata->cap; if (dev->capability & DW_I2S_MASTER) { if (pdata) { dev->i2s_clk_cfg = pdata->i2s_clk_cfg; if (!dev->i2s_clk_cfg) { dev_err(&pdev->dev, "no clock configure method\n"); return -ENODEV; } dev->clk = devm_clk_get(&pdev->dev, NULL); } else { ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); if (ret < 0) return ret; dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); } dev->clk = devm_clk_get(&pdev->dev, clk_id); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; } dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, Loading @@ -606,6 +653,7 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); return ret; } Loading @@ -614,6 +662,7 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); return 0; Loading sound/soc/fsl/fsl-asoc-card.c +110 −30 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_platform.h> #if IS_ENABLED(CONFIG_SND_AC97_CODEC) #include <sound/ac97_codec.h> #endif #include <sound/pcm_params.h> #include <sound/soc.h> Loading Loading @@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) { return priv->dai_fmt == SND_SOC_DAIFMT_AC97; } static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { Loading @@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, * set_bias_level(), bypass the remaining settings in hw_params(). * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. */ if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) if ((priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || fsl_asoc_card_is_ac97(priv)) return 0; /* Specific configurations of DAIs starts from here */ Loading Loading @@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, ext_port--; /* * Use asynchronous mode (6 wires) for all cases. * Use asynchronous mode (6 wires) for all cases except AC97. * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ Loading Loading @@ -346,16 +356,31 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TCLKDIR; break; default: if (!fsl_asoc_card_is_ac97(priv)) return -EINVAL; } if (fsl_asoc_card_is_ac97(priv)) { int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | IMX_AUDMUX_V2_PTCR_TCLKDIR; ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | IMX_AUDMUX_V2_PTCR_TFSDIR; } /* Asynchronous mode can not be set along with RCLKDIR */ if (!fsl_asoc_card_is_ac97(priv)) { unsigned int pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); ret = imx_audmux_v2_configure_port(int_port, 0, IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); pdcr); if (ret) { dev_err(dev, "audmux internal port setup failed\n"); return ret; } } ret = imx_audmux_v2_configure_port(int_port, int_ptcr, IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); Loading @@ -364,12 +389,17 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return ret; } if (!fsl_asoc_card_is_ac97(priv)) { unsigned int pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); ret = imx_audmux_v2_configure_port(ext_port, 0, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); pdcr); if (ret) { dev_err(dev, "audmux external port setup failed\n"); return ret; } } ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); Loading @@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) struct snd_soc_codec *codec = card->rtd[0].codec; struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); /* * Use slots 3/4 for S/PDIF so SSI won't try to enable * other slots and send some samples there * due to SLOTREQ bits for S/PDIF received from codec */ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); #endif return 0; } ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); if (ret) { Loading @@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; struct clk *codec_clk; const char *codec_dai_name; u32 width; int ret; Loading @@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Give a chance to old DT binding */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); codec_np = of_parse_phandle(np, "audio-codec", 0); if (!cpu_np || !codec_np) { dev_err(&pdev->dev, "phandle missing or invalid\n"); if (!cpu_np) { dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; goto fail; } Loading @@ -434,23 +479,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } codec_np = of_parse_phandle(np, "audio-codec", 0); if (codec_np) codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev) { dev_err(&pdev->dev, "failed to find codec platform device\n"); ret = -EINVAL; goto fail; } else codec_dev = NULL; asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ codec_clk = clk_get(&codec_dev->dev, NULL); if (codec_dev) { struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); if (!IS_ERR(codec_clk)) { priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); clk_put(codec_clk); } } /* Default sample rate and format, will be updated in hw_params() */ priv->sample_rate = 44100; Loading Loading @@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { codec_dai_name = "ac97-hifi"; priv->card.set_bias_level = NULL; priv->dai_fmt = SND_SOC_DAIFMT_AC97; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; goto asrc_fail; } if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { dev_err(&pdev->dev, "failed to find codec device\n"); ret = -EINVAL; goto asrc_fail; } /* Common settings for corresponding Freescale CPU DAI driver */ if (strstr(cpu_np->name, "ssi")) { /* Only SSI needs to configure AUDMUX */ Loading @@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } sprintf(priv->name, "%s-audio", codec_dev->name); snprintf(priv->name, sizeof(priv->name), "%s-audio", fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev->name); /* Initialize sound card */ priv->pdev = pdev; Loading @@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; priv->dai_link[0].codec_of_node = codec_np; priv->dai_link[0].codec_dai_name = codec_dai_name; if (!fsl_asoc_card_is_ac97(priv)) priv->dai_link[0].codec_of_node = codec_np; else { u32 idx; ret = of_property_read_u32(cpu_np, "cell-index", &idx); if (ret) { dev_err(&pdev->dev, "cannot get CPU index property\n"); goto asrc_fail; } priv->dai_link[0].codec_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); } priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; Loading @@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[1].platform_of_node = asrc_np; priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; priv->dai_link[2].codec_name = priv->dai_link[0].codec_name; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; Loading Loading @@ -579,14 +658,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); fail: of_node_put(codec_np); fail: of_node_put(cpu_np); return ret; } static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, Loading Loading
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +6 −4 Original line number Diff line number Diff line Loading @@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit from the simplification of a new card support and the capability of the wide sample rates support through ASRC. Note: The card is initially designed for those sound cards who use I2S and PCM DAI formats. However, it'll be also possible to support those non I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long as the driver has been properly upgraded. Note: The card is initially designed for those sound cards who use AC'97, I2S and PCM DAI formats. However, it'll be also possible to support those non AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long as the driver has been properly upgraded. The compatible list for this generic sound card currently: "fsl,imx-audio-ac97" "fsl,imx-audio-cs42888" "fsl,imx-audio-wm8962" Loading
include/sound/designware_i2s.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ struct i2s_clk_config_data { struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) #define DW_I2S_SLAVE (1 << 2) #define DW_I2S_MASTER (1 << 3) unsigned int cap; int channel; u32 snd_fmts; Loading
sound/soc/codecs/es8328.c +1 −1 Original line number Diff line number Diff line Loading @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); int deemph = ucontrol->value.integer.value[0]; unsigned int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) Loading
sound/soc/dwc/designware_i2s.c +86 −37 Original line number Diff line number Diff line Loading @@ -282,6 +282,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); if (dev->capability & DW_I2S_MASTER) { if (dev->i2s_clk_cfg) { ret = dev->i2s_clk_cfg(config); if (ret < 0) { Loading @@ -289,7 +290,8 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return ret; } } else { u32 bitclk = config->sample_rate * config->data_width * 2; u32 bitclk = config->sample_rate * config->data_width * 2; ret = clk_set_rate(dev->clk, bitclk); if (ret) { Loading @@ -298,7 +300,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return ret; } } } return 0; } Loading Loading @@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream, return ret; } static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); int ret = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: if (dev->capability & DW_I2S_SLAVE) ret = 0; else ret = -EINVAL; break; case SND_SOC_DAIFMT_CBS_CFS: if (dev->capability & DW_I2S_MASTER) ret = 0; else ret = -EINVAL; break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFM: ret = -EINVAL; break; default: dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); ret = -EINVAL; break; } return ret; } static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, .set_fmt = dw_i2s_set_fmt, }; static const struct snd_soc_component_driver dw_i2s_component = { Loading @@ -366,6 +399,7 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (dev->capability & DW_I2S_MASTER) clk_disable(dev->clk); return 0; } Loading @@ -374,6 +408,7 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); return 0; } Loading Loading @@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } if (COMP1_MODE_EN(comp1)) { dev_dbg(dev->dev, "designware: i2s master mode supported\n"); dev->capability |= DW_I2S_MASTER; } else { dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); dev->capability |= DW_I2S_SLAVE; } return 0; } Loading Loading @@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev) struct resource *res; int ret; struct snd_soc_dai_driver *dw_i2s_dai; const char *clk_id; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { Loading @@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; if (pdata) { dev->capability = pdata->cap; clk_id = NULL; ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { clk_id = "i2sclk"; ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); } if (ret < 0) return ret; dev->capability = pdata->cap; if (dev->capability & DW_I2S_MASTER) { if (pdata) { dev->i2s_clk_cfg = pdata->i2s_clk_cfg; if (!dev->i2s_clk_cfg) { dev_err(&pdev->dev, "no clock configure method\n"); return -ENODEV; } dev->clk = devm_clk_get(&pdev->dev, NULL); } else { ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); if (ret < 0) return ret; dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); } dev->clk = devm_clk_get(&pdev->dev, clk_id); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; } dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, Loading @@ -606,6 +653,7 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); return ret; } Loading @@ -614,6 +662,7 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); return 0; Loading
sound/soc/fsl/fsl-asoc-card.c +110 −30 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_platform.h> #if IS_ENABLED(CONFIG_SND_AC97_CODEC) #include <sound/ac97_codec.h> #endif #include <sound/pcm_params.h> #include <sound/soc.h> Loading Loading @@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) { return priv->dai_fmt == SND_SOC_DAIFMT_AC97; } static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { Loading @@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, * set_bias_level(), bypass the remaining settings in hw_params(). * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. */ if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) if ((priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || fsl_asoc_card_is_ac97(priv)) return 0; /* Specific configurations of DAIs starts from here */ Loading Loading @@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, ext_port--; /* * Use asynchronous mode (6 wires) for all cases. * Use asynchronous mode (6 wires) for all cases except AC97. * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ Loading Loading @@ -346,16 +356,31 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TCLKDIR; break; default: if (!fsl_asoc_card_is_ac97(priv)) return -EINVAL; } if (fsl_asoc_card_is_ac97(priv)) { int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | IMX_AUDMUX_V2_PTCR_TCLKDIR; ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | IMX_AUDMUX_V2_PTCR_TFSDIR; } /* Asynchronous mode can not be set along with RCLKDIR */ if (!fsl_asoc_card_is_ac97(priv)) { unsigned int pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); ret = imx_audmux_v2_configure_port(int_port, 0, IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); pdcr); if (ret) { dev_err(dev, "audmux internal port setup failed\n"); return ret; } } ret = imx_audmux_v2_configure_port(int_port, int_ptcr, IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); Loading @@ -364,12 +389,17 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return ret; } if (!fsl_asoc_card_is_ac97(priv)) { unsigned int pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); ret = imx_audmux_v2_configure_port(ext_port, 0, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); pdcr); if (ret) { dev_err(dev, "audmux external port setup failed\n"); return ret; } } ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); Loading @@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) struct snd_soc_codec *codec = card->rtd[0].codec; struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); /* * Use slots 3/4 for S/PDIF so SSI won't try to enable * other slots and send some samples there * due to SLOTREQ bits for S/PDIF received from codec */ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); #endif return 0; } ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); if (ret) { Loading @@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; struct clk *codec_clk; const char *codec_dai_name; u32 width; int ret; Loading @@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Give a chance to old DT binding */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); codec_np = of_parse_phandle(np, "audio-codec", 0); if (!cpu_np || !codec_np) { dev_err(&pdev->dev, "phandle missing or invalid\n"); if (!cpu_np) { dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; goto fail; } Loading @@ -434,23 +479,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } codec_np = of_parse_phandle(np, "audio-codec", 0); if (codec_np) codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev) { dev_err(&pdev->dev, "failed to find codec platform device\n"); ret = -EINVAL; goto fail; } else codec_dev = NULL; asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ codec_clk = clk_get(&codec_dev->dev, NULL); if (codec_dev) { struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); if (!IS_ERR(codec_clk)) { priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); clk_put(codec_clk); } } /* Default sample rate and format, will be updated in hw_params() */ priv->sample_rate = 44100; Loading Loading @@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { codec_dai_name = "ac97-hifi"; priv->card.set_bias_level = NULL; priv->dai_fmt = SND_SOC_DAIFMT_AC97; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; goto asrc_fail; } if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { dev_err(&pdev->dev, "failed to find codec device\n"); ret = -EINVAL; goto asrc_fail; } /* Common settings for corresponding Freescale CPU DAI driver */ if (strstr(cpu_np->name, "ssi")) { /* Only SSI needs to configure AUDMUX */ Loading @@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } sprintf(priv->name, "%s-audio", codec_dev->name); snprintf(priv->name, sizeof(priv->name), "%s-audio", fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev->name); /* Initialize sound card */ priv->pdev = pdev; Loading @@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; priv->dai_link[0].codec_of_node = codec_np; priv->dai_link[0].codec_dai_name = codec_dai_name; if (!fsl_asoc_card_is_ac97(priv)) priv->dai_link[0].codec_of_node = codec_np; else { u32 idx; ret = of_property_read_u32(cpu_np, "cell-index", &idx); if (ret) { dev_err(&pdev->dev, "cannot get CPU index property\n"); goto asrc_fail; } priv->dai_link[0].codec_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); } priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; Loading @@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[1].platform_of_node = asrc_np; priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; priv->dai_link[2].codec_name = priv->dai_link[0].codec_name; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; Loading Loading @@ -579,14 +658,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); fail: of_node_put(codec_np); fail: of_node_put(cpu_np); return ret; } static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, Loading