summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_esai.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_esai.c')
-rw-r--r--sound/soc/fsl/fsl_esai.c56
1 files changed, 42 insertions, 14 deletions
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index c84026c99134..496ce2eb2f1f 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -18,6 +18,7 @@
#include "fsl_esai.h"
#include "imx-pcm.h"
+#include "fsl_utils.h"
#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000
#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
@@ -257,10 +258,16 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
return -EINVAL;
}
- if (ratio == 1) {
+ /* Only EXTAL source can be output directly without using PSR and PM */
+ if (ratio == 1 && clksrc == esai_priv->extalclk) {
/* Bypass all the dividers if not being needed */
ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
goto out;
+ } else if (ratio < 2) {
+ /* The ratio should be no less than 2 if using other sources */
+ dev_err(dai->dev, "failed to derive required HCK%c rate\n",
+ tx ? 'T' : 'R');
+ return -EINVAL;
}
ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
@@ -306,7 +313,8 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL;
}
- if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
+ /* The ratio should be contented by FP alone if bypassing PM and PSR */
+ if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
return -EINVAL;
}
@@ -431,25 +439,28 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int fsl_esai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ int ret;
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
/*
* Some platforms might use the same bit to gate all three or two of
* clocks, so keep all clocks open/close at the same time for safety
*/
- clk_prepare_enable(esai_priv->coreclk);
- if (!IS_ERR(esai_priv->extalclk))
- clk_prepare_enable(esai_priv->extalclk);
- if (!IS_ERR(esai_priv->fsysclk))
- clk_prepare_enable(esai_priv->fsysclk);
+ ret = clk_prepare_enable(esai_priv->coreclk);
+ if (ret)
+ return ret;
+ if (!IS_ERR(esai_priv->extalclk)) {
+ ret = clk_prepare_enable(esai_priv->extalclk);
+ if (ret)
+ goto err_extalck;
+ }
+ if (!IS_ERR(esai_priv->fsysclk)) {
+ ret = clk_prepare_enable(esai_priv->fsysclk);
+ if (ret)
+ goto err_fsysclk;
+ }
if (!dai->active) {
- /* Reset Port C */
- regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
- ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
- ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
-
/* Set synchronous mode */
regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
ESAI_SAICR_SYNC, esai_priv->synchronous ?
@@ -463,6 +474,14 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
}
return 0;
+
+err_fsysclk:
+ if (!IS_ERR(esai_priv->extalclk))
+ clk_disable_unprepare(esai_priv->extalclk);
+err_extalck:
+ clk_disable_unprepare(esai_priv->coreclk);
+
+ return ret;
}
static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
@@ -501,6 +520,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
+ /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
return 0;
}
@@ -564,6 +588,7 @@ static struct snd_soc_dai_ops fsl_esai_dai_ops = {
.hw_params = fsl_esai_hw_params,
.set_sysclk = fsl_esai_set_dai_sysclk,
.set_fmt = fsl_esai_set_dai_fmt,
+ .xlate_tdm_slot_mask = fsl_asoc_xlate_tdm_slot_mask,
.set_tdm_slot = fsl_esai_set_dai_tdm_slot,
};
@@ -661,7 +686,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
}
}
-static const struct regmap_config fsl_esai_regmap_config = {
+static struct regmap_config fsl_esai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -687,6 +712,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
esai_priv->pdev = pdev;
strcpy(esai_priv->name, np->name);
+ if (of_property_read_bool(np, "big-endian"))
+ fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);