summaryrefslogtreecommitdiff
path: root/sound/soc/atmel/mchp-i2s-mcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/atmel/mchp-i2s-mcc.c')
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c111
1 files changed, 64 insertions, 47 deletions
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
index 86495883ca3f..befc2a3a05b0 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,
}
static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
- unsigned int bclk, unsigned int *mra)
+ unsigned int bclk, unsigned int *mra,
+ unsigned long *best_rate)
{
unsigned long clk_rate;
unsigned long lcm_rate;
- unsigned long best_rate = 0;
unsigned long best_diff_rate = ~0;
unsigned int sysclk;
struct clk *best_clk = NULL;
@@ -423,7 +423,7 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
(clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
clk_rate += lcm_rate) {
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
- &best_clk, &best_rate,
+ &best_clk, best_rate,
&best_diff_rate);
if (ret) {
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,
}
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
- &best_clk, &best_rate,
+ &best_clk, best_rate,
&best_diff_rate);
if (ret) {
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,
dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
best_clk == dev->pclk ? "pclk" : "gclk",
- best_rate, best_diff_rate);
-
- /* set the rate */
- ret = clk_set_rate(best_clk, best_rate);
- if (ret) {
- dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
- best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
- ret);
- return ret;
- }
+ *best_rate, best_diff_rate);
/* Configure divisors */
if (dev->sysclk)
- *mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
- *mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
+ *mra |= MCHP_I2SMCC_MRA_IMCKDIV(*best_rate / (2 * sysclk));
+ *mra |= MCHP_I2SMCC_MRA_ISCKDIV(*best_rate / (2 * bclk));
- if (best_clk == dev->gclk) {
+ if (best_clk == dev->gclk)
*mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
- ret = clk_prepare(dev->gclk);
- if (ret < 0)
- dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
- else
- dev->gclk_use = 1;
- } else {
+ else
*mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
- dev->gclk_use = 0;
- }
return 0;
}
@@ -502,6 +486,7 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ unsigned long rate = 0;
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
u32 mra = 0;
u32 mrb = 0;
@@ -640,6 +625,17 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ if (set_divs) {
+ bclk_rate = frame_length * params_rate(params);
+ ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra,
+ &rate);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to configure the divisors: %d\n", ret);
+ return ret;
+ }
+ }
+
/*
* If we are already running, the wanted setup must be
* the same with the one that's currently ongoing
@@ -656,22 +652,35 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
return 0;
}
- /* Save the number of channels to know what interrupts to enable */
- dev->channels = channels;
-
- if (set_divs) {
- bclk_rate = frame_length * params_rate(params);
- ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
+ if (mra & MCHP_I2SMCC_MRA_SRCCLK_GCLK && !dev->gclk_use) {
+ /* set the rate */
+ ret = clk_set_rate(dev->gclk, rate);
if (ret) {
- dev_err(dev->dev, "unable to configure the divisors: %d\n",
- ret);
+ dev_err(dev->dev,
+ "unable to set rate %lu to GCLK: %d\n",
+ rate, ret);
+ return ret;
+ }
+
+ ret = clk_prepare(dev->gclk);
+ if (ret < 0) {
+ dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
return ret;
}
+ dev->gclk_use = 1;
}
+ /* Save the number of channels to know what interrupts to enable */
+ dev->channels = channels;
+
ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
- if (ret < 0)
+ if (ret < 0) {
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
return ret;
+ }
return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
}
@@ -686,31 +695,37 @@ static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
err = wait_event_interruptible_timeout(dev->wq_txrdy,
dev->tx_rdy,
msecs_to_jiffies(500));
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Tx ready\n");
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));
+ dev->tx_rdy = 1;
+ }
} else {
err = wait_event_interruptible_timeout(dev->wq_rxrdy,
dev->rx_rdy,
msecs_to_jiffies(500));
- }
-
- if (err == 0) {
- u32 idra;
-
- dev_warn_once(dev->dev, "Timeout waiting for %s\n",
- is_playback ? "Tx ready" : "Rx ready");
- if (is_playback)
- idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
- else
- idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
- regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Rx ready\n");
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ dev->rx_rdy = 1;
+ }
}
if (!mchp_i2s_mcc_is_running(dev)) {
regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
if (dev->gclk_running) {
- clk_disable_unprepare(dev->gclk);
+ clk_disable(dev->gclk);
dev->gclk_running = 0;
}
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
}
return 0;
@@ -809,6 +824,8 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
init_waitqueue_head(&dev->wq_txrdy);
init_waitqueue_head(&dev->wq_rxrdy);
+ dev->tx_rdy = 1;
+ dev->rx_rdy = 1;
snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);