diff mbox

[v3,2/2] ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers

Message ID f4ff79269a1680f9ce128e0e98fea926d3a41834.1406259578.git.nicoleotsuka@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Nicolin Chen July 25, 2014, 4:03 a.m. UTC
The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
signal associated with an input clock into a signal associated with a different
output clock. The driver currently works as a Front End of DPCM with other Back
Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the
original sample rate to a common rate supported by Back Ends for playback while
converts the common rate of Back Ends to a desired rate for capture. It has 3
pairs to support three different substreams within totally 10 channels.

Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
 .../devicetree/bindings/sound/fsl,asrc.txt         |   62 ++
 sound/soc/fsl/Kconfig                              |    9 +
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/fsl_asrc.c                           | 1030 ++++++++++++++++++++
 sound/soc/fsl/fsl_asrc.h                           |  461 +++++++++
 sound/soc/fsl/fsl_asrc_dma.c                       |  386 ++++++++
 6 files changed, 1950 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,asrc.txt
 create mode 100644 sound/soc/fsl/fsl_asrc.c
 create mode 100644 sound/soc/fsl/fsl_asrc.h
 create mode 100644 sound/soc/fsl/fsl_asrc_dma.c

Comments

Nicolin Chen July 25, 2014, 5:54 a.m. UTC | #1
Hi Varka,

   Regarding a point you suggested.

On Fri, Jul 25, 2014 at 09:54:43AM +0530, Varka Bhadram wrote:
> On 07/25/2014 09:33 AM, Nicolin Chen wrote:
> (...)
> 
> >+
> >+static const struct platform_device_id fsl_asrc_devtype[] = {
> >+	{
> >+		.name = "imx35-asrc",
> >+		.driver_data = IMX35_ASRC,
> >+	}, {
> >+		.name = "imx53-asrc",
> >+		.driver_data = IMX53_ASRC,
> >+	}, {
> >+		/* sentinel */
> >+	}
> >+};
> >+MODULE_DEVICE_TABLE(platform, fsl_asrc_devtype);
> >+
> >+static const struct of_device_id fsl_asrc_ids[] = {
> >+	{
> >+		.compatible = "fsl,imx35-asrc",
> >+		.data = &fsl_asrc_devtype[IMX35_ASRC],
> >+	}, {
> >+		.compatible = "fsl,imx53-asrc",
> >+		.data = &fsl_asrc_devtype[IMX53_ASRC],
> >+	}, {
> >+		/* sentinel */
> >+	}
> >+};
> >+MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
> >+
> 
> move these ids after probe/remove... every driver follows same thing...

Hmm.. fsl_asrc_ids is called in probe(), so it's probably not a good choice
to put them after probe/remove. And actually not every driver does so.
For example drivers/i2c/busses/i2c-s3c2410.c

I think it should be okay to put here if it contains data.

Thank you,
Nicolin
Nicolin Chen July 25, 2014, 6:09 a.m. UTC | #2
On Fri, Jul 25, 2014 at 11:47:42AM +0530, Varka Bhadram wrote:
> Hi Nicolin,
> 
> On 07/25/2014 11:24 AM, Nicolin Chen wrote:
> >Hi Varka,
> >
> >    Regarding a point you suggested.
> >
> >On Fri, Jul 25, 2014 at 09:54:43AM +0530, Varka Bhadram wrote:
> >>On 07/25/2014 09:33 AM, Nicolin Chen wrote:
> >>(...)
> >>
> >>>+
> >>>+static const struct platform_device_id fsl_asrc_devtype[] = {
> >>>+	{
> >>>+		.name = "imx35-asrc",
> >>>+		.driver_data = IMX35_ASRC,
> >>>+	}, {
> >>>+		.name = "imx53-asrc",
> >>>+		.driver_data = IMX53_ASRC,
> >>>+	}, {
> >>>+		/* sentinel */
> >>>+	}
> >>>+};
> >>>+MODULE_DEVICE_TABLE(platform, fsl_asrc_devtype);
> >>>+
> >>>+static const struct of_device_id fsl_asrc_ids[] = {
> >>>+	{
> >>>+		.compatible = "fsl,imx35-asrc",
> >>>+		.data = &fsl_asrc_devtype[IMX35_ASRC],
> >>>+	}, {
> >>>+		.compatible = "fsl,imx53-asrc",
> >>>+		.data = &fsl_asrc_devtype[IMX53_ASRC],
> >>>+	}, {
> >>>+		/* sentinel */
> >>>+	}
> >>>+};
> >>>+MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
> >>>+
> >>move these ids after probe/remove... every driver follows same thing...
> >Hmm.. fsl_asrc_ids is called in probe(), so it's probably not a good choice
> >to put them after probe/remove. And actually not every driver does so.
> >For example drivers/i2c/busses/i2c-s3c2410.c
> >
> >I think it should be okay to put here if it contains data.
> 
> You are using these ids in the probe .Tt should be above the probe() so
> we can see of_match_device() directly...
> and also one more thing that the interrupt handler also would be immediately
> above the probe()
> 
> ex:
> static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
> {
> ...
> }
> 
> device ids..
> 
> your_probe()
> {
> ...
> }
> 
> your_remove()
> {
> ...
> }
> 
> Now every driver that is coming following this style...

I see....

Will follow this style.

Thank you!
Nicolin

---
> 
> If your not using device ids in probe then it would be like
> 
> static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
> {
> ...
> }
> 
> your_probe()
> {
> ...
> }
> 
> your_remove()
> {
> ...
> }
> 
> device ids..
> 
> Thanks...
> 
> -- 
> Regards,
> Varka Bhadram.
>
Varka Bhadram July 25, 2014, 6:17 a.m. UTC | #3
Hi Nicolin,

On 07/25/2014 11:24 AM, Nicolin Chen wrote:
> Hi Varka,
>
>     Regarding a point you suggested.
>
> On Fri, Jul 25, 2014 at 09:54:43AM +0530, Varka Bhadram wrote:
>> On 07/25/2014 09:33 AM, Nicolin Chen wrote:
>> (...)
>>
>>> +
>>> +static const struct platform_device_id fsl_asrc_devtype[] = {
>>> +	{
>>> +		.name = "imx35-asrc",
>>> +		.driver_data = IMX35_ASRC,
>>> +	}, {
>>> +		.name = "imx53-asrc",
>>> +		.driver_data = IMX53_ASRC,
>>> +	}, {
>>> +		/* sentinel */
>>> +	}
>>> +};
>>> +MODULE_DEVICE_TABLE(platform, fsl_asrc_devtype);
>>> +
>>> +static const struct of_device_id fsl_asrc_ids[] = {
>>> +	{
>>> +		.compatible = "fsl,imx35-asrc",
>>> +		.data = &fsl_asrc_devtype[IMX35_ASRC],
>>> +	}, {
>>> +		.compatible = "fsl,imx53-asrc",
>>> +		.data = &fsl_asrc_devtype[IMX53_ASRC],
>>> +	}, {
>>> +		/* sentinel */
>>> +	}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
>>> +
>> move these ids after probe/remove... every driver follows same thing...
> Hmm.. fsl_asrc_ids is called in probe(), so it's probably not a good choice
> to put them after probe/remove. And actually not every driver does so.
> For example drivers/i2c/busses/i2c-s3c2410.c
>
> I think it should be okay to put here if it contains data.

You are using these ids in the probe .Tt should be above the probe() so
we can see of_match_device() directly...
and also one more thing that the interrupt handler also would be immediately
above the probe()

ex:
static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
{
...
}

device ids..

your_probe()
{
...
}

your_remove()
{
...
}

Now every driver that is coming following this style...

If your not using device ids in probe then it would be like

static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
{
...
}

your_probe()
{
...
}

your_remove()
{
...
}

device ids..

Thanks...
Raymond Yau July 26, 2014, 4:01 a.m. UTC | #4
>
> What is the best way to test/debug the alc668 driver these days?
>
> I am running kernel 3.14.1 (debian testing/sid).
>
> The laptop is an ASUS N750JK.
>
> Sound works for front left/right speakers but the back speakers and
> external lfe channel is not working on this laptop.

You have to post output of alsa-info.sh

Do the BIOS setup all the pin default ?

How many audio jacks ?

Do it support headset or headphone ?

Do you mean there are four internal speakers ?

Do it support surround40 in windows ?

The external sonic master and headphone need two volume controls but alc668
only have three

The best way to test is using latest git version and hda jack retask to

1) four internal speaker and headphone
2) two internal speaker, headphone and external subwoofer

Once you confirm the nodes of the speakers, subwooder
You need to decide whether headphone share the volume controls with the
speaker/subwoofer
>
> - I have found a couple of kernel bug reports  for other ASUS models with
> this chipset but they are both marked resolved. Should I open a new ticket
> ?
>
> https://bugzilla.kernel.org/show_bug.cgi?id=65091
> https://bugzilla.kernel.org/show_bug.cgi?id=66271
>
> - I can hear static through the front speakers (with pulseaudio test)  if
> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> (Center/LFE) or vice versa with hdajackretask.
>
> Pin 0x16 does not appear to work at all.
>
> I see this output when I apply the config in hdajackretask.
>
> 0x12 0x90a60140
> 0x14 0x99130110
> 0x15 0x04211020
> 0x16 0x99134111
> 0x18 0x01a19840
> 0x19 0x01014011
> 0x1a 0x411111f0
> 0x1b 0x411111f0
> 0x1d 0x40c6852d
> 0x1e 0x014b1180
> 0x1f 0x411111f0
> 1
>
>
>
>
>
>
> --
> Patrick Shirkey
> Boost Hardware Ltd
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Patrick Shirkey July 26, 2014, 8:18 a.m. UTC | #5
On Sat, July 26, 2014 2:01 pm, Raymond Yau wrote:
>>
>> What is the best way to test/debug the alc668 driver these days?
>>
>> I am running kernel 3.14.1 (debian testing/sid).
>>
>> The laptop is an ASUS N750JK.
>>
>> Sound works for front left/right speakers but the back speakers and
>> external lfe channel is not working on this laptop.
>
> You have to post output of alsa-info.sh
>

http://boosthardware.com/alsa-info-alc668-n750jk.txt

> Do the BIOS setup all the pin default ?
>
> How many audio jacks ?
>

Standard headphone/mic + yellow external lfe (sonic master). There is also
hdmi but I am not testing that at the moment.

> Do it support headset or headphone ?
>
> Do you mean there are four internal speakers ?
>

There are two front speakers which produce sound through the front bottom
of the case. There are also two back speakers which do not produce sound
in my tests so far.

> Do it support surround40 in windows ?
>

I am not running windows OS on this machine but I suspect it does.

> The external sonic master and headphone need two volume controls but
> alc668 only have three
>
> The best way to test is using latest git version and hda jack retask to
>

The process has changed somewhat since I last did this.

Turns out that is not such as easy process with the 3.14.1 kernel in
debian sid/testing. From the looks of it I have to compile my own kernel
to get all the necessary build files.


make[1]: Leaving directory '/home/patrick/code/alsa/alsa-driver/alsa'
make -C /lib/modules/3.14-1-amd64/source
SUBDIRS=/home/patrick/code/alsa/alsa-driver/alsa  CPP="gcc -E" CC="gcc"
modules
make[1]: Entering directory '/usr/src/linux-headers-3.14-1-common'

  ERROR: Kernel configuration is invalid.
         include/generated/autoconf.h or include/config/auto.conf are
missing.
         Run 'make oldconfig && make prepare' on kernel src to fix it.


  WARNING: Symbol version dump
/usr/src/linux-headers-3.14-1-common/Module.symvers
           is missing; modules will have no dependencies and modversions.

find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
  Building modules, stage 2.
/usr/src/linux-headers-3.14-1-common/scripts/Makefile.modpost:42:
include/config/auto.conf: No such file or directory
find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
find: `/usr/src/linux-headers-3.14-1-common/alsa-kernel/': No such file or
directory
make[2]: *** No rule to make target 'include/config/auto.conf'.  Stop.
Makefile:1294: recipe for target 'modules' failed
make[1]: *** [modules] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-3.14-1-common'
Makefile:167: recipe for target 'compile' failed
make: *** [compile] Error 2


> 1) four internal speaker and headphone
> 2) two internal speaker, headphone and external subwoofer
>
> Once you confirm the nodes of the speakers, subwooder
> You need to decide whether headphone share the volume controls with the
> speaker/subwoofer
>>

I have spent some time on this step already with 3.14.1

Is there a specific reason this process is not already scripted other than
no one has taken the time to do it?

Can we not create a test script that will run through all the logical
combinations and provide all the relevant info?


>> - I have found a couple of kernel bug reports  for other ASUS models
>> with
>> this chipset but they are both marked resolved. Should I open a new
>> ticket
>> ?
>>
>> https://bugzilla.kernel.org/show_bug.cgi?id=65091
>> https://bugzilla.kernel.org/show_bug.cgi?id=66271
>>
>> - I can hear static through the front speakers (with pulseaudio test)
>> if
>> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
>> (Center/LFE) or vice versa with hdajackretask.
>>
>> Pin 0x16 does not appear to work at all.
>>
>> I see this output when I apply the config in hdajackretask.
>>
>> 0x12 0x90a60140
>> 0x14 0x99130110
>> 0x15 0x04211020
>> 0x16 0x99134111
>> 0x18 0x01a19840
>> 0x19 0x01014011
>> 0x1a 0x411111f0
>> 0x1b 0x411111f0
>> 0x1d 0x40c6852d
>> 0x1e 0x014b1180
>> 0x1f 0x411111f0
>> 1
>>
>>


--
Patrick Shirkey
Boost Hardware Ltd
Raymond Yau July 29, 2014, 3:58 a.m. UTC | #6
> >>
> >> What is the best way to test/debug the alc668 driver these days?
> >>
> >> I am running kernel 3.14.1 (debian testing/sid).
> >>
> >> The laptop is an ASUS N750JK.
> >>
> >> Sound works for front left/right speakers but the back speakers and
> >> external lfe channel is not working on this laptop.
> >
> > You have to post output of alsa-info.sh
> >
>
> http://boosthardware.com/alsa-info-alc668-n750jk.txt

Seem BIOS only set up the front speakers

> >
> > How many audio jacks ?
> >
>
> Standard headphone/mic + yellow external lfe (sonic master). There is also
> hdmi but I am not testing that at the moment.

http://www.asus.com/Notebooks_Ultrabooks/N750JK/specifications/

Do you mean headset ?

>
> > Do it support headset or headphone ?
> >
> > Do you mean there are four internal speakers ?
> >
>
> There are two front speakers which produce sound through the front bottom
> of the case. There are also two back speakers which do not produce sound
> in my tests so far.
>
> > Do it support surround40 in windows ?
> >
>
> I am not running windows OS on this machine but I suspect it does.
>
> > The external sonic master and headphone need two volume controls but
> > alc668 only have three
> >
> > The best way to test is using latest git version and hda jack retask to
> >
>
> The process has changed somewhat since I last did this.
>

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/log/sound/pci/hda?qt=grep&q=alc668

>
> > 1) four internal speaker and headphone
> > 2) two internal speaker, headphone and external subwoofer
> >
> > Once you confirm the nodes of the speakers, subwooder
> > You need to decide whether headphone share the volume controls with the
> > speaker/subwoofer
> >>
>
> I have spent some time on this step already with 3.14.1

Did you retask the node of subwoofer as speaker or line out ?

For multi channel, the sequence number is ascending and the driver expect
they have same type i.e, all speakers or all line out jacks

Did you find the node of the rear speakers in  (1) ?

Did surround 4.0 work when you fix the rear speaker ?

Did you find the node of the external subwoofer  in (2) ?

Did the sonic master subwoofer detected   ?
hda jack sense test -a

Do the subwoofer only use right channel ?

Do surround 2.1 works ?

>
> Is there a specific reason this process is not already scripted other than
> no one has taken the time to do it?

Do surround 4.1 or 5.1 work when you fix both speaker and subwoofer ?

Do  you prefer driver create  headphone volume control or subwoofer volume
control ?

>
> Can we not create a test script that will run through all the logical
> combinations and provide all the relevant info?
>

The driver hardcore the name of volume control to bass when there are two
speaker nodes for those 2.1

>
> >> - I have found a couple of kernel bug reports  for other ASUS models
> >> with
> >> this chipset but they are both marked resolved. Should I open a new
> >> ticket
> >> ?
> >>
> >> https://bugzilla.kernel.org/show_bug.cgi?id=65091
> >> https://bugzilla.kernel.org/show_bug.cgi?id=66271
> >>
> >> - I can hear static through the front speakers (with pulseaudio test)
> >> if
> >> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> >> (Center/LFE) or vice versa with hdajackretask.
> >>
> >> Pin 0x16 does not appear to work at all.
> >>
> >> I see this output when I apply the config in hdajackretask.
> >>
> >> 0x12 0x90a60140
> >> 0x14 0x99130110
> >> 0x15 0x04211020
> >> 0x16 0x99134111
> >> 0x18 0x01a19840
> >> 0x19 0x01014011
> >> 0x1a 0x411111f0
> >> 0x1b 0x411111f0
> >> 0x1d 0x40c6852d
> >> 0x1e 0x014b1180
> >> 0x1f 0x411111f0
> >> 1
> >>
Patrick Shirkey July 29, 2014, 4:46 a.m. UTC | #7
On Tue, July 29, 2014 1:58 pm, Raymond Yau wrote:
>> >>
>> >> What is the best way to test/debug the alc668 driver these days?
>> >>
>> >> I am running kernel 3.14.1 (debian testing/sid).
>> >>
>> >> The laptop is an ASUS N750JK.
>> >>
>> >> Sound works for front left/right speakers but the back speakers and
>> >> external lfe channel is not working on this laptop.
>> >
>> > You have to post output of alsa-info.sh
>> >
>>
>> http://boosthardware.com/alsa-info-alc668-n750jk.txt
>
> Seem BIOS only set up the front speakers
>

There is nothing in the bios to configure the audio device


>> >
>> > How many audio jacks ?
>> >
>>
>> Standard headphone/mic + yellow external lfe (sonic master). There is
>> also
>> hdmi but I am not testing that at the moment.
>
> http://www.asus.com/Notebooks_Ultrabooks/N750JK/specifications/
>
> Do you mean headset ?
>

There are the following jacks:

1 x headphone
1 x mic(line in)
1 x sonic master (yellow)
1 x hdmi (intel Haswell hdmi)


>>
>> > Do it support headset or headphone ?
>> >
>> > Do you mean there are four internal speakers ?
>> >
>>
>> There are two front speakers which produce sound through the front
>> bottom
>> of the case. There are also two back speakers which do not produce sound
>> in my tests so far.
>>
>> > Do it support surround40 in windows ?
>> >
>>
>> I am not running windows OS on this machine but I suspect it does.
>>
>> > The external sonic master and headphone need two volume controls but
>> > alc668 only have three
>> >
>> > The best way to test is using latest git version and hda jack retask
>> to
>> >
>>
>> The process has changed somewhat since I last did this.
>>
>
> https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/log/sound/pci/hda?qt=grep&q=alc668
>

That suggest not much has changed for a while?


>>
>> > 1) four internal speaker and headphone
>> > 2) two internal speaker, headphone and external subwoofer
>> >
>> > Once you confirm the nodes of the speakers, subwooder
>> > You need to decide whether headphone share the volume controls with
>> the
>> > speaker/subwoofer
>> >>
>>
>> I have spent some time on this step already with 3.14.1
>
> Did you retask the node of subwoofer as speaker or line out ?
>

I have tried both but I have not been able to identify the subwoofer.

I have tried playing with: 16, 19, 1a

> For multi channel, the sequence number is ascending and the driver expect
> they have same type i.e, all speakers or all line out jacks
>
> Did you find the node of the rear speakers in  (1) ?
>

I have not found them yet.

> Did surround 4.0 work when you fix the rear speaker ?
>

Only the front speakers play audio but they don't work with 4.0 enabled

> Did you find the node of the external subwoofer  in (2) ?
>

Not yet.

> Did the sonic master subwoofer detected   ?
> hda jack sense test -a
>

I have grabbed the scripts but I get this error when running hda-analyzer:

Traceback (most recent call last):
  File "/dev/shm/hda-analyzer/hda_analyzer.py", line 546, in <module>
    sys.exit(main(sys.argv))
  File "/dev/shm/hda-analyzer/hda_analyzer.py", line 523, in main
    if read_nodes(sys.argv[1:]) == 0:
  File "/dev/shm/hda-analyzer/hda_analyzer.py", line 89, in read_nodes
    read_nodes2(c.card, i)
  File "/dev/shm/hda-analyzer/hda_analyzer.py", line 60, in read_nodes2
    c = HDACodec(card, codec)
  File "/dev/shm/hda-analyzer/hda_codec.py", line 1041, in __init__
    self.parse_proc()
  File "/dev/shm/hda-analyzer/hda_codec.py", line 1140, in parse_proc
    self.proc_codec = HDACodecProc(self.card, self.device, file)
  File "/dev/shm/hda-analyzer/hda_proc.py", line 479, in __init__
    self.parse(proc_file)
  File "/dev/shm/hda-analyzer/hda_proc.py", line 677, in parse
    node.add_digital(line[11:])
  File "/dev/shm/hda-analyzer/hda_proc.py", line 296, in add_digital
    self.wrongfile('unknown dig1 bit %s' % repr(b))
  File "/dev/shm/hda-analyzer/hda_proc.py", line 143, in wrongfile
    raise ValueError, "wrong proc file format (%s)" % msg
ValueError: wrong proc file format (unknown dig1 bit 'KAE')


> Do the subwoofer only use right channel ?
>

I cannot hear the subwoofer at this point

> Do surround 2.1 works ?
>

It says it is working but I do not get sound out of the sub woofer.  I can
hear static(whitenoise) through the front speakers (with pulseaudio test)
if  I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
(Center/LFE) or vice versa with hdajackretask.

>>
>> Is there a specific reason this process is not already scripted other
>> than
>> no one has taken the time to do it?
>
> Do surround 4.1 or 5.1 work when you fix both speaker and subwoofer ?
>

Surround 4.1/5/1 do not produce any sound from my tests so far (using PA
Test)

> Do  you prefer driver create  headphone volume control or subwoofer volume
> control ?
>

I would like the lfe to be sent through the yellow sonic master jack. I am
not concerned with the headset jack at the moment. A cursory test shows
just the right channel is working on the headset jack.


>>
>> Can we not create a test script that will run through all the logical
>> combinations and provide all the relevant info?
>>
>
> The driver hardcore the name of volume control to bass when there are two
> speaker nodes for those 2.1
>

I do not see any controls for the bass volume in alsamixer




>>
>> >> - I have found a couple of kernel bug reports  for other ASUS models
>> >> with
>> >> this chipset but they are both marked resolved. Should I open a new
>> >> ticket
>> >> ?
>> >>
>> >> https://bugzilla.kernel.org/show_bug.cgi?id=65091
>> >> https://bugzilla.kernel.org/show_bug.cgi?id=66271
>> >>
>> >> - I can hear static through the front speakers (with pulseaudio test)
>> >> if
>> >> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
>> >> (Center/LFE) or vice versa with hdajackretask.
>> >>
>> >> Pin 0x16 does not appear to work at all.
>> >>
>> >> I see this output when I apply the config in hdajackretask.
>> >>
>> >> 0x12 0x90a60140
>> >> 0x14 0x99130110
>> >> 0x15 0x04211020
>> >> 0x16 0x99134111
>> >> 0x18 0x01a19840
>> >> 0x19 0x01014011
>> >> 0x1a 0x411111f0
>> >> 0x1b 0x411111f0
>> >> 0x1d 0x40c6852d
>> >> 0x1e 0x014b1180
>> >> 0x1f 0x411111f0
>> >> 1
>> >>
>


--
Patrick Shirkey
Boost Hardware Ltd
Mark Rutland July 29, 2014, 9:46 a.m. UTC | #8
> +   - big-endian : If this property is absent, the native endian mode will
> +                 be in use as default, or the big endian mode will be in use
> +                 for all the device registers.

Native endian is meaningless. If a CPU supports both BE and LE, there is
no native endianness. The endianness of the kernel is dynamic while the
endianness of registers in HW is fixed. 

Just choose an endianness to assume by default (presumably little). That
way this describes the HW and always works with a kernel of arbitrary
endianness.

Thanks,
Mark.
Nicolin Chen July 29, 2014, 10:13 a.m. UTC | #9
On Tue, Jul 29, 2014 at 10:46:13AM +0100, Mark Rutland wrote:
> > +   - big-endian : If this property is absent, the native endian mode will
> > +                 be in use as default, or the big endian mode will be in use
> > +                 for all the device registers.
> 
> Native endian is meaningless. If a CPU supports both BE and LE, there is
> no native endianness. The endianness of the kernel is dynamic while the
> endianness of registers in HW is fixed. 
> 
> Just choose an endianness to assume by default (presumably little). That
> way this describes the HW and always works with a kernel of arbitrary
> endianness.

Thank you for the comments.

I just revised it by using 'little endian as default' and sent the patch v6.

Please take a look at the new version.

Thanks again,
Nicolin
Raymond Yau July 31, 2014, 3:27 a.m. UTC | #10
> >> >>
> >> >> What is the best way to test/debug the alc668 driver these days?
> >> >>
> >> >> I am running kernel 3.14.1 (debian testing/sid).
> >> >>
> >> >> The laptop is an ASUS N750JK.
> >> >>
> >> >> Sound works for front left/right speakers but the back speakers and
> >> >> external lfe channel is not working on this laptop.
> >> >
> >> > You have to post output of alsa-info.sh
> >> >
> >>
> >> http://boosthardware.com/alsa-info-alc668-n750jk.txt
> >
> > Seem BIOS only set up the front speakers
> >
>
> There is nothing in the bios to configure the audio device
>
>
> >> >
> >> > How many audio jacks ?
> >> >
> >>
> >> Standard headphone/mic + yellow external lfe (sonic master). There is
> >> also
> >> hdmi but I am not testing that at the moment.
> >
> > http://www.asus.com/Notebooks_Ultrabooks/N750JK/specifications/
> >
> > Do you mean headset ?
> >
>
> There are the following jacks:
>
> 1 x headphone

Do you mean no optical spdif ?

> 1 x mic(line in)

Switching Mic Jack to line in can be performed by mic Jack mode switch
using hint add_jack_modes=true

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/Documentation/sound/alsa/HD-Audio.txt

> 1 x sonic master (yellow)
> 1 x hdmi (intel Haswell hdmi)
>
>
> >>
> >> > Do it support headset or headphone ?
> >> >
> >> > Do you mean there are four internal speakers ?
> >> >
> >>
> >> There are two front speakers which produce sound through the front
> >> bottom
> >> of the case. There are also two back speakers which do not produce
sound
> >> in my tests so far.
> >>
> >> > Do it support surround40 in windows ?
> >> >
> >>
> >> I am not running windows OS on this machine but I suspect it does.
> >>
> >> > The external sonic master and headphone need two volume controls but
> >> > alc668 only have three
> >> >
> >> > The best way to test is using latest git version and hda jack retask
> >> to
> >> >
>
> >>
> >> > 1) four internal speaker and headphone
> >> > 2) two internal speaker, headphone and external subwoofer
> >> >
> >> > Once you confirm the nodes of the speakers, subwooder
> >> > You need to decide whether headphone share the volume controls with
> >> the
> >> > speaker/subwoofer
> >> >>
> >>
> >> I have spent some time on this step already with 3.14.1
> >
> > Did you retask the node of subwoofer as speaker or line out ?
> >
>
> I have tried both but I have not been able to identify the subwoofer.
>
> I have tried playing with: 16, 19, 1a
>

> > Did you find the node of the rear speakers in  (1) ?
> >
>
> I have not found them yet.
>
> > Did surround 4.0 work when you fix the rear speaker ?
> >
>
> Only the front speakers play audio but they don't work with 4.0 enabled
>
> > Did you find the node of the external subwoofer  in (2) ?
> >
>
> Not yet.
>
> > Did the sonic master subwoofer detected   ?
> > hda jack sense test -a
> >
>
> I have grabbed the scripts but I get this error when running hda-analyzer:
>

You can use hda-verb GET_PIN_SENSE to test all the pin complex nodes which
pincap support DETECT

>
> > Do the subwoofer only use right channel ?
> >
>
> I cannot hear the subwoofer at this point
>
> > Do surround 2.1 works ?
> >
>
> It says it is working but I do not get sound out of the sub woofer.  I can
> hear static(whitenoise) through the front speakers (with pulseaudio test)
> if  I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> (Center/LFE) or vice versa with hdajackretask.
>

http://git.alsa-project.org/?p=alsa-tools.git;a=blob;f=hdajackretask/README;hb=HEAD

Did you use apply button(dyanmic reconfiguartion) or installment boot
override (early patching) ?

> >>
> >> Is there a specific reason this process is not already scripted other
> >> than
> >> no one has taken the time to do it?
> >
> > Do surround 4.1 or 5.1 work when you fix both speaker and subwoofer ?
> >
>
> Surround 4.1/5/1 do not produce any sound from my tests so far (using PA
> Test)
>
> > Do  you prefer driver create  headphone volume control or subwoofer
volume
> > control ?
> >
>
> I would like the lfe to be sent through the yellow sonic master jack. I am
> not concerned with the headset jack at the moment. A cursory test shows
> just the right channel is working on the headset jack.
>
>
> >>
> >> Can we not create a test script that will run through all the logical
> >> combinations and provide all the relevant info?
> >>
> >
> > The driver hardcore the name of volume control to bass when there are
two
> > speaker nodes for those 2.1
> >
>
> I do not see any controls for the bass volume in alsamixer
>
>

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/commit/sound/pci/hda?id=f37bc7a88d374448a1f4bba9267d308606d78bf2

> >>
> >> >> - I have found a couple of kernel bug reports  for other ASUS models
> >> >> with
> >> >> this chipset but they are both marked resolved. Should I open a new
> >> >> ticket
> >> >> ?
> >> >>
> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=65091
> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=66271
> >> >>
> >> >> - I can hear static through the front speakers (with pulseaudio
test)
> >> >> if
> >> >> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> >> >> (Center/LFE) or vice versa with hdajackretask.
> >> >>

> > For multi channel, the sequence number is ascending and the driver
expect
> > they have same type i.e, all speakers or all line out jacks
> >

Pin Default 0x90170110: [Fixed] Speaker at Int N/A
    Conn = Analog, Color = Unknown
    DefAssociation = 0x1, Sequence = 0x0
    Misc = NO_PRESENCE

The rear speaker need  same default association but  sequence number higher
than that of the front speaker

The external subwoofer may use [jack] speaker with same default association
and higher sequence number if it can be detected

The driver won't use all of them for multi channel if you retasked it as
line out
Patrick Shirkey July 31, 2014, 7:32 a.m. UTC | #11
On Thu, July 31, 2014 1:27 pm, Raymond Yau wrote:

>
> You can use hda-verb GET_PIN_SENSE to test all the pin complex nodes which
> pincap support DETECT
>

I have tried a few attempts but it's pretty confusing.

These ones don't do anything:

# hda-verb /dev/snd/hwC1D0  GET_PIN_SENSE
# hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE


I don't know what to make of this output:

# hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE PIN_CAP
nid = 0x0, verb = 0xf09, param = 0xc
value = 0x0
# hda-verb /dev/snd/hwC0D0  0x0 GET_PIN_SENSE PIN_CAP
nid = 0x0, verb = 0xf09, param = 0xc
value = 0x0
# hda-verb /dev/snd/hwC0D0  0x16 GET_PIN_SENSE PIN_CAP
nid = 0x16, verb = 0xf09, param = 0xc
value = 0xffffffff
# hda-verb /dev/snd/hwC0D0  0x19 GET_PIN_SENSE PIN_CAP
nid = 0x19, verb = 0xf09, param = 0xc
value = 0xffffffff
# hda-verb /dev/snd/hwC0D0  0x1a GET_PIN_SENSE PIN_CAP
nid = 0x1a, verb = 0xf09, param = 0xc
value = 0xffffffff
# hda-verb /dev/snd/hwC0D0  0x1b GET_PIN_SENSE PIN_CAP
nid = 0x1b, verb = 0xf09, param = 0xc
value = 0xffffffff



>> > Do surround 2.1 works ?
>> >
>>
>> It says it is working but I do not get sound out of the sub woofer.  I
>> can
>> hear static(whitenoise) through the front speakers (with pulseaudio
>> test)
>> if  I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
>> (Center/LFE) or vice versa with hdajackretask.
>>
>
> http://git.alsa-project.org/?p=alsa-tools.git;a=blob;f=hdajackretask/README;hb=HEAD
>
> Did you use apply button(dyanmic reconfiguartion) or installment boot
> override (early patching) ?
>

I have tried both options.

After rebooting with the apply option set I don't hear any static or audio
with the PA test app.

>>
>> I do not see any controls for the bass volume in alsamixer
>>
>>
>
> https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/commit/sound/pci/hda?id=f37bc7a88d374448a1f4bba9267d308606d78bf2
>

I have checked alsamixer again after the last round of tinkering with
hdajackretask and" Speaker CLFE" is now showing up but it does not have
volume control just mute on/off.

I see the following new controls

Speaker CFLE (mute only)
Speaker Front (mute + vol)
Speaker Side (mute only)
Speaker Surround (mute + vol)


>> >>
>> >> >> - I have found a couple of kernel bug reports  for other ASUS
>> models
>> >> >> with
>> >> >> this chipset but they are both marked resolved. Should I open a
>> new
>> >> >> ticket
>> >> >> ?
>> >> >>
>> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=65091
>> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=66271
>> >> >>
>> >> >> - I can hear static through the front speakers (with pulseaudio
> test)
>> >> >> if
>> >> >> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
>> >> >> (Center/LFE) or vice versa with hdajackretask.
>> >> >>
>
>> > For multi channel, the sequence number is ascending and the driver
> expect
>> > they have same type i.e, all speakers or all line out jacks
>> >
>
> Pin Default 0x90170110: [Fixed] Speaker at Int N/A
>     Conn = Analog, Color = Unknown
>     DefAssociation = 0x1, Sequence = 0x0
>     Misc = NO_PRESENCE
>
> The rear speaker need  same default association but  sequence number
> higher than that of the front speaker
>
> The external subwoofer may use [jack] speaker with same default
> association and higher sequence number if it can be detected
>
> The driver won't use all of them for multi channel if you retasked it as
> line out
>

I have tried several combinations with hdajackretask

I am not able to get audio out of the physical rear speakers or sonic
master output.

I can get audio to play back in the front l/c/r speakers with varying
combinations.

ex.

7.1 side l/r plays through front l/r or front c
4.0 rear r/l plays through front l/r
2.1 l/r plays through front l/r
2.1 subwoofer plays white noise through front c

Most of the time it is only front left/right that make any sound.
Sometimes there is no sound on one audio profile but it works on a couple
of channels on another profile.

Any time that the Center LFE works it produces white noise.

I have tried with both options: apply and patch/reboot

I can keep playing around with the settings until I eventually stumble on
a combination that works but it seems like this process should be
scriptable. Given that I will probably spend hours of my time on the test
process and I have already spent 4 separate sessions on testing it seems
like it would be better value for my time and others going forward to
write a test script.

So what do I need to do to write a script that will test all the
combinations and play audio through them? Or am I missing something that
someone else has already written for this purpose?

Currently I am using this process:

1: hdajackretask - modify params
2: pulseaudio -k
3: hdajackretask : apply settings
4: pulseaudio -D
5: open PA audio config
6: select audio output option and press test speakers button
7: manually press every button

Rinse and repeat.

Surely there is a more humane method for testing the variations ;-)




--
Patrick Shirkey
Boost Hardware Ltd
Raymond Yau July 31, 2014, 1:38 p.m. UTC | #12
http://git.alsa-project.org/?p=alsa.git;a=commitdiff;h=48ed9474fc6724dc1486d30e9a274ab27ec001da;hp=56c640a98975b03a6a78a44b2c01e49dfd7b5263

Have you try the fix ?
>
> >
> > You can use hda-verb GET_PIN_SENSE to test all the pin complex nodes
which
> > pincap support DETECT
> >
>
> I have tried a few attempts but it's pretty confusing.
>
> These ones don't do anything:
>
> # hda-verb /dev/snd/hwC1D0  GET_PIN_SENSE
> # hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE
>
>
> I don't know what to make of this output:

Bit 31 is PD

VALUE should return 0 when  Jack is UNplugged and 0x80000000 when Jack is
plugged for those pin which support DETECT

>
> # hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE PIN_CAP
> nid = 0x0, verb = 0xf09, param = 0xc
> value = 0x0
> # hda-verb /dev/snd/hwC0D0  0x0 GET_PIN_SENSE PIN_CAP
> nid = 0x0, verb = 0xf09, param = 0xc
> value = 0x0
> # hda-verb /dev/snd/hwC0D0  0x16 GET_PIN_SENSE PIN_CAP
> nid = 0x16, verb = 0xf09, param = 0xc
> value = 0xffffffff
> # hda-verb /dev/snd/hwC0D0  0x19 GET_PIN_SENSE PIN_CAP
> nid = 0x19, verb = 0xf09, param = 0xc
> value = 0xffffffff
> # hda-verb /dev/snd/hwC0D0  0x1a GET_PIN_SENSE PIN_CAP
> nid = 0x1a, verb = 0xf09, param = 0xc
> value = 0xffffffff
> # hda-verb /dev/snd/hwC0D0  0x1b GET_PIN_SENSE PIN_CAP
> nid = 0x1b, verb = 0xf09, param = 0xc
> value = 0xffffffff
>

But your alc668 is on card 1 , the second hda controller

>
>
> >> > Do surround 2.1 works ?
> >> >
> >>
> >> It says it is working but I do not get sound out of the sub woofer.  I
> >> can
> >> hear static(whitenoise) through the front speakers (with pulseaudio
> >> test)
> >> if  I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> >> (Center/LFE) or vice versa with hdajackretask.
> >>
> >
> >
http://git.alsa-project.org/?p=alsa-tools.git;a=blob;f=hdajackretask/README;hb=HEAD
> >
> > Did you use apply button(dyanmic reconfiguartion) or installment boot
> > override (early patching) ?
> >
>
> I have tried both options.
>
> After rebooting with the apply option set I don't hear any static or audio
> with the PA test app.
>
> >>
> >> I do not see any controls for the bass volume in alsamixer
> >>
> >>
> >
> >
https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/commit/sound/pci/hda?id=f37bc7a88d374448a1f4bba9267d308606d78bf2
> >
>
> I have checked alsamixer again after the last round of tinkering with
> hdajackretask and" Speaker CLFE" is now showing up but it does not have
> volume control just mute on/off.
>
> I see the following new controls
>
> Speaker CFLE (mute only)
> Speaker Front (mute + vol)
> Speaker Side (mute only)
> Speaker Surround (mute + vol)

hda-jack-retask won't check whether your codec have enough audio output if
you changed all the nodes to speaker

But the driver expect multiout support up to 8 channels when parsing the pin

As alc668 only support 6 channels,  hda_generic.c just assign audio output
and create volume control but Jack detection controls and mute switch are
created according to the number of parsed pins which support Jack detect
and mute switch

Do the headphone still work with pulseaudio since there is no headphone
playback volume and headphone is share SPEAKER FRONT PLAYBAACK VOLUME
control with front speaker ?

>
>
> >> >>
> >> >> >> - I have found a couple of kernel bug reports  for other ASUS
> >> models
> >> >> >> with
> >> >> >> this chipset but they are both marked resolved. Should I open a
> >> new
> >> >> >> ticket
> >> >> >> ?
> >> >> >>
> >> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=65091
> >> >> >> https://bugzilla.kernel.org/show_bug.cgi?id=66271
> >> >> >>
> >> >> >> - I can hear static through the front speakers (with pulseaudio
> > test)
> >> >> >> if
> >> >> >> I set pins 0x19 and 0x1a to Line Out (Center/LFE) and Internal
> >> >> >> (Center/LFE) or vice versa with hdajackretask.
> >> >> >>
> >
> >> > For multi channel, the sequence number is ascending and the driver
> > expect
> >> > they have same type i.e, all speakers or all line out jacks
> >> >
> >
> > Pin Default 0x90170110: [Fixed] Speaker at Int N/A
> >     Conn = Analog, Color = Unknown
> >     DefAssociation = 0x1, Sequence = 0x0
> >     Misc = NO_PRESENCE
> >
> > The rear speaker need  same default association but  sequence number
> > higher than that of the front speaker
> >
> > The external subwoofer may use [jack] speaker with same default
> > association and higher sequence number if it can be detected
> >
> > The driver won't use all of them for multi channel if you retasked it as
> > line out
> >
>
> I have tried several combinations with hdajackretask
>
> I am not able to get audio out of the physical rear speakers or sonic
> master output.
>:
>
> So what do I need to do to write a script that will test all the
> combinations and play audio through them? Or am I missing something that
> someone else has already written for this purpose?
>
> Currently I am using this process:
>
> 1: hdajackretask - modify params
> 2: pulseaudio -k
> 3: hdajackretask : apply settings
> 4: pulseaudio -D
> 5: open PA audio config
> 6: select audio output option and press test speakers button
> 7: manually press every button
>

Use install boot option and reboot to test the driver with
hda-jack-retask.fw patch

Should not use pulseaudio when testing

speaker-test -c2 -t wav -D hw:1,0

speaker-test -c4 -t wav -D hw:1,0

Test 6 channel only after you find both rear speaker and subwoofer

speaker-test -c6 -t wav -D hw:1,0

if the connected retasked pin is not the correct pin
,you can use hda_analyzer to  change the pin ctls of the remaining
unconnected pin to OUT, unmute and coonect the pin to second/third dac
through audio mixers
Patrick Shirkey July 31, 2014, 6:25 p.m. UTC | #13
On Thu, July 31, 2014 11:38 pm, Raymond Yau wrote:
> http://git.alsa-project.org/?p=alsa.git;a=commitdiff;h=48ed9474fc6724dc1486d30e9a274ab27ec001da;hp=56c640a98975b03a6a78a44b2c01e49dfd7b5263
>
> Have you try the fix ?
>>

I did not see that update.

run.py works now without any modifications on this machine.

I have generated a graph.

http://boosthardware.com/hda_graph-1.png


>> >
>> > You can use hda-verb GET_PIN_SENSE to test all the pin complex nodes
> which
>> > pincap support DETECT
>> >
>>
>> I have tried a few attempts but it's pretty confusing.
>>
>> These ones don't do anything:
>>
>> # hda-verb /dev/snd/hwC1D0  GET_PIN_SENSE
>> # hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE
>>
>>
>> I don't know what to make of this output:
>
> Bit 31 is PD
>
> VALUE should return 0 when  Jack is UNplugged and 0x80000000 when Jack is
> plugged for those pin which support DETECT
>
>
> But your alc668 is on card 1 , the second hda controller
>

Sorry. Missed that one.

# hda-verb /dev/snd/hwC1D0  0x16 GET_PIN_SENSE PIN_CAP
nid = 0x16, verb = 0xf09, param = 0xc
value = 0x0
# hda-verb /dev/snd/hwC1D0  0x19 GET_PIN_SENSE PIN_CAP
nid = 0x19, verb = 0xf09, param = 0xc
value = 0x0
# hda-verb /dev/snd/hwC1D0  0x11a GET_PIN_SENSE PIN_CAP
invalid nid 0x11a
# hda-verb /dev/snd/hwC1D0  0x1a GET_PIN_SENSE PIN_CAP
nid = 0x1a, verb = 0xf09, param = 0xc
value = 0x0
# hda-verb /dev/snd/hwC1D0  0x1b GET_PIN_SENSE PIN_CAP
nid = 0x1b, verb = 0xf09, param = 0xc
value = 0x0


>> I see the following new controls
>>
>> Speaker CFLE (mute only)
>> Speaker Front (mute + vol)
>> Speaker Side (mute only)
>> Speaker Surround (mute + vol)
>
> hda-jack-retask won't check whether your codec have enough audio output if
> you changed all the nodes to speaker
>
> But the driver expect multiout support up to 8 channels when parsing the
> pin
>
> As alc668 only support 6 channels,  hda_generic.c just assign audio output
> and create volume control but Jack detection controls and mute switch are
> created according to the number of parsed pins which support Jack detect
> and mute switch
>
> Do the headphone still work with pulseaudio since there is no headphone
> playback volume and headphone is share SPEAKER FRONT PLAYBAACK VOLUME
> control with front speaker ?
>

With speaker-test I can hear audio on the right channel

speaker-test -c2 -t wav -D hw:1,0

audio comes out of front left/right

speaker-test -c4 -t wav -D hw:1,0

front left/right has no audio
rear left/right audio comes out of front left/right speakers

# speaker-test -c6 -t wav -D hw:1,0

speaker-test 1.0.28

Playback device is hw:1,0
Stream parameters are 48000Hz, S16_LE, 6 channels
WAV file(s)
Channels count (6) not available for playbacks: Invalid argument
Setting of hwparams failed: Invalid argument


>
> if the connected retasked pin is not the correct pin
> ,you can use hda_analyzer to  change the pin ctls of the remaining
> unconnected pin to OUT, unmute and coonect the pin to second/third dac
> through audio mixers
>

This is a bit confusing to master. How should I approach it given the
layout of the graph linked above?




--
Patrick Shirkey
Boost Hardware Ltd
Raymond Yau Aug. 1, 2014, 7:19 a.m. UTC | #14
> >
http://git.alsa-project.org/?p=alsa.git;a=commitdiff;h=48ed9474fc6724dc1486d30e9a274ab27ec001da;hp=56c640a98975b03a6a78a44b2c01e49dfd7b5263
> >
> > Have you try the fix ?
> >>
>
> I did not see that update.
>
> run.py works now without any modifications on this machine.
>
> I have generated a graph.
>
> http://boosthardware.com/hda_graph-1.png
>

>
> >> >
> >> > You can use hda-verb GET_PIN_SENSE to test all the pin complex nodes
> > which
> >> > pincap support DETECT
> >> >
> >>
> >> I have tried a few attempts but it's pretty confusing.
> >>
> >> These ones don't do anything:
> >>
> >> # hda-verb /dev/snd/hwC1D0  GET_PIN_SENSE
> >> # hda-verb /dev/snd/hwC1D0  0x0 GET_PIN_SENSE
> >>
> >>
> >> I don't know what to make of this output:
> >
> > Bit 31 is PD
> >
> > VALUE should return 0 when  Jack is UNplugged and 0x80000000 when Jack
is
> > plugged for those pin which support DETECT
> >
> >
> > But your alc668 is on card 1 , the second hda controller
> >
>
> Sorry. Missed that one.
>
> # hda-verb /dev/snd/hwC1D0  0x16 GET_PIN_SENSE PIN_CAP
> nid = 0x16, verb = 0xf09, param = 0xc
> value = 0x0
> # hda-verb /dev/snd/hwC1D0  0x19 GET_PIN_SENSE PIN_CAP
> nid = 0x19, verb = 0xf09, param = 0xc
> value = 0x0
> # hda-verb /dev/snd/hwC1D0  0x11a GET_PIN_SENSE PIN_CAP
> invalid nid 0x11a
> # hda-verb /dev/snd/hwC1D0  0x1a GET_PIN_SENSE PIN_CAP
> nid = 0x1a, verb = 0xf09, param = 0xc
> value = 0x0
> # hda-verb /dev/snd/hwC1D0  0x1b GET_PIN_SENSE PIN_CAP
> nid = 0x1b, verb = 0xf09, param = 0xc
> value = 0x0
>
>

Do you mean the plugging of subwoofer cannot be detected

hda-jack-sense-test -c 1 -a

> >> I see the following new controls
> >>
> >> Speaker CFLE (mute only)
> >> Speaker Front (mute + vol)
> >> Speaker Side (mute only)
> >> Speaker Surround (mute + vol)
> >
> > hda-jack-retask won't check whether your codec have enough audio output
if
> > you changed all the nodes to speaker
> >
> > But the driver expect multiout support up to 8 channels when parsing the
> > pin
> >
> > As alc668 only support 6 channels,  hda_generic.c just assign audio
output
> > and create volume control but Jack detection controls and mute switch
are
> > created according to the number of parsed pins which support Jack detect
> > and mute switch
> >
> > Do the headphone still work with pulseaudio since there is no headphone
> > playback volume and headphone is share SPEAKER FRONT PLAYBAACK VOLUME
> > control with front speaker ?
> >
>
> With speaker-test I can hear audio on the right channel
>
> speaker-test -c2 -t wav -D hw:1,0
>
> audio comes out of front left/right
>
> speaker-test -c4 -t wav -D hw:1,0
>
> front left/right has no audio
> rear left/right audio comes out of front left/right speakers
>
> # speaker-test -c6 -t wav -D hw:1,0
>
> speaker-test 1.0.28
>
> Playback device is hw:1,0
> Stream parameters are 48000Hz, S16_LE, 6 channels
> WAV file(s)
> Channels count (6) not available for playbacks: Invalid argument
> Setting of hwparams failed: Invalid argument
>

The graph don't contain enough into for debugg

Post output of alsa-info.sh after retasked

Seem headphone is more important than multichannel speaker when evaluating
the badness of config

You may need to configure alsa driver --with-debug=verbose to produce more
information in system log about how it evaluate the badness
Patrick Shirkey Aug. 1, 2014, 9:07 a.m. UTC | #15
On Fri, August 1, 2014 5:19 pm, Raymond Yau wrote:
>

alsa-info:

retask and apply
http://boosthardware.com/alsa-info1.txt

store and reboot
http://boosthardware.com/alsa-info2.txt



>
> Do you mean the plugging of subwoofer cannot be detected
>
> hda-jack-sense-test -c 1 -a
>

prior to latest changes with hdajackretask:

# python alsa-jack-sense-test.py -c 1 -a
Pin 0x12 (Internal Mic): present = No
Pin 0x14 (Internal Speaker): present = No
Pin 0x15 (Black HP Out): present = No
Pin 0x16 (Not connected): present = No
Pin 0x18 (Black Mic): present = No
Pin 0x19 (Not connected): present = No
Pin 0x1a (Not connected): present = No
Pin 0x1b (Not connected): present = No
Pin 0x1d (Not connected): present = No
Pin 0x1e (Black SPDIF Out): present = No
Pin 0x1f (Not connected): present = No

headset connected:

# python alsa-jack-sense-test.py -c 1 -a
Pin 0x12 (Internal Mic): present = No
Pin 0x14 (Internal Speaker): present = No
Pin 0x15 (Black HP Out): present = Yes
Pin 0x16 (Not connected): present = No
Pin 0x18 (Black Mic): present = No
Pin 0x19 (Not connected): present = No
Pin 0x1a (Not connected): present = No
Pin 0x1b (Not connected): present = No
Pin 0x1d (Not connected): present = No
Pin 0x1e (Black SPDIF Out): present = No
Pin 0x1f (Not connected): present = No

after latest reboot (alsa-info2.txt):

# python alsa-jack-sense-test.py -c 1 -a
Pin 0x12 (Internal Mic): present = No
Pin 0x14 (Internal Speaker): present = No
Pin 0x15 (Black HP Out): present = No
Pin 0x16 (Not connected): present = No
Pin 0x18 (Black Mic): present = No
Pin 0x19 (Not connected): present = No
Pin 0x1a (Not connected): present = No
Pin 0x1b (Not connected): present = No
Pin 0x1d (Not connected): present = No
Pin 0x1e (Black SPDIF Out): present = No
Pin 0x1f (Not connected): present = No

headset connected:

# python alsa-jack-sense-test.py -c 1 -a
Pin 0x12 (Internal Mic): present = No
Pin 0x14 (Internal Speaker): present = No
Pin 0x15 (Black HP Out): present = Yes
Pin 0x16 (Not connected): present = No
Pin 0x18 (Black Mic): present = No
Pin 0x19 (Not connected): present = No
Pin 0x1a (Not connected): present = No
Pin 0x1b (Not connected): present = No
Pin 0x1d (Not connected): present = No
Pin 0x1e (Black SPDIF Out): present = No
Pin 0x1f (Not connected): present = No



>
> The graph don't contain enough into for debugg
>
> Post output of alsa-info.sh after retasked
>
> Seem headphone is more important than multichannel speaker when evaluating
> the badness of config
>
> You may need to configure alsa driver --with-debug=verbose to produce more
> information in system log about how it evaluate the badness
>

Easier said than done.  I have to build a custom kernel in order to get
custom alsa-drivers to build on this machine so at the moment I am stuck
with whatever I got in 3.14.1.  From the list above can you make a
recommendation for the best options to test?

According to hdajackretask the pins I can retask for output are the
following:

Pin 0x14 (Internal Speaker): present = No
Pin 0x15 (Black HP Out): present = No
Pin 0x16 (Not connected): present = No
Pin 0x18 (Black Mic): present = No
Pin 0x19 (Not connected): present = No
Pin 0x1a (Not connected): present = No


- The other pins present just a few options for input ex, mic, line in,
spdif.




--
Patrick Shirkey
Boost Hardware Ltd
Raymond Yau Aug. 1, 2014, 10:04 a.m. UTC | #16
>
> alsa-info:
>
> retask and apply
> http://boosthardware.com/alsa-info1.txt
>
> store and reboot
> http://boosthardware.com/alsa-info2.txt
>
>

Any reason to use asus-mode4 and hda-jack-retask at the same time ?

Try model=auto when you use patch=,hda-jack-retask.fw

Your alsa-info missing many important info , need all info from alsactl,
amixer, sysfs about the driver init pins and user init pins, allay/arecord
-l

No information about which Jack have been retasked
Patrick Shirkey Aug. 1, 2014, 10:33 a.m. UTC | #17
On Fri, August 1, 2014 8:04 pm, Raymond Yau wrote:
>>
>> alsa-info:
>>
>> retask and apply
>> http://boosthardware.com/alsa-info1.txt
>>
>> store and reboot
>> http://boosthardware.com/alsa-info2.txt
>>
>>
>
> Any reason to use asus-mode4 and hda-jack-retask at the same time ?
>

I had it in place because at one point it seemed to allow PA to have
addition profiles other than stereo. However the line below seems to be a
better combination.

> Try model=auto when you use patch=,hda-jack-retask.fw
>

That nugget of information seems to have enabled the bass speaker in alsa
after the last reboot.

/etc/modprobe.d/alsa-base.conf
options snd-hda-intel model=auto patch=,hda-jack-retask.fw


> Your alsa-info missing many important info , need all info from alsactl,
> amixer, sysfs about the driver init pins and user init pins, allay/arecord
> -l
>

http://boosthardware.com/alsa-info4.txt

> No information about which Jack have been retasked
>

For the info file above I have the following retask assignments:

0x14 Line out (front)
0x15 Line out (center/LFE)
0x16 Line Out (Back)
0x18 Internal Speaker
0x19 Internal Speaker(Back)
0x1a Internal mic

- With the following command I can hear "rear center" through the sonic
master speaker when channel 5 is testing.

speaker-test -D hw:1 -c 6 -t wav

- I do not hear any sound when rear left/right is testing.

- I do not hear any audio through the LFE with PA test on any of the audio
profiles.

- I will play around with the retask assignments now that something seems
to be working. Looks like I'm on the right track now.

Thanks for all your help so far.




--
Patrick Shirkey
Boost Hardware Ltd
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
new file mode 100644
index 0000000..637028c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
@@ -0,0 +1,62 @@ 
+Freescale Asynchronous Sample Rate Converter (ASRC) Controller
+
+The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
+signal associated with an input clock into a signal associated with a different
+output clock. The driver currently works as a Front End of DPCM with other Back
+Ends Audio controller such as ESAI, SSI and SAI. It has three pairs to support
+three substreams within totally 10 channels.
+
+Required properties:
+
+  - compatible : Compatible list, must contain "fsl,imx35-asrc" or
+    "fsl,imx53-asrc".
+
+  - reg : Offset and length of the register set for the device.
+
+  - interrupts : Contains the spdif interrupt.
+
+  - dmas : Generic dma devicetree binding as described in
+	   Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names : Six dmas have to be defined: "rxa", "rxb", "rxc", "txa", "txb",
+		and "txc".
+
+  - clocks : Contains an entry for each entry in clock-names.
+
+  - clock-names : Includes the following entries:
+	"mem"		Peripheral access clock to access registers.
+	"ipg"		Peripheral clock to driver module.
+	"asrck_<0-f>"	Clock sources for input and output clock.
+
+   - big-endian : If this property is absent, the native endian mode will
+		  be in use as default, or the big endian mode will be in use
+		  for all the device registers.
+
+   - fsl,asrc-rate : Defines a mutual sample rate used by Back End DAI link.
+
+   - fsl,asrc-width : Defines a mutual sample width used by Back End DAI link.
+
+Example:
+
+asrc: asrc@02034000 {
+	compatible = "fsl,imx53-asrc";
+	reg = <0x02034000 0x4000>;
+	interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&clks 107>, <&clks 107>, <&clks 0>,
+	       <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+	       <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+	       <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+	       <&clks 107>, <&clks 0>, <&clks 0>;
+	clock-names = "mem", "ipg", "asrck0",
+		"asrck_1", "asrck_2", "asrck_3", "asrck_4",
+		"asrck_5", "asrck_6", "asrck_7", "asrck_8",
+		"asrck_9", "asrck_a", "asrck_b", "asrck_c",
+		"asrck_d", "asrck_e", "asrck_f";
+	dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,
+	     <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
+	dma-names = "rxa", "rxb", "rxc",
+		"txa", "txb", "txc";
+	fsl,asrc-rate  = <48000>;
+	fsl,asrc-width = <16>;
+	status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2d60e37..e06fbfe 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -2,6 +2,15 @@  menu "SoC Audio for Freescale CPUs"
 
 comment "Common SoC Audio options for Freescale CPUs:"
 
+config SND_SOC_FSL_ASRC
+	tristate "Asynchronous Sample Rate Converter (ASRC) module support"
+	select REGMAP_MMIO
+	help
+	  Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
+	  support for the Freescale CPUs.
+	  This option is only useful for out-of-tree drivers since
+	  in-tree drivers select it automatically.
+
 config SND_SOC_FSL_SAI
 	tristate "Synchronous Audio Interface (SAI) module support"
 	select REGMAP_MMIO
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index ce49570..ff7ee2c 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -11,6 +11,7 @@  snd-soc-p1022-rdk-objs := p1022_rdk.o
 obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
 snd-soc-fsl-sai-objs := fsl_sai.o
 snd-soc-fsl-ssi-y := fsl_ssi.o
 snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
@@ -18,6 +19,7 @@  snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-esai-objs := fsl_esai.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
 obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
 obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
 obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
new file mode 100644
index 0000000..800e88e
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -0,0 +1,1030 @@ 
+/*
+ * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/dma-imx.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define IDEAL_RATIO_DECIMAL_DEPTH 26
+
+#define pair_err(fmt, ...) \
+	dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+#define pair_dbg(fmt, ...) \
+	dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const u8 process_option[][8][2] = {
+	/* 32kHz 44.1kHz 48kHz   64kHz   88.2kHz 96kHz   176kHz  192kHz */
+	{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},	/* 5512Hz */
+	{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},	/* 8kHz */
+	{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},	/* 11025Hz */
+	{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},	/* 16kHz */
+	{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},	/* 22050Hz */
+	{{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},	/* 32kHz */
+	{{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},	/* 44.1kHz */
+	{{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},	/* 48kHz */
+	{{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},	/* 64kHz */
+	{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},	/* 88.2kHz */
+	{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},	/* 96kHz */
+	{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},	/* 176kHz */
+	{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},	/* 192kHz */
+};
+
+/* Corresponding to process_option */
+static int supported_input_rate[] = {
+	5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200,
+	96000, 176400, 192000,
+};
+
+static int supported_asrc_rate[] = {
+	32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
+};
+
+/**
+ * The following tables map the relationship between asrc_inclk/asrc_outclk in
+ * fsl_asrc.h and the registers of ASRCSR
+ */
+static unsigned char input_clk_map_imx35[] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char output_clk_map_imx35[] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+/* i.MX53 uses the same map for input and output */
+static unsigned char input_clk_map_imx53[] = {
+/*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
+	0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
+};
+
+static unsigned char output_clk_map_imx53[] = {
+/*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
+	0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
+};
+
+static unsigned char *clk_map[2];
+
+enum fsl_asrc_type {
+	IMX35_ASRC,
+	IMX53_ASRC,
+};
+
+static const struct platform_device_id fsl_asrc_devtype[] = {
+	{
+		.name = "imx35-asrc",
+		.driver_data = IMX35_ASRC,
+	}, {
+		.name = "imx53-asrc",
+		.driver_data = IMX53_ASRC,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, fsl_asrc_devtype);
+
+static const struct of_device_id fsl_asrc_ids[] = {
+	{
+		.compatible = "fsl,imx35-asrc",
+		.data = &fsl_asrc_devtype[IMX35_ASRC],
+	}, {
+		.compatible = "fsl,imx53-asrc",
+		.data = &fsl_asrc_devtype[IMX53_ASRC],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
+
+static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
+{
+	struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id;
+	struct device *dev = &asrc_priv->pdev->dev;
+	enum asrc_pair_index index;
+	u32 status;
+
+	regmap_read(asrc_priv->regmap, REG_ASRSTR, &status);
+
+	/* Clean overload error */
+	regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE);
+
+	/*
+	 * We here use dev_dbg() for all exceptions because ASRC itself does
+	 * not care if FIFO overflowed or underrun while a warning in the
+	 * interrupt would result a ridged conversion.
+	 */
+	for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
+		if (!asrc_priv->pair[index])
+			continue;
+
+		if (status & ASRSTR_ATQOL) {
+			asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
+			dev_dbg(dev, "ASRC Task Queue FIFO overload");
+		}
+
+		if (status & ASRSTR_AOOL(index)) {
+			asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
+			pair_dbg("Output Task Overload");
+		}
+
+		if (status & ASRSTR_AIOL(index)) {
+			asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
+			pair_dbg("Input Task Overload");
+		}
+
+		if (status & ASRSTR_AODO(index)) {
+			asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
+			pair_dbg("Output Data Buffer has overflowed");
+		}
+
+		if (status & ASRSTR_AIDU(index)) {
+			asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
+			pair_dbg("Input Data Buffer has underflowed");
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * Request ASRC pair
+ *
+ * It assigns pair by the order of A->C->B because allocation of pair B,
+ * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
+ * while pair A and pair C are comparatively independent.
+ */
+static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
+{
+	enum asrc_pair_index index = ASRC_INVALID_PAIR;
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	struct device *dev = &asrc_priv->pdev->dev;
+	unsigned long lock_flags;
+	int i, ret = 0;
+
+	spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+		if (asrc_priv->pair[i] != NULL)
+			continue;
+
+		index = i;
+
+		if (i != ASRC_PAIR_B)
+			break;
+	}
+
+	if (index == ASRC_INVALID_PAIR) {
+		dev_err(dev, "all pairs are busy now\n");
+		ret = -EBUSY;
+	} else if (asrc_priv->channel_avail < channels) {
+		dev_err(dev, "can't afford required channels: %d\n", channels);
+		ret = -EINVAL;
+	} else {
+		asrc_priv->channel_avail -= channels;
+		asrc_priv->pair[index] = pair;
+		pair->channels = channels;
+		pair->index = index;
+	}
+
+	spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+
+	return ret;
+}
+
+/**
+ * Release ASRC pair
+ *
+ * It clears the resource from asrc_priv and releases the occupied channels.
+ */
+static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+	unsigned long lock_flags;
+
+	/* Make sure the pair is disabled */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ASRCEi_MASK(index), 0);
+
+	spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+	asrc_priv->channel_avail += pair->channels;
+	asrc_priv->pair[index] = NULL;
+	pair->error = 0;
+
+	spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+}
+
+/**
+ * Configure input and output thresholds
+ */
+static int fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+
+	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+			   ASRMCRi_EXTTHRSHi_MASK |
+			   ASRMCRi_INFIFO_THRESHOLD_MASK |
+			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
+			   ASRMCRi_EXTTHRSHi |
+			   ASRMCRi_INFIFO_THRESHOLD(in) |
+			   ASRMCRi_OUTFIFO_THRESHOLD(out));
+
+	return 0;
+}
+
+/**
+ * Calculate the total divisor between asrck clock rate and sample rate
+ *
+ * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
+ */
+static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
+{
+	u32 ps;
+
+	/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
+	for (ps = 0; div > 8; ps++)
+		div >>= 1;
+
+	return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
+}
+
+/**
+ * Calculate and set the ratio for Ideal Ratio mode only
+ *
+ * The ratio is a 32-bit fixed point value with 26 fractional bits.
+ */
+static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
+				    int inrate, int outrate)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+	unsigned long ratio;
+	int i;
+
+	if (!outrate) {
+		pair_err("output rate should not be zero\n");
+		return -EINVAL;
+	}
+
+	/* Calculate the intergal part of the ratio */
+	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
+
+	/* ... and then the 26 depth decimal part */
+	inrate %= outrate;
+
+	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
+		inrate <<= 1;
+
+		if (inrate < outrate)
+			continue;
+
+		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
+		inrate -= outrate;
+
+		if (!inrate)
+			break;
+	}
+
+	regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
+	regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);
+
+	return 0;
+}
+
+/**
+ * Configure the assigned ASRC pair
+ *
+ * It configures those ASRC registers according to a configuration instance
+ * of struct asrc_config which includes in/output sample rate, width, channel
+ * and clock settings.
+ */
+static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
+{
+	struct asrc_config *config = pair->config;
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+	u32 inrate = config->input_sample_rate, indiv;
+	u32 outrate = config->output_sample_rate, outdiv;
+	bool ideal = config->inclk == INCLK_NONE;
+	u32 clk_index[2], div[2];
+	int in, out, channels;
+	struct clk *clk;
+
+	if (!config) {
+		pair_err("invalid pair config\n");
+		return -EINVAL;
+	}
+
+	/* Validate channels */
+	if (config->channel_num < 1 || config->channel_num > 10) {
+		pair_err("does not support %d channels\n", config->channel_num);
+		return -EINVAL;
+	}
+
+	/* Validate output width */
+	if (config->output_word_width == ASRC_WIDTH_8_BIT) {
+		pair_err("does not support 8bit width output\n");
+		return -EINVAL;
+	}
+
+	/* Validate input and output sample rates */
+	for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++)
+		if (inrate == supported_input_rate[in])
+			break;
+
+	if (in == ARRAY_SIZE(supported_input_rate)) {
+		pair_err("unsupported input sample rate: %dHz\n", inrate);
+		return -EINVAL;
+	}
+
+	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
+		if (outrate == supported_asrc_rate[out])
+			break;
+
+	if (out == ARRAY_SIZE(supported_asrc_rate)) {
+		pair_err("unsupported output sample rate: %dHz\n", outrate);
+		return -EINVAL;
+	}
+
+	/* Validate input and output clock sources */
+	clk_index[IN] = clk_map[IN][config->inclk];
+	clk_index[OUT] = clk_map[OUT][config->outclk];
+
+	/* We only have output clock for ideal ratio mode */
+	clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
+
+	div[IN] = clk_get_rate(clk) / inrate;
+	if (div[IN] == 0) {
+		pair_err("failed to support input sample rate %dHz by asrck_%x",
+				inrate, clk_index[ideal ? OUT : IN]);
+		return -EINVAL;
+	}
+
+	clk = asrc_priv->asrck_clk[clk_index[OUT]];
+
+	/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
+	if (ideal)
+		div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
+	else
+		div[OUT] = clk_get_rate(clk) / outrate;
+
+	if (div[OUT] == 0) {
+		pair_err("failed to support output sample rate %dHz by asrck_%x",
+				outrate, clk_index[OUT]);
+		return -EINVAL;
+	}
+
+	/* Set the channel number */
+	channels = config->channel_num;
+
+	if (asrc_priv->channel_bits < 4)
+		channels /= 2;
+
+	/* Update channels for current pair */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
+			   ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
+			   ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));
+
+	/* Default setting: Automatic selection for processing mode */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_USRi_MASK(index), 0);
+
+	/* Set the input and output clock sources */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
+			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
+			   ASRCSR_AICS(index, clk_index[IN]) |
+			   ASRCSR_AOCS(index, clk_index[OUT]));
+
+	/* Calculate the input clock divisors */
+	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
+	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
+
+	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
+			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
+			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
+			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
+
+	/* Implement word_width configurations */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
+			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
+			   ASRMCR1i_OW16(config->output_word_width) |
+			   ASRMCR1i_IWD(config->input_word_width));
+
+	/* Enable BUFFER STALL */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
+
+	/* Set default thresholds for input and output FIFO */
+	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
+				ASRC_INPUTFIFO_THRESHOLD);
+
+	/* Configure the followings only for Ideal Ratio mode */
+	if (!ideal)
+		return 0;
+
+	/* Clear ASTSx bit to use Ideal Ratio mode */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ATSi_MASK(index), 0);
+
+	/* Enable Ideal Ratio mode */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
+			   ASRCTR_IDR(index) | ASRCTR_USR(index));
+
+	/* Apply configurations for pre- and post-processing */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
+			   ASRCFG_PREMOD(index, process_option[in][out][0]) |
+			   ASRCFG_POSTMOD(index, process_option[in][out][1]));
+
+	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
+}
+
+/**
+ * Start the assigned ASRC pair
+ *
+ * It enables the assigned pair and makes it stopped at the stall level.
+ */
+static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+	int reg, retry = 10, i;
+
+	/* Enable the current pair */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
+
+	/* Wait for status of initialization */
+	do {
+		udelay(5);
+		regmap_read(asrc_priv->regmap, REG_ASRCFG, &reg);
+		reg &= ASRCFG_INIRQi_MASK(index);
+	} while (!reg && --retry);
+
+	/* Make the input fifo to ASRC STALL level */
+	regmap_read(asrc_priv->regmap, REG_ASRCNCR, &reg);
+	for (i = 0; i < pair->channels * 4; i++)
+		regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0);
+
+	/* Enable overload interrupt */
+	regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE);
+}
+
+/**
+ * Stop the assigned ASRC pair
+ */
+static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+
+	/* Stop the current pair */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ASRCEi_MASK(index), 0);
+}
+
+/**
+ * Get DMA channel according to the pair and direction.
+ */
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
+{
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	enum asrc_pair_index index = pair->index;
+	char name[4];
+
+	sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
+
+	return dma_request_slave_channel(&asrc_priv->pdev->dev, name);
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
+
+static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+	int width = snd_pcm_format_width(params_format(params));
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+	struct asrc_config config;
+	int word_width, ret;
+
+	ret = fsl_asrc_request_pair(channels, pair);
+	if (ret) {
+		dev_err(dai->dev, "fail to request asrc pair\n");
+		return ret;
+	}
+
+	pair->config = &config;
+
+	if (width == 16)
+		width = ASRC_WIDTH_16_BIT;
+	else
+		width = ASRC_WIDTH_24_BIT;
+
+	if (asrc_priv->asrc_width == 16)
+		word_width = ASRC_WIDTH_16_BIT;
+	else
+		word_width = ASRC_WIDTH_24_BIT;
+
+	config.pair = pair->index;
+	config.channel_num = channels;
+	config.inclk = INCLK_NONE;
+	config.outclk = OUTCLK_ASRCK1_CLK;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		config.input_word_width   = width;
+		config.output_word_width  = word_width;
+		config.input_sample_rate  = rate;
+		config.output_sample_rate = asrc_priv->asrc_rate;
+	} else {
+		config.input_word_width   = word_width;
+		config.output_word_width  = width;
+		config.input_sample_rate  = asrc_priv->asrc_rate;
+		config.output_sample_rate = rate;
+	}
+
+	ret = fsl_asrc_config_pair(pair);
+	if (ret) {
+		dev_err(dai->dev, "fail to config asrc pair\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+
+	if (pair)
+		fsl_asrc_release_pair(pair);
+
+	return 0;
+}
+
+static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		fsl_asrc_start_pair(pair);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		fsl_asrc_stop_pair(pair);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops fsl_asrc_dai_ops = {
+	.hw_params    = fsl_asrc_dai_hw_params,
+	.hw_free      = fsl_asrc_dai_hw_free,
+	.trigger      = fsl_asrc_dai_trigger,
+};
+
+static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
+{
+	struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+
+	snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx,
+				  &asrc_priv->dma_params_rx);
+
+	return 0;
+}
+
+#define FSL_ASRC_RATES		 SNDRV_PCM_RATE_8000_192000
+#define FSL_ASRC_FORMATS	(SNDRV_PCM_FMTBIT_S24_LE | \
+				 SNDRV_PCM_FMTBIT_S16_LE | \
+				 SNDRV_PCM_FORMAT_S20_3LE)
+
+static struct snd_soc_dai_driver fsl_asrc_dai = {
+	.probe = fsl_asrc_dai_probe,
+	.playback = {
+		.stream_name = "ASRC-Playback",
+		.channels_min = 1,
+		.channels_max = 10,
+		.rates = FSL_ASRC_RATES,
+		.formats = FSL_ASRC_FORMATS,
+	},
+	.capture = {
+		.stream_name = "ASRC-Capture",
+		.channels_min = 1,
+		.channels_max = 10,
+		.rates = FSL_ASRC_RATES,
+		.formats = FSL_ASRC_FORMATS,
+	},
+	.ops = &fsl_asrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_asrc_component = {
+	.name = "fsl-asrc-dai",
+};
+
+static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_ASRCTR:
+	case REG_ASRIER:
+	case REG_ASRCNCR:
+	case REG_ASRCFG:
+	case REG_ASRCSR:
+	case REG_ASRCDR1:
+	case REG_ASRCDR2:
+	case REG_ASRSTR:
+	case REG_ASRPM1:
+	case REG_ASRPM2:
+	case REG_ASRPM3:
+	case REG_ASRPM4:
+	case REG_ASRPM5:
+	case REG_ASRTFR1:
+	case REG_ASRCCR:
+	case REG_ASRDOA:
+	case REG_ASRDOB:
+	case REG_ASRDOC:
+	case REG_ASRIDRHA:
+	case REG_ASRIDRLA:
+	case REG_ASRIDRHB:
+	case REG_ASRIDRLB:
+	case REG_ASRIDRHC:
+	case REG_ASRIDRLC:
+	case REG_ASR76K:
+	case REG_ASR56K:
+	case REG_ASRMCRA:
+	case REG_ASRFSTA:
+	case REG_ASRMCRB:
+	case REG_ASRFSTB:
+	case REG_ASRMCRC:
+	case REG_ASRFSTC:
+	case REG_ASRMCR1A:
+	case REG_ASRMCR1B:
+	case REG_ASRMCR1C:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_ASRSTR:
+	case REG_ASRDIA:
+	case REG_ASRDIB:
+	case REG_ASRDIC:
+	case REG_ASRDOA:
+	case REG_ASRDOB:
+	case REG_ASRDOC:
+	case REG_ASRFSTA:
+	case REG_ASRFSTB:
+	case REG_ASRFSTC:
+	case REG_ASRCFG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_ASRCTR:
+	case REG_ASRIER:
+	case REG_ASRCNCR:
+	case REG_ASRCFG:
+	case REG_ASRCSR:
+	case REG_ASRCDR1:
+	case REG_ASRCDR2:
+	case REG_ASRSTR:
+	case REG_ASRPM1:
+	case REG_ASRPM2:
+	case REG_ASRPM3:
+	case REG_ASRPM4:
+	case REG_ASRPM5:
+	case REG_ASRTFR1:
+	case REG_ASRCCR:
+	case REG_ASRDIA:
+	case REG_ASRDIB:
+	case REG_ASRDIC:
+	case REG_ASRIDRHA:
+	case REG_ASRIDRLA:
+	case REG_ASRIDRHB:
+	case REG_ASRIDRLB:
+	case REG_ASRIDRHC:
+	case REG_ASRIDRLC:
+	case REG_ASR76K:
+	case REG_ASR56K:
+	case REG_ASRMCRA:
+	case REG_ASRMCRB:
+	case REG_ASRMCRC:
+	case REG_ASRMCR1A:
+	case REG_ASRMCR1B:
+	case REG_ASRMCR1C:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static struct regmap_config fsl_asrc_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = REG_ASRMCR1C,
+	.readable_reg = fsl_asrc_readable_reg,
+	.volatile_reg = fsl_asrc_volatile_reg,
+	.writeable_reg = fsl_asrc_writeable_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+/**
+ * Initialize ASRC registers with a default configurations
+ */
+static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
+{
+	/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
+	regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
+
+	/* Disable interrupt by default */
+	regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0);
+
+	/* Apply recommended settings for parameters from Reference Manual */
+	regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff);
+	regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555);
+	regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280);
+	regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280);
+	regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280);
+
+	/* Base address for task queue FIFO. Set to 0x7C */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
+			   ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
+
+	/* Set the processing clock for 76KHz to 133M */
+	regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
+
+	/* Set the processing clock for 56KHz to 133M */
+	regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
+
+	return 0;
+}
+
+static int fsl_asrc_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+		of_match_device(fsl_asrc_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
+	struct fsl_asrc *asrc_priv;
+	enum fsl_asrc_type devtype;
+	struct resource *res;
+	void __iomem *regs;
+	int irq, ret, i;
+	char tmp[16];
+
+	asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
+	if (!asrc_priv)
+		return -ENOMEM;
+
+	if (of_id)
+		pdev->id_entry = of_id->data;
+	devtype = pdev->id_entry->driver_data;
+
+	switch (devtype) {
+	case IMX35_ASRC:
+		asrc_priv->channel_bits = 3;
+		clk_map[IN] = input_clk_map_imx35;
+		clk_map[OUT] = output_clk_map_imx35;
+		break;
+	case IMX53_ASRC:
+		asrc_priv->channel_bits = 4;
+		clk_map[IN] = input_clk_map_imx53;
+		clk_map[OUT] = output_clk_map_imx53;
+		break;
+	default:
+		dev_err(&pdev->dev, "unsupported device type\n");
+		return -EINVAL;
+	}
+
+	asrc_priv->pdev = pdev;
+	strcpy(asrc_priv->name, np->name);
+
+	/* Get the addresses and IRQ */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	asrc_priv->paddr = res->start;
+
+	/* Register regmap and let it prepare core clock */
+	if (of_property_read_bool(np, "big-endian"))
+		fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
+	asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+			"mem", regs, &fsl_asrc_regmap_config);
+	if (IS_ERR(asrc_priv->regmap)) {
+		dev_err(&pdev->dev, "failed to init regmap\n");
+		return PTR_ERR(asrc_priv->regmap);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
+			       asrc_priv->name, asrc_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
+		return ret;
+	}
+
+	asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem");
+	if (IS_ERR(asrc_priv->mem_clk)) {
+		dev_err(&pdev->dev, "failed to get mem clock\n");
+		return PTR_ERR(asrc_priv->ipg_clk);
+	}
+
+	asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(asrc_priv->ipg_clk)) {
+		dev_err(&pdev->dev, "failed to get ipg clock\n");
+		return PTR_ERR(asrc_priv->ipg_clk);
+	}
+
+	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
+		sprintf(tmp, "asrck_%x", i);
+		asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
+		if (IS_ERR(asrc_priv->asrck_clk[i])) {
+			dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
+			return PTR_ERR(asrc_priv->asrck_clk[i]);
+		}
+	}
+
+	ret = fsl_asrc_init(asrc_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
+		return -EINVAL;
+	}
+
+	asrc_priv->channel_avail = 10;
+
+	ret = of_property_read_u32(np, "fsl,asrc-rate",
+				   &asrc_priv->asrc_rate);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to get output rate\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "fsl,asrc-width",
+				   &asrc_priv->asrc_width);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to get output width\n");
+		return -EINVAL;
+	}
+
+	if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
+		dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
+		asrc_priv->asrc_width = 24;
+	}
+
+	platform_set_drvdata(pdev, asrc_priv);
+	pm_runtime_enable(&pdev->dev);
+	spin_lock_init(&asrc_priv->lock);
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
+					      &fsl_asrc_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ASoC platform\n");
+		return ret;
+	}
+
+	dev_info(&pdev->dev, "driver registered\n");
+
+	return 0;
+}
+
+#if CONFIG_PM_RUNTIME
+static int fsl_asrc_runtime_resume(struct device *dev)
+{
+	struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+	int i;
+
+	clk_prepare_enable(asrc_priv->mem_clk);
+	clk_prepare_enable(asrc_priv->ipg_clk);
+	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+		clk_prepare_enable(asrc_priv->asrck_clk[i]);
+
+	return 0;
+}
+
+static int fsl_asrc_runtime_suspend(struct device *dev)
+{
+	struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+	clk_disable_unprepare(asrc_priv->ipg_clk);
+	clk_disable_unprepare(asrc_priv->mem_clk);
+
+	return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#if CONFIG_PM_SLEEP
+static int fsl_asrc_suspend(struct device *dev)
+{
+	struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+
+	regcache_cache_only(asrc_priv->regmap, true);
+	regcache_mark_dirty(asrc_priv->regmap);
+
+	return 0;
+}
+
+static int fsl_asrc_resume(struct device *dev)
+{
+	struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+	u32 asrctr;
+
+	/* Stop all pairs provisionally */
+	regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ASRCEi_ALL_MASK, 0);
+
+	/* Restore all registers */
+	regcache_cache_only(asrc_priv->regmap, false);
+	regcache_sync(asrc_priv->regmap);
+
+	/* Restart enabled pairs */
+	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+			   ASRCTR_ASRCEi_ALL_MASK, asrctr);
+
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_asrc_pm = {
+	SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
+};
+
+static struct platform_driver fsl_asrc_driver = {
+	.probe = fsl_asrc_probe,
+	.driver = {
+		.name = "fsl-asrc",
+		.of_match_table = fsl_asrc_ids,
+		.pm = &fsl_asrc_pm,
+	},
+};
+module_platform_driver(fsl_asrc_driver);
+
+MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
+MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
+MODULE_ALIAS("platform:fsl-asrc");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
new file mode 100644
index 0000000..a3f211f
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -0,0 +1,461 @@ 
+/*
+ * fsl_asrc.h - Freescale ASRC ALSA SoC header file
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_ASRC_H
+#define _FSL_ASRC_H
+
+#define IN	0
+#define OUT	1
+
+#define ASRC_DMA_BUFFER_NUM		2
+#define ASRC_INPUTFIFO_THRESHOLD	32
+#define ASRC_OUTPUTFIFO_THRESHOLD	32
+#define ASRC_FIFO_THRESHOLD_MIN		0
+#define ASRC_FIFO_THRESHOLD_MAX		63
+#define ASRC_DMA_BUFFER_SIZE		(1024 * 48 * 4)
+#define ASRC_MAX_BUFFER_SIZE		(1024 * 48)
+#define ASRC_OUTPUT_LAST_SAMPLE		8
+
+#define IDEAL_RATIO_RATE		1000000
+
+#define REG_ASRCTR			0x00
+#define REG_ASRIER			0x04
+#define REG_ASRCNCR			0x0C
+#define REG_ASRCFG			0x10
+#define REG_ASRCSR			0x14
+
+#define REG_ASRCDR1			0x18
+#define REG_ASRCDR2			0x1C
+#define REG_ASRCDR(i)			((i < 2) ? REG_ASRCDR1 : REG_ASRCDR2)
+
+#define REG_ASRSTR			0x20
+#define REG_ASRRA			0x24
+#define REG_ASRRB			0x28
+#define REG_ASRRC			0x2C
+#define REG_ASRPM1			0x40
+#define REG_ASRPM2			0x44
+#define REG_ASRPM3			0x48
+#define REG_ASRPM4			0x4C
+#define REG_ASRPM5			0x50
+#define REG_ASRTFR1			0x54
+#define REG_ASRCCR			0x5C
+
+#define REG_ASRDIA			0x60
+#define REG_ASRDOA			0x64
+#define REG_ASRDIB			0x68
+#define REG_ASRDOB			0x6C
+#define REG_ASRDIC			0x70
+#define REG_ASRDOC			0x74
+#define REG_ASRDI(i)			(REG_ASRDIA + (i << 3))
+#define REG_ASRDO(i)			(REG_ASRDOA + (i << 3))
+#define REG_ASRDx(x, i)			(x == IN ? REG_ASRDI(i) : REG_ASRDO(i))
+
+#define REG_ASRIDRHA			0x80
+#define REG_ASRIDRLA			0x84
+#define REG_ASRIDRHB			0x88
+#define REG_ASRIDRLB			0x8C
+#define REG_ASRIDRHC			0x90
+#define REG_ASRIDRLC			0x94
+#define REG_ASRIDRH(i)			(REG_ASRIDRHA + (i << 3))
+#define REG_ASRIDRL(i)			(REG_ASRIDRLA + (i << 3))
+
+#define REG_ASR76K			0x98
+#define REG_ASR56K			0x9C
+
+#define REG_ASRMCRA			0xA0
+#define REG_ASRFSTA			0xA4
+#define REG_ASRMCRB			0xA8
+#define REG_ASRFSTB			0xAC
+#define REG_ASRMCRC			0xB0
+#define REG_ASRFSTC			0xB4
+#define REG_ASRMCR(i)			(REG_ASRMCRA + (i << 3))
+#define REG_ASRFST(i)			(REG_ASRFSTA + (i << 3))
+
+#define REG_ASRMCR1A			0xC0
+#define REG_ASRMCR1B			0xC4
+#define REG_ASRMCR1C			0xC8
+#define REG_ASRMCR1(i)			(REG_ASRMCR1A + (i << 2))
+
+
+/* REG0 0x00 REG_ASRCTR */
+#define ASRCTR_ATSi_SHIFT(i)		(20 + i)
+#define ASRCTR_ATSi_MASK(i)		(1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_ATS(i)			(1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_USRi_SHIFT(i)		(14 + (i << 1))
+#define ASRCTR_USRi_MASK(i)		(1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_USR(i)			(1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_IDRi_SHIFT(i)		(13 + (i << 1))
+#define ASRCTR_IDRi_MASK(i)		(1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_IDR(i)			(1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_SRST_SHIFT		4
+#define ASRCTR_SRST_MASK		(1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_SRST			(1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_ASRCEi_SHIFT(i)		(1 + i)
+#define ASRCTR_ASRCEi_MASK(i)		(1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCE(i)			(1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCEi_ALL_MASK		(0x7 << ASRCTR_ASRCEi_SHIFT(0))
+#define ASRCTR_ASRCEN_SHIFT		0
+#define ASRCTR_ASRCEN_MASK		(1 << ASRCTR_ASRCEN_SHIFT)
+#define ASRCTR_ASRCEN			(1 << ASRCTR_ASRCEN_SHIFT)
+
+/* REG1 0x04 REG_ASRIER */
+#define ASRIER_AFPWE_SHIFT		7
+#define ASRIER_AFPWE_MASK		(1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AFPWE			(1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AOLIE_SHIFT		6
+#define ASRIER_AOLIE_MASK		(1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_AOLIE			(1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_ADOEi_SHIFT(i)		(3 + i)
+#define ASRIER_ADOEi_MASK(i)		(1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADOE(i)			(1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADIEi_SHIFT(i)		(0 + i)
+#define ASRIER_ADIEi_MASK(i)		(1 << ASRIER_ADIEi_SHIFT(i))
+#define ASRIER_ADIE(i)			(1 << ASRIER_ADIEi_SHIFT(i))
+
+/* REG2 0x0C REG_ASRCNCR */
+#define ASRCNCR_ANCi_SHIFT(i, b)	(b * i)
+#define ASRCNCR_ANCi_MASK(i, b)		(((1 << b) - 1) << ASRCNCR_ANCi_SHIFT(i, b))
+#define ASRCNCR_ANCi(i, v, b)		((v << ASRCNCR_ANCi_SHIFT(i, b)) & ASRCNCR_ANCi_MASK(i, b))
+
+/* REG3 0x10 REG_ASRCFG */
+#define ASRCFG_INIRQi_SHIFT(i)		(21 + i)
+#define ASRCFG_INIRQi_MASK(i)		(1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_INIRQi			(1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_NDPRi_SHIFT(i)		(18 + i)
+#define ASRCFG_NDPRi_MASK(i)		(1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_NDPRi			(1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_POSTMODi_SHIFT(i)	(8 + (i << 2))
+#define ASRCFG_POSTMODi_WIDTH		2
+#define ASRCFG_POSTMODi_MASK(i)		(((1 << ASRCFG_POSTMODi_WIDTH) - 1) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMOD(i, v)		((v) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_UP(i)		(0 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DCON(i)		(1 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DOWN(i)		(2 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_PREMODi_SHIFT(i)		(6 + (i << 2))
+#define ASRCFG_PREMODi_WIDTH		2
+#define ASRCFG_PREMODi_MASK(i)		(((1 << ASRCFG_PREMODi_WIDTH) - 1) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMOD(i, v)		((v) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_UP(i)		(0 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DCON(i)		(1 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DOWN(i)		(2 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_BYPASS(i)	(3 << ASRCFG_PREMODi_SHIFT(i))
+
+/* REG4 0x14 REG_ASRCSR */
+#define ASRCSR_AxCSi_WIDTH		4
+#define ASRCSR_AxCSi_MASK		((1 << ASRCSR_AxCSi_WIDTH) - 1)
+#define ASRCSR_AOCSi_SHIFT(i)		(12 + (i << 2))
+#define ASRCSR_AOCSi_MASK(i)		(((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AOCS(i, v)		((v) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AICSi_SHIFT(i)		(i << 2)
+#define ASRCSR_AICSi_MASK(i)		(((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AICSi_SHIFT(i))
+#define ASRCSR_AICS(i, v)		((v) << ASRCSR_AICSi_SHIFT(i))
+
+/* REG5&6 0x18 & 0x1C REG_ASRCDR1 & ASRCDR2 */
+#define ASRCDRi_AxCPi_WIDTH		3
+#define ASRCDRi_AICPi_SHIFT(i)		(0 + (i % 2) * 6)
+#define ASRCDRi_AICPi_MASK(i)		(((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICP(i, v)		((v) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICDi_SHIFT(i)		(3 + (i % 2) * 6)
+#define ASRCDRi_AICDi_MASK(i)		(((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AICD(i, v)		((v) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AOCPi_SHIFT(i)		((i < 2) ? 12 + i * 6 : 6)
+#define ASRCDRi_AOCPi_MASK(i)		(((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCP(i, v)		((v) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCDi_SHIFT(i)		((i < 2) ? 15 + i * 6 : 9)
+#define ASRCDRi_AOCDi_MASK(i)		(((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCDi_SHIFT(i))
+#define ASRCDRi_AOCD(i, v)		((v) << ASRCDRi_AOCDi_SHIFT(i))
+
+/* REG7 0x20 REG_ASRSTR */
+#define ASRSTR_DSLCNT_SHIFT		21
+#define ASRSTR_DSLCNT_MASK		(1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_DSLCNT			(1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_ATQOL_SHIFT		20
+#define ASRSTR_ATQOL_MASK		(1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_ATQOL			(1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_AOOLi_SHIFT(i)		(17 + i)
+#define ASRSTR_AOOLi_MASK(i)		(1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AOOL(i)			(1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AIOLi_SHIFT(i)		(14 + i)
+#define ASRSTR_AIOLi_MASK(i)		(1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AIOL(i)			(1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AODOi_SHIFT(i)		(11 + i)
+#define ASRSTR_AODOi_MASK(i)		(1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AODO(i)			(1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AIDUi_SHIFT(i)		(8 + i)
+#define ASRSTR_AIDUi_MASK(i)		(1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_AIDU(i)			(1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_FPWT_SHIFT		7
+#define ASRSTR_FPWT_MASK		(1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_FPWT			(1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_AOLE_SHIFT		6
+#define ASRSTR_AOLE_MASK		(1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AOLE			(1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AODEi_SHIFT(i)		(3 + i)
+#define ASRSTR_AODFi_MASK(i)		(1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AODF(i)			(1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AIDEi_SHIFT(i)		(0 + i)
+#define ASRSTR_AIDEi_MASK(i)		(1 << ASRSTR_AIDEi_SHIFT(i))
+#define ASRSTR_AIDE(i)			(1 << ASRSTR_AIDEi_SHIFT(i))
+
+/* REG10 0x54 REG_ASRTFR1 */
+#define ASRTFR1_TF_BASE_WIDTH		7
+#define ASRTFR1_TF_BASE_SHIFT		6
+#define ASRTFR1_TF_BASE_MASK		(((1 << ASRTFR1_TF_BASE_WIDTH) - 1) << ASRTFR1_TF_BASE_SHIFT)
+#define ASRTFR1_TF_BASE(i)		((i) << ASRTFR1_TF_BASE_SHIFT)
+
+/*
+ * REG22 0xA0 REG_ASRMCRA
+ * REG24 0xA8 REG_ASRMCRB
+ * REG26 0xB0 REG_ASRMCRC
+ */
+#define ASRMCRi_ZEROBUFi_SHIFT		23
+#define ASRMCRi_ZEROBUFi_MASK		(1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_ZEROBUFi		(1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_EXTTHRSHi_SHIFT		22
+#define ASRMCRi_EXTTHRSHi_MASK		(1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_EXTTHRSHi		(1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_BUFSTALLi_SHIFT		21
+#define ASRMCRi_BUFSTALLi_MASK		(1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BUFSTALLi		(1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi_SHIFT	20
+#define ASRMCRi_BYPASSPOLYi_MASK	(1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi		(1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD_WIDTH	6
+#define ASRMCRi_OUTFIFO_THRESHOLD_SHIFT	12
+#define ASRMCRi_OUTFIFO_THRESHOLD_MASK	(((1 << ASRMCRi_OUTFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD(v)	(((v) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) & ASRMCRi_OUTFIFO_THRESHOLD_MASK)
+#define ASRMCRi_RSYNIFi_SHIFT		11
+#define ASRMCRi_RSYNIFi_MASK		(1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNIFi			(1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNOFi_SHIFT		10
+#define ASRMCRi_RSYNOFi_MASK		(1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_RSYNOFi			(1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD_WIDTH	6
+#define ASRMCRi_INFIFO_THRESHOLD_SHIFT	0
+#define ASRMCRi_INFIFO_THRESHOLD_MASK	(((1 << ASRMCRi_INFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_INFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD(v)	(((v) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) & ASRMCRi_INFIFO_THRESHOLD_MASK)
+
+/*
+ * REG23 0xA4 REG_ASRFSTA
+ * REG25 0xAC REG_ASRFSTB
+ * REG27 0xB4 REG_ASRFSTC
+ */
+#define ASRFSTi_OAFi_SHIFT		23
+#define ASRFSTi_OAFi_MASK		(1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OAFi			(1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_WIDTH	7
+#define ASRFSTi_OUTPUT_FIFO_SHIFT	12
+#define ASRFSTi_OUTPUT_FIFO_MASK	(((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_IAEi_SHIFT		11
+#define ASRFSTi_IAEi_MASK		(1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_IAEi			(1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_INPUT_FIFO_WIDTH	7
+#define ASRFSTi_INPUT_FIFO_SHIFT	0
+#define ASRFSTi_INPUT_FIFO_MASK		((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1)
+
+/* REG28 0xC0 & 0xC4 & 0xC8 REG_ASRMCR1i */
+#define ASRMCR1i_IWD_WIDTH		3
+#define ASRMCR1i_IWD_SHIFT		9
+#define ASRMCR1i_IWD_MASK		(((1 << ASRMCR1i_IWD_WIDTH) - 1) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IWD(v)			((v) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IMSB_SHIFT		8
+#define ASRMCR1i_IMSB_MASK		(1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_MSB		(1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_LSB		(0 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_OMSB_SHIFT		2
+#define ASRMCR1i_OMSB_MASK		(1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_MSB		(1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_LSB		(0 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OSGN_SHIFT		1
+#define ASRMCR1i_OSGN_MASK		(1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OSGN			(1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OW16_SHIFT		0
+#define ASRMCR1i_OW16_MASK		(1 << ASRMCR1i_OW16_SHIFT)
+#define ASRMCR1i_OW16(v)		((v) << ASRMCR1i_OW16_SHIFT)
+
+
+enum asrc_pair_index {
+	ASRC_INVALID_PAIR = -1,
+	ASRC_PAIR_A = 0,
+	ASRC_PAIR_B = 1,
+	ASRC_PAIR_C = 2,
+};
+
+#define ASRC_PAIR_MAX_NUM	(ASRC_PAIR_C + 1)
+
+enum asrc_inclk {
+	INCLK_NONE = 0x03,
+	INCLK_ESAI_RX = 0x00,
+	INCLK_SSI1_RX = 0x01,
+	INCLK_SSI2_RX = 0x02,
+	INCLK_SSI3_RX = 0x07,
+	INCLK_SPDIF_RX = 0x04,
+	INCLK_MLB_CLK = 0x05,
+	INCLK_PAD = 0x06,
+	INCLK_ESAI_TX = 0x08,
+	INCLK_SSI1_TX = 0x09,
+	INCLK_SSI2_TX = 0x0a,
+	INCLK_SSI3_TX = 0x0b,
+	INCLK_SPDIF_TX = 0x0c,
+	INCLK_ASRCK1_CLK = 0x0f,
+};
+
+enum asrc_outclk {
+	OUTCLK_NONE = 0x03,
+	OUTCLK_ESAI_TX = 0x00,
+	OUTCLK_SSI1_TX = 0x01,
+	OUTCLK_SSI2_TX = 0x02,
+	OUTCLK_SSI3_TX = 0x07,
+	OUTCLK_SPDIF_TX = 0x04,
+	OUTCLK_MLB_CLK = 0x05,
+	OUTCLK_PAD = 0x06,
+	OUTCLK_ESAI_RX = 0x08,
+	OUTCLK_SSI1_RX = 0x09,
+	OUTCLK_SSI2_RX = 0x0a,
+	OUTCLK_SSI3_RX = 0x0b,
+	OUTCLK_SPDIF_RX = 0x0c,
+	OUTCLK_ASRCK1_CLK = 0x0f,
+};
+
+#define ASRC_CLK_MAX_NUM	16
+
+enum asrc_word_width {
+	ASRC_WIDTH_24_BIT = 0,
+	ASRC_WIDTH_16_BIT = 1,
+	ASRC_WIDTH_8_BIT = 2,
+};
+
+struct asrc_config {
+	enum asrc_pair_index pair;
+	unsigned int channel_num;
+	unsigned int buffer_num;
+	unsigned int dma_buffer_size;
+	unsigned int input_sample_rate;
+	unsigned int output_sample_rate;
+	enum asrc_word_width input_word_width;
+	enum asrc_word_width output_word_width;
+	enum asrc_inclk inclk;
+	enum asrc_outclk outclk;
+};
+
+struct asrc_req {
+	unsigned int chn_num;
+	enum asrc_pair_index index;
+};
+
+struct asrc_querybuf {
+	unsigned int buffer_index;
+	unsigned int input_length;
+	unsigned int output_length;
+	unsigned long input_offset;
+	unsigned long output_offset;
+};
+
+struct asrc_convert_buffer {
+	void *input_buffer_vaddr;
+	void *output_buffer_vaddr;
+	unsigned int input_buffer_length;
+	unsigned int output_buffer_length;
+};
+
+struct asrc_status_flags {
+	enum asrc_pair_index index;
+	unsigned int overload_error;
+};
+
+enum asrc_error_status {
+	ASRC_TASK_Q_OVERLOAD		= 0x01,
+	ASRC_OUTPUT_TASK_OVERLOAD	= 0x02,
+	ASRC_INPUT_TASK_OVERLOAD	= 0x04,
+	ASRC_OUTPUT_BUFFER_OVERFLOW	= 0x08,
+	ASRC_INPUT_BUFFER_UNDERRUN	= 0x10,
+};
+
+struct dma_block {
+	dma_addr_t dma_paddr;
+	void *dma_vaddr;
+	unsigned int length;
+};
+
+/**
+ * fsl_asrc_pair: ASRC Pair private data
+ *
+ * @asrc_priv: pointer to its parent module
+ * @config: configuration profile
+ * @error: error record
+ * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
+ * @channels: occupied channel number
+ * @desc: input and output dma descriptors
+ * @dma_chan: inputer and output DMA channels
+ * @dma_data: private dma data
+ * @pos: hardware pointer position
+ * @private: pair private area
+ */
+struct fsl_asrc_pair {
+	struct fsl_asrc *asrc_priv;
+	struct asrc_config *config;
+	unsigned int error;
+
+	enum asrc_pair_index index;
+	unsigned int channels;
+
+	struct dma_async_tx_descriptor *desc[2];
+	struct dma_chan *dma_chan[2];
+	struct imx_dma_data dma_data;
+	unsigned int pos;
+
+	void *private;
+};
+
+/**
+ * fsl_asrc_pair: ASRC private data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @paddr: physical address to the base address of registers
+ * @mem_clk: clock source to access register
+ * @ipg_clk: clock source to drive peripheral
+ * @asrck_clk: clock sources to driver ASRC internal logic
+ * @lock: spin lock for resource protection
+ * @pair: pair pointers
+ * @channel_bits: width of ASRCNCR register for each pair
+ * @channel_avail: non-occupied channel numbers
+ * @asrc_rate: default sample rate for ASoC Back-Ends
+ * @asrc_width: default sample width for ASoC Back-Ends
+ * @name: driver name
+ */
+struct fsl_asrc {
+	struct snd_dmaengine_dai_dma_data dma_params_rx;
+	struct snd_dmaengine_dai_dma_data dma_params_tx;
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	unsigned long paddr;
+	struct clk *mem_clk;
+	struct clk *ipg_clk;
+	struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
+	spinlock_t lock;
+
+	struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
+	unsigned int channel_bits;
+	unsigned int channel_avail;
+
+	int asrc_rate;
+	int asrc_width;
+
+	char name[32];
+};
+
+extern struct snd_soc_platform_driver fsl_asrc_platform;
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
+#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
new file mode 100644
index 0000000..5b1e73e
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -0,0 +1,386 @@ 
+/*
+ * Freescale ASRC ALSA SoC Platform (DMA) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_data/dma-imx.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define FSL_ASRC_DMABUF_SIZE	(256 * 1024)
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_RESUME,
+	.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
+	.period_bytes_min = 128,
+	.period_bytes_max = 65535, /* Limited by SDMA engine */
+	.periods_min = 2,
+	.periods_max = 255,
+	.fifo_size = 0,
+};
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	if (!imx_dma_is_general_purpose(chan))
+		return false;
+
+	chan->private = param;
+
+	return true;
+}
+
+static void fsl_asrc_dma_complete(void *arg)
+{
+	struct snd_pcm_substream *substream = arg;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+
+	pair->pos += snd_pcm_lib_period_bytes(substream);
+	if (pair->pos >= snd_pcm_lib_buffer_bytes(substream))
+		pair->pos = 0;
+
+	snd_pcm_period_elapsed(substream);
+}
+
+static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+	u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+	struct device *dev = rtd->platform->dev;
+	unsigned long flags = DMA_CTRL_ACK;
+
+	/* Prepare and submit Front-End DMA channel */
+	if (!substream->runtime->no_period_wakeup)
+		flags |= DMA_PREP_INTERRUPT;
+
+	pair->pos = 0;
+	pair->desc[!dir] = dmaengine_prep_dma_cyclic(
+			pair->dma_chan[!dir], runtime->dma_addr,
+			snd_pcm_lib_buffer_bytes(substream),
+			snd_pcm_lib_period_bytes(substream),
+			dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+	if (!pair->desc[!dir]) {
+		dev_err(dev, "failed to prepare slave DMA for Front-End\n");
+		return -ENOMEM;
+	}
+
+	pair->desc[!dir]->callback = fsl_asrc_dma_complete;
+	pair->desc[!dir]->callback_param = substream;
+
+	dmaengine_submit(pair->desc[!dir]);
+
+	/* Prepare and submit Back-End DMA channel */
+	pair->desc[dir] = dmaengine_prep_dma_cyclic(
+			pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
+	if (!pair->desc[dir]) {
+		dev_err(dev, "failed to prepare slave DMA for Back-End\n");
+		return -ENOMEM;
+	}
+
+	dmaengine_submit(pair->desc[dir]);
+
+	return 0;
+}
+
+static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = fsl_asrc_dma_prepare_and_submit(substream);
+		if (ret)
+			return ret;
+		dma_async_issue_pending(pair->dma_chan[IN]);
+		dma_async_issue_pending(pair->dma_chan[OUT]);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dmaengine_terminate_all(pair->dma_chan[OUT]);
+		dmaengine_terminate_all(pair->dma_chan[IN]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params)
+{
+	enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
+	struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+	struct dma_slave_config config_fe, config_be;
+	enum asrc_pair_index index = pair->index;
+	struct device *dev = rtd->platform->dev;
+	int stream = substream->stream;
+	struct imx_dma_data *tmp_data;
+	struct snd_soc_dpcm *dpcm;
+	struct dma_chan *tmp_chan;
+	struct device *dev_be;
+	u8 dir = tx ? OUT : IN;
+	dma_cap_mask_t mask;
+	int ret;
+
+	/* Fetch the Back-End dma_data from DPCM */
+	list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *substream_be;
+		struct snd_soc_dai *dai = be->cpu_dai;
+
+		if (dpcm->fe != rtd)
+			continue;
+
+		substream_be = snd_soc_dpcm_get_substream(be, stream);
+		dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
+		dev_be = dai->dev;
+		break;
+	}
+
+	if (!dma_params_be) {
+		dev_err(dev, "failed to get the substream of Back-End\n");
+		return -EINVAL;
+	}
+
+	/* Override dma_data of the Front-End and config its dmaengine */
+	dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index);
+	dma_params_fe->maxburst = dma_params_be->maxburst;
+
+	pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
+	if (!pair->dma_chan[!dir]) {
+		dev_err(dev, "failed to request DMA channel\n");
+		return -EINVAL;
+	}
+
+	memset(&config_fe, 0, sizeof(config_fe));
+	ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe);
+	if (ret) {
+		dev_err(dev, "failed to prepare DMA config for Front-End\n");
+		return ret;
+	}
+
+	ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe);
+	if (ret) {
+		dev_err(dev, "failed to config DMA channel for Front-End\n");
+		return ret;
+	}
+
+	/* Request and config DMA channel for Back-End */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	/* Get DMA request of Back-End */
+	tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
+	tmp_data = tmp_chan->private;
+	pair->dma_data.dma_request = tmp_data->dma_request;
+	dma_release_channel(tmp_chan);
+
+	/* Get DMA request of Front-End */
+	tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+	tmp_data = tmp_chan->private;
+	pair->dma_data.dma_request2 = tmp_data->dma_request;
+	pair->dma_data.peripheral_type = tmp_data->peripheral_type;
+	pair->dma_data.priority = tmp_data->priority;
+	dma_release_channel(tmp_chan);
+
+	pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
+	if (!pair->dma_chan[dir]) {
+		dev_err(dev, "failed to request DMA channel for Back-End\n");
+		return -EINVAL;
+	}
+
+	if (asrc_priv->asrc_width == 16)
+		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	else
+		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	config_be.direction = DMA_DEV_TO_DEV;
+	config_be.src_addr_width = buswidth;
+	config_be.src_maxburst = dma_params_be->maxburst;
+	config_be.dst_addr_width = buswidth;
+	config_be.dst_maxburst = dma_params_be->maxburst;
+
+	if (tx) {
+		config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index);
+		config_be.dst_addr = dma_params_be->addr;
+	} else {
+		config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index);
+		config_be.src_addr = dma_params_be->addr;
+	}
+
+	ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
+	if (ret) {
+		dev_err(dev, "failed to config DMA channel for Back-End\n");
+		return ret;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	if (pair->dma_chan[IN])
+		dma_release_channel(pair->dma_chan[IN]);
+
+	if (pair->dma_chan[OUT])
+		dma_release_channel(pair->dma_chan[OUT]);
+
+	pair->dma_chan[IN] = NULL;
+	pair->dma_chan[OUT] = NULL;
+
+	return 0;
+}
+
+static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct device *dev = rtd->platform->dev;
+	struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+	struct fsl_asrc_pair *pair;
+
+	pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
+	if (!pair) {
+		dev_err(dev, "failed to allocate pair\n");
+		return -ENOMEM;
+	}
+
+	pair->asrc_priv = asrc_priv;
+
+	runtime->private_data = pair;
+
+	snd_pcm_hw_constraint_integer(substream->runtime,
+				      SNDRV_PCM_HW_PARAM_PERIODS);
+	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+
+	return 0;
+}
+
+static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+	struct fsl_asrc *asrc_priv = pair->asrc_priv;
+
+	if (pair && asrc_priv->pair[pair->index] == pair)
+		asrc_priv->pair[pair->index] = NULL;
+
+	kfree(pair);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_asrc_pair *pair = runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, pair->pos);
+}
+
+static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= fsl_asrc_dma_hw_params,
+	.hw_free	= fsl_asrc_dma_hw_free,
+	.trigger	= fsl_asrc_dma_trigger,
+	.open		= fsl_asrc_dma_startup,
+	.close		= fsl_asrc_dma_shutdown,
+	.pointer	= fsl_asrc_dma_pcm_pointer,
+};
+
+static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm_substream *substream;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret, i;
+
+	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(card->dev, "failed to set DMA mask\n");
+		return ret;
+	}
+
+	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+		substream = pcm->streams[i].substream;
+		if (!substream)
+			continue;
+
+		ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+				FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer);
+		if (ret) {
+			dev_err(card->dev, "failed to allocate DMA buffer\n");
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	if (--i == 0 && pcm->streams[i].substream)
+		snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
+
+	return ret;
+}
+
+static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	int i;
+
+	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+		substream = pcm->streams[i].substream;
+		if (!substream)
+			continue;
+
+		snd_dma_free_pages(&substream->dma_buffer);
+		substream->dma_buffer.area = NULL;
+		substream->dma_buffer.addr = 0;
+	}
+}
+
+struct snd_soc_platform_driver fsl_asrc_platform = {
+	.ops		= &fsl_asrc_dma_pcm_ops,
+	.pcm_new	= fsl_asrc_dma_pcm_new,
+	.pcm_free	= fsl_asrc_dma_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsl_asrc_platform);