MLK-18898-2: ASoC: imx-ak4458: refine mclk rate calculation

The existing implementation calculates mclk rate as function
of audio sample rate multiplied to multiplier taken from Table 5.
However this is not accurate for Manual Setting Mode - tables 3 & 4 from
AK4458 RM defines rate (LRCK/FS) and frame width (MCLK/16fs..1152fs) ranges
as parameters to calculate mclk frequency. Aside of this - adjust
bclk:mclk ratio from machine driver as function of "compatible" id.

Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
(cherry picked from commit 527b8b7032dcb75c14bb2790330ab96743d83b16)
This commit is contained in:
Viorel Suman 2018-08-27 14:09:25 +03:00 committed by Dong Aisheng
parent 38e7229732
commit e49e74b10a
2 changed files with 79 additions and 71 deletions

View File

@ -2,7 +2,7 @@ Freescale i.MX audio complex with AK4458 DAC
Required properties:
- compatible : "fsl,imx-audio-ak4458"
- compatible : "fsl,imx-audio-ak4458", "fsl,imx-audio-ak4458-mq"
- model : The user-visible name of this sound complex
- audio-cpu : The phandle of CPU DAI
- audio-codec : The phandle of the AK4458 audio DAC

View File

@ -33,31 +33,43 @@ struct imx_ak4458_data {
int pdn_gpio;
unsigned int slots;
unsigned int slot_width;
};
struct imx_ak4458_fs_mul {
unsigned int min;
unsigned int max;
unsigned int mul;
bool one2one_ratio;
};
static struct snd_soc_dapm_widget imx_ak4458_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out", NULL),
};
static const struct imx_ak4458_fs_mul fs_mul[] = {
/*
* Table 2 - mapping multiplier and speed mode
* Tables 3 & 4 - mapping speed mode and LRCK fs
*/
{ .min = 8000, .max = 48000, .mul = 1024 }, /* Normal */
{ .min = 88200, .max = 96000, .mul = 512 }, /* Double */
{ .min = 176400, .max = 192000, .mul = 256 }, /* Quad */
{ .min = 352800, .max = 384000, .mul = 128 }, /* Oct */
{ .min = 705600, .max = 768000, .mul = 64 }, /* Hex */
/**
* Tables 3 & 4 - mapping LRCK fs and frame width
*/
static const struct imx_ak4458_fs_map {
unsigned int rmin;
unsigned int rmax;
unsigned int wmin;
unsigned int wmax;
} fs_map[] = {
/* Normal, < 32kHz */
{ .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, },
/* Normal, 32kHz */
{ .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, },
/* Normal */
{ .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, },
/* Double */
{ .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 512, },
/* Quad */
{ .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 256, },
/* Oct */
{ .rmin = 352800, .rmax = 384000, .wmin = 32, .wmax = 128, },
/* Hex */
{ .rmin = 705600, .rmax = 768000, .wmin = 16, .wmax = 64, },
};
static const struct imx_ak4458_fs_mul fs_mul_tdm[] = {
static const struct imx_ak4458_fs_mul {
unsigned int min;
unsigned int max;
unsigned int mul;
} fs_mul_tdm[] = {
/*
* Table 13 - Audio Interface Format
* For TDM mode, MCLK should is set to
@ -94,9 +106,8 @@ static unsigned long ak4458_get_mclk_rate(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct imx_ak4458_data *data = snd_soc_card_get_drvdata(rtd->card);
unsigned int rate = params_rate(params);
int i;
int mode;
unsigned int freq = 0; /* Let DAI manage clk frequency by default */
unsigned int width = data->slots * data->slot_width;
int i, mode;
if (data->tdm_mode) {
/* can be 128, 256 or 512 */
@ -106,20 +117,24 @@ static unsigned long ak4458_get_mclk_rate(struct snd_pcm_substream *substream,
/* min = max = slots * slots_width */
if (mode != fs_mul_tdm[i].min)
continue;
freq = rate * fs_mul_tdm[i].mul;
break;
return rate * fs_mul_tdm[i].mul;
}
} else {
for (i = 0; i < ARRAY_SIZE(fs_mul); i++) {
if (rate < fs_mul[i].min || rate > fs_mul[i].max)
continue;
/* rate is within min and max */
freq = rate * fs_mul[i].mul;
break;
for (i = 0; i < ARRAY_SIZE(fs_map); i++) {
if (rate >= fs_map[i].rmin && rate <= fs_map[i].rmax) {
width = max(width, fs_map[i].wmin);
width = min(width, fs_map[i].wmax);
/* Adjust SAI bclk:mclk ratio */
width *= data->one2one_ratio ? 1 : 2;
return rate * width;
}
}
}
return freq;
/* Let DAI manage clk frequency by default */
return 0;
}
static int imx_aif_hw_params(struct snd_pcm_substream *substream,
@ -128,54 +143,28 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct device *dev = card->dev;
struct imx_ak4458_data *data = snd_soc_card_get_drvdata(card);
unsigned int channels = params_channels(params);
unsigned int fmt;
unsigned int fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS;
unsigned long mclk_freq;
int ret;
int i;
int ret, i;
if (data->tdm_mode)
fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
else
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
if (data->tdm_mode) {
data->slots = 8;
data->slot_width = 32;
} else {
data->slots = 2;
data->slot_width = params_physical_width(params);
}
fmt |= data->tdm_mode ? SND_SOC_DAIFMT_DSP_B : SND_SOC_DAIFMT_I2S;
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
if (ret) {
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
return ret;
}
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
if (ret) {
dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
return ret;
}
}
if (data->tdm_mode) {
data->slots = 8;
data->slot_width = 32;
ret = snd_soc_dai_set_tdm_slot(codec_dai,
BIT(channels) - 1, BIT(channels) - 1,
data->slots, data->slot_width);
if (ret) {
dev_err(dev, "failed to set codec dai tdm slot: %d\n", ret);
return ret;
}
} else {
data->slots = 2;
data->slot_width = params_physical_width(params);
}
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
BIT(channels) - 1, BIT(channels) - 1,
data->slots, data->slot_width);
@ -184,16 +173,32 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
if (ret) {
dev_err(dev, "failed to set codec dai[%d] fmt: %d\n",
i, ret);
return ret;
}
ret = snd_soc_dai_set_tdm_slot(codec_dai,
BIT(channels) - 1, BIT(channels) - 1,
data->slots, data->slot_width);
if (ret) {
dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n",
i, ret);
return ret;
}
}
/* set MCLK freq */
mclk_freq = ak4458_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) {
if (ret < 0)
dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n",
mclk_freq, ret);
return ret;
}
return ret;
}
@ -328,6 +333,8 @@ static int imx_ak4458_probe(struct platform_device *pdev)
priv->card.num_dapm_widgets = ARRAY_SIZE(imx_ak4458_dapm_widgets);
priv->card.codec_conf = priv->codec_conf;
priv->card.num_configs = priv->num_codec_conf;
priv->one2one_ratio = !of_device_is_compatible(pdev->dev.of_node,
"fsl,imx-audio-ak4458-mq");
priv->pdn_gpio = of_get_named_gpio(pdev->dev.of_node, "ak4458,pdn-gpio", 0);
if (gpio_is_valid(priv->pdn_gpio)) {
@ -370,6 +377,7 @@ static int imx_ak4458_probe(struct platform_device *pdev)
static const struct of_device_id imx_ak4458_dt_ids[] = {
{ .compatible = "fsl,imx-audio-ak4458", },
{ .compatible = "fsl,imx-audio-ak4458-mq", },
{ },
};
MODULE_DEVICE_TABLE(of, imx_ak4458_dt_ids);