ASoC: DaVinci: Added two clocking possibilities to McBSP (I2S)
authorRaffaele Recalcati <raffaele.recalcati@bticino.it>
Tue, 6 Jul 2010 08:39:02 +0000 (10:39 +0200)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 6 Jul 2010 14:54:06 +0000 (23:54 +0900)
    Added two clocking options for dm365 McBSP peripheral when used
    with I2S timings, that are SND_SOC_DAIFMT_CBS_CFS (the cpu generates
    clock and frame sync) and SND_SOC_DAIFMT_CBS_CFM (the cpu gets clock
    from external pin and generates frame sync).
    A slave clock management can be important when the external codec needs
    the system clock and the bit clock synchronized (tested with uda1345).
    This patch has been developed against the:
        http://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git
    git tree and has been tested on bmx board (similar to dm365 evm, but using
    uda1345 as external audio codec).

Signed-off-by: Raffaele Recalcati <raffaele.recalcati@bticino.it>
Signed-off-by: Davide Bonfanti <davide.bonfanti@bticino.it>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Acked-by: Sudhakar Rajashekhara <sudhakar.raj@ti.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-i2s.h

index adadcd3..c8f038c 100644 (file)
@@ -26,6 +26,7 @@
 #include <mach/asp.h>
 
 #include "davinci-pcm.h"
+#include "davinci-i2s.h"
 
 
 /*
 #define DAVINCI_MCBSP_RCR_RDATDLY(v)   ((v) << 16)
 #define DAVINCI_MCBSP_RCR_RFIG         (1 << 18)
 #define DAVINCI_MCBSP_RCR_RWDLEN2(v)   ((v) << 21)
+#define DAVINCI_MCBSP_RCR_RFRLEN2(v)   ((v) << 24)
+#define DAVINCI_MCBSP_RCR_RPHASE       BIT(31)
 
 #define DAVINCI_MCBSP_XCR_XWDLEN1(v)   ((v) << 5)
 #define DAVINCI_MCBSP_XCR_XFRLEN1(v)   ((v) << 8)
 #define DAVINCI_MCBSP_XCR_XDATDLY(v)   ((v) << 16)
 #define DAVINCI_MCBSP_XCR_XFIG         (1 << 18)
 #define DAVINCI_MCBSP_XCR_XWDLEN2(v)   ((v) << 21)
+#define DAVINCI_MCBSP_XCR_XFRLEN2(v)   ((v) << 24)
+#define DAVINCI_MCBSP_XCR_XPHASE       BIT(31)
 
 #define DAVINCI_MCBSP_SRGR_FWID(v)     ((v) << 8)
 #define DAVINCI_MCBSP_SRGR_FPER(v)     ((v) << 16)
 #define DAVINCI_MCBSP_SRGR_FSGM                (1 << 28)
+#define DAVINCI_MCBSP_SRGR_CLKSM       BIT(29)
 
 #define DAVINCI_MCBSP_PCR_CLKRP                (1 << 0)
 #define DAVINCI_MCBSP_PCR_CLKXP                (1 << 1)
@@ -144,6 +150,9 @@ struct davinci_mcbsp_dev {
         * won't end up being swapped because of the underrun.
         */
        unsigned enable_channel_combine:1;
+
+       unsigned int fmt;
+       int clk_div;
 };
 
 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -254,10 +263,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
        unsigned int pcr;
        unsigned int srgr;
+       /* Attention srgr is updated by hw_params! */
        srgr = DAVINCI_MCBSP_SRGR_FSGM |
                DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
                DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
+       dev->fmt = fmt;
        /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBS_CFS:
@@ -372,6 +383,18 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        return 0;
 }
 
+static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
+                               int div_id, int div)
+{
+       struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+
+       if (div_id != DAVINCI_MCBSP_CLKGDV)
+               return -ENODEV;
+
+       dev->clk_div = div;
+       return 0;
+}
+
 static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
@@ -380,8 +403,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        struct davinci_pcm_dma_params *dma_params =
                                        &dev->dma_params[substream->stream];
        struct snd_interval *i = NULL;
-       int mcbsp_word_length;
-       unsigned int rcr, xcr, srgr;
+       int mcbsp_word_length, master;
+       unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
        u32 spcr;
        snd_pcm_format_t fmt;
        unsigned element_cnt = 1;
@@ -396,12 +419,47 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
        }
 
-       i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-       srgr = DAVINCI_MCBSP_SRGR_FSGM;
-       srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+       master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+       fmt = params_format(params);
+       mcbsp_word_length = asp_word_length[fmt];
 
-       i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-       srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+       switch (master) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               freq = clk_get_rate(dev->clk);
+               srgr = DAVINCI_MCBSP_SRGR_FSGM |
+                      DAVINCI_MCBSP_SRGR_CLKSM;
+               srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
+                                               8 - 1);
+               /* symmetric waveforms */
+               clk_div = freq / (mcbsp_word_length * 16) /
+                         params->rate_num * params->rate_den;
+               srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
+                                               16 - 1);
+               clk_div &= 0xFF;
+               srgr |= clk_div;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               srgr = DAVINCI_MCBSP_SRGR_FSGM;
+               clk_div = dev->clk_div - 1;
+               srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
+               srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
+               clk_div &= 0xFF;
+               srgr |= clk_div;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* Clock and frame sync given from external sources */
+               i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+               srgr = DAVINCI_MCBSP_SRGR_FSGM;
+               srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+               pr_debug("%s - %d  FWID set: re-read srgr = %X\n",
+                       __func__, __LINE__, snd_interval_value(i) - 1);
+
+               i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
+               srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+               break;
+       default:
+               return -EINVAL;
+       }
        davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
 
        rcr = DAVINCI_MCBSP_RCR_RFIG;
@@ -426,12 +484,41 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                        element_cnt = 1;
                        fmt = double_fmt[fmt];
                }
+               switch (master) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+               case SND_SOC_DAIFMT_CBS_CFM:
+                       rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
+                       xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
+                       rcr |= DAVINCI_MCBSP_RCR_RPHASE;
+                       xcr |= DAVINCI_MCBSP_XCR_XPHASE;
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+                       rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
+                       xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
        dma_params->acnt = dma_params->data_type = data_type[fmt];
        dma_params->fifo_level = 0;
        mcbsp_word_length = asp_word_length[fmt];
-       rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
-       xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+
+       switch (master) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+               xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_CBM_CFS:
+               rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+               xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+               break;
+       default:
+               return -EINVAL;
+       }
 
        rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
                DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -442,6 +529,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
        else
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+
+       pr_debug("%s - %d  srgr=%X\n", __func__, __LINE__, srgr);
+       pr_debug("%s - %d  xcr=%X\n", __func__, __LINE__, xcr);
+       pr_debug("%s - %d  rcr=%X\n", __func__, __LINE__, rcr);
        return 0;
 }
 
@@ -500,6 +591,7 @@ static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
        .trigger        = davinci_i2s_trigger,
        .hw_params      = davinci_i2s_hw_params,
        .set_fmt        = davinci_i2s_set_dai_fmt,
+       .set_clkdiv     = davinci_i2s_dai_set_clkdiv,
 
 };
 
index 241648c..0b1e77b 100644 (file)
 #ifndef _DAVINCI_I2S_H
 #define _DAVINCI_I2S_H
 
+/* McBSP dividers */
+enum davinci_mcbsp_div {
+       DAVINCI_MCBSP_CLKGDV,              /* Sample rate generator divider */
+};
+
 extern struct snd_soc_dai davinci_i2s_dai;
 
 #endif