ASoC: davinci-mcasp: Use a copy of pdata per instance during DT boot
[deliverable/linux.git] / sound / soc / davinci / davinci-mcasp.c
index e1324989bd6b9a498d35698f9748f5dae14ffe07..05c2d33aa74dd9273100c9e8edf1b00bb57d8e35 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;
 
@@ -1487,8 +1513,9 @@ static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
 };
 
 static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
-       .tx_dma_offset = 0x200,
-       .rx_dma_offset = 0x284,
+       /* The CFG port offset will be calculated if it is needed */
+       .tx_dma_offset = 0,
+       .rx_dma_offset = 0,
        .version = MCASP_VERSION_4,
 };
 
@@ -1572,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
                pdata = pdev->dev.platform_data;
                return pdata;
        } else if (match) {
-               pdata = (struct davinci_mcasp_pdata*) match->data;
+               pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
+                                    GFP_KERNEL);
+               if (!pdata) {
+                       dev_err(&pdev->dev,
+                               "Failed to allocate memory for pdata\n");
+                       ret = -ENOMEM;
+                       return pdata;
+               }
        } else {
                /* control shouldn't reach here. something is wrong */
                ret = -EINVAL;
@@ -1708,6 +1742,52 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
        return PCM_EDMA;
 }
 
+static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+       int i;
+       u32 offset = 0;
+
+       if (pdata->version != MCASP_VERSION_4)
+               return pdata->tx_dma_offset;
+
+       for (i = 0; i < pdata->num_serializer; i++) {
+               if (pdata->serial_dir[i] == TX_MODE) {
+                       if (!offset) {
+                               offset = DAVINCI_MCASP_TXBUF_REG(i);
+                       } else {
+                               pr_err("%s: Only one serializer allowed!\n",
+                                      __func__);
+                               break;
+                       }
+               }
+       }
+
+       return offset;
+}
+
+static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+       int i;
+       u32 offset = 0;
+
+       if (pdata->version != MCASP_VERSION_4)
+               return pdata->rx_dma_offset;
+
+       for (i = 0; i < pdata->num_serializer; i++) {
+               if (pdata->serial_dir[i] == RX_MODE) {
+                       if (!offset) {
+                               offset = DAVINCI_MCASP_RXBUF_REG(i);
+                       } else {
+                               pr_err("%s: Only one serializer allowed!\n",
+                                      __func__);
+                               break;
+                       }
+               }
+       }
+
+       return offset;
+}
+
 static int davinci_mcasp_probe(struct platform_device *pdev)
 {
        struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1836,7 +1916,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        if (dat)
                dma_data->addr = dat->start;
        else
-               dma_data->addr = mem->start + pdata->tx_dma_offset;
+               dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
 
        dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -1857,7 +1937,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
                if (dat)
                        dma_data->addr = dat->start;
                else
-                       dma_data->addr = mem->start + pdata->rx_dma_offset;
+                       dma_data->addr =
+                               mem->start + davinci_mcasp_rxdma_offset(pdata);
 
                dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
                res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
This page took 0.030821 seconds and 5 git commands to generate.