diff options
Diffstat (limited to 'sound/soc/fsl/fsl_esai.c')
-rw-r--r-- | sound/soc/fsl/fsl_esai.c | 54 |
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 | ||
273 | out: | 288 | out: |
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 | ||
319 | static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, | 344 | static 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) | |||
432 | static int fsl_esai_startup(struct snd_pcm_substream *substream, | 457 | static 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 | ||
817 | static const struct of_device_id fsl_esai_dt_ids[] = { | 842 | static 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 | }; |
821 | MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); | 847 | MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); |