124a01710SShengjiu Wang // SPDX-License-Identifier: GPL-2.0
224a01710SShengjiu Wang //
324a01710SShengjiu Wang // Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
424a01710SShengjiu Wang // Copyright (C) 2019-2024 NXP
524a01710SShengjiu Wang //
624a01710SShengjiu Wang // Freescale ASRC Memory to Memory (M2M) driver
724a01710SShengjiu Wang
824a01710SShengjiu Wang #include <linux/dma/imx-dma.h>
924a01710SShengjiu Wang #include <linux/dma-buf.h>
1024a01710SShengjiu Wang #include <linux/dma-mapping.h>
1124a01710SShengjiu Wang #include <linux/pm_runtime.h>
1224a01710SShengjiu Wang #include <sound/asound.h>
1324a01710SShengjiu Wang #include <sound/dmaengine_pcm.h>
1424a01710SShengjiu Wang #include <sound/initval.h>
1524a01710SShengjiu Wang
1624a01710SShengjiu Wang #include "fsl_asrc_common.h"
1724a01710SShengjiu Wang
1824a01710SShengjiu Wang #define DIR_STR(dir) (dir) == IN ? "in" : "out"
1924a01710SShengjiu Wang
2024a01710SShengjiu Wang #define ASRC_xPUT_DMA_CALLBACK(dir) \
2124a01710SShengjiu Wang (((dir) == IN) ? asrc_input_dma_callback \
2224a01710SShengjiu Wang : asrc_output_dma_callback)
2324a01710SShengjiu Wang
2424a01710SShengjiu Wang /* Maximum output and capture buffer size */
2524a01710SShengjiu Wang #define ASRC_M2M_BUFFER_SIZE (512 * 1024)
2624a01710SShengjiu Wang
2724a01710SShengjiu Wang /* Maximum output and capture period size */
2824a01710SShengjiu Wang #define ASRC_M2M_PERIOD_SIZE (48 * 1024)
2924a01710SShengjiu Wang
3024a01710SShengjiu Wang /* dma complete callback */
asrc_input_dma_callback(void * data)3124a01710SShengjiu Wang static void asrc_input_dma_callback(void *data)
3224a01710SShengjiu Wang {
3324a01710SShengjiu Wang struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
3424a01710SShengjiu Wang
3524a01710SShengjiu Wang complete(&pair->complete[IN]);
3624a01710SShengjiu Wang }
3724a01710SShengjiu Wang
3824a01710SShengjiu Wang /* dma complete callback */
asrc_output_dma_callback(void * data)3924a01710SShengjiu Wang static void asrc_output_dma_callback(void *data)
4024a01710SShengjiu Wang {
4124a01710SShengjiu Wang struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
4224a01710SShengjiu Wang
4324a01710SShengjiu Wang complete(&pair->complete[OUT]);
4424a01710SShengjiu Wang }
4524a01710SShengjiu Wang
4624a01710SShengjiu Wang /**
4724a01710SShengjiu Wang *asrc_read_last_fifo: read all the remaining data from FIFO
4824a01710SShengjiu Wang *@pair: Structure pointer of fsl_asrc_pair
4924a01710SShengjiu Wang *@dma_vaddr: virtual address of capture buffer
5024a01710SShengjiu Wang *@length: payload length of capture buffer
5124a01710SShengjiu Wang */
asrc_read_last_fifo(struct fsl_asrc_pair * pair,void * dma_vaddr,u32 * length)5224a01710SShengjiu Wang static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
5324a01710SShengjiu Wang {
5424a01710SShengjiu Wang struct fsl_asrc *asrc = pair->asrc;
5524a01710SShengjiu Wang enum asrc_pair_index index = pair->index;
5624a01710SShengjiu Wang u32 i, reg, size, t_size = 0, width;
5724a01710SShengjiu Wang u32 *reg32 = NULL;
5824a01710SShengjiu Wang u16 *reg16 = NULL;
5924a01710SShengjiu Wang u8 *reg24 = NULL;
6024a01710SShengjiu Wang
6124a01710SShengjiu Wang width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
6224a01710SShengjiu Wang if (width == 32)
6324a01710SShengjiu Wang reg32 = dma_vaddr + *length;
6424a01710SShengjiu Wang else if (width == 16)
6524a01710SShengjiu Wang reg16 = dma_vaddr + *length;
6624a01710SShengjiu Wang else
6724a01710SShengjiu Wang reg24 = dma_vaddr + *length;
6824a01710SShengjiu Wang retry:
6924a01710SShengjiu Wang size = asrc->get_output_fifo_size(pair);
7024a01710SShengjiu Wang if (size + *length > ASRC_M2M_BUFFER_SIZE)
7124a01710SShengjiu Wang goto end;
7224a01710SShengjiu Wang
7324a01710SShengjiu Wang for (i = 0; i < size * pair->channels; i++) {
7424a01710SShengjiu Wang regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®);
7524a01710SShengjiu Wang if (reg32) {
7624a01710SShengjiu Wang *reg32++ = reg;
7724a01710SShengjiu Wang } else if (reg16) {
7824a01710SShengjiu Wang *reg16++ = (u16)reg;
7924a01710SShengjiu Wang } else {
8024a01710SShengjiu Wang *reg24++ = (u8)reg;
8124a01710SShengjiu Wang *reg24++ = (u8)(reg >> 8);
8224a01710SShengjiu Wang *reg24++ = (u8)(reg >> 16);
8324a01710SShengjiu Wang }
8424a01710SShengjiu Wang }
8524a01710SShengjiu Wang t_size += size;
8624a01710SShengjiu Wang
8724a01710SShengjiu Wang /* In case there is data left in FIFO */
8824a01710SShengjiu Wang if (size)
8924a01710SShengjiu Wang goto retry;
9024a01710SShengjiu Wang end:
9124a01710SShengjiu Wang /* Update payload length */
9224a01710SShengjiu Wang if (reg32)
9324a01710SShengjiu Wang *length += t_size * pair->channels * 4;
9424a01710SShengjiu Wang else if (reg16)
9524a01710SShengjiu Wang *length += t_size * pair->channels * 2;
9624a01710SShengjiu Wang else
9724a01710SShengjiu Wang *length += t_size * pair->channels * 3;
9824a01710SShengjiu Wang }
9924a01710SShengjiu Wang
10024a01710SShengjiu Wang /* config dma channel */
asrc_dmaconfig(struct fsl_asrc_pair * pair,struct dma_chan * chan,u32 dma_addr,dma_addr_t buf_addr,u32 buf_len,int dir,int width)10124a01710SShengjiu Wang static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
10224a01710SShengjiu Wang struct dma_chan *chan,
10324a01710SShengjiu Wang u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
10424a01710SShengjiu Wang int dir, int width)
10524a01710SShengjiu Wang {
10624a01710SShengjiu Wang struct fsl_asrc *asrc = pair->asrc;
10724a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
10824a01710SShengjiu Wang struct dma_slave_config slave_config;
10924a01710SShengjiu Wang enum dma_slave_buswidth buswidth;
11024a01710SShengjiu Wang unsigned int sg_len, max_period_size;
11124a01710SShengjiu Wang struct scatterlist *sg;
11224a01710SShengjiu Wang int ret, i;
11324a01710SShengjiu Wang
11424a01710SShengjiu Wang switch (width) {
11524a01710SShengjiu Wang case 8:
11624a01710SShengjiu Wang buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
11724a01710SShengjiu Wang break;
11824a01710SShengjiu Wang case 16:
11924a01710SShengjiu Wang buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
12024a01710SShengjiu Wang break;
12124a01710SShengjiu Wang case 24:
12224a01710SShengjiu Wang buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
12324a01710SShengjiu Wang break;
12424a01710SShengjiu Wang case 32:
12524a01710SShengjiu Wang buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
12624a01710SShengjiu Wang break;
12724a01710SShengjiu Wang default:
12824a01710SShengjiu Wang dev_err(dev, "invalid word width\n");
12924a01710SShengjiu Wang return -EINVAL;
13024a01710SShengjiu Wang }
13124a01710SShengjiu Wang
13224a01710SShengjiu Wang memset(&slave_config, 0, sizeof(slave_config));
13324a01710SShengjiu Wang if (dir == IN) {
13424a01710SShengjiu Wang slave_config.direction = DMA_MEM_TO_DEV;
13524a01710SShengjiu Wang slave_config.dst_addr = dma_addr;
13624a01710SShengjiu Wang slave_config.dst_addr_width = buswidth;
13724a01710SShengjiu Wang slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
13824a01710SShengjiu Wang } else {
13924a01710SShengjiu Wang slave_config.direction = DMA_DEV_TO_MEM;
14024a01710SShengjiu Wang slave_config.src_addr = dma_addr;
14124a01710SShengjiu Wang slave_config.src_addr_width = buswidth;
14224a01710SShengjiu Wang slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
14324a01710SShengjiu Wang }
14424a01710SShengjiu Wang
14524a01710SShengjiu Wang ret = dmaengine_slave_config(chan, &slave_config);
14624a01710SShengjiu Wang if (ret) {
14724a01710SShengjiu Wang dev_err(dev, "failed to config dmaengine for %s task: %d\n",
14824a01710SShengjiu Wang DIR_STR(dir), ret);
14924a01710SShengjiu Wang return -EINVAL;
15024a01710SShengjiu Wang }
15124a01710SShengjiu Wang
15224a01710SShengjiu Wang max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
15324a01710SShengjiu Wang /* scatter gather mode */
15424a01710SShengjiu Wang sg_len = buf_len / max_period_size;
15524a01710SShengjiu Wang if (buf_len % max_period_size)
15624a01710SShengjiu Wang sg_len += 1;
15724a01710SShengjiu Wang
15824a01710SShengjiu Wang sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
15924a01710SShengjiu Wang if (!sg)
16024a01710SShengjiu Wang return -ENOMEM;
16124a01710SShengjiu Wang
16224a01710SShengjiu Wang sg_init_table(sg, sg_len);
16324a01710SShengjiu Wang for (i = 0; i < (sg_len - 1); i++) {
16424a01710SShengjiu Wang sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
16524a01710SShengjiu Wang sg_dma_len(&sg[i]) = max_period_size;
16624a01710SShengjiu Wang }
16724a01710SShengjiu Wang sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
16824a01710SShengjiu Wang sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
16924a01710SShengjiu Wang
17024a01710SShengjiu Wang pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
17124a01710SShengjiu Wang slave_config.direction,
17224a01710SShengjiu Wang DMA_PREP_INTERRUPT);
17324a01710SShengjiu Wang kfree(sg);
17424a01710SShengjiu Wang if (!pair->desc[dir]) {
17524a01710SShengjiu Wang dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
17624a01710SShengjiu Wang return -EINVAL;
17724a01710SShengjiu Wang }
17824a01710SShengjiu Wang
17924a01710SShengjiu Wang pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
18024a01710SShengjiu Wang pair->desc[dir]->callback_param = pair;
18124a01710SShengjiu Wang
18224a01710SShengjiu Wang return 0;
18324a01710SShengjiu Wang }
18424a01710SShengjiu Wang
18524a01710SShengjiu Wang /* main function of converter */
asrc_m2m_device_run(struct fsl_asrc_pair * pair,struct snd_compr_task_runtime * task)186*abe01a78SShengjiu Wang static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
18724a01710SShengjiu Wang {
18824a01710SShengjiu Wang struct fsl_asrc *asrc = pair->asrc;
18924a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
19024a01710SShengjiu Wang enum asrc_pair_index index = pair->index;
19124a01710SShengjiu Wang struct snd_dma_buffer *src_buf, *dst_buf;
19224a01710SShengjiu Wang unsigned int in_buf_len;
19324a01710SShengjiu Wang unsigned int out_dma_len;
19424a01710SShengjiu Wang unsigned int width;
19524a01710SShengjiu Wang u32 fifo_addr;
196*abe01a78SShengjiu Wang int ret = 0;
19724a01710SShengjiu Wang
19824a01710SShengjiu Wang /* set ratio mod */
19924a01710SShengjiu Wang if (asrc->m2m_set_ratio_mod) {
20024a01710SShengjiu Wang if (pair->ratio_mod_flag) {
20124a01710SShengjiu Wang asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
20224a01710SShengjiu Wang pair->ratio_mod_flag = false;
20324a01710SShengjiu Wang }
20424a01710SShengjiu Wang }
20524a01710SShengjiu Wang
20624a01710SShengjiu Wang src_buf = &pair->dma_buffer[IN];
20724a01710SShengjiu Wang dst_buf = &pair->dma_buffer[OUT];
20824a01710SShengjiu Wang
20924a01710SShengjiu Wang width = snd_pcm_format_physical_width(pair->sample_format[IN]);
21024a01710SShengjiu Wang fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
21124a01710SShengjiu Wang
21224a01710SShengjiu Wang in_buf_len = task->input_size;
21324a01710SShengjiu Wang
21424a01710SShengjiu Wang if (in_buf_len < width * pair->channels / 8 ||
21524a01710SShengjiu Wang in_buf_len > ASRC_M2M_BUFFER_SIZE ||
21624a01710SShengjiu Wang in_buf_len % (width * pair->channels / 8)) {
21724a01710SShengjiu Wang dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
218*abe01a78SShengjiu Wang ret = -EINVAL;
21924a01710SShengjiu Wang goto end;
22024a01710SShengjiu Wang }
22124a01710SShengjiu Wang
22224a01710SShengjiu Wang /* dma config for output dma channel */
22324a01710SShengjiu Wang ret = asrc_dmaconfig(pair,
22424a01710SShengjiu Wang pair->dma_chan[IN],
22524a01710SShengjiu Wang fifo_addr,
22624a01710SShengjiu Wang src_buf->addr,
22724a01710SShengjiu Wang in_buf_len, IN, width);
22824a01710SShengjiu Wang if (ret) {
22924a01710SShengjiu Wang dev_err(dev, "out dma config error\n");
23024a01710SShengjiu Wang goto end;
23124a01710SShengjiu Wang }
23224a01710SShengjiu Wang
23324a01710SShengjiu Wang width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
23424a01710SShengjiu Wang fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
23524a01710SShengjiu Wang out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
23624a01710SShengjiu Wang if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
23724a01710SShengjiu Wang /* dma config for capture dma channel */
23824a01710SShengjiu Wang ret = asrc_dmaconfig(pair,
23924a01710SShengjiu Wang pair->dma_chan[OUT],
24024a01710SShengjiu Wang fifo_addr,
24124a01710SShengjiu Wang dst_buf->addr,
24224a01710SShengjiu Wang out_dma_len, OUT, width);
24324a01710SShengjiu Wang if (ret) {
24424a01710SShengjiu Wang dev_err(dev, "cap dma config error\n");
24524a01710SShengjiu Wang goto end;
24624a01710SShengjiu Wang }
24724a01710SShengjiu Wang } else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
24824a01710SShengjiu Wang dev_err(dev, "cap buffer size error\n");
249*abe01a78SShengjiu Wang ret = -EINVAL;
25024a01710SShengjiu Wang goto end;
25124a01710SShengjiu Wang }
25224a01710SShengjiu Wang
25324a01710SShengjiu Wang reinit_completion(&pair->complete[IN]);
25424a01710SShengjiu Wang reinit_completion(&pair->complete[OUT]);
25524a01710SShengjiu Wang
25624a01710SShengjiu Wang /* Submit DMA request */
25724a01710SShengjiu Wang dmaengine_submit(pair->desc[IN]);
25824a01710SShengjiu Wang dma_async_issue_pending(pair->desc[IN]->chan);
25924a01710SShengjiu Wang if (out_dma_len > 0) {
26024a01710SShengjiu Wang dmaengine_submit(pair->desc[OUT]);
26124a01710SShengjiu Wang dma_async_issue_pending(pair->desc[OUT]->chan);
26224a01710SShengjiu Wang }
26324a01710SShengjiu Wang
26424a01710SShengjiu Wang asrc->m2m_start(pair);
26524a01710SShengjiu Wang
26624a01710SShengjiu Wang if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
26724a01710SShengjiu Wang dev_err(dev, "out DMA task timeout\n");
268*abe01a78SShengjiu Wang ret = -ETIMEDOUT;
26924a01710SShengjiu Wang goto end;
27024a01710SShengjiu Wang }
27124a01710SShengjiu Wang
27224a01710SShengjiu Wang if (out_dma_len > 0) {
27324a01710SShengjiu Wang if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
27424a01710SShengjiu Wang dev_err(dev, "cap DMA task timeout\n");
275*abe01a78SShengjiu Wang ret = -ETIMEDOUT;
27624a01710SShengjiu Wang goto end;
27724a01710SShengjiu Wang }
27824a01710SShengjiu Wang }
27924a01710SShengjiu Wang
28024a01710SShengjiu Wang /* read the last words from FIFO */
28124a01710SShengjiu Wang asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
28224a01710SShengjiu Wang /* update payload length for capture */
28324a01710SShengjiu Wang task->output_size = out_dma_len;
28424a01710SShengjiu Wang end:
285*abe01a78SShengjiu Wang return ret;
28624a01710SShengjiu Wang }
28724a01710SShengjiu Wang
fsl_asrc_m2m_comp_open(struct snd_compr_stream * stream)28824a01710SShengjiu Wang static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
28924a01710SShengjiu Wang {
29024a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
29124a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
29224a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
29324a01710SShengjiu Wang struct fsl_asrc_pair *pair;
29424a01710SShengjiu Wang int size, ret;
29524a01710SShengjiu Wang
29624a01710SShengjiu Wang pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
29724a01710SShengjiu Wang if (!pair)
29824a01710SShengjiu Wang return -ENOMEM;
29924a01710SShengjiu Wang
30024a01710SShengjiu Wang pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
30124a01710SShengjiu Wang pair->asrc = asrc;
30224a01710SShengjiu Wang
30324a01710SShengjiu Wang init_completion(&pair->complete[IN]);
30424a01710SShengjiu Wang init_completion(&pair->complete[OUT]);
30524a01710SShengjiu Wang
30624a01710SShengjiu Wang runtime->private_data = pair;
30724a01710SShengjiu Wang
30824a01710SShengjiu Wang size = ASRC_M2M_BUFFER_SIZE;
30924a01710SShengjiu Wang ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
31024a01710SShengjiu Wang if (ret)
31124a01710SShengjiu Wang goto error_alloc_in_buf;
31224a01710SShengjiu Wang
31324a01710SShengjiu Wang ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
31424a01710SShengjiu Wang if (ret)
31524a01710SShengjiu Wang goto error_alloc_out_buf;
31624a01710SShengjiu Wang
31724a01710SShengjiu Wang ret = pm_runtime_get_sync(dev);
31824a01710SShengjiu Wang if (ret < 0) {
31924a01710SShengjiu Wang dev_err(dev, "Failed to power up asrc\n");
32024a01710SShengjiu Wang goto err_pm_runtime;
32124a01710SShengjiu Wang }
32224a01710SShengjiu Wang
32324a01710SShengjiu Wang return 0;
32424a01710SShengjiu Wang
32524a01710SShengjiu Wang err_pm_runtime:
32624a01710SShengjiu Wang snd_dma_free_pages(&pair->dma_buffer[OUT]);
32724a01710SShengjiu Wang error_alloc_out_buf:
32824a01710SShengjiu Wang snd_dma_free_pages(&pair->dma_buffer[IN]);
32924a01710SShengjiu Wang error_alloc_in_buf:
33024a01710SShengjiu Wang kfree(pair);
33124a01710SShengjiu Wang return ret;
33224a01710SShengjiu Wang }
33324a01710SShengjiu Wang
fsl_asrc_m2m_comp_release(struct snd_compr_stream * stream)33424a01710SShengjiu Wang static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
33524a01710SShengjiu Wang {
33624a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
33724a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
33824a01710SShengjiu Wang struct fsl_asrc_pair *pair = runtime->private_data;
33924a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
34024a01710SShengjiu Wang
34124a01710SShengjiu Wang pm_runtime_put_sync(dev);
34224a01710SShengjiu Wang
34324a01710SShengjiu Wang snd_dma_free_pages(&pair->dma_buffer[IN]);
34424a01710SShengjiu Wang snd_dma_free_pages(&pair->dma_buffer[OUT]);
34524a01710SShengjiu Wang
34624a01710SShengjiu Wang kfree(runtime->private_data);
34724a01710SShengjiu Wang
34824a01710SShengjiu Wang return 0;
34924a01710SShengjiu Wang }
35024a01710SShengjiu Wang
fsl_asrc_m2m_comp_set_params(struct snd_compr_stream * stream,struct snd_compr_params * params)35124a01710SShengjiu Wang static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
35224a01710SShengjiu Wang struct snd_compr_params *params)
35324a01710SShengjiu Wang {
35424a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
35524a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
35624a01710SShengjiu Wang struct fsl_asrc_pair *pair = runtime->private_data;
35724a01710SShengjiu Wang struct fsl_asrc_m2m_cap cap;
35824a01710SShengjiu Wang int ret, i;
35924a01710SShengjiu Wang
36024a01710SShengjiu Wang ret = asrc->m2m_get_cap(&cap);
36124a01710SShengjiu Wang if (ret)
36224a01710SShengjiu Wang return -EINVAL;
36324a01710SShengjiu Wang
364cf126261SShengjiu Wang if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in)
365cf126261SShengjiu Wang pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format;
36624a01710SShengjiu Wang else
36724a01710SShengjiu Wang return -EINVAL;
36824a01710SShengjiu Wang
369cf126261SShengjiu Wang if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out)
370cf126261SShengjiu Wang pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format;
37124a01710SShengjiu Wang else
37224a01710SShengjiu Wang return -EINVAL;
37324a01710SShengjiu Wang
37424a01710SShengjiu Wang /* check input rate is in scope */
37524a01710SShengjiu Wang for (i = 0; i < cap.rate_in_count; i++)
37624a01710SShengjiu Wang if (params->codec.sample_rate == cap.rate_in[i]) {
37724a01710SShengjiu Wang pair->rate[IN] = params->codec.sample_rate;
37824a01710SShengjiu Wang break;
37924a01710SShengjiu Wang }
38024a01710SShengjiu Wang if (i == cap.rate_in_count)
38124a01710SShengjiu Wang return -EINVAL;
38224a01710SShengjiu Wang
38324a01710SShengjiu Wang /* check output rate is in scope */
38424a01710SShengjiu Wang for (i = 0; i < cap.rate_out_count; i++)
38524a01710SShengjiu Wang if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
38624a01710SShengjiu Wang pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
38724a01710SShengjiu Wang break;
38824a01710SShengjiu Wang }
38924a01710SShengjiu Wang if (i == cap.rate_out_count)
39024a01710SShengjiu Wang return -EINVAL;
39124a01710SShengjiu Wang
39224a01710SShengjiu Wang if (params->codec.ch_in != params->codec.ch_out ||
39324a01710SShengjiu Wang params->codec.ch_in < cap.chan_min ||
39424a01710SShengjiu Wang params->codec.ch_in > cap.chan_max)
39524a01710SShengjiu Wang return -EINVAL;
39624a01710SShengjiu Wang
39724a01710SShengjiu Wang pair->channels = params->codec.ch_in;
39824a01710SShengjiu Wang pair->buf_len[IN] = params->buffer.fragment_size;
39924a01710SShengjiu Wang pair->buf_len[OUT] = params->buffer.fragment_size;
40024a01710SShengjiu Wang
40124a01710SShengjiu Wang return 0;
40224a01710SShengjiu Wang }
40324a01710SShengjiu Wang
fsl_asrc_m2m_mmap(struct dma_buf * dmabuf,struct vm_area_struct * vma)40424a01710SShengjiu Wang static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
40524a01710SShengjiu Wang {
40624a01710SShengjiu Wang struct snd_dma_buffer *dmab = dmabuf->priv;
40724a01710SShengjiu Wang
40824a01710SShengjiu Wang return snd_dma_buffer_mmap(dmab, vma);
40924a01710SShengjiu Wang }
41024a01710SShengjiu Wang
fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment * attachment,enum dma_data_direction direction)41124a01710SShengjiu Wang static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
41224a01710SShengjiu Wang enum dma_data_direction direction)
41324a01710SShengjiu Wang {
41424a01710SShengjiu Wang struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
41524a01710SShengjiu Wang struct sg_table *sgt;
41624a01710SShengjiu Wang
41724a01710SShengjiu Wang sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
41824a01710SShengjiu Wang if (!sgt)
41924a01710SShengjiu Wang return NULL;
42024a01710SShengjiu Wang
42124a01710SShengjiu Wang if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
42224a01710SShengjiu Wang goto free;
42324a01710SShengjiu Wang
42424a01710SShengjiu Wang if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
42524a01710SShengjiu Wang goto free;
42624a01710SShengjiu Wang
42724a01710SShengjiu Wang return sgt;
42824a01710SShengjiu Wang
42924a01710SShengjiu Wang free:
43024a01710SShengjiu Wang sg_free_table(sgt);
43124a01710SShengjiu Wang kfree(sgt);
43224a01710SShengjiu Wang return NULL;
43324a01710SShengjiu Wang }
43424a01710SShengjiu Wang
fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment * attachment,struct sg_table * table,enum dma_data_direction direction)43524a01710SShengjiu Wang static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
43624a01710SShengjiu Wang struct sg_table *table,
43724a01710SShengjiu Wang enum dma_data_direction direction)
43824a01710SShengjiu Wang {
43924a01710SShengjiu Wang dma_unmap_sgtable(attachment->dev, table, direction, 0);
44024a01710SShengjiu Wang }
44124a01710SShengjiu Wang
fsl_asrc_m2m_release(struct dma_buf * dmabuf)44224a01710SShengjiu Wang static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
44324a01710SShengjiu Wang {
44424a01710SShengjiu Wang /* buffer is released by fsl_asrc_m2m_comp_release() */
44524a01710SShengjiu Wang }
44624a01710SShengjiu Wang
44724a01710SShengjiu Wang static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
44824a01710SShengjiu Wang .mmap = fsl_asrc_m2m_mmap,
44924a01710SShengjiu Wang .map_dma_buf = fsl_asrc_m2m_map_dma_buf,
45024a01710SShengjiu Wang .unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
45124a01710SShengjiu Wang .release = fsl_asrc_m2m_release,
45224a01710SShengjiu Wang };
45324a01710SShengjiu Wang
fsl_asrc_m2m_comp_task_create(struct snd_compr_stream * stream,struct snd_compr_task_runtime * task)45424a01710SShengjiu Wang static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
45524a01710SShengjiu Wang struct snd_compr_task_runtime *task)
45624a01710SShengjiu Wang {
45724a01710SShengjiu Wang DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
45824a01710SShengjiu Wang DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
45924a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
46024a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
46124a01710SShengjiu Wang struct fsl_asrc_pair *pair = runtime->private_data;
46224a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
46324a01710SShengjiu Wang int ret;
46424a01710SShengjiu Wang
46524a01710SShengjiu Wang exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
46624a01710SShengjiu Wang exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
46724a01710SShengjiu Wang exp_info_in.flags = O_RDWR;
46824a01710SShengjiu Wang exp_info_in.priv = &pair->dma_buffer[IN];
46924a01710SShengjiu Wang task->input = dma_buf_export(&exp_info_in);
47024a01710SShengjiu Wang if (IS_ERR(task->input)) {
47124a01710SShengjiu Wang ret = PTR_ERR(task->input);
47224a01710SShengjiu Wang return ret;
47324a01710SShengjiu Wang }
47424a01710SShengjiu Wang
47524a01710SShengjiu Wang exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
47624a01710SShengjiu Wang exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
47724a01710SShengjiu Wang exp_info_out.flags = O_RDWR;
47824a01710SShengjiu Wang exp_info_out.priv = &pair->dma_buffer[OUT];
47924a01710SShengjiu Wang task->output = dma_buf_export(&exp_info_out);
48024a01710SShengjiu Wang if (IS_ERR(task->output)) {
48124a01710SShengjiu Wang ret = PTR_ERR(task->output);
48224a01710SShengjiu Wang return ret;
48324a01710SShengjiu Wang }
48424a01710SShengjiu Wang
48524a01710SShengjiu Wang /* Request asrc pair/context */
48624a01710SShengjiu Wang ret = asrc->request_pair(pair->channels, pair);
48724a01710SShengjiu Wang if (ret) {
48824a01710SShengjiu Wang dev_err(dev, "failed to request pair: %d\n", ret);
48924a01710SShengjiu Wang goto err_request_pair;
49024a01710SShengjiu Wang }
49124a01710SShengjiu Wang
49224a01710SShengjiu Wang ret = asrc->m2m_prepare(pair);
49324a01710SShengjiu Wang if (ret) {
49424a01710SShengjiu Wang dev_err(dev, "failed to start pair part one: %d\n", ret);
49524a01710SShengjiu Wang goto err_start_part_one;
49624a01710SShengjiu Wang }
49724a01710SShengjiu Wang
49824a01710SShengjiu Wang /* Request dma channels */
49924a01710SShengjiu Wang pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
50024a01710SShengjiu Wang if (!pair->dma_chan[IN]) {
50124a01710SShengjiu Wang dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
50224a01710SShengjiu Wang ret = -EBUSY;
50324a01710SShengjiu Wang goto err_dma_channel_in;
50424a01710SShengjiu Wang }
50524a01710SShengjiu Wang
50624a01710SShengjiu Wang pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
50724a01710SShengjiu Wang if (!pair->dma_chan[OUT]) {
50824a01710SShengjiu Wang dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
50924a01710SShengjiu Wang ret = -EBUSY;
51024a01710SShengjiu Wang goto err_dma_channel_out;
51124a01710SShengjiu Wang }
51224a01710SShengjiu Wang
51324a01710SShengjiu Wang return 0;
51424a01710SShengjiu Wang
51524a01710SShengjiu Wang err_dma_channel_out:
51624a01710SShengjiu Wang dma_release_channel(pair->dma_chan[IN]);
51724a01710SShengjiu Wang err_dma_channel_in:
51824a01710SShengjiu Wang if (asrc->m2m_unprepare)
51924a01710SShengjiu Wang asrc->m2m_unprepare(pair);
52024a01710SShengjiu Wang err_start_part_one:
52124a01710SShengjiu Wang asrc->release_pair(pair);
52224a01710SShengjiu Wang err_request_pair:
52324a01710SShengjiu Wang return ret;
52424a01710SShengjiu Wang }
52524a01710SShengjiu Wang
fsl_asrc_m2m_comp_task_start(struct snd_compr_stream * stream,struct snd_compr_task_runtime * task)52624a01710SShengjiu Wang static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
52724a01710SShengjiu Wang struct snd_compr_task_runtime *task)
52824a01710SShengjiu Wang {
52924a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
53024a01710SShengjiu Wang struct fsl_asrc_pair *pair = runtime->private_data;
53124a01710SShengjiu Wang
532*abe01a78SShengjiu Wang return asrc_m2m_device_run(pair, task);
53324a01710SShengjiu Wang }
53424a01710SShengjiu Wang
fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream * stream,struct snd_compr_task_runtime * task)53524a01710SShengjiu Wang static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
53624a01710SShengjiu Wang struct snd_compr_task_runtime *task)
53724a01710SShengjiu Wang {
53824a01710SShengjiu Wang return 0;
53924a01710SShengjiu Wang }
54024a01710SShengjiu Wang
fsl_asrc_m2m_comp_task_free(struct snd_compr_stream * stream,struct snd_compr_task_runtime * task)54124a01710SShengjiu Wang static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
54224a01710SShengjiu Wang struct snd_compr_task_runtime *task)
54324a01710SShengjiu Wang {
54424a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
54524a01710SShengjiu Wang struct snd_compr_runtime *runtime = stream->runtime;
54624a01710SShengjiu Wang struct fsl_asrc_pair *pair = runtime->private_data;
54724a01710SShengjiu Wang
54824a01710SShengjiu Wang /* Stop & release pair/context */
54924a01710SShengjiu Wang if (asrc->m2m_stop)
55024a01710SShengjiu Wang asrc->m2m_stop(pair);
55124a01710SShengjiu Wang
55224a01710SShengjiu Wang if (asrc->m2m_unprepare)
55324a01710SShengjiu Wang asrc->m2m_unprepare(pair);
55424a01710SShengjiu Wang asrc->release_pair(pair);
55524a01710SShengjiu Wang
55624a01710SShengjiu Wang /* Release dma channel */
55724a01710SShengjiu Wang if (pair->dma_chan[IN])
55824a01710SShengjiu Wang dma_release_channel(pair->dma_chan[IN]);
55924a01710SShengjiu Wang if (pair->dma_chan[OUT])
56024a01710SShengjiu Wang dma_release_channel(pair->dma_chan[OUT]);
56124a01710SShengjiu Wang
56224a01710SShengjiu Wang return 0;
56324a01710SShengjiu Wang }
56424a01710SShengjiu Wang
fsl_asrc_m2m_get_caps(struct snd_compr_stream * cstream,struct snd_compr_caps * caps)56524a01710SShengjiu Wang static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
56624a01710SShengjiu Wang struct snd_compr_caps *caps)
56724a01710SShengjiu Wang {
56824a01710SShengjiu Wang caps->num_codecs = 1;
56924a01710SShengjiu Wang caps->min_fragment_size = 4096;
57024a01710SShengjiu Wang caps->max_fragment_size = 4096;
57124a01710SShengjiu Wang caps->min_fragments = 1;
57224a01710SShengjiu Wang caps->max_fragments = 1;
57324a01710SShengjiu Wang caps->codecs[0] = SND_AUDIOCODEC_PCM;
57424a01710SShengjiu Wang
57524a01710SShengjiu Wang return 0;
57624a01710SShengjiu Wang }
57724a01710SShengjiu Wang
fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc * asrc,struct snd_compr_codec_caps * codec)57824a01710SShengjiu Wang static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
57924a01710SShengjiu Wang struct snd_compr_codec_caps *codec)
58024a01710SShengjiu Wang {
58124a01710SShengjiu Wang struct fsl_asrc_m2m_cap cap;
58224a01710SShengjiu Wang snd_pcm_format_t k;
58324a01710SShengjiu Wang int j = 0;
58424a01710SShengjiu Wang int ret;
58524a01710SShengjiu Wang
58624a01710SShengjiu Wang ret = asrc->m2m_get_cap(&cap);
58724a01710SShengjiu Wang if (ret)
58824a01710SShengjiu Wang return -EINVAL;
58924a01710SShengjiu Wang
59024a01710SShengjiu Wang pcm_for_each_format(k) {
59124a01710SShengjiu Wang if (pcm_format_to_bits(k) & cap.fmt_in) {
59224a01710SShengjiu Wang codec->descriptor[j].max_ch = cap.chan_max;
59324a01710SShengjiu Wang memcpy(codec->descriptor[j].sample_rates,
59424a01710SShengjiu Wang cap.rate_in,
59524a01710SShengjiu Wang cap.rate_in_count * sizeof(__u32));
59624a01710SShengjiu Wang codec->descriptor[j].num_sample_rates = cap.rate_in_count;
597cf126261SShengjiu Wang codec->descriptor[j].formats = (__force __u32)k;
59824a01710SShengjiu Wang codec->descriptor[j].pcm_formats = cap.fmt_out;
59924a01710SShengjiu Wang codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
60024a01710SShengjiu Wang codec->descriptor[j].src.out_sample_rate_max =
60124a01710SShengjiu Wang cap.rate_out[cap.rate_out_count - 1];
60224a01710SShengjiu Wang j++;
60324a01710SShengjiu Wang }
60424a01710SShengjiu Wang }
60524a01710SShengjiu Wang
60624a01710SShengjiu Wang codec->codec = SND_AUDIOCODEC_PCM;
60724a01710SShengjiu Wang codec->num_descriptors = j;
60824a01710SShengjiu Wang return 0;
60924a01710SShengjiu Wang }
61024a01710SShengjiu Wang
fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream * stream,struct snd_compr_codec_caps * codec)61124a01710SShengjiu Wang static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
61224a01710SShengjiu Wang struct snd_compr_codec_caps *codec)
61324a01710SShengjiu Wang {
61424a01710SShengjiu Wang struct fsl_asrc *asrc = stream->private_data;
61524a01710SShengjiu Wang
61624a01710SShengjiu Wang return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
61724a01710SShengjiu Wang }
61824a01710SShengjiu Wang
61924a01710SShengjiu Wang static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
62024a01710SShengjiu Wang .open = fsl_asrc_m2m_comp_open,
62124a01710SShengjiu Wang .free = fsl_asrc_m2m_comp_release,
62224a01710SShengjiu Wang .set_params = fsl_asrc_m2m_comp_set_params,
62324a01710SShengjiu Wang .get_caps = fsl_asrc_m2m_get_caps,
62424a01710SShengjiu Wang .get_codec_caps = fsl_asrc_m2m_get_codec_caps,
62524a01710SShengjiu Wang .task_create = fsl_asrc_m2m_comp_task_create,
62624a01710SShengjiu Wang .task_start = fsl_asrc_m2m_comp_task_start,
62724a01710SShengjiu Wang .task_stop = fsl_asrc_m2m_comp_task_stop,
62824a01710SShengjiu Wang .task_free = fsl_asrc_m2m_comp_task_free,
62924a01710SShengjiu Wang };
63024a01710SShengjiu Wang
fsl_asrc_m2m_suspend(struct fsl_asrc * asrc)63124a01710SShengjiu Wang int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
63224a01710SShengjiu Wang {
63324a01710SShengjiu Wang struct fsl_asrc_pair *pair;
63424a01710SShengjiu Wang int i;
63524a01710SShengjiu Wang
63624a01710SShengjiu Wang for (i = 0; i < PAIR_CTX_NUM; i++) {
63724a01710SShengjiu Wang pair = asrc->pair[i];
638d12ca6d4SShengjiu Wang if (!pair || !pair->dma_buffer[IN].area || !pair->dma_buffer[OUT].area)
63924a01710SShengjiu Wang continue;
64024a01710SShengjiu Wang if (!completion_done(&pair->complete[IN])) {
64124a01710SShengjiu Wang if (pair->dma_chan[IN])
64224a01710SShengjiu Wang dmaengine_terminate_all(pair->dma_chan[IN]);
64324a01710SShengjiu Wang asrc_input_dma_callback((void *)pair);
64424a01710SShengjiu Wang }
64524a01710SShengjiu Wang if (!completion_done(&pair->complete[OUT])) {
64624a01710SShengjiu Wang if (pair->dma_chan[OUT])
64724a01710SShengjiu Wang dmaengine_terminate_all(pair->dma_chan[OUT]);
64824a01710SShengjiu Wang asrc_output_dma_callback((void *)pair);
64924a01710SShengjiu Wang }
65024a01710SShengjiu Wang
65124a01710SShengjiu Wang if (asrc->m2m_pair_suspend)
65224a01710SShengjiu Wang asrc->m2m_pair_suspend(pair);
65324a01710SShengjiu Wang }
65424a01710SShengjiu Wang
65524a01710SShengjiu Wang return 0;
65624a01710SShengjiu Wang }
65724a01710SShengjiu Wang EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
65824a01710SShengjiu Wang
fsl_asrc_m2m_resume(struct fsl_asrc * asrc)65924a01710SShengjiu Wang int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
66024a01710SShengjiu Wang {
66124a01710SShengjiu Wang struct fsl_asrc_pair *pair;
66224a01710SShengjiu Wang int i;
66324a01710SShengjiu Wang
66424a01710SShengjiu Wang for (i = 0; i < PAIR_CTX_NUM; i++) {
66524a01710SShengjiu Wang pair = asrc->pair[i];
66624a01710SShengjiu Wang if (!pair)
66724a01710SShengjiu Wang continue;
66824a01710SShengjiu Wang if (asrc->m2m_pair_resume)
66924a01710SShengjiu Wang asrc->m2m_pair_resume(pair);
67024a01710SShengjiu Wang }
67124a01710SShengjiu Wang
67224a01710SShengjiu Wang return 0;
67324a01710SShengjiu Wang }
67424a01710SShengjiu Wang EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
67524a01710SShengjiu Wang
fsl_asrc_m2m_init(struct fsl_asrc * asrc)67624a01710SShengjiu Wang int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
67724a01710SShengjiu Wang {
67824a01710SShengjiu Wang struct device *dev = &asrc->pdev->dev;
67924a01710SShengjiu Wang struct snd_card *card;
68024a01710SShengjiu Wang struct snd_compr *compr;
68124a01710SShengjiu Wang int ret;
68224a01710SShengjiu Wang
68324a01710SShengjiu Wang ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
68424a01710SShengjiu Wang THIS_MODULE, 0, &card);
68524a01710SShengjiu Wang if (ret < 0)
68624a01710SShengjiu Wang return ret;
68724a01710SShengjiu Wang
68824a01710SShengjiu Wang strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
68924a01710SShengjiu Wang strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
69024a01710SShengjiu Wang strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
69124a01710SShengjiu Wang
69224a01710SShengjiu Wang asrc->card = card;
69324a01710SShengjiu Wang
69424a01710SShengjiu Wang compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
69524a01710SShengjiu Wang if (!compr) {
69624a01710SShengjiu Wang ret = -ENOMEM;
69724a01710SShengjiu Wang goto err;
69824a01710SShengjiu Wang }
69924a01710SShengjiu Wang
70024a01710SShengjiu Wang compr->ops = &fsl_asrc_m2m_compr_ops;
70124a01710SShengjiu Wang compr->private_data = asrc;
70224a01710SShengjiu Wang
70324a01710SShengjiu Wang ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
70424a01710SShengjiu Wang if (ret < 0)
70524a01710SShengjiu Wang goto err;
70624a01710SShengjiu Wang
70724a01710SShengjiu Wang ret = snd_card_register(card);
70824a01710SShengjiu Wang if (ret < 0)
70924a01710SShengjiu Wang goto err;
71024a01710SShengjiu Wang
71124a01710SShengjiu Wang return 0;
71224a01710SShengjiu Wang err:
71324a01710SShengjiu Wang snd_card_free(card);
71424a01710SShengjiu Wang return ret;
71524a01710SShengjiu Wang }
71624a01710SShengjiu Wang EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
71724a01710SShengjiu Wang
fsl_asrc_m2m_exit(struct fsl_asrc * asrc)71824a01710SShengjiu Wang void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
71924a01710SShengjiu Wang {
72024a01710SShengjiu Wang struct snd_card *card = asrc->card;
72124a01710SShengjiu Wang
72224a01710SShengjiu Wang snd_card_free(card);
72324a01710SShengjiu Wang }
72424a01710SShengjiu Wang EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
72524a01710SShengjiu Wang
72624a01710SShengjiu Wang MODULE_IMPORT_NS("DMA_BUF");
72724a01710SShengjiu Wang MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
72824a01710SShengjiu Wang MODULE_DESCRIPTION("Freescale ASRC M2M driver");
72924a01710SShengjiu Wang MODULE_LICENSE("GPL");
730