]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/soc/davinci/davinci-i2s.c
ASoC: DaVinci: Audio: Fix swapping of channels at start of stereo playback
[linux-2.6-omap-h63xx.git] / sound / soc / davinci / davinci-i2s.c
index abb5fedb0b1e0952ecb17fedb7592ab966c0ffda..8c1bf876031d76ab3b9e550642dac073585ff526 100644 (file)
@@ -59,6 +59,7 @@
 #define DAVINCI_MCBSP_PCR_CLKXP                (1 << 1)
 #define DAVINCI_MCBSP_PCR_FSRP         (1 << 2)
 #define DAVINCI_MCBSP_PCR_FSXP         (1 << 3)
+#define DAVINCI_MCBSP_PCR_SCLKME       (1 << 7)
 #define DAVINCI_MCBSP_PCR_CLKRM                (1 << 8)
 #define DAVINCI_MCBSP_PCR_CLKXM                (1 << 9)
 #define DAVINCI_MCBSP_PCR_FSRM         (1 << 10)
@@ -110,16 +111,59 @@ static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_platform *platform = socdev->platform;
        u32 w;
+       int ret;
 
        /* Start the sample generator and enable transmitter/receiver */
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
        MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Stop the DMA to avoid data loss */
+               /* while the transmitter is out of reset to handle XSYNCERR */
+               if (platform->pcm_ops->trigger) {
+                       ret = platform->pcm_ops->trigger(substream,
+                               SNDRV_PCM_TRIGGER_STOP);
+                       if (ret < 0)
+                               printk(KERN_DEBUG "Playback DMA stop failed\n");
+               }
+
+               /* Enable the transmitter */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
                MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-       else
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+               /* wait for any unexpected frame sync error to occur */
+               udelay(100);
+
+               /* Disable the transmitter to clear any outstanding XSYNCERR */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+               /* Restart the DMA */
+               if (platform->pcm_ops->trigger) {
+                       ret = platform->pcm_ops->trigger(substream,
+                               SNDRV_PCM_TRIGGER_START);
+                       if (ret < 0)
+                               printk(KERN_DEBUG "Playback DMA start failed\n");
+               }
+               /* Enable the transmitter */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+       } else {
+
+               /* Enable the reciever */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
                MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       }
+
 
        /* Start frame sync */
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -171,6 +215,16 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
                                        DAVINCI_MCBSP_SRGR_FSGM);
                break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               /* McBSP CLKR pin is the input for the Sample Rate Generator.
+                * McBSP FSR and FSX are driven by the Sample Rate Generator. */
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
+                                       DAVINCI_MCBSP_PCR_SCLKME |
+                                       DAVINCI_MCBSP_PCR_FSXM |
+                                       DAVINCI_MCBSP_PCR_FSRM);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
+                                       DAVINCI_MCBSP_SRGR_FSGM);
+               break;
        case SND_SOC_DAIFMT_CBM_CFM:
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
                break;
@@ -205,6 +259,28 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
+                                       DAVINCI_MCBSP_RCR_RFRLEN1(1) |
+                                       DAVINCI_MCBSP_RCR_RDATDLY(0));
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
+                                       DAVINCI_MCBSP_XCR_XFRLEN1(1) |
+                                       DAVINCI_MCBSP_XCR_XDATDLY(0) |
+                                       DAVINCI_MCBSP_XCR_XFIG);
+               break;
+       case SND_SOC_DAIFMT_I2S:
+       default:
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
+                                       DAVINCI_MCBSP_RCR_RFRLEN1(1) |
+                                       DAVINCI_MCBSP_RCR_RDATDLY(1));
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
+                                       DAVINCI_MCBSP_XCR_XFRLEN1(1) |
+                                       DAVINCI_MCBSP_XCR_XDATDLY(1) |
+                                       DAVINCI_MCBSP_XCR_XFIG);
+               break;
+       }
+
        return 0;
 }
 
@@ -223,13 +299,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                                DAVINCI_MCBSP_SPCR_RINTM(3) |
                                DAVINCI_MCBSP_SPCR_XINTM(3) |
                                DAVINCI_MCBSP_SPCR_FREE);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
-                               DAVINCI_MCBSP_RCR_RFRLEN1(1) |
-                               DAVINCI_MCBSP_RCR_RDATDLY(1));
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
-                               DAVINCI_MCBSP_XCR_XFRLEN1(1) |
-                               DAVINCI_MCBSP_XCR_XDATDLY(1) |
-                               DAVINCI_MCBSP_XCR_XFIG);
 
        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);