2014-09-09 03:21:25 -06:00
|
|
|
/*
|
|
|
|
* Freescale ASRC Memory to Memory (M2M) driver
|
|
|
|
*
|
2014-12-08 00:20:41 -07:00
|
|
|
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
2014-09-09 03:21:25 -06:00
|
|
|
*
|
|
|
|
* This file is licensed under the terms of the GNU General Public License
|
|
|
|
* version 2. This program is licensed "as is" without any warranty of any
|
|
|
|
* kind, whether express or implied.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define FSL_ASRC_INPUTFIFO_WML 0x4
|
|
|
|
#define FSL_ASRC_OUTPUTFIFO_WML 0x2
|
|
|
|
|
|
|
|
#define DIR_STR(dir) dir == IN ? "in" : "out"
|
|
|
|
|
|
|
|
struct fsl_asrc_m2m {
|
|
|
|
struct fsl_asrc_pair *pair;
|
|
|
|
struct completion complete[2];
|
|
|
|
struct dma_block dma_block[2];
|
|
|
|
unsigned int pair_hold;
|
|
|
|
unsigned int asrc_active;
|
|
|
|
unsigned int sg_nodes[2];
|
|
|
|
struct scatterlist sg[2][4];
|
|
|
|
|
|
|
|
enum asrc_word_width word_width[2];
|
|
|
|
unsigned int rate[2];
|
2014-12-15 02:28:00 -07:00
|
|
|
unsigned int last_period_size;
|
2014-09-09 03:21:25 -06:00
|
|
|
u32 watermark[2];
|
|
|
|
spinlock_t lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice asrc_miscdev = {
|
|
|
|
.name = "mxc_asrc",
|
|
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void fsl_asrc_get_status(struct fsl_asrc_pair *pair,
|
|
|
|
struct asrc_status_flags *flags)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&asrc_priv->lock, lock_flags);
|
|
|
|
|
|
|
|
flags->overload_error = pair->error;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ASRC_xPUT_DMA_CALLBACK(dir) \
|
|
|
|
((dir == IN) ? fsl_asrc_input_dma_callback : fsl_asrc_output_dma_callback)
|
|
|
|
|
|
|
|
static void fsl_asrc_input_dma_callback(void *data)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
|
|
|
|
complete(&m2m->complete[IN]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fsl_asrc_output_dma_callback(void *data)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
|
|
|
|
complete(&m2m->complete[OUT]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int fsl_asrc_get_output_FIFO_size(struct fsl_asrc_pair *pair)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRFST(index), &val);
|
|
|
|
|
|
|
|
val &= ASRFSTi_OUTPUT_FIFO_MASK;
|
|
|
|
|
|
|
|
return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fsl_asrc_read_last_FIFO(struct fsl_asrc_pair *pair)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
struct dma_block *output = &m2m->dma_block[OUT];
|
|
|
|
u32 i, reg, size, t_size = 0;
|
|
|
|
u32 *reg24 = NULL;
|
|
|
|
u16 *reg16 = NULL;
|
|
|
|
|
|
|
|
if (m2m->word_width[OUT] == ASRC_WIDTH_24_BIT)
|
|
|
|
reg24 = output->dma_vaddr + output->length;
|
|
|
|
else
|
|
|
|
reg16 = output->dma_vaddr + output->length;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
size = fsl_asrc_get_output_FIFO_size(pair);
|
|
|
|
|
|
|
|
for (i = 0; i < size * pair->channels; i++) {
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), ®);
|
|
|
|
if (reg24) {
|
|
|
|
*(reg24) = reg;
|
|
|
|
reg24++;
|
|
|
|
} else {
|
|
|
|
*(reg16) = (u16)reg;
|
|
|
|
reg16++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t_size += size;
|
|
|
|
|
|
|
|
if (size)
|
|
|
|
goto retry;
|
|
|
|
|
2014-12-15 02:28:00 -07:00
|
|
|
if (t_size > m2m->last_period_size)
|
|
|
|
t_size = m2m->last_period_size;
|
2014-09-09 03:21:25 -06:00
|
|
|
|
|
|
|
if (reg24)
|
|
|
|
output->length += t_size * pair->channels * 4;
|
|
|
|
else
|
|
|
|
output->length += t_size * pair->channels * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_allocate_dma_buf(struct fsl_asrc_pair *pair)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct dma_block *input = &m2m->dma_block[IN];
|
|
|
|
struct dma_block *output = &m2m->dma_block[OUT];
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
|
|
|
|
input->dma_vaddr = kzalloc(input->length, GFP_KERNEL);
|
|
|
|
if (!input->dma_vaddr) {
|
|
|
|
pair_err("failed to allocate input DMA buffer\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
input->dma_paddr = virt_to_dma(NULL, input->dma_vaddr);
|
|
|
|
|
|
|
|
output->dma_vaddr = kzalloc(output->length, GFP_KERNEL);
|
|
|
|
if (!output->dma_vaddr) {
|
|
|
|
pair_err("failed to allocate output DMA buffer\n");
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
output->dma_paddr = virt_to_dma(NULL, output->dma_vaddr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
kfree(input->dma_vaddr);
|
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_dmaconfig(struct fsl_asrc_pair *pair, struct dma_chan *chan,
|
|
|
|
u32 dma_addr, void *buf_addr, u32 buf_len,
|
|
|
|
bool dir, enum asrc_word_width word_width)
|
|
|
|
{
|
|
|
|
struct dma_async_tx_descriptor *desc = pair->desc[dir];
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
unsigned int sg_nent = m2m->sg_nodes[dir];
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
struct scatterlist *sg = m2m->sg[dir];
|
|
|
|
struct dma_slave_config slave_config;
|
|
|
|
enum dma_slave_buswidth buswidth;
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
switch (word_width) {
|
|
|
|
case ASRC_WIDTH_16_BIT:
|
|
|
|
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
|
|
|
break;
|
|
|
|
case ASRC_WIDTH_24_BIT:
|
|
|
|
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pair_err("invalid word width\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir == IN) {
|
|
|
|
slave_config.direction = DMA_MEM_TO_DEV;
|
|
|
|
slave_config.dst_addr = dma_addr;
|
|
|
|
slave_config.dst_addr_width = buswidth;
|
|
|
|
slave_config.dst_maxburst =
|
2014-12-15 02:28:00 -07:00
|
|
|
m2m->watermark[IN] * pair->channels;
|
2014-09-09 03:21:25 -06:00
|
|
|
} else {
|
|
|
|
slave_config.direction = DMA_DEV_TO_MEM;
|
|
|
|
slave_config.src_addr = dma_addr;
|
|
|
|
slave_config.src_addr_width = buswidth;
|
|
|
|
slave_config.src_maxburst =
|
2014-12-15 02:28:00 -07:00
|
|
|
m2m->watermark[OUT] * pair->channels;
|
2014-09-09 03:21:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = dmaengine_slave_config(chan, &slave_config);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to config dmaengine for %sput task: %d\n",
|
|
|
|
DIR_STR(dir), ret);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sg_init_table(sg, sg_nent);
|
|
|
|
switch (sg_nent) {
|
|
|
|
case 1:
|
|
|
|
sg_init_one(sg, buf_addr, buf_len);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
for (i = 0; i < (sg_nent - 1); i++)
|
|
|
|
sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE,
|
|
|
|
ASRC_MAX_BUFFER_SIZE);
|
|
|
|
|
|
|
|
sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE,
|
|
|
|
buf_len - ASRC_MAX_BUFFER_SIZE * i);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pair_err("invalid input DMA nodes number: %d\n", sg_nent);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dma_map_sg(NULL, sg, sg_nent, slave_config.direction);
|
|
|
|
if (ret != sg_nent) {
|
|
|
|
pair_err("failed to map DMA sg for %sput task\n", DIR_STR(dir));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc = dmaengine_prep_slave_sg(chan, sg, sg_nent,
|
|
|
|
slave_config.direction, DMA_PREP_INTERRUPT);
|
|
|
|
if (!desc) {
|
|
|
|
pair_err("failed to prepare dmaengine for %sput task\n",
|
|
|
|
DIR_STR(dir));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair->desc[dir] = desc;
|
|
|
|
pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
|
|
|
|
|
|
|
|
desc->callback = ASRC_xPUT_DMA_CALLBACK(dir);
|
|
|
|
desc->callback_param = pair;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_prepare_io_buffer(struct fsl_asrc_pair *pair,
|
|
|
|
struct asrc_convert_buffer *pbuf, bool dir)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
unsigned int *dma_len = &m2m->dma_block[dir].length;
|
|
|
|
enum asrc_word_width width = m2m->word_width[dir];
|
|
|
|
void *dma_vaddr = m2m->dma_block[dir].dma_vaddr;
|
|
|
|
struct dma_chan *dma_chan = pair->dma_chan[dir];
|
|
|
|
unsigned int buf_len, wm = m2m->watermark[dir];
|
|
|
|
unsigned int *sg_nodes = &m2m->sg_nodes[dir];
|
2014-12-15 02:28:00 -07:00
|
|
|
unsigned int last_period_size = m2m->last_period_size;
|
2014-09-09 03:21:25 -06:00
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
u32 word_size, fifo_addr;
|
|
|
|
void __user *buf_vaddr;
|
|
|
|
|
|
|
|
/* Clean the DMA buffer */
|
|
|
|
memset(dma_vaddr, 0, ASRC_DMA_BUFFER_SIZE);
|
|
|
|
|
|
|
|
if (dir == IN) {
|
|
|
|
buf_vaddr = (void __user *)pbuf->input_buffer_vaddr;
|
|
|
|
buf_len = pbuf->input_buffer_length;
|
|
|
|
} else {
|
|
|
|
buf_vaddr = (void __user *)pbuf->output_buffer_vaddr;
|
|
|
|
buf_len = pbuf->output_buffer_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width == ASRC_WIDTH_24_BIT)
|
|
|
|
word_size = 4;
|
|
|
|
else
|
|
|
|
word_size = 2;
|
|
|
|
|
2015-11-26 22:50:12 -07:00
|
|
|
if (buf_len < word_size * pair->channels * wm ||
|
|
|
|
buf_len > ASRC_DMA_BUFFER_SIZE) {
|
|
|
|
pair_err("%sput buffer size is error: [%d]\n",
|
2014-09-09 03:21:25 -06:00
|
|
|
DIR_STR(dir), buf_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy origin data into input buffer */
|
|
|
|
if (dir == IN && copy_from_user(dma_vaddr, buf_vaddr, buf_len))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
*dma_len = buf_len;
|
|
|
|
if (dir == OUT)
|
2014-12-15 02:28:00 -07:00
|
|
|
*dma_len -= last_period_size * word_size * pair->channels;
|
2014-09-09 03:21:25 -06:00
|
|
|
|
|
|
|
*sg_nodes = *dma_len / ASRC_MAX_BUFFER_SIZE + 1;
|
|
|
|
|
|
|
|
fifo_addr = asrc_priv->paddr + REG_ASRDx(dir, index);
|
|
|
|
|
|
|
|
return fsl_asrc_dmaconfig(pair, dma_chan, fifo_addr, dma_vaddr,
|
|
|
|
*dma_len, dir, width);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_prepare_buffer(struct fsl_asrc_pair *pair,
|
|
|
|
struct asrc_convert_buffer *pbuf)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = fsl_asrc_prepare_io_buffer(pair, pbuf, IN);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to prepare input buffer: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fsl_asrc_prepare_io_buffer(pair, pbuf, OUT);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to prepare output buffer: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fsl_asrc_process_buffer_pre(struct completion *complete,
|
|
|
|
enum asrc_pair_index index, bool dir)
|
|
|
|
{
|
|
|
|
if (!wait_for_completion_interruptible_timeout(complete, 10 * HZ)) {
|
|
|
|
pr_err("%sput DMA task timeout\n", DIR_STR(dir));
|
|
|
|
return -ETIME;
|
|
|
|
} else if (signal_pending(current)) {
|
|
|
|
pr_err("%sput task forcibly aborted\n", DIR_STR(dir));
|
2014-12-07 22:04:58 -07:00
|
|
|
return -EBUSY;
|
2014-09-09 03:21:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
init_completion(complete);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define mxc_asrc_dma_umap(m2m) \
|
|
|
|
do { \
|
|
|
|
dma_unmap_sg(NULL, m2m->sg[IN], m2m->sg_nodes[IN], \
|
|
|
|
DMA_MEM_TO_DEV); \
|
|
|
|
dma_unmap_sg(NULL, m2m->sg[OUT], m2m->sg_nodes[OUT], \
|
|
|
|
DMA_DEV_TO_MEM); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
int fsl_asrc_process_buffer(struct fsl_asrc_pair *pair,
|
|
|
|
struct asrc_convert_buffer *pbuf)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Check input task first */
|
|
|
|
ret = fsl_asrc_process_buffer_pre(&m2m->complete[IN], index, OUT);
|
|
|
|
if (ret) {
|
|
|
|
mxc_asrc_dma_umap(m2m);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ...then output task*/
|
|
|
|
ret = fsl_asrc_process_buffer_pre(&m2m->complete[OUT], index, IN);
|
|
|
|
if (ret) {
|
|
|
|
mxc_asrc_dma_umap(m2m);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
mxc_asrc_dma_umap(m2m);
|
|
|
|
|
|
|
|
/* Fetch the remaining data */
|
|
|
|
spin_lock_irqsave(&m2m->lock, lock_flags);
|
|
|
|
if (!m2m->pair_hold) {
|
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
|
|
|
|
2014-12-08 00:08:27 -07:00
|
|
|
fsl_asrc_read_last_FIFO(pair);
|
|
|
|
|
2014-09-09 03:21:25 -06:00
|
|
|
/* Update final lengths after getting last FIFO */
|
|
|
|
pbuf->input_buffer_length = m2m->dma_block[IN].length;
|
|
|
|
pbuf->output_buffer_length = m2m->dma_block[OUT].length;
|
|
|
|
|
|
|
|
if (copy_to_user((void __user *)pbuf->output_buffer_vaddr,
|
|
|
|
m2m->dma_block[OUT].dma_vaddr,
|
|
|
|
m2m->dma_block[OUT].length))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ASRC_POLLING_WITHOUT_DMA
|
|
|
|
/* THIS FUNCTION ONLY EXISTS FOR DEBUGGING AND ONLY SUPPORTS TWO CHANNELS */
|
|
|
|
static void fsl_asrc_polling_debug(struct fsl_asrc_pair *pair)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
u32 *in24 = m2m->dma_block[IN].dma_vaddr;
|
|
|
|
u32 dma_len = m2m->dma_block[IN].length / (pair->channels * 4);
|
|
|
|
u32 *reg24 = m2m->dma_block[OUT].dma_vaddr;
|
|
|
|
u32 size, i, j, t_size, reg;
|
|
|
|
|
|
|
|
t_size = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < dma_len; ) {
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
regmap_write(asrc_priv->regmap, REG_ASRDx(index), *in24);
|
|
|
|
in24++;
|
|
|
|
regmap_write(asrc_priv->regmap, REG_ASRDx(index), *in24);
|
|
|
|
in24++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
udelay(50);
|
|
|
|
udelay(50 * m2m->rate[OUT] / m2m->rate[IN]);
|
|
|
|
|
|
|
|
size = fsl_asrc_get_output_FIFO_size(index);
|
|
|
|
for (j = 0; j < size; j++) {
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), ®);
|
|
|
|
*(reg24) = reg;
|
|
|
|
reg24++;
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), ®);
|
|
|
|
*(reg24) = reg;
|
|
|
|
reg24++;
|
|
|
|
}
|
|
|
|
t_size += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
mdelay(1);
|
|
|
|
size = fsl_asrc_get_output_FIFO_size(index);
|
|
|
|
for (j = 0; j < size; j++) {
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), ®);
|
|
|
|
*(reg24) = reg;
|
|
|
|
reg24++;
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), ®);
|
|
|
|
*(reg24) = reg;
|
|
|
|
reg24++;
|
|
|
|
}
|
|
|
|
t_size += size;
|
|
|
|
|
|
|
|
m2m->dma_block[OUT].length = t_size * pair->channels * 4;
|
|
|
|
|
|
|
|
complete(&m2m->complete[OUT]);
|
|
|
|
complete(&m2m->complete[IN]);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void fsl_asrc_submit_dma(struct fsl_asrc_pair *pair)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
u32 size = fsl_asrc_get_output_FIFO_size(pair);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Read all data in OUTPUT FIFO */
|
|
|
|
while (size) {
|
|
|
|
u32 val;
|
|
|
|
for (i = 0; i < size * pair->channels; i++)
|
|
|
|
regmap_read(asrc_priv->regmap, REG_ASRDO(index), &val);
|
|
|
|
/* Fetch the data every 100us */
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
size = fsl_asrc_get_output_FIFO_size(pair);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Submit DMA request */
|
|
|
|
dmaengine_submit(pair->desc[IN]);
|
|
|
|
dma_async_issue_pending(pair->desc[IN]->chan);
|
|
|
|
|
|
|
|
dmaengine_submit(pair->desc[OUT]);
|
|
|
|
dma_async_issue_pending(pair->desc[OUT]->chan);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear DMA request during the stall state of ASRC:
|
|
|
|
* During STALL state, the remaining in input fifo would never be
|
|
|
|
* smaller than the input threshold while the output fifo would not
|
|
|
|
* be bigger than output one. Thus the DMA request would be cleared.
|
|
|
|
*/
|
|
|
|
fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
|
|
|
|
ASRC_FIFO_THRESHOLD_MAX);
|
|
|
|
|
|
|
|
/* Update the real input threshold to raise DMA request */
|
|
|
|
fsl_asrc_set_watermarks(pair, m2m->watermark[IN], m2m->watermark[OUT]);
|
|
|
|
}
|
|
|
|
#endif /* ASRC_POLLING_WITHOUT_DMA */
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_req_pair(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct device *dev = &asrc_priv->pdev->dev;
|
|
|
|
struct asrc_req req;
|
2014-12-08 00:08:27 -07:00
|
|
|
unsigned long lock_flags;
|
2014-09-09 03:21:25 -06:00
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&req, user, sizeof(req));
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to get req from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fsl_asrc_request_pair(req.chn_num, pair);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to request pair: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_lock_irqsave(&m2m->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
m2m->pair_hold = 1;
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
pair->channels = req.chn_num;
|
|
|
|
|
|
|
|
req.index = pair->index;
|
|
|
|
|
|
|
|
ret = copy_to_user(user, &req, sizeof(req));
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to send req to user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_config_pair(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct device *dev = &asrc_priv->pdev->dev;
|
|
|
|
struct asrc_config config;
|
|
|
|
enum asrc_pair_index index;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&config, user, sizeof(config));
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to get config from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
index = config.pair;
|
|
|
|
|
|
|
|
pair->config = &config;
|
2014-12-08 00:20:41 -07:00
|
|
|
ret = fsl_asrc_config_pair(pair, false, false);
|
2014-09-09 03:21:25 -06:00
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to config pair: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
m2m->watermark[IN] = FSL_ASRC_INPUTFIFO_WML;
|
|
|
|
m2m->watermark[OUT] = FSL_ASRC_OUTPUTFIFO_WML;
|
|
|
|
|
|
|
|
fsl_asrc_set_watermarks(pair, m2m->watermark[IN], m2m->watermark[OUT]);
|
|
|
|
|
|
|
|
m2m->dma_block[IN].length = ASRC_DMA_BUFFER_SIZE;
|
|
|
|
m2m->dma_block[OUT].length = ASRC_DMA_BUFFER_SIZE;
|
|
|
|
|
|
|
|
m2m->word_width[IN] = config.input_word_width;
|
|
|
|
m2m->word_width[OUT] = config.output_word_width;
|
|
|
|
|
|
|
|
m2m->rate[IN] = config.input_sample_rate;
|
|
|
|
m2m->rate[OUT] = config.output_sample_rate;
|
|
|
|
|
2014-12-15 02:28:00 -07:00
|
|
|
if (m2m->rate[OUT] > m2m->rate[IN])
|
|
|
|
m2m->last_period_size = ASRC_OUTPUT_LAST_SAMPLE_MAX;
|
|
|
|
else
|
|
|
|
m2m->last_period_size = ASRC_OUTPUT_LAST_SAMPLE;
|
|
|
|
|
2014-09-09 03:21:25 -06:00
|
|
|
ret = fsl_allocate_dma_buf(pair);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to allocate DMA buffer: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Request DMA channel for both input and output */
|
|
|
|
pair->dma_chan[IN] = fsl_asrc_get_dma_channel(pair, IN);
|
|
|
|
if (pair->dma_chan[IN] == NULL) {
|
|
|
|
pair_err("failed to request input task DMA channel\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair->dma_chan[OUT] = fsl_asrc_get_dma_channel(pair, OUT);
|
|
|
|
if (pair->dma_chan[OUT] == NULL) {
|
|
|
|
pair_err("failed to request output task DMA channel\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = copy_to_user(user, &config, sizeof(config));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to send config to user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_release_pair(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&index, user, sizeof(index));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to get index from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* index might be not valid due to some application failure. */
|
|
|
|
if (index < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
m2m->asrc_active = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&m2m->lock, lock_flags);
|
|
|
|
m2m->pair_hold = 0;
|
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
|
|
|
|
|
|
|
if (pair->dma_chan[IN])
|
|
|
|
dma_release_channel(pair->dma_chan[IN]);
|
|
|
|
if (pair->dma_chan[OUT])
|
|
|
|
dma_release_channel(pair->dma_chan[OUT]);
|
|
|
|
kfree(m2m->dma_block[IN].dma_vaddr);
|
|
|
|
kfree(m2m->dma_block[OUT].dma_vaddr);
|
|
|
|
fsl_asrc_release_pair(pair);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_convert(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
struct asrc_convert_buffer buf;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&buf, user, sizeof(buf));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to get buf from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fsl_asrc_prepare_buffer(pair, &buf);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to prepare buffer: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ASRC_POLLING_WITHOUT_DMA
|
|
|
|
fsl_asrc_polling_debug(pair);
|
|
|
|
#else
|
|
|
|
fsl_asrc_submit_dma(pair);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ret = fsl_asrc_process_buffer(pair, &buf);
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to process buffer: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = copy_to_user(user, &buf, sizeof(buf));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to send buf to user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_start_conv(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
enum asrc_pair_index index;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&index, user, sizeof(index));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to get index from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
m2m->asrc_active = 1;
|
|
|
|
fsl_asrc_start_pair(pair);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_stop_conv(struct fsl_asrc_pair *pair,
|
|
|
|
void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
enum asrc_pair_index index;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&index, user, sizeof(index));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to get index from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[IN]);
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[OUT]);
|
|
|
|
|
|
|
|
fsl_asrc_stop_pair(pair);
|
|
|
|
m2m->asrc_active = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_status(struct fsl_asrc_pair *pair, void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
struct asrc_status_flags flags;
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = copy_from_user(&flags, user, sizeof(flags));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to get flags from user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
fsl_asrc_get_status(pair, &flags);
|
|
|
|
|
|
|
|
ret = copy_to_user(user, &flags, sizeof(flags));
|
|
|
|
if (ret) {
|
|
|
|
pair_err("failed to send flags to user space: %ld\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl_flush(struct fsl_asrc_pair *pair, void __user *user)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
enum asrc_pair_index index = pair->index;
|
|
|
|
|
|
|
|
init_completion(&m2m->complete[IN]);
|
|
|
|
init_completion(&m2m->complete[OUT]);
|
|
|
|
|
|
|
|
/* Release DMA and request again */
|
|
|
|
dma_release_channel(pair->dma_chan[IN]);
|
|
|
|
dma_release_channel(pair->dma_chan[OUT]);
|
|
|
|
|
|
|
|
pair->dma_chan[IN] = fsl_asrc_get_dma_channel(pair, IN);
|
|
|
|
if (pair->dma_chan[IN] == NULL) {
|
|
|
|
pair_err("failed to request input task DMA channel\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair->dma_chan[OUT] = fsl_asrc_get_dma_channel(pair, OUT);
|
|
|
|
if (pair->dma_chan[OUT] == NULL) {
|
|
|
|
pair_err("failed to request output task DMA channel\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fsl_asrc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_pair *pair = file->private_data;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
void __user *user = (void __user *)arg;
|
|
|
|
long ret = 0;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case ASRC_REQ_PAIR:
|
|
|
|
ret = fsl_asrc_ioctl_req_pair(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_CONFIG_PAIR:
|
|
|
|
ret = fsl_asrc_ioctl_config_pair(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_RELEASE_PAIR:
|
|
|
|
ret = fsl_asrc_ioctl_release_pair(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_CONVERT:
|
|
|
|
ret = fsl_asrc_ioctl_convert(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_START_CONV:
|
|
|
|
ret = fsl_asrc_ioctl_start_conv(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_STOP_CONV:
|
|
|
|
ret = fsl_asrc_ioctl_stop_conv(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_STATUS:
|
|
|
|
ret = fsl_asrc_ioctl_status(pair, user);
|
|
|
|
break;
|
|
|
|
case ASRC_FLUSH:
|
|
|
|
ret = fsl_asrc_ioctl_flush(pair, user);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(&asrc_priv->pdev->dev, "invalid ioctl cmd!\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct fsl_asrc *asrc_priv = dev_get_drvdata(asrc_miscdev.this_device);
|
|
|
|
struct device *dev = &asrc_priv->pdev->dev;
|
|
|
|
struct fsl_asrc_pair *pair;
|
|
|
|
struct fsl_asrc_m2m *m2m;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = signal_pending(current);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "current process has a signal pending\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
|
|
|
|
if (!pair) {
|
|
|
|
dev_err(dev, "failed to allocate pair\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
m2m = kzalloc(sizeof(struct fsl_asrc_m2m), GFP_KERNEL);
|
2015-11-26 22:50:12 -07:00
|
|
|
if (!m2m) {
|
2014-09-09 03:21:25 -06:00
|
|
|
dev_err(dev, "failed to allocate m2m resource\n");
|
2015-12-01 20:02:12 -07:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
2014-09-09 03:21:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pair->private = m2m;
|
|
|
|
pair->asrc_priv = asrc_priv;
|
|
|
|
|
2014-12-08 00:08:27 -07:00
|
|
|
init_completion(&m2m->complete[IN]);
|
|
|
|
init_completion(&m2m->complete[OUT]);
|
|
|
|
|
2014-09-09 03:21:25 -06:00
|
|
|
spin_lock_init(&m2m->lock);
|
|
|
|
|
|
|
|
file->private_data = pair;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(dev);
|
|
|
|
|
|
|
|
return 0;
|
2015-12-01 20:02:12 -07:00
|
|
|
out:
|
|
|
|
kfree(pair);
|
|
|
|
|
|
|
|
return ret;
|
2014-09-09 03:21:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_close(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_pair *pair = file->private_data;
|
|
|
|
struct fsl_asrc_m2m *m2m = pair->private;
|
|
|
|
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
|
|
|
struct device *dev = &asrc_priv->pdev->dev;
|
|
|
|
unsigned long lock_flags;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Make sure we have clear the pointer */
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_lock_irqsave(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
for (i = 0; i < ASRC_PAIR_MAX_NUM; i++)
|
|
|
|
if (asrc_priv->pair[i] == pair)
|
|
|
|
asrc_priv->pair[i] = NULL;
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
|
|
|
|
if (m2m->asrc_active) {
|
|
|
|
m2m->asrc_active = 0;
|
|
|
|
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[IN]);
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[OUT]);
|
|
|
|
|
|
|
|
fsl_asrc_stop_pair(pair);
|
|
|
|
fsl_asrc_input_dma_callback((void *)pair);
|
|
|
|
fsl_asrc_output_dma_callback((void *)pair);
|
|
|
|
}
|
|
|
|
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_lock_irqsave(&m2m->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
if (m2m->pair_hold) {
|
|
|
|
m2m->pair_hold = 0;
|
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
|
|
|
|
|
|
|
if (pair->dma_chan[IN])
|
|
|
|
dma_release_channel(pair->dma_chan[IN]);
|
|
|
|
if (pair->dma_chan[OUT])
|
|
|
|
dma_release_channel(pair->dma_chan[OUT]);
|
|
|
|
|
|
|
|
kfree(m2m->dma_block[IN].dma_vaddr);
|
|
|
|
kfree(m2m->dma_block[OUT].dma_vaddr);
|
|
|
|
|
|
|
|
fsl_asrc_release_pair(pair);
|
2014-12-08 00:08:27 -07:00
|
|
|
} else
|
|
|
|
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_lock_irqsave(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
kfree(m2m);
|
|
|
|
kfree(pair);
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
file->private_data = NULL;
|
|
|
|
|
|
|
|
pm_runtime_put_sync(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations asrc_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.unlocked_ioctl = fsl_asrc_ioctl,
|
|
|
|
.open = fsl_asrc_open,
|
|
|
|
.release = fsl_asrc_close,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int fsl_asrc_m2m_init(struct fsl_asrc *asrc_priv)
|
|
|
|
{
|
|
|
|
struct device *dev = &asrc_priv->pdev->dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
asrc_miscdev.fops = &asrc_fops,
|
|
|
|
ret = misc_register(&asrc_miscdev);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to register char device %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
dev_set_drvdata(asrc_miscdev.this_device, asrc_priv);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fsl_asrc_m2m_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
misc_deregister(&asrc_miscdev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fsl_asrc_m2m_suspend(struct fsl_asrc *asrc_priv)
|
|
|
|
{
|
|
|
|
struct fsl_asrc_pair *pair;
|
|
|
|
struct fsl_asrc_m2m *m2m;
|
2014-12-08 00:08:27 -07:00
|
|
|
unsigned long lock_flags;
|
2014-09-09 03:21:25 -06:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ASRC_PAIR_MAX_NUM; i++) {
|
2014-12-08 00:08:27 -07:00
|
|
|
spin_lock_irqsave(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
pair = asrc_priv->pair[i];
|
2014-12-08 00:08:27 -07:00
|
|
|
if (!pair || !pair->private) {
|
|
|
|
spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
continue;
|
2014-12-08 00:08:27 -07:00
|
|
|
}
|
2014-09-09 03:21:25 -06:00
|
|
|
m2m = pair->private;
|
|
|
|
|
|
|
|
if (!completion_done(&m2m->complete[IN])) {
|
2014-12-08 00:08:27 -07:00
|
|
|
if (pair->dma_chan[IN])
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[IN]);
|
2014-09-09 03:21:25 -06:00
|
|
|
fsl_asrc_input_dma_callback((void *)pair);
|
|
|
|
}
|
|
|
|
if (!completion_done(&m2m->complete[OUT])) {
|
2014-12-08 00:08:27 -07:00
|
|
|
if (pair->dma_chan[OUT])
|
|
|
|
dmaengine_terminate_all(pair->dma_chan[OUT]);
|
2014-09-09 03:21:25 -06:00
|
|
|
fsl_asrc_output_dma_callback((void *)pair);
|
|
|
|
}
|
2014-12-08 00:08:27 -07:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
|
2014-09-09 03:21:25 -06:00
|
|
|
}
|
|
|
|
}
|