]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - sound/soc/davinci/davinci-i2s.c
Merge git://git.infradead.org/users/dwmw2/libraid-2.6 into for-linus
[mv-sheeva.git] / sound / soc / davinci / davinci-i2s.c
index adadcd3aa1b1b20a2a2b50c84965deeff8fdf2f7..9e8932abf158b7f8d22660081fd73183a73a4469 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)
@@ -116,6 +122,7 @@ static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
 };
 
 struct davinci_mcbsp_dev {
+       struct device *dev;
        struct davinci_pcm_dma_params   dma_params[2];
        void __iomem                    *base;
 #define MOD_DSP_A      0
@@ -144,6 +151,11 @@ 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;
+       int clk_input_pin;
+       bool i2s_accurate_sck;
 };
 
 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -254,10 +266,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:
@@ -268,11 +282,26 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                        DAVINCI_MCBSP_PCR_CLKRM;
                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. */
-               pcr = DAVINCI_MCBSP_PCR_SCLKME |
-                       DAVINCI_MCBSP_PCR_FSXM |
-                       DAVINCI_MCBSP_PCR_FSRM;
+               pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
+               /*
+                * Selection of the clock input pin that is the
+                * input for the Sample Rate Generator.
+                * McBSP FSR and FSX are driven by the Sample Rate
+                * Generator.
+                */
+               switch (dev->clk_input_pin) {
+               case MCBSP_CLKS:
+                       pcr |= DAVINCI_MCBSP_PCR_CLKXM |
+                               DAVINCI_MCBSP_PCR_CLKRM;
+                       break;
+               case MCBSP_CLKR:
+                       pcr |= DAVINCI_MCBSP_PCR_SCLKME;
+                       break;
+               default:
+                       dev_err(dev->dev, "bad clk_input_pin\n");
+                       return -EINVAL;
+               }
+
                break;
        case SND_SOC_DAIFMT_CBM_CFM:
                /* codec is master */
@@ -372,6 +401,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 +421,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 +437,59 @@ 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);
+               if (dev->i2s_accurate_sck) {
+                       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_FPER(framesize - 1);
+               } else {
+                       /* 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 +514,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 +559,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 +621,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,
 
 };
 
@@ -526,6 +648,8 @@ static int davinci_i2s_probe(struct platform_device *pdev)
        struct snd_platform_data *pdata = pdev->dev.platform_data;
        struct davinci_mcbsp_dev *dev;
        struct resource *mem, *ioarea, *res;
+       enum dma_event_q asp_chan_q = EVENTQ_0;
+       enum dma_event_q ram_chan_q = EVENTQ_1;
        int ret;
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -552,7 +676,17 @@ static int davinci_i2s_probe(struct platform_device *pdev)
                        pdata->sram_size_playback;
                dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
                        pdata->sram_size_capture;
+               dev->clk_input_pin = pdata->clk_input_pin;
+               dev->i2s_accurate_sck = pdata->i2s_accurate_sck;
+               asp_chan_q = pdata->asp_chan_q;
+               ram_chan_q = pdata->ram_chan_q;
        }
+
+       dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q   = asp_chan_q;
+       dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q   = ram_chan_q;
+       dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q    = asp_chan_q;
+       dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q    = ram_chan_q;
+
        dev->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(dev->clk)) {
                ret = -ENODEV;
@@ -584,6 +718,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
                goto err_free_mem;
        }
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
+       dev->dev = &pdev->dev;
 
        davinci_i2s_dai.private_data = dev;
        davinci_i2s_dai.capture.dma_data = dev->dma_params;