aboutsummaryrefslogtreecommitdiffstats
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.c54
1 files changed, 40 insertions, 14 deletions
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index c8e5db1414d7..d719caf26dc2 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -39,6 +39,8 @@
39 * @fifo_depth: depth of tx/rx FIFO 39 * @fifo_depth: depth of tx/rx FIFO
40 * @slot_width: width of each DAI slot 40 * @slot_width: width of each DAI slot
41 * @hck_rate: clock rate of desired HCKx clock 41 * @hck_rate: clock rate of desired HCKx clock
42 * @sck_rate: clock rate of desired SCKx clock
43 * @hck_dir: the direction of HCKx pads
42 * @sck_div: if using PSR/PM dividers for SCKx clock 44 * @sck_div: if using PSR/PM dividers for SCKx clock
43 * @slave_mode: if fully using DAI slave mode 45 * @slave_mode: if fully using DAI slave mode
44 * @synchronous: if using tx/rx synchronous mode 46 * @synchronous: if using tx/rx synchronous mode
@@ -55,6 +57,8 @@ struct fsl_esai {
55 u32 fifo_depth; 57 u32 fifo_depth;
56 u32 slot_width; 58 u32 slot_width;
57 u32 hck_rate[2]; 59 u32 hck_rate[2];
60 u32 sck_rate[2];
61 bool hck_dir[2];
58 bool sck_div[2]; 62 bool sck_div[2];
59 bool slave_mode; 63 bool slave_mode;
60 bool synchronous; 64 bool synchronous;
@@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
209 struct clk *clksrc = esai_priv->extalclk; 213 struct clk *clksrc = esai_priv->extalclk;
210 bool tx = clk_id <= ESAI_HCKT_EXTAL; 214 bool tx = clk_id <= ESAI_HCKT_EXTAL;
211 bool in = dir == SND_SOC_CLOCK_IN; 215 bool in = dir == SND_SOC_CLOCK_IN;
212 u32 ret, ratio, ecr = 0; 216 u32 ratio, ecr = 0;
213 unsigned long clk_rate; 217 unsigned long clk_rate;
218 int ret;
219
220 /* Bypass divider settings if the requirement doesn't change */
221 if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
222 return 0;
214 223
215 /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ 224 /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
216 esai_priv->sck_div[tx] = true; 225 esai_priv->sck_div[tx] = true;
@@ -258,10 +267,16 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
258 return -EINVAL; 267 return -EINVAL;
259 } 268 }
260 269
261 if (ratio == 1) { 270 /* Only EXTAL source can be output directly without using PSR and PM */
271 if (ratio == 1 && clksrc == esai_priv->extalclk) {
262 /* Bypass all the dividers if not being needed */ 272 /* Bypass all the dividers if not being needed */
263 ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; 273 ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
264 goto out; 274 goto out;
275 } else if (ratio < 2) {
276 /* The ratio should be no less than 2 if using other sources */
277 dev_err(dai->dev, "failed to derive required HCK%c rate\n",
278 tx ? 'T' : 'R');
279 return -EINVAL;
265 } 280 }
266 281
267 ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); 282 ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
@@ -271,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
271 esai_priv->sck_div[tx] = false; 286 esai_priv->sck_div[tx] = false;
272 287
273out: 288out:
289 esai_priv->hck_dir[tx] = dir;
274 esai_priv->hck_rate[tx] = freq; 290 esai_priv->hck_rate[tx] = freq;
275 291
276 regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, 292 regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
@@ -288,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
288 struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 304 struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
289 u32 hck_rate = esai_priv->hck_rate[tx]; 305 u32 hck_rate = esai_priv->hck_rate[tx];
290 u32 sub, ratio = hck_rate / freq; 306 u32 sub, ratio = hck_rate / freq;
307 int ret;
291 308
292 /* Don't apply for fully slave mode*/ 309 /* Don't apply for fully slave mode or unchanged bclk */
293 if (esai_priv->slave_mode) 310 if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
294 return 0; 311 return 0;
295 312
296 if (ratio * freq > hck_rate) 313 if (ratio * freq > hck_rate)
@@ -307,13 +324,21 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
307 return -EINVAL; 324 return -EINVAL;
308 } 325 }
309 326
310 if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { 327 /* The ratio should be contented by FP alone if bypassing PM and PSR */
328 if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
311 dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); 329 dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
312 return -EINVAL; 330 return -EINVAL;
313 } 331 }
314 332
315 return fsl_esai_divisor_cal(dai, tx, ratio, true, 333 ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
316 esai_priv->sck_div[tx] ? 0 : ratio); 334 esai_priv->sck_div[tx] ? 0 : ratio);
335 if (ret)
336 return ret;
337
338 /* Save current bclk rate */
339 esai_priv->sck_rate[tx] = freq;
340
341 return 0;
317} 342}
318 343
319static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, 344static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
@@ -432,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
432static int fsl_esai_startup(struct snd_pcm_substream *substream, 457static int fsl_esai_startup(struct snd_pcm_substream *substream,
433 struct snd_soc_dai *dai) 458 struct snd_soc_dai *dai)
434{ 459{
435 int ret;
436 struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 460 struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
461 int ret;
437 462
438 /* 463 /*
439 * Some platforms might use the same bit to gate all three or two of 464 * Some platforms might use the same bit to gate all three or two of
@@ -454,12 +479,6 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
454 } 479 }
455 480
456 if (!dai->active) { 481 if (!dai->active) {
457 /* Reset Port C */
458 regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
459 ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
460 regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
461 ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
462
463 /* Set synchronous mode */ 482 /* Set synchronous mode */
464 regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, 483 regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
465 ESAI_SAICR_SYNC, esai_priv->synchronous ? 484 ESAI_SAICR_SYNC, esai_priv->synchronous ?
@@ -491,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
491 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 510 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
492 u32 width = snd_pcm_format_width(params_format(params)); 511 u32 width = snd_pcm_format_width(params_format(params));
493 u32 channels = params_channels(params); 512 u32 channels = params_channels(params);
494 u32 bclk, mask, val, ret; 513 u32 bclk, mask, val;
514 int ret;
495 515
496 bclk = params_rate(params) * esai_priv->slot_width * 2; 516 bclk = params_rate(params) * esai_priv->slot_width * 2;
497 517
@@ -519,6 +539,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
519 539
520 regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); 540 regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
521 541
542 /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
543 regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
544 ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
545 regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
546 ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
522 return 0; 547 return 0;
523} 548}
524 549
@@ -816,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
816 841
817static const struct of_device_id fsl_esai_dt_ids[] = { 842static const struct of_device_id fsl_esai_dt_ids[] = {
818 { .compatible = "fsl,imx35-esai", }, 843 { .compatible = "fsl,imx35-esai", },
844 { .compatible = "fsl,vf610-esai", },
819 {} 845 {}
820}; 846};
821MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); 847MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);