Merge remote-tracking branches 'asoc/topic/pxa' and 'asoc/topic/qcom' into asoc-next
authorMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:17:28 +0000 (15:17 +0700)
committerMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:17:28 +0000 (15:17 +0700)
sound/soc/pxa/brownstone.c
sound/soc/qcom/Kconfig
sound/soc/qcom/apq8016_sbc.c
sound/soc/qcom/lpass-apq8016.c
sound/soc/qcom/lpass-cpu.c
sound/soc/qcom/lpass-ipq806x.c
sound/soc/qcom/lpass-lpaif-reg.h
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/lpass.h

index 416ea64..ec522e9 100644 (file)
@@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int freq_out, sspa_mclk, sysclk;
-       int sspa_div;
 
        if (params_rate(params) > 11025) {
                freq_out  = params_rate(params) * 512;
@@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
                sysclk    = params_rate(params) * 512;
                sspa_mclk = params_rate(params) * 64;
        }
-       sspa_div = freq_out / sspa_mclk;
 
        snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
        snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
index 3cc252e..8ec9a07 100644 (file)
@@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU
 
 config SND_SOC_LPASS_PLATFORM
        tristate
+       depends on HAS_DMA
        select REGMAP_MMIO
 
 config SND_SOC_LPASS_IPQ806X
        tristate
+       depends on HAS_DMA
        select SND_SOC_LPASS_CPU
        select SND_SOC_LPASS_PLATFORM
 
 config SND_SOC_LPASS_APQ8016
        tristate
+       depends on HAS_DMA
        select SND_SOC_LPASS_CPU
        select SND_SOC_LPASS_PLATFORM
 
 config SND_SOC_STORM
        tristate "ASoC I2S support for Storm boards"
-       depends on SND_SOC_QCOM
+       depends on SND_SOC_QCOM && HAS_DMA
        select SND_SOC_LPASS_IPQ806X
        select SND_SOC_MAX98357A
        help
@@ -34,7 +37,7 @@ config SND_SOC_STORM
 
 config SND_SOC_APQ8016_SBC
        tristate "SoC Audio support for APQ8016 SBC platforms"
-       depends on SND_SOC_QCOM
+       depends on SND_SOC_QCOM && HAS_DMA
        select SND_SOC_LPASS_APQ8016
        help
           Support for Qualcomm Technologies LPASS audio block in
index 1efdf00..1289543 100644 (file)
@@ -30,6 +30,7 @@ struct apq8016_sbc_data {
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
+#define MIC_CTRL_TER_WS_SLAVE_SEL      BIT(21)
 #define MIC_CTRL_QUA_WS_SLAVE_SEL_10   BIT(17)
 #define MIC_CTRL_TLMM_SCLK_EN          BIT(1)
 #define        SPKR_CTL_PRI_WS_SLAVE_SEL_11    (BIT(17) | BIT(16))
@@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
                        MIC_CTRL_TLMM_SCLK_EN,
                        pdata->mic_iomux);
                break;
+       case MI2S_TERTIARY:
+               writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL |
+                       MIC_CTRL_TLMM_SCLK_EN,
+                       pdata->mic_iomux);
+
+               break;
 
        default:
                dev_err(card->dev, "unsupported cpu dai configuration\n");
@@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
                }
 
                link->platform_of_node = link->cpu_of_node;
-               /* For now we only support playback */
-               link->playback_only = true;
-
                ret = of_property_read_string(np, "link-name", &link->name);
                if (ret) {
                        dev_err(card->dev, "error getting codec dai_link name\n");
index 94efc01..3eef0c3 100644 (file)
@@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
        },
 };
 
-static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+                                          int direction)
 {
        struct lpass_variant *v = drvdata->variant;
-       int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+       int chan = 0;
+
+       if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
                                        v->rdma_channels);
 
-       if (chan >= v->rdma_channels)
-               return -EBUSY;
+               if (chan >= v->rdma_channels)
+                       return -EBUSY;
+       } else {
+               chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+                                       v->wrdma_channel_start +
+                                       v->wrdma_channels,
+                                       v->wrdma_channel_start);
+
+               if (chan >=  v->wrdma_channel_start + v->wrdma_channels)
+                       return -EBUSY;
+       }
 
-       set_bit(chan, &drvdata->rdma_ch_bit_map);
+       set_bit(chan, &drvdata->dma_ch_bit_map);
 
        return chan;
 }
 
 static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
 {
-       clear_bit(chan, &drvdata->rdma_ch_bit_map);
+       clear_bit(chan, &drvdata->dma_ch_bit_map);
 
        return 0;
 }
@@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = {
        .rdma_reg_base          = 0x8400,
        .rdma_reg_stride        = 0x1000,
        .rdma_channels          = 2,
-       .rdmactl_audif_start    = 1,
+       .dmactl_audif_start     = 1,
+       .wrdma_reg_base         = 0xB000,
+       .wrdma_reg_stride       = 0x1000,
+       .wrdma_channel_start    = 5,
+       .wrdma_channels         = 2,
        .dai_driver             = apq8016_lpass_cpu_dai_driver,
        .num_dai                = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
        .init                   = apq8016_lpass_init,
index e5101e0..3cde9fb 100644 (file)
@@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       switch (channels) {
-       case 1:
-               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-               regval |= LPAIF_I2SCTL_SPKMONO_MONO;
-               break;
-       case 2:
-               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 4:
-               regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 6:
-               regval |= LPAIF_I2SCTL_SPKMODE_6CH;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 8:
-               regval |= LPAIF_I2SCTL_SPKMODE_8CH;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       default:
-               dev_err(dai->dev, "%s() invalid channels given: %u\n",
-                               __func__, channels);
-               return -EINVAL;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               switch (channels) {
+               case 1:
+                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+                       regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+                       break;
+               case 2:
+                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 4:
+                       regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 6:
+                       regval |= LPAIF_I2SCTL_SPKMODE_6CH;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 8:
+                       regval |= LPAIF_I2SCTL_SPKMODE_8CH;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               default:
+                       dev_err(dai->dev, "%s() invalid channels given: %u\n",
+                                       __func__, channels);
+                       return -EINVAL;
+               }
+       } else {
+               switch (channels) {
+               case 1:
+                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
+                       regval |= LPAIF_I2SCTL_MICMONO_MONO;
+                       break;
+               case 2:
+                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 4:
+                       regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 6:
+                       regval |= LPAIF_I2SCTL_MICMODE_6CH;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 8:
+                       regval |= LPAIF_I2SCTL_MICMODE_8CH;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               default:
+                       dev_err(dai->dev, "%s() invalid channels given: %u\n",
+                                       __func__, channels);
+                       return -EINVAL;
+               }
        }
 
        ret = regmap_write(drvdata->lpaif_map,
@@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 {
        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
        int ret;
+       unsigned int val, mask;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               val = LPAIF_I2SCTL_SPKEN_ENABLE;
+               mask = LPAIF_I2SCTL_SPKEN_MASK;
+       } else  {
+               val = LPAIF_I2SCTL_MICEN_ENABLE;
+               mask = LPAIF_I2SCTL_MICEN_MASK;
+       }
 
        ret = regmap_update_bits(drvdata->lpaif_map,
                        LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
-                       LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
+                       mask, val);
        if (ret)
                dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                __func__, ret);
@@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 {
        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
        int ret = -EINVAL;
+       unsigned int val, mask;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       val = LPAIF_I2SCTL_SPKEN_ENABLE;
+                       mask = LPAIF_I2SCTL_SPKEN_MASK;
+               } else  {
+                       val = LPAIF_I2SCTL_MICEN_ENABLE;
+                       mask = LPAIF_I2SCTL_MICEN_MASK;
+               }
+
                ret = regmap_update_bits(drvdata->lpaif_map,
                                LPAIF_I2SCTL_REG(drvdata->variant,
                                                dai->driver->id),
-                               LPAIF_I2SCTL_SPKEN_MASK,
-                               LPAIF_I2SCTL_SPKEN_ENABLE);
+                               mask, val);
                if (ret)
                        dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                        __func__, ret);
@@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       val = LPAIF_I2SCTL_SPKEN_DISABLE;
+                       mask = LPAIF_I2SCTL_SPKEN_MASK;
+               } else  {
+                       val = LPAIF_I2SCTL_MICEN_DISABLE;
+                       mask = LPAIF_I2SCTL_MICEN_MASK;
+               }
+
                ret = regmap_update_bits(drvdata->lpaif_map,
                                LPAIF_I2SCTL_REG(drvdata->variant,
                                                dai->driver->id),
-                               LPAIF_I2SCTL_SPKEN_MASK,
-                               LPAIF_I2SCTL_SPKEN_DISABLE);
+                               mask, val);
                if (ret)
                        dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                        __func__, ret);
@@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
                        return true;
        }
 
+       for (i = 0; i < v->wrdma_channels; ++i) {
+               if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+                       return true;
+       }
+
        return false;
 }
 
@@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
                        return true;
        }
 
+       for (i = 0; i < v->wrdma_channels; ++i) {
+               if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+                       return true;
+       }
+
        return false;
 }
 
@@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
                if (reg == LPAIF_RDMACURR_REG(v, i))
                        return true;
 
+       for (i = 0; i < v->wrdma_channels; ++i)
+               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+                       return true;
+
        return false;
 }
 
@@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
                return PTR_ERR((void const __force *)drvdata->lpaif);
        }
 
-       lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
-                                               variant->rdma_channels);
+       lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
+                                               variant->wrdma_channels +
+                                               variant->wrdma_channel_start);
 
        drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
                        &lpass_cpu_regmap_config);
index 7a41679..608c1a9 100644 (file)
@@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
        .ops    = &asoc_qcom_lpass_cpu_dai_ops,
 };
 
-static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir)
 {
-       return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+       else    /* Capture currently not implemented */
+               return -EINVAL;
 }
 
 static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
@@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = {
        .rdma_reg_base          = 0x6000,
        .rdma_reg_stride        = 0x1000,
        .rdma_channels          = 4,
+       .wrdma_reg_base         = 0xB000,
+       .wrdma_reg_stride       = 0x1000,
+       .wrdma_channel_start    = 5,
+       .wrdma_channels         = 4,
        .dai_driver             = &ipq806x_lpass_cpu_dai_driver,
        .num_dai                = 1,
        .alloc_dma_channel      = ipq806x_lpass_alloc_dma_channel,
index 95e22f1..2240bc6 100644 (file)
 #define LPAIF_I2SCTL_SPKMONO_STEREO    (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
 #define LPAIF_I2SCTL_SPKMONO_MONO      (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
 
+#define LPAIF_I2SCTL_MICEN_MASK                GENMASK(8, 8)
+#define LPAIF_I2SCTL_MICEN_SHIFT       8
+#define LPAIF_I2SCTL_MICEN_DISABLE     (0 << LPAIF_I2SCTL_MICEN_SHIFT)
+#define LPAIF_I2SCTL_MICEN_ENABLE      (1 << LPAIF_I2SCTL_MICEN_SHIFT)
+
+#define LPAIF_I2SCTL_MICMODE_MASK      GENMASK(7, 4)
+#define LPAIF_I2SCTL_MICMODE_SHIFT     4
+#define LPAIF_I2SCTL_MICMODE_NONE      (0 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD0       (1 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD1       (2 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD2       (3 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD3       (4 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD01    (5 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD23    (6 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_6CH       (7 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_8CH       (8 << LPAIF_I2SCTL_MICMODE_SHIFT)
+
+#define LPAIF_I2SCTL_MIMONO_MASK       GENMASK(3, 3)
+#define LPAIF_I2SCTL_MICMONO_SHIFT     3
+#define LPAIF_I2SCTL_MICMONO_STEREO    (0 << LPAIF_I2SCTL_MICMONO_SHIFT)
+#define LPAIF_I2SCTL_MICMONO_MONO      (1 << LPAIF_I2SCTL_MICMONO_SHIFT)
+
 #define LPAIF_I2SCTL_WSSRC_MASK                0x0004
 #define LPAIF_I2SCTL_WSSRC_SHIFT       2
 #define LPAIF_I2SCTL_WSSRC_INTERNAL    (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
 #define        LPAIF_RDMAPER_REG(v, chan)      LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
 #define        LPAIF_RDMAPERCNT_REG(v, chan)   LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
 
-#define LPAIF_RDMACTL_BURSTEN_MASK     0x800
-#define LPAIF_RDMACTL_BURSTEN_SHIFT    11
-#define LPAIF_RDMACTL_BURSTEN_SINGLE   (0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-#define LPAIF_RDMACTL_BURSTEN_INCR4    (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-
-#define LPAIF_RDMACTL_WPSCNT_MASK      0x700
-#define LPAIF_RDMACTL_WPSCNT_SHIFT     8
-#define LPAIF_RDMACTL_WPSCNT_ONE       (0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_TWO       (1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_THREE     (2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_FOUR      (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_SIX       (5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_EIGHT     (7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-
-#define LPAIF_RDMACTL_AUDINTF_MASK     0x0F0
-#define LPAIF_RDMACTL_AUDINTF_SHIFT    4
-
-#define LPAIF_RDMACTL_FIFOWM_MASK      0x00E
-#define LPAIF_RDMACTL_FIFOWM_SHIFT     1
-#define LPAIF_RDMACTL_FIFOWM_1         (0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_2         (1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_3         (2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_4         (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_5         (4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_6         (5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_7         (6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_8         (7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-
-#define LPAIF_RDMACTL_ENABLE_MASK      0x1
-#define LPAIF_RDMACTL_ENABLE_SHIFT     0
-#define LPAIF_RDMACTL_ENABLE_OFF       (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
-#define LPAIF_RDMACTL_ENABLE_ON                (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
-
+#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \
+       (v->wrdma_reg_base + (addr) + \
+        v->wrdma_reg_stride * (chan - v->wrdma_channel_start))
+
+#define LPAIF_WRDMACTL_REG(v, chan)    LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_WRDMABASE_REG(v, chan)   LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define        LPAIF_WRDMABUFF_REG(v, chan)    LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_WRDMACURR_REG(v, chan)   LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan))
+#define        LPAIF_WRDMAPER_REG(v, chan)     LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define        LPAIF_WRDMAPERCNT_REG(v, chan)  LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+
+#define __LPAIF_DMA_REG(v, chan, dir, reg)  \
+       (dir ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+               LPAIF_RDMA##reg##_REG(v, chan) : \
+               LPAIF_WRDMA##reg##_REG(v, chan)
+
+#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL)
+#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE)
+#define        LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF)
+#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR)
+#define        LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER)
+#define        LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT)
+
+#define LPAIF_DMACTL_BURSTEN_MASK      0x800
+#define LPAIF_DMACTL_BURSTEN_SHIFT     11
+#define LPAIF_DMACTL_BURSTEN_SINGLE    (0 << LPAIF_DMACTL_BURSTEN_SHIFT)
+#define LPAIF_DMACTL_BURSTEN_INCR4     (1 << LPAIF_DMACTL_BURSTEN_SHIFT)
+
+#define LPAIF_DMACTL_WPSCNT_MASK       0x700
+#define LPAIF_DMACTL_WPSCNT_SHIFT      8
+#define LPAIF_DMACTL_WPSCNT_ONE        (0 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_TWO        (1 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_THREE      (2 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_FOUR       (3 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_SIX        (5 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_EIGHT      (7 << LPAIF_DMACTL_WPSCNT_SHIFT)
+
+#define LPAIF_DMACTL_AUDINTF_MASK      0x0F0
+#define LPAIF_DMACTL_AUDINTF_SHIFT     4
+#define LPAIF_DMACTL_AUDINTF(id)       (id << LPAIF_DMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_DMACTL_FIFOWM_MASK       0x00E
+#define LPAIF_DMACTL_FIFOWM_SHIFT      1
+#define LPAIF_DMACTL_FIFOWM_1          (0 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_2          (1 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_3          (2 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_4          (3 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_5          (4 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_6          (5 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_7          (6 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_8          (7 << LPAIF_DMACTL_FIFOWM_SHIFT)
+
+#define LPAIF_DMACTL_ENABLE_MASK       0x1
+#define LPAIF_DMACTL_ENABLE_SHIFT      0
+#define LPAIF_DMACTL_ENABLE_OFF        (0 << LPAIF_DMACTL_ENABLE_SHIFT)
+#define LPAIF_DMACTL_ENABLE_ON         (1 << LPAIF_DMACTL_ENABLE_SHIFT)
+
+#define LPAIF_DMACTL_DYNCLK_MASK       BIT(12)
+#define LPAIF_DMACTL_DYNCLK_SHIFT      12
+#define LPAIF_DMACTL_DYNCLK_OFF        (0 << LPAIF_DMACTL_DYNCLK_SHIFT)
+#define LPAIF_DMACTL_DYNCLK_ON         (1 << LPAIF_DMACTL_DYNCLK_SHIFT)
 #endif /* __LPASS_LPAIF_REG_H__ */
index 4aeb8e1..6e86654 100644 (file)
@@ -26,6 +26,7 @@
 
 struct lpass_pcm_data {
        int rdma_ch;
+       int wrdma_ch;
        int i2s_port;
 };
 
@@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        snd_pcm_format_t format = params_format(params);
        unsigned int channels = params_channels(params);
        unsigned int regval;
+       int ch, dir = substream->stream;
        int bitwidth;
-       int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
+       int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+
+       if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        bitwidth = snd_pcm_format_width(format);
        if (bitwidth < 0) {
@@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
                return bitwidth;
        }
 
-       regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
-                       LPAIF_RDMACTL_AUDINTF(rdma_port) |
-                       LPAIF_RDMACTL_FIFOWM_8;
+       regval = LPAIF_DMACTL_BURSTEN_INCR4 |
+                       LPAIF_DMACTL_AUDINTF(dma_port) |
+                       LPAIF_DMACTL_FIFOWM_8;
 
        switch (bitwidth) {
        case 16:
                switch (channels) {
                case 1:
                case 2:
-                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       regval |= LPAIF_DMACTL_WPSCNT_ONE;
                        break;
                case 4:
-                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       regval |= LPAIF_DMACTL_WPSCNT_TWO;
                        break;
                case 6:
-                       regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+                       regval |= LPAIF_DMACTL_WPSCNT_THREE;
                        break;
                case 8:
-                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       regval |= LPAIF_DMACTL_WPSCNT_FOUR;
                        break;
                default:
                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        case 32:
                switch (channels) {
                case 1:
-                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       regval |= LPAIF_DMACTL_WPSCNT_ONE;
                        break;
                case 2:
-                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       regval |= LPAIF_DMACTL_WPSCNT_TWO;
                        break;
                case 4:
-                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       regval |= LPAIF_DMACTL_WPSCNT_FOUR;
                        break;
                case 6:
-                       regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+                       regval |= LPAIF_DMACTL_WPSCNT_SIX;
                        break;
                case 8:
-                       regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+                       regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
                        break;
                default:
                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
+                       LPAIF_DMACTL_REG(v, ch, dir), regval);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
+       unsigned int reg;
        int ret;
 
-       ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
+       if (substream->stream ==  SNDRV_PCM_STREAM_PLAYBACK)
+               reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch);
+       else
+               reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch);
+
+       ret = regmap_write(drvdata->lpaif_map, reg, 0);
        if (ret)
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMABASE_REG(v, ch),
+                       LPAIF_DMABASE_REG(v, ch, dir),
                        runtime->dma_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMABUFF_REG(v, ch),
+                       LPAIF_DMABUFF_REG(v, ch, dir),
                        (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMAPER_REG(v, ch),
+                       LPAIF_DMAPER_REG(v, ch, dir),
                        (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_update_bits(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, ch),
-                       LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+                       LPAIF_DMACTL_REG(v, ch, dir),
+                       LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
                }
 
                ret = regmap_update_bits(drvdata->lpaif_map,
-                               LPAIF_RDMACTL_REG(v, ch),
-                               LPAIF_RDMACTL_ENABLE_MASK,
-                               LPAIF_RDMACTL_ENABLE_ON);
+                               LPAIF_DMACTL_REG(v, ch, dir),
+                               LPAIF_DMACTL_ENABLE_MASK,
+                               LPAIF_DMACTL_ENABLE_ON);
                if (ret) {
                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                        __func__, ret);
@@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                ret = regmap_update_bits(drvdata->lpaif_map,
-                               LPAIF_RDMACTL_REG(v, ch),
-                               LPAIF_RDMACTL_ENABLE_MASK,
-                               LPAIF_RDMACTL_ENABLE_OFF);
+                               LPAIF_DMACTL_REG(v, ch, dir),
+                               LPAIF_DMACTL_ENABLE_MASK,
+                               LPAIF_DMACTL_ENABLE_OFF);
                if (ret) {
                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                        __func__, ret);
@@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
                        snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
        unsigned int base_addr, curr_addr;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        ret = regmap_read(drvdata->lpaif_map,
-                       LPAIF_RDMABASE_REG(v, ch), &base_addr);
+                       LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
                                __func__, ret);
@@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
        }
 
        ret = regmap_read(drvdata->lpaif_map,
-                       LPAIF_RDMACURR_REG(v, ch), &curr_addr);
+                       LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
                                __func__, ret);
@@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
-               struct snd_soc_pcm_runtime *rt)
-{
-       struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
-
-       buf->dev.type = SNDRV_DMA_TYPE_DEV;
-       buf->dev.dev = rt->platform->dev;
-       buf->private_data = NULL;
-       buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr,
-                       GFP_KERNEL);
-       if (!buf->area) {
-               dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n",
-                               __func__);
-               return -ENOMEM;
-       }
-       buf->bytes = size;
-
-       return 0;
-}
-
-static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
-               struct snd_soc_pcm_runtime *rt)
-{
-       struct snd_dma_buffer *buf = &substream->dma_buffer;
-
-       if (buf->area) {
-               dma_free_coherent(rt->dev, buf->bytes, buf->area,
-                               buf->addr);
-       }
-       buf->area = NULL;
-}
-
 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 {
        struct snd_pcm *pcm = soc_runtime->pcm;
-       struct snd_pcm_substream *substream =
-               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       struct snd_pcm_substream *psubstream, *csubstream;
        struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
        int ret;
        struct lpass_pcm_data *data;
+       size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 
        data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       if (v->alloc_dma_channel)
-               data->rdma_ch = v->alloc_dma_channel(drvdata);
+       data->i2s_port = cpu_dai->driver->id;
+       snd_soc_pcm_set_drvdata(soc_runtime, data);
 
-       if (IS_ERR_VALUE(data->rdma_ch))
-               return data->rdma_ch;
+       psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (psubstream) {
+               if (v->alloc_dma_channel)
+                       data->rdma_ch = v->alloc_dma_channel(drvdata,
+                                               SNDRV_PCM_STREAM_PLAYBACK);
 
-       drvdata->substream[data->rdma_ch] = substream;
-       data->i2s_port = cpu_dai->driver->id;
+               if (IS_ERR_VALUE(data->rdma_ch))
+                       return data->rdma_ch;
 
-       snd_soc_pcm_set_drvdata(soc_runtime, data);
+               drvdata->substream[data->rdma_ch] = psubstream;
 
-       ret = lpass_platform_alloc_buffer(substream, soc_runtime);
-       if (ret)
-               return ret;
+               ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                       soc_runtime->platform->dev,
+                                       size, &psubstream->dma_buffer);
+               if (ret)
+                       goto playback_alloc_err;
 
-       ret = regmap_write(drvdata->lpaif_map,
+               ret = regmap_write(drvdata->lpaif_map,
                        LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
-       if (ret) {
-               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+               if (ret) {
+                       dev_err(soc_runtime->dev,
+                               "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+                       goto capture_alloc_err;
+               }
+       }
+
+       csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       if (csubstream) {
+               if (v->alloc_dma_channel)
+                       data->wrdma_ch = v->alloc_dma_channel(drvdata,
+                                               SNDRV_PCM_STREAM_CAPTURE);
+
+               if (IS_ERR_VALUE(data->wrdma_ch))
+                       goto capture_alloc_err;
+
+               drvdata->substream[data->wrdma_ch] = csubstream;
+
+               ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                       soc_runtime->platform->dev,
+                                       size, &csubstream->dma_buffer);
+               if (ret)
+                       goto capture_alloc_err;
+
+               ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
+               if (ret) {
+                       dev_err(soc_runtime->dev,
+                               "%s() error writing to wrdmactl reg: %d\n",
                                __func__, ret);
-               goto err_buf;
+                       goto capture_reg_err;
+               }
        }
 
        return 0;
 
-err_buf:
-       lpass_platform_free_buffer(substream, soc_runtime);
+capture_reg_err:
+       if (csubstream)
+               snd_dma_free_pages(&csubstream->dma_buffer);
+
+capture_alloc_err:
+       if (psubstream)
+               snd_dma_free_pages(&psubstream->dma_buffer);
+
+ playback_alloc_err:
+       dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
+
        return ret;
 }
 
 static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 {
-       struct snd_pcm_substream *substream =
-               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct lpass_data *drvdata =
-               snd_soc_platform_get_drvdata(soc_runtime->platform);
-       struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
-       struct lpass_variant *v = drvdata->variant;
-
-       drvdata->substream[data->rdma_ch] = NULL;
-
-       if (v->free_dma_channel)
-               v->free_dma_channel(drvdata, data->rdma_ch);
-
-       lpass_platform_free_buffer(substream, soc_runtime);
+       struct snd_soc_pcm_runtime *rt;
+       struct lpass_data *drvdata;
+       struct lpass_pcm_data *data;
+       struct lpass_variant *v;
+       struct snd_pcm_substream *substream;
+       int ch, i;
+
+       for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
+               substream = pcm->streams[i].substream;
+               if (substream) {
+                       rt = substream->private_data;
+                       data = snd_soc_pcm_get_drvdata(rt);
+                       drvdata = snd_soc_platform_get_drvdata(rt->platform);
+
+                       ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               ? data->rdma_ch
+                               : data->wrdma_ch;
+                       v = drvdata->variant;
+                       drvdata->substream[ch] = NULL;
+                       if (v->free_dma_channel)
+                               v->free_dma_channel(drvdata, ch);
+
+                       snd_dma_free_pages(&substream->dma_buffer);
+                       substream->dma_buffer.area = NULL;
+                       substream->dma_buffer.addr = 0;
+               }
+       }
 }
 
 static struct snd_soc_platform_driver lpass_platform_driver = {
index 0b63e2e..30714ad 100644 (file)
@@ -50,7 +50,7 @@ struct lpass_data {
        struct lpass_variant *variant;
 
        /* bit map to keep track of static channel allocations */
-       unsigned long rdma_ch_bit_map;
+       unsigned long dma_ch_bit_map;
 
        /* used it for handling interrupt per dma channel */
        struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
@@ -71,16 +71,20 @@ struct lpass_variant {
        u32     rdma_reg_base;
        u32     rdma_reg_stride;
        u32     rdma_channels;
+       u32     wrdma_reg_base;
+       u32     wrdma_reg_stride;
+       u32     wrdma_channels;
 
        /**
         * on SOCs like APQ8016 the channel control bits start
         * at different offset to ipq806x
         **/
-       u32     rdmactl_audif_start;
+       u32     dmactl_audif_start;
+       u32     wrdma_channel_start;
        /* SOC specific intialization like clocks */
        int (*init)(struct platform_device *pdev);
        int (*exit)(struct platform_device *pdev);
-       int (*alloc_dma_channel)(struct lpass_data *data);
+       int (*alloc_dma_channel)(struct lpass_data *data, int direction);
        int (*free_dma_channel)(struct lpass_data *data, int ch);
 
        /* SOC specific dais */