|
@@ -540,21 +540,19 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
|
|
+static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
|
|
|
int div, bool explicit)
|
|
|
{
|
|
|
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
|
|
-
|
|
|
pm_runtime_get_sync(mcasp->dev);
|
|
|
switch (div_id) {
|
|
|
- case 0: /* MCLK divider */
|
|
|
+ case MCASP_CLKDIV_AUXCLK: /* MCLK divider */
|
|
|
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
|
|
|
AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
|
|
|
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
|
|
|
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
|
|
|
break;
|
|
|
|
|
|
- case 1: /* BCLK divider */
|
|
|
+ case MCASP_CLKDIV_BCLK: /* BCLK divider */
|
|
|
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
|
|
|
ACLKXDIV(div - 1), ACLKXDIV_MASK);
|
|
|
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
|
|
@@ -563,7 +561,8 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
|
|
mcasp->bclk_div = div;
|
|
|
break;
|
|
|
|
|
|
- case 2: /*
|
|
|
+ case MCASP_CLKDIV_BCLK_FS_RATIO:
|
|
|
+ /*
|
|
|
* BCLK/LRCLK ratio descries how many bit-clock cycles
|
|
|
* fit into one frame. The clock ratio is given for a
|
|
|
* full period of data (for I2S format both left and
|
|
@@ -591,7 +590,9 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
|
|
static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
|
|
int div)
|
|
|
{
|
|
|
- return __davinci_mcasp_set_clkdiv(dai, div_id, div, 1);
|
|
|
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
|
|
+
|
|
|
+ return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1);
|
|
|
}
|
|
|
|
|
|
static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|
@@ -999,27 +1000,53 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
|
|
|
}
|
|
|
|
|
|
static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
|
|
|
- unsigned int bclk_freq,
|
|
|
- int *error_ppm)
|
|
|
+ unsigned int bclk_freq, bool set)
|
|
|
{
|
|
|
- int div = mcasp->sysclk_freq / bclk_freq;
|
|
|
- int rem = mcasp->sysclk_freq % bclk_freq;
|
|
|
+ int error_ppm;
|
|
|
+ unsigned int sysclk_freq = mcasp->sysclk_freq;
|
|
|
+ u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
|
|
|
+ int div = sysclk_freq / bclk_freq;
|
|
|
+ int rem = sysclk_freq % bclk_freq;
|
|
|
+ int aux_div = 1;
|
|
|
+
|
|
|
+ if (div > (ACLKXDIV_MASK + 1)) {
|
|
|
+ if (reg & AHCLKXE) {
|
|
|
+ aux_div = div / (ACLKXDIV_MASK + 1);
|
|
|
+ if (div % (ACLKXDIV_MASK + 1))
|
|
|
+ aux_div++;
|
|
|
+
|
|
|
+ sysclk_freq /= aux_div;
|
|
|
+ div = sysclk_freq / bclk_freq;
|
|
|
+ rem = sysclk_freq % bclk_freq;
|
|
|
+ } else if (set) {
|
|
|
+ dev_warn(mcasp->dev, "Too fast reference clock (%u)\n",
|
|
|
+ sysclk_freq);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (rem != 0) {
|
|
|
if (div == 0 ||
|
|
|
- ((mcasp->sysclk_freq / div) - bclk_freq) >
|
|
|
- (bclk_freq - (mcasp->sysclk_freq / (div+1)))) {
|
|
|
+ ((sysclk_freq / div) - bclk_freq) >
|
|
|
+ (bclk_freq - (sysclk_freq / (div+1)))) {
|
|
|
div++;
|
|
|
rem = rem - bclk_freq;
|
|
|
}
|
|
|
}
|
|
|
- if (error_ppm)
|
|
|
- *error_ppm =
|
|
|
- (div*1000000 + (int)div64_long(1000000LL*rem,
|
|
|
- (int)bclk_freq))
|
|
|
- /div - 1000000;
|
|
|
+ error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
|
|
|
+ (int)bclk_freq)) / div - 1000000;
|
|
|
+
|
|
|
+ if (set) {
|
|
|
+ if (error_ppm)
|
|
|
+ dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
|
|
|
+ error_ppm);
|
|
|
+
|
|
|
+ __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
|
|
|
+ if (reg & AHCLKXE)
|
|
|
+ __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
|
|
|
+ aux_div, 0);
|
|
|
+ }
|
|
|
|
|
|
- return div;
|
|
|
+ return error_ppm;
|
|
|
}
|
|
|
|
|
|
static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
|
|
@@ -1044,18 +1071,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
|
|
|
int slots = mcasp->tdm_slots;
|
|
|
int rate = params_rate(params);
|
|
|
int sbits = params_width(params);
|
|
|
- int ppm, div;
|
|
|
|
|
|
if (mcasp->slot_width)
|
|
|
sbits = mcasp->slot_width;
|
|
|
|
|
|
- div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
|
|
|
- &ppm);
|
|
|
- if (ppm)
|
|
|
- dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
|
|
|
- ppm);
|
|
|
-
|
|
|
- __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0);
|
|
|
+ davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
|
|
|
}
|
|
|
|
|
|
ret = mcasp_common_hw_param(mcasp, substream->stream,
|
|
@@ -1166,7 +1186,8 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
|
|
|
davinci_mcasp_dai_rates[i];
|
|
|
int ppm;
|
|
|
|
|
|
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
|
|
|
+ ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq,
|
|
|
+ false);
|
|
|
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
|
|
|
if (range.empty) {
|
|
|
range.min = davinci_mcasp_dai_rates[i];
|
|
@@ -1205,8 +1226,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
|
|
|
if (rd->mcasp->slot_width)
|
|
|
sbits = rd->mcasp->slot_width;
|
|
|
|
|
|
- davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
|
|
|
- &ppm);
|
|
|
+ ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
|
|
|
+ sbits * slots * rate,
|
|
|
+ false);
|
|
|
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
|
|
|
snd_mask_set(&nfmt, i);
|
|
|
count++;
|
|
@@ -1230,11 +1252,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
|
|
|
int i, dir;
|
|
|
int tdm_slots = mcasp->tdm_slots;
|
|
|
|
|
|
- if (mcasp->tdm_mask[substream->stream])
|
|
|
- tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
|
|
|
+ /* Do not allow more then one stream per direction */
|
|
|
+ if (mcasp->substreams[substream->stream])
|
|
|
+ return -EBUSY;
|
|
|
|
|
|
mcasp->substreams[substream->stream] = substream;
|
|
|
|
|
|
+ if (mcasp->tdm_mask[substream->stream])
|
|
|
+ tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
|
|
|
+
|
|
|
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
|
|
|
return 0;
|
|
|
|