Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f72362e6 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branches 'asoc/topic/dwc', 'asoc/topic/es8328',...

Merge remote-tracking branches 'asoc/topic/dwc', 'asoc/topic/es8328', 'asoc/topic/fsl' and 'asoc/topic/fsl-card' into asoc-next
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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"
+2 −0
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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)
+86 −37
Original line number Diff line number Diff line
@@ -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) {
@@ -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) {
@@ -298,7 +300,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
				return ret;
			}
		}

	}
	return 0;
}

@@ -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 = {
@@ -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;
}
@@ -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;
}
@@ -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;
}

@@ -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) {
@@ -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,
@@ -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;
}
@@ -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;
+110 −30
Original line number Diff line number Diff line
@@ -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>

@@ -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)
{
@@ -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 */
@@ -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.
	 */
@@ -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));
@@ -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));
@@ -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) {
@@ -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;
@@ -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;
	}
@@ -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;
@@ -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 */
@@ -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;
@@ -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;
@@ -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;
@@ -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