Merge tag 'sound-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.git] / sound / soc / davinci / davinci-mcasp.c
index e1324989bd6b9a498d35698f9748f5dae14ffe07..0f66fda2c7727c3c0821c97228054234b7fcbdba 100644 (file)
@@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
 
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
-                              ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
+                              ACLKX | AFSX | ACLKR | AHCLKR | AFSR);
                mcasp->bclk_master = 0;
                break;
        default:
@@ -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;