diff options
Diffstat (limited to 'sound/soc/fsl/imx-cs42888.c')
-rw-r--r-- | sound/soc/fsl/imx-cs42888.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/sound/soc/fsl/imx-cs42888.c b/sound/soc/fsl/imx-cs42888.c index 8bdb1709be26..43a1584a688f 100644 --- a/sound/soc/fsl/imx-cs42888.c +++ b/sound/soc/fsl/imx-cs42888.c @@ -50,12 +50,22 @@ static int imx_cs42888_surround_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct imx_priv *priv = &card_priv; struct device *dev = &priv->pdev->dev; - u32 dai_format = 0; + u32 channels = params_channels(params); + + bool enable_tdm = channels > 1 && channels % 2; + u32 dai_format = SND_SOC_DAIFMT_NB_NF | + (enable_tdm ? SND_SOC_DAIFMT_DSP_A : SND_SOC_DAIFMT_LEFT_J); + int ret = 0; if (priv->is_codec_master) { - dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + /* TDM is not supported by codec in master mode */ + if (enable_tdm) { + dev_err(dev, "%d channels are not supported in codec master mode\n", + channels); + return -EINVAL; + } + dai_format |= SND_SOC_DAIFMT_CBM_CFM; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKT_EXTAL, priv->mclk_freq, SND_SOC_CLOCK_IN); @@ -75,8 +85,7 @@ static int imx_cs42888_surround_hw_params(struct snd_pcm_substream *substream, } } else { - dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; + dai_format |= SND_SOC_DAIFMT_CBS_CFS; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKT_EXTAL, priv->mclk_freq, SND_SOC_CLOCK_OUT); @@ -103,7 +112,16 @@ static int imx_cs42888_surround_hw_params(struct snd_pcm_substream *substream, return ret; } /* set i.MX active slot mask */ - snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); + if (enable_tdm) + /* + * Per datasheet, the codec expects 8 slots and 32 bits + * for every slot in TDM mode. + */ + snd_soc_dai_set_tdm_slot(cpu_dai, + BIT(channels) - 1, BIT(channels) - 1, + 8, 32); + else + snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, dai_format); @@ -114,6 +132,39 @@ static int imx_cs42888_surround_hw_params(struct snd_pcm_substream *substream, return 0; } +static int imx_cs42888_hw_rule_rate_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval t; + snd_interval_any(&t); + if (r->min > 48000) { + if (c->min > 2 && c->min % 2) + return -EINVAL; + } + return 0; +} + +static int imx_cs42888_hw_rule_channels_by_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval t; + snd_interval_any(&t); + if (c->min > 2 && c->min % 2) { + t.min = t.max = 48000; + t.integer = 1; + return snd_interval_refine(r, &t); + } + return 0; +} + static int imx_cs42888_surround_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -134,6 +185,15 @@ static int imx_cs42888_surround_startup(struct snd_pcm_substream *substream) &constraint_rates); if (ret) return ret; + + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + imx_cs42888_hw_rule_rate_by_channels, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + imx_cs42888_hw_rule_channels_by_rate, + 0, SNDRV_PCM_HW_PARAM_RATE, -1); } else dev_warn(dev, "mclk may be not supported %d\n", priv->mclk_freq); |