diff mbox

[7/9] ASoC: tegra: move to generic DMA DT binding

Message ID 1374639002-16753-8-git-send-email-rizhao@nvidia.com
State Superseded, archived
Headers show

Commit Message

Richard Zhao July 24, 2013, 4:10 a.m. UTC
- add tegra_dma_filter_data to specify dma info
  DMA DT binding needs the device that raise dma request and dma name
  to request a dma channel. tegra30_i2s is a special case. It should be ahub
  device and it also has dma name that cannot handled by ASoC dmaengine code.
  So we pass the info using filter data in snd_dmaengine_dai_dma_data.
- change i2s/ac97 drivers to use generic DT binding
- tegra30_i2s: alloc ahub tx/rx fifo at driver probe time

Signed-off-by: Richard Zhao <rizhao@nvidia.com>
---
 .../bindings/sound/nvidia,tegra20-ac97.txt         |  8 ++--
 .../bindings/sound/nvidia,tegra20-i2s.txt          |  8 ++--
 .../bindings/sound/nvidia,tegra30-ahub.txt         | 12 +++---
 sound/soc/tegra/tegra20_ac97.c                     | 17 +++------
 sound/soc/tegra/tegra20_ac97.h                     |  2 +
 sound/soc/tegra/tegra20_i2s.c                      | 26 ++++---------
 sound/soc/tegra/tegra20_i2s.h                      |  2 +
 sound/soc/tegra/tegra30_ahub.c                     | 25 +++++-------
 sound/soc/tegra/tegra30_ahub.h                     | 11 +++---
 sound/soc/tegra/tegra30_i2s.c                      | 44 +++++++++++++++-------
 sound/soc/tegra/tegra30_i2s.h                      |  2 +
 sound/soc/tegra/tegra_pcm.c                        | 14 +++++++
 sound/soc/tegra/tegra_pcm.h                        | 13 +++++++
 13 files changed, 107 insertions(+), 77 deletions(-)

Comments

Lars-Peter Clausen July 24, 2013, 6:58 a.m. UTC | #1
On 07/24/2013 06:10 AM, Richard Zhao wrote:
> - add tegra_dma_filter_data to specify dma info
>    DMA DT binding needs the device that raise dma request and dma name
>    to request a dma channel. tegra30_i2s is a special case. It should be ahub
>    device and it also has dma name that cannot handled by ASoC dmaengine code.
>    So we pass the info using filter data in snd_dmaengine_dai_dma_data.

How about extending the generic dmaengine PCM driver so that it is possible to 
specify the the DMA channel names? I think that will make the code a bit 
simpler and also allow you to remove tegra_pcm.c completely eventually.

- Lars
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Warren July 26, 2013, 7:58 p.m. UTC | #2
On 07/23/2013 10:10 PM, Richard Zhao wrote:
> - add tegra_dma_filter_data to specify dma info
>   DMA DT binding needs the device that raise dma request and dma name
>   to request a dma channel. tegra30_i2s is a special case. It should be ahub
>   device and it also has dma name that cannot handled by ASoC dmaengine code.
>   So we pass the info using filter data in snd_dmaengine_dai_dma_data.
> - change i2s/ac97 drivers to use generic DT binding
> - tegra30_i2s: alloc ahub tx/rx fifo at driver probe time

> diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c

I don't understand why the FIFO alloc/free had to move from
startup()/shutdown() to probe()/remove(). This will prevent later
changes from making the connectivity between the AHUB FIFOs and I2S
modules more dynamic, which is kind of the whole point of the AHUB HW
module.

> diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h

> +#define TEGRA_DMA_NAME_MAX_LEN	10
> +
> +/*
> + * Generic DMA DT binding needs the device that raise dma request and dma name
> + * to request a dma channel. tegra30_i2s is a special case. It should be ahub
> + * device and it also has dma name that cannot handled by ASoC dmaengine code.
> + * So we pass the info using filter data in snd_dmaengine_dai_dma_data.
> + */
> +struct tegra_dma_filter_data {
> +	struct device *dma_dev;
> +	char dma_name[TEGRA_DMA_NAME_MAX_LEN];
> +};

I agree with Lars-Peter's comment that it'd be better to enhance the
ASoC dmaengine support to allow the channel name to be specified so we
don't need to much custom code. And that should use "char *" not a
fixed-length array.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Richard Zhao July 30, 2013, 10:12 a.m. UTC | #3
On Sat, Jul 27, 2013 at 03:58:21AM +0800, Stephen Warren wrote:
> On 07/23/2013 10:10 PM, Richard Zhao wrote:
> > - add tegra_dma_filter_data to specify dma info
> >   DMA DT binding needs the device that raise dma request and dma name
> >   to request a dma channel. tegra30_i2s is a special case. It should be ahub
> >   device and it also has dma name that cannot handled by ASoC dmaengine code.
> >   So we pass the info using filter data in snd_dmaengine_dai_dma_data.
> > - change i2s/ac97 drivers to use generic DT binding
> > - tegra30_i2s: alloc ahub tx/rx fifo at driver probe time
> 
> > diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
> 
> I don't understand why the FIFO alloc/free had to move from
> startup()/shutdown() to probe()/remove().

The call path is:
[<c0520e38>] (dump_stack+0x78/0xbc) from [<c03bb854>] (tegra_pcm_request_chan+0x10/0x34)
[<c03bb854>] (tegra_pcm_request_chan+0x10/0x34) from [<c03b6c2c>] (dmaengine_pcm_new+0xe0/0x134)
[<c03b6c2c>] (dmaengine_pcm_new+0xe0/0x134) from [<c03b5824>] (soc_new_pcm+0x2f0/0x3cc)
[<c03b5824>] (soc_new_pcm+0x2f0/0x3cc) from [<c03aa1a0>] (soc_probe_link_dais+0x358/0x39c)
[<c03aa1a0>] (soc_probe_link_dais+0x358/0x39c) from [<c03ab334>] (snd_soc_instantiate_card+0x35c/0x7a0)
[<c03ab334>] (snd_soc_instantiate_card+0x35c/0x7a0) from [<c03ab9dc>] (snd_soc_register_card+0x264/0x330)
[<c03ab9dc>] (snd_soc_register_card+0x264/0x330) from [<c03be350>] (tegra_rt5640_probe+0xf4/0x17c)
[<c03be350>] (tegra_rt5640_probe+0xf4/0x17c) from [<c0294290>] (platform_drv_probe+0x18/0x1c)

So I need to request dma channel at probe time.

> This will prevent later
> changes from making the connectivity between the AHUB FIFOs and I2S
> modules more dynamic, which is kind of the whole point of the AHUB HW
> module.

That's because it's i2s driver to register pcm device rather not ahub.
Ideally dma channel is only bound to ahub.
> 
> > diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
> 
> > +#define TEGRA_DMA_NAME_MAX_LEN	10
> > +
> > +/*
> > + * Generic DMA DT binding needs the device that raise dma request and dma name
> > + * to request a dma channel. tegra30_i2s is a special case. It should be ahub
> > + * device and it also has dma name that cannot handled by ASoC dmaengine code.
> > + * So we pass the info using filter data in snd_dmaengine_dai_dma_data.
> > + */
> > +struct tegra_dma_filter_data {
> > +	struct device *dma_dev;
> > +	char dma_name[TEGRA_DMA_NAME_MAX_LEN];
> > +};
> 
> I agree with Lars-Peter's comment that it'd be better to enhance the
> ASoC dmaengine support to allow the channel name to be specified so we
> don't need to much custom code.

Not only dma-name but also dma client device for this case.

> And that should use "char *" not a
> fixed-length array.
Ok, then there' two solutions.

struct tegra30_i2s {
...
        struct snd_dmaengine_dai_dma_data capture_dma_data;
        char dma_rx_name[10];
        struct tegra_dma_filter_data filter_data_rx;
...
        struct snd_dmaengine_dai_dma_data playback_dma_data;
        char dma_tx_name[10];
        struct tegra_dma_filter_data filter_data_tx;
};

probe: i2s->filter_data_rx.dma_name = i2s.dma_rx_name

or

probe: i2s->filter_data_rx.dma_name = kasprintf(GFP_KERNEL, "channel%d" ...);
remove: kfree(i2s->filter_data_rx.dma.name)

Which one do you like?


Thanks for review!
Richard
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Warren July 30, 2013, 4:54 p.m. UTC | #4
On 07/30/2013 04:12 AM, Richard Zhao wrote:
> On Sat, Jul 27, 2013 at 03:58:21AM +0800, Stephen Warren wrote:
>> On 07/23/2013 10:10 PM, Richard Zhao wrote:
>>> - add tegra_dma_filter_data to specify dma info
>>>   DMA DT binding needs the device that raise dma request and dma name
>>>   to request a dma channel. tegra30_i2s is a special case. It should be ahub
>>>   device and it also has dma name that cannot handled by ASoC dmaengine code.
>>>   So we pass the info using filter data in snd_dmaengine_dai_dma_data.
>>> - change i2s/ac97 drivers to use generic DT binding
>>> - tegra30_i2s: alloc ahub tx/rx fifo at driver probe time
>>
>>> diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
>>
>> I don't understand why the FIFO alloc/free had to move from
>> startup()/shutdown() to probe()/remove().
> 
> The call path is:
> [<c0520e38>] (dump_stack+0x78/0xbc) from [<c03bb854>] (tegra_pcm_request_chan+0x10/0x34)
> [<c03bb854>] (tegra_pcm_request_chan+0x10/0x34) from [<c03b6c2c>] (dmaengine_pcm_new+0xe0/0x134)
> [<c03b6c2c>] (dmaengine_pcm_new+0xe0/0x134) from [<c03b5824>] (soc_new_pcm+0x2f0/0x3cc)
> [<c03b5824>] (soc_new_pcm+0x2f0/0x3cc) from [<c03aa1a0>] (soc_probe_link_dais+0x358/0x39c)
> [<c03aa1a0>] (soc_probe_link_dais+0x358/0x39c) from [<c03ab334>] (snd_soc_instantiate_card+0x35c/0x7a0)
> [<c03ab334>] (snd_soc_instantiate_card+0x35c/0x7a0) from [<c03ab9dc>] (snd_soc_register_card+0x264/0x330)
> [<c03ab9dc>] (snd_soc_register_card+0x264/0x330) from [<c03be350>] (tegra_rt5640_probe+0xf4/0x17c)
> [<c03be350>] (tegra_rt5640_probe+0xf4/0x17c) from [<c0294290>] (platform_drv_probe+0x18/0x1c)
> 
> So I need to request dma channel at probe time.

The AHUB driver should look up and cache the DMA channel information
during its probe() call, and defer probe() there if necessary.

The FIFO alloc/free calls should simply use the cached information for
the allocated channel.

That way, the changes are isolated in the AHUB driver and shouldn't
require any changes in the I2S driver.

The DT parsing and DMA request lookup will then happen well before the
snd_soc_instantiate_card() backtrace you mention above, since it will
prevent the card from being instantiated until all the ASoC components
are fully probed.

>> This will prevent later
>> changes from making the connectivity between the AHUB FIFOs and I2S
>> modules more dynamic, which is kind of the whole point of the AHUB HW
>> module.
> 
> That's because it's i2s driver to register pcm device rather not ahub.
> Ideally dma channel is only bound to ahub.

Long-term, this should change. However, I believe if you make the
changes I mention above, this won't be an issue.

>>> diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
>>
>>> +#define TEGRA_DMA_NAME_MAX_LEN	10
>>> +
>>> +/*
>>> + * Generic DMA DT binding needs the device that raise dma request and dma name
>>> + * to request a dma channel. tegra30_i2s is a special case. It should be ahub
>>> + * device and it also has dma name that cannot handled by ASoC dmaengine code.
>>> + * So we pass the info using filter data in snd_dmaengine_dai_dma_data.
>>> + */
>>> +struct tegra_dma_filter_data {
>>> +	struct device *dma_dev;
>>> +	char dma_name[TEGRA_DMA_NAME_MAX_LEN];
>>> +};
>>
>> I agree with Lars-Peter's comment that it'd be better to enhance the
>> ASoC dmaengine support to allow the channel name to be specified so we
>> don't need to much custom code.
> 
> Not only dma-name but also dma client device for this case.
> 
>> And that should use "char *" not a
>> fixed-length array.
>
> Ok, then there' two solutions.
> 
> struct tegra30_i2s {
> ...
>         struct snd_dmaengine_dai_dma_data capture_dma_data;
>         char dma_rx_name[10];
>         struct tegra_dma_filter_data filter_data_rx;
> ...
>         struct snd_dmaengine_dai_dma_data playback_dma_data;
>         char dma_tx_name[10];
>         struct tegra_dma_filter_data filter_data_tx;
> };
> 
> probe: i2s->filter_data_rx.dma_name = i2s.dma_rx_name
> 
> or
> 
> probe: i2s->filter_data_rx.dma_name = kasprintf(GFP_KERNEL, "channel%d" ...);
> remove: kfree(i2s->filter_data_rx.dma.name)
> 
> Which one do you like?

Either is fine, although the first option has less dynamic allocation,
so is probably better. The AHUB driver should be constructing the
channel names though.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
index c145497..972f444 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
@@ -4,8 +4,9 @@  Required properties:
 - compatible : "nvidia,tegra20-ac97"
 - reg : Should contain AC97 controller registers location and length
 - interrupts : Should contain AC97 interrupt
-- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
-  request selector for the AC97 controller
+- dmas : The Tegra DMA controller's phandle and request selector for
+  the AC97 controller
+- dma-names : Should be "rx-tx"
 - nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number
   of the GPIO used to reset the external AC97 codec
 - nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number
@@ -16,7 +17,8 @@  ac97@70002000 {
 	compatible = "nvidia,tegra20-ac97";
 	reg = <0x70002000 0x200>;
 	interrupts = <0 81 0x04>;
-	nvidia,dma-request-selector = <&apbdma 12>;
+	dmas = <&apbdma 12>;
+	dma-names = "rx-tx";
 	nvidia,codec-reset-gpio = <&gpio 170 0>;
 	nvidia,codec-sync-gpio = <&gpio 120 0>;
 };
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt
index 0df2b5c..61a6c8d 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt
@@ -4,8 +4,9 @@  Required properties:
 - compatible : "nvidia,tegra20-i2s"
 - reg : Should contain I2S registers location and length
 - interrupts : Should contain I2S interrupt
-- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
-  request selector for this I2S controller
+- dmas : The Tegra DMA controller's phandle and request selector
+  for this I2S controller
+- dma-names : Should be "rx-tx"
 
 Example:
 
@@ -13,5 +14,6 @@  i2s@70002800 {
 	compatible = "nvidia,tegra20-i2s";
 	reg = <0x70002800 0x200>;
 	interrupts = < 45 >;
-	nvidia,dma-request-selector = < &apbdma 2 >;
+	dmas = < &apbdma 2 >;
+	dma-names = "rx-tx";
 };
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt
index 0e5c12c..0ac563b 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt
@@ -7,11 +7,10 @@  Required properties:
   - Tegra30 requires 2 entries, for the APBIF and AHUB/AUDIO register blocks.
   - Tegra114 requires an additional entry, for the APBIF2 register block.
 - interrupts : Should contain AHUB interrupt
-- nvidia,dma-request-selector : A list of the DMA channel specifiers. Each
-  entry contains the Tegra DMA controller's phandle and request selector.
-  If a single entry is present, the request selectors for the channels are
-  assumed to be contiguous, and increment from this value.
-  If multiple values are given, one value must be given per channel.
+- dmas : A list of the DMA channel specifiers. Each entry contains the Tegra
+  DMA controller's phandle and request selector.
+- dma-names : Should be a list of "channelx", in which x is 0, 1, 2, ...
+  One entry is required for each RX/TX FIFO pair that exists in hardware.
 - clocks : Must contain an entry for each required entry in clock-names.
 - clock-names : Must include the following entries:
   - Tegra30: Requires d_audio, apbif, i2s0, i2s1, i2s2, i2s3, i2s4, dam0,
@@ -34,7 +33,8 @@  ahub@70080000 {
 	compatible = "nvidia,tegra30-ahub";
 	reg = <0x70080000 0x200 0x70080200 0x100>;
 	interrupts = < 0 103 0x04 >;
-	nvidia,dma-request-selector = <&apbdma 1>;
+	dmas = <&apbdma 1>, <&apbdma 2>, <&apbdma 3>, <&apbdma 4>;
+	dma-names = "channel0", "channel1", "channel2", "channel3";
 	clocks = <&tegra_car 106>, <&tegra_car 107>, <&tegra_car 30>,
 		<&tegra_car 11>, <&tegra_car 18>, <&tegra_car 101>,
 		<&tegra_car 102>, <&tegra_car 108>, <&tegra_car 109>,
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 6c48662..935900a 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -313,7 +313,6 @@  static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 {
 	struct tegra20_ac97 *ac97;
 	struct resource *mem;
-	u32 of_dma[2];
 	void __iomem *regs;
 	int ret = 0;
 
@@ -354,14 +353,6 @@  static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 		goto err_clk_put;
 	}
 
-	if (of_property_read_u32_array(pdev->dev.of_node,
-				       "nvidia,dma-request-selector",
-				       of_dma, 2) < 0) {
-		dev_err(&pdev->dev, "No DMA resource\n");
-		ret = -ENODEV;
-		goto err_clk_put;
-	}
-
 	ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
 					     "nvidia,codec-reset-gpio", 0);
 	if (gpio_is_valid(ac97->reset_gpio)) {
@@ -386,12 +377,16 @@  static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 	ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
 	ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 	ac97->capture_dma_data.maxburst = 4;
-	ac97->capture_dma_data.slave_id = of_dma[1];
+	ac97->filter_data_rx.dma_dev = &pdev->dev;
+	sprintf(ac97->filter_data_rx.dma_name, "rx-tx");
+	ac97->capture_dma_data.filter_data = &ac97->filter_data_rx;
 
 	ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
 	ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 	ac97->playback_dma_data.maxburst = 4;
-	ac97->playback_dma_data.slave_id = of_dma[1];
+	ac97->filter_data_tx.dma_dev = &pdev->dev;
+	sprintf(ac97->filter_data_tx.dma_name, "rx-tx");
+	ac97->playback_dma_data.filter_data = &ac97->filter_data_tx;
 
 	ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
 	if (ret)
diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h
index 4acb3aa..ac09d34 100644
--- a/sound/soc/tegra/tegra20_ac97.h
+++ b/sound/soc/tegra/tegra20_ac97.h
@@ -86,7 +86,9 @@ 
 struct tegra20_ac97 {
 	struct clk *clk_ac97;
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
+	struct tegra_dma_filter_data filter_data_rx;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
+	struct tegra_dma_filter_data filter_data_tx;
 	struct regmap *regmap;
 	int reset_gpio;
 	int sync_gpio;
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 52af7f6..af98338 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -339,9 +339,7 @@  static const struct regmap_config tegra20_i2s_regmap_config = {
 static int tegra20_i2s_platform_probe(struct platform_device *pdev)
 {
 	struct tegra20_i2s *i2s;
-	struct resource *mem, *memregion, *dmareq;
-	u32 of_dma[2];
-	u32 dma_ch;
+	struct resource *mem, *memregion;
 	void __iomem *regs;
 	int ret;
 
@@ -370,20 +368,6 @@  static int tegra20_i2s_platform_probe(struct platform_device *pdev)
 		goto err_clk_put;
 	}
 
-	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-	if (!dmareq) {
-		if (of_property_read_u32_array(pdev->dev.of_node,
-					"nvidia,dma-request-selector",
-					of_dma, 2) < 0) {
-			dev_err(&pdev->dev, "No DMA resource\n");
-			ret = -ENODEV;
-			goto err_clk_put;
-		}
-		dma_ch = of_dma[1];
-	} else {
-		dma_ch = dmareq->start;
-	}
-
 	memregion = devm_request_mem_region(&pdev->dev, mem->start,
 					    resource_size(mem), DRV_NAME);
 	if (!memregion) {
@@ -410,12 +394,16 @@  static int tegra20_i2s_platform_probe(struct platform_device *pdev)
 	i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
 	i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 	i2s->capture_dma_data.maxburst = 4;
-	i2s->capture_dma_data.slave_id = dma_ch;
+	i2s->filter_data_rx.dma_dev = &pdev->dev;
+	sprintf(i2s->filter_data_rx.dma_name, "rx-tx");
+	i2s->capture_dma_data.filter_data = &i2s->filter_data_rx;
 
 	i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
 	i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 	i2s->playback_dma_data.maxburst = 4;
-	i2s->playback_dma_data.slave_id = dma_ch;
+	i2s->filter_data_tx.dma_dev = &pdev->dev;
+	sprintf(i2s->filter_data_tx.dma_name, "rx-tx");
+	i2s->playback_dma_data.filter_data = &i2s->filter_data_tx;
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h
index fa6c29c..794d487 100644
--- a/sound/soc/tegra/tegra20_i2s.h
+++ b/sound/soc/tegra/tegra20_i2s.h
@@ -156,7 +156,9 @@  struct tegra20_i2s {
 	struct snd_soc_dai_driver dai;
 	struct clk *clk_i2s;
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
+	struct tegra_dma_filter_data filter_data_rx;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
+	struct tegra_dma_filter_data filter_data_tx;
 	struct regmap *regmap;
 };
 
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index d554d46..afa8a0b 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -96,7 +96,7 @@  static int tegra30_ahub_runtime_resume(struct device *dev)
 
 int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
 				  dma_addr_t *fiforeg,
-				  unsigned int *reqsel)
+				 struct tegra_dma_filter_data *filter_data)
 {
 	int channel;
 	u32 reg, val;
@@ -111,7 +111,9 @@  int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
 	*rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel;
 	*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
 		   (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE);
-	*reqsel = ahub->dma_sel + channel;
+
+	filter_data->dma_dev = ahub->dev;
+	sprintf(filter_data->dma_name, "channel%d", channel);
 
 	reg = TEGRA30_AHUB_CHANNEL_CTRL +
 	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
@@ -178,8 +180,8 @@  int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif)
 EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo);
 
 int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
-				  dma_addr_t *fiforeg,
-				  unsigned int *reqsel)
+				dma_addr_t *fiforeg,
+				struct tegra_dma_filter_data *filter_data)
 {
 	int channel;
 	u32 reg, val;
@@ -194,7 +196,9 @@  int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
 	*txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel;
 	*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
 		   (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE);
-	*reqsel = ahub->dma_sel + channel;
+
+	filter_data->dma_dev = ahub->dev;
+	sprintf(filter_data->dma_name, "channel%d", channel);
 
 	reg = TEGRA30_AHUB_CHANNEL_CTRL +
 	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
@@ -456,7 +460,6 @@  static int tegra30_ahub_probe(struct platform_device *pdev)
 	struct clk *clk;
 	int i;
 	struct resource *res0, *res1, *region;
-	u32 of_dma[2];
 	void __iomem *regs_apbif, *regs_ahub;
 	int ret = 0;
 
@@ -513,16 +516,6 @@  static int tegra30_ahub_probe(struct platform_device *pdev)
 		goto err_clk_put_d_audio;
 	}
 
-	if (of_property_read_u32_array(pdev->dev.of_node,
-				"nvidia,dma-request-selector",
-				of_dma, 2) < 0) {
-		dev_err(&pdev->dev,
-			"Missing property nvidia,dma-request-selector\n");
-		ret = -ENODEV;
-		goto err_clk_put_d_audio;
-	}
-	ahub->dma_sel = of_dma[1];
-
 	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res0) {
 		dev_err(&pdev->dev, "No apbif memory resource\n");
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
index 09766cd..3eae224 100644
--- a/sound/soc/tegra/tegra30_ahub.h
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -19,6 +19,8 @@ 
 #ifndef __TEGRA30_AHUB_H__
 #define __TEGRA30_AHUB_H__
 
+#include "tegra_pcm.h"
+
 /* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
 
 #define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT	28
@@ -451,15 +453,15 @@  enum tegra30_ahub_rxcif {
 };
 
 extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
-					 dma_addr_t *fiforeg,
-					 unsigned int *reqsel);
+				 dma_addr_t *fiforeg,
+				 struct tegra_dma_filter_data *filter_data);
 extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
 extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
 extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif);
 
 extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
-					 dma_addr_t *fiforeg,
-					 unsigned int *reqsel);
+				 dma_addr_t *fiforeg,
+				 struct tegra_dma_filter_data *filter_data);
 extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif);
 extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif);
 extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif);
@@ -488,7 +490,6 @@  struct tegra30_ahub {
 	struct device *dev;
 	struct clk *clk_d_audio;
 	struct clk *clk_apbif;
-	int dma_sel;
 	resource_size_t apbif_addr;
 	struct regmap *regmap_apbif;
 	struct regmap *regmap_ahub;
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index d04146c..e1672c0 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -76,27 +76,16 @@  static int tegra30_i2s_startup(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
 	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-	int ret;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
-					&i2s->playback_dma_data.addr,
-					&i2s->playback_dma_data.slave_id);
-		i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		i2s->playback_dma_data.maxburst = 4;
 		tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif,
 					       i2s->playback_fifo_cif);
 	} else {
-		ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif,
-					&i2s->capture_dma_data.addr,
-					&i2s->capture_dma_data.slave_id);
-		i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		i2s->capture_dma_data.maxburst = 4;
 		tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif,
 					       i2s->capture_i2s_cif);
 	}
 
-	return ret;
+	return 0;
 }
 
 static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
@@ -106,10 +95,8 @@  static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
-		tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
 	} else {
 		tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif);
-		tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
 	}
 }
 
@@ -412,6 +399,9 @@  static int tegra30_i2s_platform_probe(struct platform_device *pdev)
 	}
 	dev_set_drvdata(&pdev->dev, i2s);
 
+	i2s->capture_dma_data.filter_data = &i2s->filter_data_rx;
+	i2s->playback_dma_data.filter_data = &i2s->filter_data_tx;
+
 	i2s->dai = tegra30_i2s_dai_template;
 	i2s->dai.name = dev_name(&pdev->dev);
 
@@ -462,6 +452,26 @@  static int tegra30_i2s_platform_probe(struct platform_device *pdev)
 	}
 	regcache_cache_only(i2s->regmap, true);
 
+	ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
+					&i2s->playback_dma_data.addr,
+					&i2s->filter_data_tx);
+	if (ret) {
+		dev_err(&pdev->dev, "ahub allocate tx fifo failed\n");
+		goto err_clk_put;
+	}
+	ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif,
+					&i2s->capture_dma_data.addr,
+					&i2s->filter_data_rx);
+	if (ret) {
+		dev_err(&pdev->dev, "ahub allocate rx fifo failed\n");
+		goto err_ahub_free_tx;
+	}
+
+	i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	i2s->playback_dma_data.maxburst = 4;
+	i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	i2s->capture_dma_data.maxburst = 4;
+
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
 		ret = tegra30_i2s_runtime_resume(&pdev->dev);
@@ -492,6 +502,9 @@  err_suspend:
 		tegra30_i2s_runtime_suspend(&pdev->dev);
 err_pm_disable:
 	pm_runtime_disable(&pdev->dev);
+	tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
+err_ahub_free_tx:
+	tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
 err_clk_put:
 	clk_put(i2s->clk_i2s);
 err:
@@ -506,6 +519,9 @@  static int tegra30_i2s_platform_remove(struct platform_device *pdev)
 	if (!pm_runtime_status_suspended(&pdev->dev))
 		tegra30_i2s_runtime_suspend(&pdev->dev);
 
+	tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
+	tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
+
 	tegra_pcm_platform_unregister(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index bea23af..73f5650 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -232,9 +232,11 @@  struct tegra30_i2s {
 	enum tegra30_ahub_txcif capture_i2s_cif;
 	enum tegra30_ahub_rxcif capture_fifo_cif;
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
+	struct tegra_dma_filter_data filter_data_rx;
 	enum tegra30_ahub_rxcif playback_i2s_cif;
 	enum tegra30_ahub_txcif playback_fifo_cif;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
+	struct tegra_dma_filter_data filter_data_tx;
 	struct regmap *regmap;
 };
 
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index f056f63..3baea16 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -53,8 +53,22 @@  static const struct snd_pcm_hardware tegra_pcm_hardware = {
 	.fifo_size		= 4,
 };
 
+static struct dma_chan *tegra_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
+	struct snd_pcm_substream *substream)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	struct tegra_dma_filter_data *filter_data;
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	filter_data = dma_data->filter_data;
+
+	return dma_request_slave_channel(filter_data->dma_dev,
+					filter_data->dma_name);
+}
+
 static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
 	.pcm_hardware = &tegra_pcm_hardware,
+	.compat_request_channel = tegra_pcm_request_chan,
 	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 	.compat_filter_fn = NULL,
 	.prealloc_buffer_size = PAGE_SIZE * 8,
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index 68ad901..8c0b1eb 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -31,6 +31,19 @@ 
 #ifndef __TEGRA_PCM_H__
 #define __TEGRA_PCM_H__
 
+#define TEGRA_DMA_NAME_MAX_LEN	10
+
+/*
+ * Generic DMA DT binding needs the device that raise dma request and dma name
+ * to request a dma channel. tegra30_i2s is a special case. It should be ahub
+ * device and it also has dma name that cannot handled by ASoC dmaengine code.
+ * So we pass the info using filter data in snd_dmaengine_dai_dma_data.
+ */
+struct tegra_dma_filter_data {
+	struct device *dma_dev;
+	char dma_name[TEGRA_DMA_NAME_MAX_LEN];
+};
+
 int tegra_pcm_platform_register(struct device *dev);
 void tegra_pcm_platform_unregister(struct device *dev);