aboutsummaryrefslogblamecommitdiffstats
path: root/sound/soc/fsl/fsl_asrc.c
blob: 7fc8da3888b2c9a27aff18f9cdb0a69d8cf61806 (plain) (tree)
1
2
  
                                                                             






































                                                                     

































































































                                                                                                                 









                                                                           

                                                                                          

                                             
                                  
                                                                       
                
























                                                                                         



                                                                            
 
                                      




                                                      
                                                                                         
                                                          
                                                                                                            





                                      

                                                                
                                                                                                                    
                                                                 
                                                                            

                             

                                                                                        


                                                                         





                                                                                    
                                               
                                                              
                                               
                                                              
                                                                   









                                                                                                          
 
                                                                          



                                                                        


                 


                                                          










                                                                           
                                                                                          


                                                       

                               





                                                                        
                                                                                     




                                                                     

                                                   
            
                                                   




                                        
                                               










                                      
                                                









                                                                  
                                                           
                                            
                                               




























                                                                                    



























                                                                         


                                                                                          
 
                   
                                              

                                            

         



                                                                     
         



                 


                                                                               


                                                                                          













                                                                                  



                                                                             


                                                                                          
                




                                             


                                                                           

                                                                   


                                                                           



                                          

                                                                  







                               
















                                                                           






                                                              

                                              







                                                                     
                



                                                          




                                                                   

























                                                                       















































































                                                                                         




























                                                                                     

                                                
 
                                                               
                       
                                                              
              
                                                                     

                               
                                                                 
                        
                                                                
 

                                                                     





















                                                                               



                                                                     






















                                                                                               

                                                                   














































                                                                   
/*
 * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_i2c.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mxc_asrc.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/dmaengine_pcm.h>

#include "fsl_asrc.h"
#include "imx-pcm.h"

static bool filter(struct dma_chan *chan, void *param)
{
	if (!imx_dma_is_general_purpose(chan))
		return false;

	chan->private = param;

	return true;
}

static const char *p2p_width_sel[] = {"16 bit", "24 bit"};
static const unsigned int p2p_width_val[] = { 0, 1 };
static const struct soc_enum p2p_width_enum =
	SOC_VALUE_ENUM_SINGLE(-1, 0, 1,
			      ARRAY_SIZE(p2p_width_sel),
			      p2p_width_sel,
			      p2p_width_val);

static int fsl_asrc_p2p_width_get(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_value *uvalue)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);

	uvalue->value.integer.value[0] = asrc_p2p->p2p_width == 16 ? 0 : 1;
	return 0;
}

static int fsl_asrc_p2p_width_put(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_value *uvalue)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);

	asrc_p2p->p2p_width = uvalue->value.integer.value[0] ? 24 : 16;
	return 0;
}

/* p2p_support_rate is a intersection of input and output */
static const int p2p_support_rate[] = { 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 };
static const char *p2p_rate_sel[] = { "32000", "44100", "48000", "64000", "88200", "96000", "176400", "192000" };
static const unsigned int p2p_rate_val[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
static const struct soc_enum p2p_rate_enum =
	SOC_VALUE_ENUM_SINGLE(-1, 0, 7,
			      ARRAY_SIZE(p2p_rate_sel),
			      p2p_rate_sel,
			      p2p_rate_val);

static int fsl_asrc_p2p_rate_get(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_value *uvalue)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);
	int i;

	for (i = 0; i < ARRAY_SIZE(p2p_support_rate); i++) {
		if (p2p_support_rate[i] == asrc_p2p->p2p_rate)
			break;
	}

	if (i == ARRAY_SIZE(p2p_support_rate))
		return 0;

	uvalue->value.integer.value[0] = i;

	return 0;
}

static int fsl_asrc_p2p_rate_put(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_value *uvalue)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);

	if (uvalue->value.integer.value[0] < 0 ||
		uvalue->value.integer.value[0] >= ARRAY_SIZE(p2p_support_rate))
		return 0;

	asrc_p2p->p2p_rate = p2p_support_rate[uvalue->value.integer.value[0]];

	return 0;
}

static struct snd_kcontrol_new fsl_asrc_p2p_ctrls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "ASRC P2P width",
		.access = SNDRV_CTL_ELEM_ACCESS_READ |
			SNDRV_CTL_ELEM_ACCESS_WRITE |
			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
		.info = snd_soc_info_enum_double,
		.get = fsl_asrc_p2p_width_get,
		.put = fsl_asrc_p2p_width_put,
		.private_value = (unsigned long)&p2p_width_enum,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "ASRC P2P Rate",
		.access = SNDRV_CTL_ELEM_ACCESS_READ |
			SNDRV_CTL_ELEM_ACCESS_WRITE |
			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
		.info = snd_soc_info_enum_double,
		.get = fsl_asrc_p2p_rate_get,
		.put = fsl_asrc_p2p_rate_put,
		.private_value = (unsigned long)&p2p_rate_enum,
	},
};

static int asrc_p2p_request_channel(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai     = rtd->cpu_dai;
	struct fsl_asrc_p2p *asrc_p2p   = snd_soc_dai_get_drvdata(cpu_dai);
	enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
	struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
	struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
	struct imx_dma_data *fe_filter_data = NULL;
	struct imx_dma_data *be_filter_data = NULL;
	struct fsl_asrc_p2p_params *p2p_params = &asrc_p2p->p2p_params[substream->stream];
	enum asrc_pair_index asrc_index = p2p_params->asrc_index;
	struct dma_slave_config slave_config;
	dma_cap_mask_t mask;
	struct snd_soc_dpcm *dpcm;
	bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
	int ret;

	/* find the be for this fe stream */
	list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) {
		if (dpcm->fe == rtd) {
			struct snd_soc_pcm_runtime *be = dpcm->be;
			struct snd_soc_dai *dai = be->cpu_dai;
			struct snd_pcm_substream *be_substream;
			be_substream = snd_soc_dpcm_get_substream(be, substream->stream);
			dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream);
			break;
		}
	}

	if (!dma_params_be) {
		dev_err(rtd->card->dev, "can not get be substream\n");
		return -EINVAL;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		dma_params_fe = &asrc_p2p->dma_params_tx;
	else
		dma_params_fe = &asrc_p2p->dma_params_rx;

	fe_filter_data = dma_params_fe->filter_data;
	be_filter_data = dma_params_be->filter_data;
	if (!fe_filter_data || !be_filter_data) {
		dev_err(rtd->card->dev, "can't get be or fe filter data\n");
		return -EINVAL;
	}

	if (asrc_p2p->p2p_width == 16)
		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
	else
		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;

	/* reconfig memory to FIFO dma request */
	dma_params_fe->addr = asrc_p2p->asrc_ops.asrc_p2p_per_addr(asrc_index, playback);
	dma_params_fe->maxburst = dma_params_be->maxburst;
	fe_filter_data->dma_request0 = playback ? asrc_p2p->dmarx[asrc_index] : asrc_p2p->dmatx[asrc_index];

	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);
	dma_cap_set(DMA_CYCLIC, mask);

	/* config p2p dma channel */
	p2p_params->dma_data.peripheral_type = IMX_DMATYPE_ASRC;
	p2p_params->dma_data.priority        = DMA_PRIO_HIGH;
	p2p_params->dma_data.dma_request1    = playback ? asrc_p2p->dmatx[asrc_index] : asrc_p2p->dmarx[asrc_index];
	/* need to get target device's dma dma_addr, burstsize */
	p2p_params->dma_data.dma_request0    = be_filter_data->dma_request0;

	/* Request channel */
	p2p_params->dma_chan = dma_request_channel(mask, filter, &p2p_params->dma_data);
	if (!p2p_params->dma_chan) {
		dev_err(rtd->card->dev, "can not request dma channel\n");
		goto error;
	}

	/*
	 * Buswidth is not used in the sdma for p2p. Here we set the maxburst fix to
	 * twice of dma_params's burstsize.
	 */
	slave_config.direction      = DMA_DEV_TO_DEV;
	slave_config.src_addr_width = buswidth;
	slave_config.src_maxburst   = dma_params_be->maxburst;
	slave_config.dst_addr_width = buswidth;
	slave_config.dst_maxburst   = dma_params_be->maxburst;
	slave_config.dma_request0   = be_filter_data->dma_request0;

	if (playback) {
		slave_config.src_addr       = asrc_p2p->asrc_ops.asrc_p2p_per_addr(asrc_index, !playback);
		slave_config.dst_addr       = dma_params_be->addr;
		slave_config.dma_request1   = asrc_p2p->dmatx[asrc_index];
	} else {
		slave_config.dst_addr       = asrc_p2p->asrc_ops.asrc_p2p_per_addr(asrc_index, !playback);
		slave_config.src_addr       = dma_params_be->addr;
		slave_config.dma_request1   = asrc_p2p->dmarx[asrc_index];
	}

	ret = dmaengine_slave_config(p2p_params->dma_chan, &slave_config);
	if (ret) {
		dev_err(rtd->card->dev, "can not config dma channel\n");
		goto error;
	}

	return 0;
error:
	if (p2p_params->dma_chan) {
		dma_release_channel(p2p_params->dma_chan);
		p2p_params->dma_chan = NULL;
	}

	return -EINVAL;
}

static int config_asrc(struct snd_pcm_substream *substream,
					 struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai     = rtd->cpu_dai;
	struct fsl_asrc_p2p *asrc_p2p   = snd_soc_dai_get_drvdata(cpu_dai);
	struct fsl_asrc_p2p_params *p2p_params = &asrc_p2p->p2p_params[substream->stream];
	unsigned int rate    = params_rate(params);
	unsigned int channel = params_channels(params);
	struct asrc_config config = {0};
	int p2p_word_width = 0;
	int word_width = 0;
	int ret = 0;
	if ((channel != 2) && (channel != 4) && (channel != 6)) {
		dev_err(cpu_dai->dev, "param channel is not correct\n");
		return -EINVAL;
	}

	ret = asrc_p2p->asrc_ops.asrc_p2p_req_pair(channel, &p2p_params->asrc_index);
	if (ret < 0) {
		dev_err(cpu_dai->dev, "Fail to request asrc pair\n");
		return -EINVAL;
	}

	if (asrc_p2p->p2p_width == 16)
		p2p_word_width = ASRC_WIDTH_16_BIT;
	else
		p2p_word_width = ASRC_WIDTH_24_BIT;

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_U16:
	case SNDRV_PCM_FORMAT_S16_LE:
	case SNDRV_PCM_FORMAT_S16_BE:
		word_width = ASRC_WIDTH_16_BIT;
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
	case SNDRV_PCM_FORMAT_S20_3BE:
	case SNDRV_PCM_FORMAT_S24_3LE:
	case SNDRV_PCM_FORMAT_S24_3BE:
	case SNDRV_PCM_FORMAT_S24_BE:
	case SNDRV_PCM_FORMAT_S24_LE:
	case SNDRV_PCM_FORMAT_U24_BE:
	case SNDRV_PCM_FORMAT_U24_LE:
	case SNDRV_PCM_FORMAT_U24_3BE:
	case SNDRV_PCM_FORMAT_U24_3LE:
		word_width =  ASRC_WIDTH_24_BIT;
		break;
	case SNDRV_PCM_FORMAT_S8:
	case SNDRV_PCM_FORMAT_U8:
	case SNDRV_PCM_FORMAT_S32:
	case SNDRV_PCM_FORMAT_U32:
	default:
		dev_err(cpu_dai->dev, "Format is not support!\n");
		return -EINVAL;
	}

	config.pair               = p2p_params->asrc_index;
	config.channel_num        = channel;
	config.inclk              = INCLK_NONE;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		config.input_word_width   = word_width;
		config.output_word_width  = p2p_word_width;
		config.input_sample_rate  = rate;
		config.output_sample_rate = asrc_p2p->p2p_rate;
		switch (asrc_p2p->per_dev) {
		case SSI1:
			config.outclk    = OUTCLK_SSI1_TX;
			break;
		case SSI2:
			config.outclk    = OUTCLK_SSI2_TX;
			break;
		case SSI3:
			config.outclk    = OUTCLK_SSI3_TX;
			break;
		case ESAI:
			config.outclk    = OUTCLK_ESAI_TX;
			break;
		default:
			dev_err(cpu_dai->dev, "peripheral device is not correct\n");
			return -EINVAL;
		}
	} else {
		config.input_word_width   = p2p_word_width;
		config.output_word_width  = word_width;
		config.input_sample_rate  = asrc_p2p->p2p_rate;
		config.output_sample_rate = rate;
		config.outclk             = OUTCLK_ASRCK1_CLK;
	}

	ret = asrc_p2p->asrc_ops.asrc_p2p_config_pair(&config);
	if (ret < 0) {
		dev_err(cpu_dai->dev, "Fail to config asrc\n");
		return ret;
	}

	return 0;
}

static int fsl_asrc_p2p_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *cpu_dai)
{
	int ret = 0;

	ret = config_asrc(substream, params);
	if (ret < 0)
		return ret;

	return asrc_p2p_request_channel(substream);
}

static int fsl_asrc_p2p_hw_free(struct snd_pcm_substream *substream,
			      struct snd_soc_dai *cpu_dai)
{
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);
	struct fsl_asrc_p2p_params *p2p_params = &asrc_p2p->p2p_params[substream->stream];
	struct dma_chan *chan = p2p_params->dma_chan;
	enum asrc_pair_index asrc_index = p2p_params->asrc_index;

	if (chan) {
		/* Release p2p dma resource */
		dma_release_channel(chan);
		p2p_params->dma_chan = NULL;
	}

	if (asrc_index != -1) {
		asrc_p2p->asrc_ops.asrc_p2p_release_pair(asrc_index);
		asrc_p2p->asrc_ops.asrc_p2p_finish_conv(asrc_index);
		p2p_params->asrc_index = -1;
	}

	return 0;
}

static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
					struct fsl_asrc_p2p *asrc_p2p)
{
	struct fsl_asrc_p2p_params *p2p_params = &asrc_p2p->p2p_params[substream->stream];
	struct dma_chan *chan = p2p_params->dma_chan;
	struct dma_async_tx_descriptor *desc = p2p_params->desc;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct device *dev = rtd->platform->dev;

	desc = dmaengine_prep_dma_cyclic(chan, 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
	if (!desc) {
		dev_err(dev, "failed to prepare slave dma\n");
		return -EINVAL;
	}

	dmaengine_submit(desc);

	return 0;
}

static int fsl_asrc_p2p_trigger(struct snd_pcm_substream *substream, int cmd,
			    struct snd_soc_dai *cpu_dai)
{
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);
	struct fsl_asrc_p2p_params *p2p_params = &asrc_p2p->p2p_params[substream->stream];
	struct dma_chan *chan = p2p_params->dma_chan;
	enum asrc_pair_index asrc_index = p2p_params->asrc_index;
	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, asrc_p2p);
		if (ret)
			return ret;
		dma_async_issue_pending(chan);
		asrc_p2p->asrc_ops.asrc_p2p_start_conv(asrc_index);

		/* Output enough data to content the DMA burstsize of BE */
		mdelay(1);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_STOP:
		dmaengine_terminate_all(chan);
		asrc_p2p->asrc_ops.asrc_p2p_stop_conv(asrc_index);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int fsl_asrc_p2p_startup(struct snd_pcm_substream *substream,
			    struct snd_soc_dai *cpu_dai)
{
	struct fsl_asrc_p2p *asrc_p2p   = snd_soc_dai_get_drvdata(cpu_dai);

	asrc_p2p->substream[substream->stream] = substream;
	return 0;
}

static void fsl_asrc_p2p_shutdown(struct snd_pcm_substream *substream,
			    struct snd_soc_dai *cpu_dai)
{
	struct fsl_asrc_p2p *asrc_p2p   = snd_soc_dai_get_drvdata(cpu_dai);

	asrc_p2p->substream[substream->stream] = NULL;
}

#define IMX_ASRC_RATES  SNDRV_PCM_RATE_8000_192000

#define IMX_ASRC_FORMATS \
	(SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE | \
	SNDRV_PCM_FORMAT_S20_3LE)

static struct snd_soc_dai_ops fsl_asrc_p2p_dai_ops = {
	.startup      = fsl_asrc_p2p_startup,
	.shutdown     = fsl_asrc_p2p_shutdown,
	.trigger      = fsl_asrc_p2p_trigger,
	.hw_params    = fsl_asrc_p2p_hw_params,
	.hw_free      = fsl_asrc_p2p_hw_free,
};

static int fsl_asrc_p2p_dai_probe(struct snd_soc_dai *dai)
{
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(dai);
	int ret;

	dai->playback_dma_data = &asrc_p2p->dma_params_tx;
	dai->capture_dma_data = &asrc_p2p->dma_params_rx;

	ret = snd_soc_add_dai_controls(dai, fsl_asrc_p2p_ctrls,
			ARRAY_SIZE(fsl_asrc_p2p_ctrls));
	if (ret)
		dev_warn(dai->dev, "failed to add dai controls\n");

	return 0;
}

static struct snd_soc_dai_driver fsl_asrc_p2p_dai = {
	.probe = fsl_asrc_p2p_dai_probe,
	.playback = {
		.stream_name = "asrc-Playback",
		.channels_min = 1,
		.channels_max = 10,
		.rates = IMX_ASRC_RATES,
		.formats = IMX_ASRC_FORMATS,
	},
	.capture  = {
		.stream_name = "asrc-Capture",
		.channels_min = 1,
		.channels_max = 4,
		.rates = IMX_ASRC_RATES,
		.formats = IMX_ASRC_FORMATS,
	},
	.ops      = &fsl_asrc_p2p_dai_ops,
};

static const struct snd_soc_component_driver fsl_asrc_p2p_component = {
	.name		= "fsl-asrc-p2p",
};

static bool fsl_asrc_p2p_check_xrun(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
	struct snd_pcm_substream *be_substream;
	struct snd_soc_dpcm *dpcm;
	int ret = 0;

	/* find the be for this fe stream */
	list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) {
		struct snd_soc_pcm_runtime *be = dpcm->be;
		struct snd_soc_dai *dai = be->cpu_dai;

		if (dpcm->fe != rtd)
			continue;

		be_substream = snd_soc_dpcm_get_substream(be, substream->stream);
		dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream);
		if (dma_params_be->check_xrun && dma_params_be->check_xrun(be_substream))
			ret = 1;
	}

	return ret;
}

static int stop_lock_stream(struct snd_pcm_substream *substream)
{
	if (substream) {
		snd_pcm_stream_lock_irq(substream);
		if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING)
			substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
	}
	return 0;
}

static int start_unlock_stream(struct snd_pcm_substream *substream)
{
	if (substream) {
		if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING)
			substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
		snd_pcm_stream_unlock_irq(substream);
	}
	return 0;
}

static void fsl_asrc_p2p_reset(struct snd_pcm_substream *substream, bool stop)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct fsl_asrc_p2p *asrc_p2p = snd_soc_dai_get_drvdata(cpu_dai);
	struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
	struct snd_soc_dpcm *dpcm;
	struct snd_pcm_substream *be_substream;

	if (stop) {
		stop_lock_stream(asrc_p2p->substream[0]);
		stop_lock_stream(asrc_p2p->substream[1]);
	}

	/* find the be for this fe stream */
	list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) {
		struct snd_soc_pcm_runtime *be = dpcm->be;
		struct snd_soc_dai *dai = be->cpu_dai;

		if (dpcm->fe != rtd)
			continue;

		be_substream = snd_soc_dpcm_get_substream(be, substream->stream);
		dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream);
		dma_params_be->device_reset(be_substream, 0);
		break;
	}

	if (stop) {
		start_unlock_stream(asrc_p2p->substream[1]);
		start_unlock_stream(asrc_p2p->substream[0]);
	}
}


/*
 * This function will register the snd_soc_pcm_link drivers.
 */
static int fsl_asrc_p2p_probe(struct platform_device *pdev)
{
	struct fsl_asrc_p2p *asrc_p2p;
	struct device_node *np = pdev->dev.of_node;
	const char *p;
	const uint32_t *iprop_rate, *iprop_width;
	int ret = 0;

	if (!of_device_is_available(np)) {
		dev_err(&pdev->dev, "There is no device node\n");
		return -ENODEV;
	}

	asrc_p2p = devm_kzalloc(&pdev->dev, sizeof(struct fsl_asrc_p2p), GFP_KERNEL);
	if (!asrc_p2p) {
		dev_err(&pdev->dev, "can not alloc memory\n");
		return -ENOMEM;
	}
	asrc_p2p->asrc_ops.asrc_p2p_start_conv      = asrc_start_conv;
	asrc_p2p->asrc_ops.asrc_p2p_stop_conv       = asrc_stop_conv;
	asrc_p2p->asrc_ops.asrc_p2p_per_addr        = asrc_get_per_addr;
	asrc_p2p->asrc_ops.asrc_p2p_req_pair        = asrc_req_pair;
	asrc_p2p->asrc_ops.asrc_p2p_config_pair     = asrc_config_pair;
	asrc_p2p->asrc_ops.asrc_p2p_release_pair    = asrc_release_pair;
	asrc_p2p->asrc_ops.asrc_p2p_finish_conv     = asrc_finish_conv;

	asrc_p2p->p2p_params[0].asrc_index = -1;
	asrc_p2p->p2p_params[1].asrc_index = -1;

	iprop_rate = of_get_property(np, "fsl,p2p-rate", NULL);
	if (iprop_rate)
		asrc_p2p->p2p_rate = be32_to_cpup(iprop_rate);
	else {
		dev_err(&pdev->dev, "There is no p2p-rate in dts\n");
		return -EINVAL;
	}
	iprop_width = of_get_property(np, "fsl,p2p-width", NULL);
	if (iprop_width)
		asrc_p2p->p2p_width = be32_to_cpup(iprop_width);

	if (asrc_p2p->p2p_width != 16 && asrc_p2p->p2p_width != 24) {
		dev_err(&pdev->dev, "p2p_width is not acceptable\n");
		return -EINVAL;
	}

	ret = of_property_read_u32_array(np,
			"fsl,asrc-dma-tx-events", asrc_p2p->dmatx, 3);
	if (ret) {
		dev_err(&pdev->dev, "Failed to get fsl,asrc-dma-tx-events.\n");
		return -EINVAL;
	}

	ret = of_property_read_u32_array(np,
			"fsl,asrc-dma-rx-events", asrc_p2p->dmarx, 3);
	if (ret) {
		dev_err(&pdev->dev, "Failed to get fsl,asrc-dma-rx-events.\n");
		return -EINVAL;
	}

	asrc_p2p->filter_data_tx.peripheral_type = IMX_DMATYPE_ASRC;
	asrc_p2p->filter_data_rx.peripheral_type = IMX_DMATYPE_ASRC;

	asrc_p2p->dma_params_tx.filter_data = &asrc_p2p->filter_data_tx;
	asrc_p2p->dma_params_rx.filter_data = &asrc_p2p->filter_data_rx;
	asrc_p2p->dma_params_tx.check_xrun = fsl_asrc_p2p_check_xrun;
	asrc_p2p->dma_params_rx.check_xrun = fsl_asrc_p2p_check_xrun;
	asrc_p2p->dma_params_tx.device_reset = fsl_asrc_p2p_reset;
	asrc_p2p->dma_params_rx.device_reset = fsl_asrc_p2p_reset;

	platform_set_drvdata(pdev, asrc_p2p);

	p = strrchr(np->full_name, '/') + 1;
	strcpy(asrc_p2p->name, p);
	fsl_asrc_p2p_dai.name = asrc_p2p->name;

	ret = snd_soc_register_component(&pdev->dev, &fsl_asrc_p2p_component,
					 &fsl_asrc_p2p_dai, 1);
	if (ret) {
		dev_err(&pdev->dev, "register DAI failed\n");
		goto failed_register;
	}

	asrc_p2p->soc_platform_pdev = platform_device_register_simple(
					"imx-pcm-asrc", -1, NULL, 0);
	if (IS_ERR(asrc_p2p->soc_platform_pdev)) {
		ret = PTR_ERR(asrc_p2p->soc_platform_pdev);
		goto failed_pdev_alloc;
	}

	ret = imx_pcm_dma_init(asrc_p2p->soc_platform_pdev, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
				     SND_DMAENGINE_PCM_FLAG_NO_DT |
				     SND_DMAENGINE_PCM_FLAG_COMPAT,
				     IMX_ASRC_DMABUF_SIZE);
	if (ret) {
		dev_err(&pdev->dev, "init pcm dma failed\n");
		goto failed_pcm_init;
	}

	return 0;

failed_pcm_init:
	platform_device_unregister(asrc_p2p->soc_platform_pdev);
failed_pdev_alloc:
	snd_soc_unregister_component(&pdev->dev);
failed_register:

	return ret;
}

static int fsl_asrc_p2p_remove(struct platform_device *pdev)
{
	struct fsl_asrc_p2p *asrc_p2p = platform_get_drvdata(pdev);

	imx_pcm_dma_exit(asrc_p2p->soc_platform_pdev);
	platform_device_unregister(asrc_p2p->soc_platform_pdev);
	snd_soc_unregister_component(&pdev->dev);

	return 0;
}

static const struct of_device_id fsl_asrc_p2p_dt_ids[] = {
	{ .compatible = "fsl,imx6q-asrc-p2p", },
	{ /* sentinel */ }
};

static struct platform_driver fsl_asrc_p2p_driver = {
	.probe = fsl_asrc_p2p_probe,
	.remove = fsl_asrc_p2p_remove,
	.driver = {
		.name = "fsl-asrc-p2p",
		.owner = THIS_MODULE,
		.of_match_table = fsl_asrc_p2p_dt_ids,
	},
};
module_platform_driver(fsl_asrc_p2p_driver);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX ASoC ASRC P2P driver");
MODULE_ALIAS("platform:fsl-asrc-p2p");
MODULE_LICENSE("GPL");