/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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");