From: Raffaele Recalcati <[email protected]>
Date: Thu, 3 Jun 2010 14:40:05 +0200
Subject: [PATCH 01/12] ASoC: DaVinci: Added support for stereo I2S

     - Frame Sync master and Clock master (internally generated)
     - Frame Sync master and Clock slave

Signed-off-by: Davide Bonfanti <[email protected]>
---
 sound/soc/davinci/davinci-i2s.c |  137
++++++++++++++++++++++++++++++++++-----
 1 files changed, 121 insertions(+), 16 deletions(-)

diff --git a/sound/soc/davinci/davinci-i2s.c
b/sound/soc/davinci/davinci-i2s.c
index adadcd3..c1281c5 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -9,6 +9,8 @@
  * published by the Free Software Foundation.
  */

+#define DEBUG 3
+
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/device.h>
@@ -68,16 +70,23 @@
 #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        (1 << 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    (1 << 31)
+

+#define CLKGDV(v)                (v)     /* Bits 0:7 */
 #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    (1 << 29)

 #define DAVINCI_MCBSP_PCR_CLKRP        (1 << 0)
 #define DAVINCI_MCBSP_PCR_CLKXP        (1 << 1)
@@ -89,6 +98,11 @@
 #define DAVINCI_MCBSP_PCR_FSRM        (1 << 10)
 #define DAVINCI_MCBSP_PCR_FSXM        (1 << 11)

+/* this define works when both clock and FS are output for the cpu
+   and makes clock very fast (FS is not simmetrical, but sampling
+   frequency is better approximated */
+#define I2S_FAST_CLOCK
+
 enum {
     DAVINCI_MCBSP_WORD_8 = 0,
     DAVINCI_MCBSP_WORD_12,
@@ -146,6 +160,14 @@ struct davinci_mcbsp_dev {
     unsigned enable_channel_combine:1;
 };

+struct davinci_mcbsp_data {
+    unsigned int    fmt;
+    int             clk_div;
+};
+
+static struct davinci_mcbsp_data mcbsp_data;
+
+
 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
                        int reg, u32 val)
 {
@@ -197,7 +219,7 @@ static void davinci_mcbsp_start(struct davinci_mcbsp_dev
*dev,
             int ret = platform->pcm_ops->trigger(substream,
                 SNDRV_PCM_TRIGGER_STOP);
             if (ret < 0)
-                printk(KERN_DEBUG "Playback DMA stop failed\n");
+                pr_debug("Playback DMA stop failed\n");
         }

         /* Enable the transmitter */
@@ -219,7 +241,7 @@ static void davinci_mcbsp_start(struct davinci_mcbsp_dev
*dev,
             int ret = platform->pcm_ops->trigger(substream,
                 SNDRV_PCM_TRIGGER_START);
             if (ret < 0)
-                printk(KERN_DEBUG "Playback DMA start failed\n");
+                pr_debug("Playback DMA start failed\n");
         }
     }

@@ -254,12 +276,15 @@ 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;
-    srgr = DAVINCI_MCBSP_SRGR_FSGM |
+    srgr = DAVINCI_MCBSP_SRGR_FSGM |
         DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
         DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
+    /* Attention srgr is updated by hw_params! */

+    mcbsp_data.fmt = fmt;
     /* set master/slave audio interface */
     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+    case SND_SOC_DAIFMT_CBS_CFM:
     case SND_SOC_DAIFMT_CBS_CFS:
         /* cpu is master */
         pcr = DAVINCI_MCBSP_PCR_FSXM |
@@ -279,7 +304,7 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai
*cpu_dai,
         pcr = 0;
         break;
     default:
-        printk(KERN_ERR "%s:bad master\n", __func__);
+        dev_err(dev, "%s:bad master\n", __func__);
         return -EINVAL;
     }

@@ -310,7 +335,7 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai
*cpu_dai,
         dev->mode = MOD_DSP_B;
         break;
     default:
-        printk(KERN_ERR "%s:bad format\n", __func__);
+        dev_err(dev, "%s:bad format\n", __func__);
         return -EINVAL;
     }

@@ -372,6 +397,25 @@ 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;
+    int srgr;
+
+    mcbsp_data.clk_div = div;
+/* register set in hw_params: not needed here
+    srgr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+    srgr |= CLKGDV(div - 1);
+    davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+    pr_debug("%s - %d  DIV set re-read srgr = %X \n",
+        __func__,__LINE__, davinci_mcbsp_read_reg(dev,
DAVINCI_MCBSP_SRGR_REG));
+*/
+
+    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,11 +424,12 @@ 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;
+    struct clk *clk;

     /* general line settings */
     spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -396,12 +441,51 @@ 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 = mcbsp_data.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);
+    if (master == SND_SOC_DAIFMT_CBS_CFS) {
+        clk = clk_get(NULL, "pll1_sysclk6");//"pll1");
+        if (clk)
+            freq = clk_get_rate(clk);
+        freq = 122000000; /* FIXME ask to Texas */
+#ifdef I2S_FAST_CLOCK
+        clk_div=256;
+        do {
+            framesize = (freq / (--clk_div)) / params->rate_num *
params->rate_den;
+        } while ( ((framesize < 33) || (framesize > 4095)) && (clk_div) );
+        clk_div--;
+        srgr = DAVINCI_MCBSP_SRGR_FSGM | DAVINCI_MCBSP_SRGR_CLKSM;
+        srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length*8-1);
+        srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
+#else
+        /* simmetric waveforms */
+        srgr = DAVINCI_MCBSP_SRGR_FSGM | DAVINCI_MCBSP_SRGR_CLKSM;
+        srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
+        srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
+        clk_div = freq / (mcbsp_word_length * 16) / params->rate_num *
params->rate_den;
+#endif
+        clk_div &= 0xFF;
+        srgr |= clk_div;
+    } else if (master == SND_SOC_DAIFMT_CBS_CFM) {
+        /* Clock given on CLKS */
+        srgr = DAVINCI_MCBSP_SRGR_FSGM;
+        clk_div = mcbsp_data.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;
+    } else {
+        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);
+    }
     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);

     rcr = DAVINCI_MCBSP_RCR_RFIG;
@@ -416,7 +500,7 @@ static int davinci_i2s_hw_params(struct
snd_pcm_substream *substream,
     /* Determine xfer data type */
     fmt = params_format(params);
     if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
-        printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
+        dev_warn(dev, "davinci-i2s: unsupported PCM format\n");
         return -EINVAL;
     }

@@ -426,12 +510,29 @@ static int davinci_i2s_hw_params(struct
snd_pcm_substream *substream,
             element_cnt = 1;
             fmt = double_fmt[fmt];
         }
+        if (master == SND_SOC_DAIFMT_CBS_CFS || master ==
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;
+        } else {
+            /* FIXME ask to Texas */
+            rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
+            xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
+        }
     }
     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);
+
+    if (master == SND_SOC_DAIFMT_CBS_CFS || master ==
SND_SOC_DAIFMT_CBS_CFM) {
+        rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+        xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+    } else {
+        /* FIXME ask to Texas */
+        rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+        xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+    }

     rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
         DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -442,6 +543,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,7 +605,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,
 };

 struct snd_soc_dai davinci_i2s_dai = {
-- 
1.6.3.3
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to