aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorCodrin Ciubotariu <codrin.ciubotariu@microchip.com>2019-08-20 12:24:11 -0400
committerMark Brown <broonie@kernel.org>2019-08-20 13:39:46 -0400
commitc9cff337eab394c4dc8b128dde7308a1dd2e653a (patch)
treeb5a16c15ab52b337f53fd27927ec3212c6a912f9 /sound
parentbbf9a127abca4aac5cc75f882bc7efcc398e86ae (diff)
ASoC: mchp-i2s-mcc: Fix simultaneous capture and playback in master mode
This controller supports capture and playback running at the same time, with the limitation that both capture and playback must be configured the same way (sample rate, sample format, number of channels, etc). For this, we have to assure that the configuration registers look the same when capture and playback are initiated. This patch fixes a bug in which the controller is in master mode and the hw_params() callback fails for the second audio stream. The fail occurs because the divisors are calculated after comparing the configuration registers for capture and playback. The fix consists in calculating the divisors before comparing the configuration registers. BCLK and LRC are then configured and started only if the controller is not already running. Fixes: 7e0cdf545a55 ("ASoC: mchp-i2s-mcc: add driver for I2SC Multi-Channel Controller") Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> Link: https://lore.kernel.org/r/20190820162411.24836-4-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c70
1 files changed, 37 insertions, 33 deletions
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
index 86495883ca3f..9a406144b18f 100644
--- a/sound/soc/atmel/mchp-i2s-mcc.c
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -392,11 +392,11 @@ static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
392} 392}
393 393
394static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev, 394static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
395 unsigned int bclk, unsigned int *mra) 395 unsigned int bclk, unsigned int *mra,
396 unsigned long *best_rate)
396{ 397{
397 unsigned long clk_rate; 398 unsigned long clk_rate;
398 unsigned long lcm_rate; 399 unsigned long lcm_rate;
399 unsigned long best_rate = 0;
400 unsigned long best_diff_rate = ~0; 400 unsigned long best_diff_rate = ~0;
401 unsigned int sysclk; 401 unsigned int sysclk;
402 struct clk *best_clk = NULL; 402 struct clk *best_clk = NULL;
@@ -423,7 +423,7 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
423 (clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0)); 423 (clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
424 clk_rate += lcm_rate) { 424 clk_rate += lcm_rate) {
425 ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate, 425 ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
426 &best_clk, &best_rate, 426 &best_clk, best_rate,
427 &best_diff_rate); 427 &best_diff_rate);
428 if (ret) { 428 if (ret) {
429 dev_err(dev->dev, "gclk error for rate %lu: %d", 429 dev_err(dev->dev, "gclk error for rate %lu: %d",
@@ -437,7 +437,7 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
437 } 437 }
438 438
439 ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate, 439 ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
440 &best_clk, &best_rate, 440 &best_clk, best_rate,
441 &best_diff_rate); 441 &best_diff_rate);
442 if (ret) { 442 if (ret) {
443 dev_err(dev->dev, "pclk error for rate %lu: %d", 443 dev_err(dev->dev, "pclk error for rate %lu: %d",
@@ -459,33 +459,17 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
459 459
460 dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n", 460 dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
461 best_clk == dev->pclk ? "pclk" : "gclk", 461 best_clk == dev->pclk ? "pclk" : "gclk",
462 best_rate, best_diff_rate); 462 *best_rate, best_diff_rate);
463
464 /* set the rate */
465 ret = clk_set_rate(best_clk, best_rate);
466 if (ret) {
467 dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
468 best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
469 ret);
470 return ret;
471 }
472 463
473 /* Configure divisors */ 464 /* Configure divisors */
474 if (dev->sysclk) 465 if (dev->sysclk)
475 *mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk)); 466 *mra |= MCHP_I2SMCC_MRA_IMCKDIV(*best_rate / (2 * sysclk));
476 *mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk)); 467 *mra |= MCHP_I2SMCC_MRA_ISCKDIV(*best_rate / (2 * bclk));
477 468
478 if (best_clk == dev->gclk) { 469 if (best_clk == dev->gclk)
479 *mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK; 470 *mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
480 ret = clk_prepare(dev->gclk); 471 else
481 if (ret < 0)
482 dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
483 else
484 dev->gclk_use = 1;
485 } else {
486 *mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK; 472 *mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
487 dev->gclk_use = 0;
488 }
489 473
490 return 0; 474 return 0;
491} 475}
@@ -502,6 +486,7 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
502 struct snd_pcm_hw_params *params, 486 struct snd_pcm_hw_params *params,
503 struct snd_soc_dai *dai) 487 struct snd_soc_dai *dai)
504{ 488{
489 unsigned long rate = 0;
505 struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai); 490 struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
506 u32 mra = 0; 491 u32 mra = 0;
507 u32 mrb = 0; 492 u32 mrb = 0;
@@ -640,6 +625,17 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
640 return -EINVAL; 625 return -EINVAL;
641 } 626 }
642 627
628 if (set_divs) {
629 bclk_rate = frame_length * params_rate(params);
630 ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra,
631 &rate);
632 if (ret) {
633 dev_err(dev->dev,
634 "unable to configure the divisors: %d\n", ret);
635 return ret;
636 }
637 }
638
643 /* 639 /*
644 * If we are already running, the wanted setup must be 640 * If we are already running, the wanted setup must be
645 * the same with the one that's currently ongoing 641 * the same with the one that's currently ongoing
@@ -656,19 +652,27 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
656 return 0; 652 return 0;
657 } 653 }
658 654
659 /* Save the number of channels to know what interrupts to enable */ 655 if (mra & MCHP_I2SMCC_MRA_SRCCLK_GCLK && !dev->gclk_use) {
660 dev->channels = channels; 656 /* set the rate */
661 657 ret = clk_set_rate(dev->gclk, rate);
662 if (set_divs) {
663 bclk_rate = frame_length * params_rate(params);
664 ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
665 if (ret) { 658 if (ret) {
666 dev_err(dev->dev, "unable to configure the divisors: %d\n", 659 dev_err(dev->dev,
667 ret); 660 "unable to set rate %lu to GCLK: %d\n",
661 rate, ret);
662 return ret;
663 }
664
665 ret = clk_prepare(dev->gclk);
666 if (ret < 0) {
667 dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
668 return ret; 668 return ret;
669 } 669 }
670 dev->gclk_use = 1;
670 } 671 }
671 672
673 /* Save the number of channels to know what interrupts to enable */
674 dev->channels = channels;
675
672 ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra); 676 ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
673 if (ret < 0) 677 if (ret < 0)
674 return ret; 678 return ret;