From 4c76f8ab0034744d6ea125d37f927fec427e2c21 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 16 Feb 2018 08:59:19 +0200 Subject: MLK-17600: ASoC: imx-ak5558: Set MCLK as a function of fs MCLK frequency is determined based on LRCK frequency, according to the operation mode. Because AK5558 runs in Auto Mode, we use table 5 from datasheet to set the correct MCLK. Multiplier must be set twice as value shown in RM because SAI MCLK must be at least double the BCLK. Signed-off-by: Daniel Baluta Reviewed-by: Cosmin Samoila --- sound/soc/fsl/imx-ak5558.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'sound/soc/fsl/imx-ak5558.c') diff --git a/sound/soc/fsl/imx-ak5558.c b/sound/soc/fsl/imx-ak5558.c index 2c5bfcf512b8..eb6f505df88b 100644 --- a/sound/soc/fsl/imx-ak5558.c +++ b/sound/soc/fsl/imx-ak5558.c @@ -10,6 +10,7 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include #include #include #include @@ -20,12 +21,40 @@ #include #include +#include "fsl_sai.h" #include "../codecs/ak5558.h" struct imx_ak5558_data { struct snd_soc_card card; bool tdm_mode; + unsigned long freq; + unsigned long slots; + unsigned long slot_width; +}; + +/* + * imx_ack5558_fs_mul - sampling frequency multiplier + * + * min <= fs <= max, MCLK = mul * LRCK + */ +struct imx_ak5558_fs_mul { + unsigned int min; + unsigned int max; + unsigned int mul; +}; + +/* + * Auto MCLK selection based on LRCK for Normal Mode + * (Table 4 from datasheet) + */ +static const struct imx_ak5558_fs_mul fs_mul[] = { + { .min = 8000, .max = 32000, .mul = 1024 }, + { .min = 48000, .max = 48000, .mul = 512 }, + { .min = 96000, .max = 96000, .mul = 256 }, + { .min = 192000, .max = 192000, .mul = 128 }, + { .min = 384000, .max = 384000, .mul = 64 }, + { .min = 768000, .max = 768000, .mul = 32 }, }; static struct snd_soc_dapm_widget imx_ak5558_dapm_widgets[] = { @@ -47,6 +76,25 @@ static const u32 ak5558_channels[] = { 1, 2, 4, 6, 8, }; +static unsigned long ak5558_get_mclk_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct imx_ak5558_data *data = snd_soc_card_get_drvdata(rtd->card); + unsigned int rate = params_rate(params); + unsigned int freq = data->freq; + int i; + + for (i = 0; i < ARRAY_SIZE(fs_mul); i++) { + if (rate < fs_mul[i].min || rate > fs_mul[i].max) + continue; + freq = rate * fs_mul[i].mul; + break; + } + + return freq; +} + static int imx_aif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -57,6 +105,7 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream, struct device *dev = card->dev; struct imx_ak5558_data *data = snd_soc_card_get_drvdata(card); unsigned int channels = params_channels(params); + unsigned long mclk_freq; unsigned int fmt; int ret; @@ -105,6 +154,13 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream, } } + mclk_freq = ak5558_get_mclk_rate(substream, params); + ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret < 0) + dev_err(dev, "failed to set cpu_dai mclk1 rate %lu\n", + mclk_freq); + return ret; } @@ -160,6 +216,7 @@ static int imx_ak5558_probe(struct platform_device *pdev) struct imx_ak5558_data *priv; struct device_node *cpu_np, *codec_np = NULL; struct platform_device *cpu_pdev; + struct clk *mclk; int ret; @@ -203,6 +260,15 @@ static int imx_ak5558_probe(struct platform_device *pdev) priv->card.dapm_widgets = imx_ak5558_dapm_widgets; priv->card.num_dapm_widgets = ARRAY_SIZE(imx_ak5558_dapm_widgets); + mclk = devm_clk_get(&cpu_pdev->dev, "mclk1"); + if (IS_ERR(mclk)) { + ret = PTR_ERR(mclk); + dev_err(&pdev->dev, "failed to get DAI mclk1: %d\n", ret); + return -EINVAL; + } + + priv->freq = clk_get_rate(mclk); + ret = snd_soc_of_parse_card_name(&priv->card, "model"); if (ret) goto fail; -- cgit v1.2.3