blob: 7fc8da3888b2c9a27aff18f9cdb0a69d8cf61806 [file] [log] [blame]
/*
* 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");