summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen Liangjun <b36089@freescale.com>2012-10-26 17:43:30 +0800
committerChen Liangjun <b36089@freescale.com>2012-11-06 13:43:41 +0800
commite49af26851c3f5a899b69682189f7849da45452b (patch)
treea823f9fcc7007964763de781c257b35eddee287d
parent9e81e368be12a07cdff2b54ec7e7a13e82aa38ec (diff)
ENGR00231773-4 ASRC: use scatter list and stall bit for asrc convert
In the origin code, ASRC driver use cyclic way to process DMA task transfering data to/from ASRC input/output FIFO. In this case, it is necessary that user application should promise that the input buffer flow is continuous. If not, there would be 0 data be inserted into data flow. The output data would be noisy. In this patch, 1 use scatter list instead of cyclic SDMA: with scatter list, SDMA would stop when the applied scatter list nents are finished. 2 set stall bit for ASRC "memory->ASRC->memory" convert to stop ASRC convert when input data is not send into ASRC input FIFO in time. Signed-off-by: Chen Liangjun <b36089@freescale.com>
-rw-r--r--drivers/mxc/asrc/mxc_asrc.c351
-rw-r--r--include/linux/mxc_asrc.h21
2 files changed, 238 insertions, 134 deletions
diff --git a/drivers/mxc/asrc/mxc_asrc.c b/drivers/mxc/asrc/mxc_asrc.c
index f9a646a9a227..95677ea5de6c 100644
--- a/drivers/mxc/asrc/mxc_asrc.c
+++ b/drivers/mxc/asrc/mxc_asrc.c
@@ -17,7 +17,7 @@
* @brief MXC Asynchronous Sample Rate Converter
*
* @ingroup SOUND
- */
+*/
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -152,10 +152,10 @@ static unsigned char output_clk_map_v2[] = {
static unsigned char *input_clk_map, *output_clk_map;
static struct dma_chan *imx_asrc_dma_alloc(u32 dma_req);
-struct dma_async_tx_descriptor *imx_asrc_dma_config(
+static int imx_asrc_dma_config(
struct asrc_pair_params *params,
struct dma_chan *chan,
- u32 dma_addr, dma_addr_t buf_addr,
+ u32 dma_addr, void *buf_addr,
u32 buf_len, bool in,
enum asrc_word_width word_width);
@@ -632,6 +632,19 @@ int asrc_config_pair(struct asrc_config *config)
__raw_writel(reg,
g_asrc->vaddr + ASRC_ASRMCR1A_REG + (config->pair << 2));
+
+ /* Enable BUFFER STALL*/
+ reg = __raw_readl(
+ g_asrc->vaddr + ASRC_ASRMCRA_REG + (config->pair << 3));
+ reg |= 1 << 21;
+ reg |= 1 << 22;
+ reg &= ~0x3f;
+ reg += ASRC_INPUTFIFO_THRESHOLD;
+ reg &= ~(0x3f << 12);
+ reg += ASRC_OUTPUTFIFO_THRESHOLD << 12;
+ __raw_writel(reg,
+ g_asrc->vaddr + ASRC_ASRMCRA_REG + (config->pair << 3));
+
return err;
}
@@ -842,6 +855,8 @@ static void asrc_input_dma_callback(void *data)
unsigned long lock_flags;
params = data;
+ dma_unmap_sg(NULL, params->input_sg,
+ params->input_sg_nodes, DMA_MEM_TO_DEV);
spin_lock_irqsave(&input_int_lock, lock_flags);
params->input_counter++;
wake_up_interruptible(&params->input_wait_queue);
@@ -855,7 +870,8 @@ static void asrc_output_dma_callback(void *data)
unsigned long lock_flags;
params = data;
-
+ dma_unmap_sg(NULL, params->output_sg,
+ params->output_sg_nodes, DMA_MEM_TO_DEV);
spin_lock_irqsave(&output_int_lock, lock_flags);
params->output_counter++;
wake_up_interruptible(&params->output_wait_queue);
@@ -942,18 +958,30 @@ static struct dma_chan *imx_asrc_dma_alloc(u32 dma_req)
return dma_request_channel(mask, filter, &dma_data);
}
-
-struct dma_async_tx_descriptor *imx_asrc_dma_config(
+static int imx_asrc_dma_config(
struct asrc_pair_params *params,
struct dma_chan *chan,
- u32 dma_addr, dma_addr_t buf_addr,
+ u32 dma_addr, void *buf_addr,
u32 buf_len, bool in,
enum asrc_word_width word_width)
{
struct dma_slave_config slave_config;
enum dma_slave_buswidth buswidth;
+ struct scatterlist *sg;
+ unsigned int sg_nent, sg_index;
+ struct dma_async_tx_descriptor *desc;
int ret;
+ if (in) {
+ sg = params->input_sg;
+ sg_nent = params->input_sg_nodes;
+ desc = params->desc_in;
+ } else {
+ sg = params->output_sg;
+ sg_nent = params->output_sg_nodes;
+ desc = params->desc_out;
+ }
+
switch (word_width) {
case ASRC_WIDTH_16_BIT:
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
@@ -963,7 +991,7 @@ struct dma_async_tx_descriptor *imx_asrc_dma_config(
break;
default:
pr_err("Error word_width\n");
- return NULL;
+ return -EINVAL;
}
if (in) {
@@ -982,16 +1010,177 @@ struct dma_async_tx_descriptor *imx_asrc_dma_config(
ret = dmaengine_slave_config(chan, &slave_config);
if (ret) {
pr_err("imx_asrc_dma_config(%d) failed\r\n", in);
- return NULL;
+ 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 (sg_index = 0; sg_index < (sg_nent - 1); sg_index++) {
+ sg_set_buf(&sg[sg_index],
+ buf_addr + sg_index * ASRC_MAX_BUFFER_SIZE,
+ ASRC_MAX_BUFFER_SIZE);
+ }
+ sg_set_buf(&sg[sg_index],
+ buf_addr + sg_index * ASRC_MAX_BUFFER_SIZE,
+ buf_len - ASRC_MAX_BUFFER_SIZE * sg_index);
+ break;
+ default:
+ pr_err("Error Input DMA nodes number!");
+ }
+
+ ret = dma_map_sg(NULL, sg, sg_nent, slave_config.direction);
+ if (ret != sg_nent) {
+ pr_err("DMA mapping error!!\n");
+ return -EINVAL;
+ }
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ sg, sg_nent, slave_config.direction, 1);
+
+ if (in) {
+ params->desc_in = desc;
+ params->desc_in->callback = asrc_input_dma_callback;
+ } else {
+ params->desc_out = desc;
+ params->desc_out->callback = asrc_output_dma_callback;
+ }
+ if (desc) {
+ desc->callback = in ?
+ asrc_input_dma_callback : asrc_output_dma_callback;
+ desc->callback_param = params;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+int mxc_asrc_prepare_input_buffer(struct asrc_pair_params *params,
+ struct asrc_convert_buffer *pbuf)
+{
+ u32 word_size;
+
+ switch (params->input_word_width) {
+ case ASRC_WIDTH_24_BIT:
+ word_size = 4;
+ break;
+ case ASRC_WIDTH_16_BIT:
+ case ASRC_WIDTH_8_BIT:
+ word_size = 2;
+ break;
+ default:
+ pr_err("error input word size!\n");
+ return -EINVAL;
+ }
+
+ if (pbuf->input_buffer_length <
+ word_size * params->channel_nums * ASRC_INPUTFIFO_THRESHOLD) {
+ pr_err("input buffer size[%d] is too small!\n",
+ pbuf->input_buffer_length);
+ return -EINVAL;
+ }
+
+ /* copy origin data into input buffer */
+ if (copy_from_user(
+ params->input_dma_total.dma_vaddr,
+ (void __user *)pbuf->input_buffer_vaddr,
+ pbuf->input_buffer_length)) {
+ return -EFAULT;
+ }
+
+ params->input_dma_total.length = pbuf->input_buffer_length;
+ params->input_sg_nodes =
+ params->input_dma_total.length / ASRC_MAX_BUFFER_SIZE + 1;
+
+ return imx_asrc_dma_config(
+ params,
+ params->input_dma_channel,
+ asrc_get_per_addr(params->index, 1),
+ params->input_dma_total.dma_vaddr,
+ params->input_dma_total.length, 1,
+ params->input_word_width);
+
+}
+
+int mxc_asrc_prepare_output_buffer(struct asrc_pair_params *params,
+ struct asrc_convert_buffer *pbuf)
+{
+ params->output_dma_total.length = pbuf->output_buffer_length;
+ params->output_sg_nodes =
+ params->output_dma_total.length / ASRC_MAX_BUFFER_SIZE + 1;
+
+ return imx_asrc_dma_config(
+ params,
+ params->output_dma_channel,
+ asrc_get_per_addr(params->index, 0),
+ params->output_dma_total.dma_vaddr,
+ params->output_dma_total.length, 0,
+ params->output_word_width);
+}
+
+int mxc_asrc_process_output_buffer(struct asrc_pair_params *params,
+ struct asrc_convert_buffer *pbuf)
+{
+ unsigned long lock_flags;
+
+ if (!wait_event_interruptible_timeout
+ (params->output_wait_queue,
+ params->output_counter != 0, 10 * HZ)) {
+ pr_info
+ ("ASRC_DQ_OUTBUF timeout counter %x\n",
+ params->output_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ pr_info("ASRC_DQ_INBUF interrupt received\n");
+ return -ERESTARTSYS;
+ }
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_counter--;
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+
+ pbuf->output_buffer_length = params->output_dma_total.length;
+ if (copy_to_user((void __user *)pbuf->output_buffer_vaddr,
+ params->output_dma_total.dma_vaddr,
+ params->output_dma_total.length))
+ return -EFAULT;
+
+ return 0;
+
+}
+
+int mxc_asrc_process_input_buffer(struct asrc_pair_params *params,
+ struct asrc_convert_buffer *pbuf)
+{
+ unsigned long lock_flags;
+
+ if (!wait_event_interruptible_timeout
+ (params->input_wait_queue,
+ params->input_counter != 0, 10 * HZ)) {
+ pr_info
+ ("ASRC_DQ_INBUF timeout counter %x\n",
+ params->input_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ pr_info("ASRC_DQ_INBUF interrupt received\n");
+ return -ERESTARTSYS;
}
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_counter--;
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+
+ pbuf->input_buffer_length = params->input_dma_total.length;
- return chan->device->device_prep_dma_cyclic(chan, buf_addr,
- buf_len * params->buffer_num,
- buf_len,
- in == true ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ return 0;
}
+
/*!
* asrc interface - function
*
@@ -1058,6 +1247,9 @@ static long asrc_ioctl(struct file *file,
params->input_dma_total.length = ASRC_DMA_BUFFER_SIZE;
params->output_dma_total.length = ASRC_DMA_BUFFER_SIZE;
+ params->input_word_width = config.input_word_width;
+ params->output_word_width = config.output_word_width;
+
err = mxc_allocate_dma_buf(params);
if (err < 0)
break;
@@ -1086,42 +1278,11 @@ static long asrc_ioctl(struct file *file,
err = -EBUSY;
}
- params->desc_in = imx_asrc_dma_config(params,
- params->input_dma_channel,
- asrc_get_per_addr(params->index, 1),
- params->input_dma[0].dma_paddr,
- params->input_buffer_size, 1,
- config.input_word_width);
- if (params->desc_in) {
- params->desc_in->callback =
- asrc_input_dma_callback;
- params->desc_in->callback_param = params;
- } else {
- pr_err("unable to get desc_in\r\n");
- err = -EINVAL;
- break;
- }
-
params->output_dma_channel = imx_asrc_dma_alloc(tx_id);
if (params->output_dma_channel == NULL) {
pr_err("unable to get tx channel %d\n", tx_id);
err = -EBUSY;
}
- params->desc_out = imx_asrc_dma_config(params,
- params->output_dma_channel,
- asrc_get_per_addr(params->index, 0),
- params->output_dma[0].dma_paddr,
- params->output_buffer_size, 0,
- config.output_word_width);
- if (params->desc_out) {
- params->desc_out->callback =
- asrc_output_dma_callback;
- params->desc_out->callback_param = params;
- } else {
- pr_err("unable to get desc_out\r\n");
- err = -EINVAL;
- break;
- }
init_waitqueue_head(&params->input_wait_queue);
init_waitqueue_head(&params->output_wait_queue);
@@ -1185,131 +1346,57 @@ static long asrc_ioctl(struct file *file,
}
case ASRC_Q_INBUF:
{
- struct asrc_buffer buf;
- struct dma_block *block;
- unsigned long lock_flags;
+ struct asrc_convert_buffer buf;
if (copy_from_user
(&buf, (void __user *)arg,
- sizeof(struct asrc_buffer))) {
+ sizeof(struct asrc_convert_buffer))) {
err = -EFAULT;
break;
}
+ err = mxc_asrc_prepare_input_buffer(params, &buf);
+
break;
}
case ASRC_DQ_INBUF:{
- struct asrc_buffer buf;
- struct dma_block *block;
- unsigned long lock_flags;
+ struct asrc_convert_buffer buf;
if (copy_from_user
(&buf, (void __user *)arg,
- sizeof(struct asrc_buffer))) {
+ sizeof(struct asrc_convert_buffer))) {
err = -EFAULT;
break;
}
- /* if ASRC is inactive, nonsense to DQ buffer */
- if (params->asrc_active == 0) {
- err = -EFAULT;
- buf.buf_valid = ASRC_BUF_NA;
- if (copy_to_user
- ((void __user *)arg, &buf,
- sizeof(struct asrc_buffer)))
- err = -EFAULT;
- break;
- }
- if (!wait_event_interruptible_timeout
- (params->input_wait_queue,
- params->input_counter != 0, 10 * HZ)) {
- pr_info
- ("ASRC_DQ_INBUF timeout counter %x\n",
- params->input_counter);
- err = -ETIME;
- break;
- } else if (signal_pending(current)) {
- pr_info("ASRC_DQ_INBUF interrupt received\n");
- err = -ERESTARTSYS;
- break;
- }
- spin_lock_irqsave(&input_int_lock, lock_flags);
- params->input_counter--;
- spin_unlock_irqrestore(&input_int_lock, lock_flags);
- block = &params->input_dma[0];
- buf.index = block->index;
- buf.length = block->length;
- buf.buf_valid = ASRC_BUF_AV;
- if (copy_to_user
- ((void __user *)arg, &buf,
- sizeof(struct asrc_buffer)))
- err = -EFAULT;
+ err = mxc_asrc_process_input_buffer(params, &buf);
break;
}
case ASRC_Q_OUTBUF:{
- struct asrc_buffer buf;
- struct dma_block *block;
- unsigned long lock_flags;
+ struct asrc_convert_buffer buf;
if (copy_from_user
(&buf, (void __user *)arg,
- sizeof(struct asrc_buffer))) {
+ sizeof(struct asrc_convert_buffer))) {
err = -EFAULT;
break;
}
+ err = mxc_asrc_prepare_output_buffer(params, &buf);
break;
}
case ASRC_DQ_OUTBUF:{
- struct asrc_buffer buf;
- struct dma_block *block;
- unsigned long lock_flags;
+ struct asrc_convert_buffer buf;
if (copy_from_user
(&buf, (void __user *)arg,
- sizeof(struct asrc_buffer))) {
- err = -EFAULT;
- break;
- }
- /* if ASRC is inactive, nonsense to DQ buffer */
- if (params->asrc_active == 0) {
- buf.buf_valid = ASRC_BUF_NA;
+ sizeof(struct asrc_convert_buffer))) {
err = -EFAULT;
- if (copy_to_user
- ((void __user *)arg, &buf,
- sizeof(struct asrc_buffer)))
- err = -EFAULT;
- break;
- }
-
- if (!wait_event_interruptible_timeout
- (params->output_wait_queue,
- params->output_counter != 0, 10 * HZ)) {
- pr_info
- ("ASRC_DQ_OUTBUF timeout counter %x\n",
- params->output_counter);
- err = -ETIME;
- break;
- } else if (signal_pending(current)) {
- pr_info("ASRC_DQ_INBUF interrupt received\n");
- err = -ERESTARTSYS;
break;
}
- spin_lock_irqsave(&output_int_lock, lock_flags);
- params->output_counter--;
-
- spin_unlock_irqrestore(&output_int_lock, lock_flags);
- block = &params->output_dma[0];
- buf.index = block->index;
- buf.length = block->length;
- buf.buf_valid = ASRC_BUF_AV;
- if (copy_to_user
- ((void __user *)arg, &buf,
- sizeof(struct asrc_buffer)))
- err = -EFAULT;
+ err = mxc_asrc_process_output_buffer(params, &buf);
break;
}
case ASRC_START_CONV:{
enum asrc_pair_index index;
- unsigned long lock_flags;
if (copy_from_user
(&index, (void __user *)arg,
sizeof(enum asrc_pair_index))) {
diff --git a/include/linux/mxc_asrc.h b/include/linux/mxc_asrc.h
index a970fe6fa773..28137a0ce104 100644
--- a/include/linux/mxc_asrc.h
+++ b/include/linux/mxc_asrc.h
@@ -112,12 +112,21 @@ struct asrc_querybuf {
unsigned long output_offset;
};
+struct asrc_convert_buffer {
+ void *input_buffer_vaddr;
+ void *output_buffer_vaddr;
+ unsigned int input_buffer_length;
+ unsigned int output_buffer_length;
+};
+
struct asrc_buffer {
unsigned int index;
unsigned int length;
+ unsigned int output_last_length;
int buf_valid;
};
+
struct asrc_status_flags {
enum asrc_pair_index index;
unsigned int overload_error;
@@ -134,11 +143,14 @@ enum asrc_error_status {
};
#ifdef __KERNEL__
+#include <linux/scatterlist.h>
#define ASRC_DMA_BUFFER_NUM 2
#define ASRC_INPUTFIFO_THRESHOLD 32
#define ASRC_OUTPUTFIFO_THRESHOLD 32
-#define ASRC_DMA_BUFFER_SIZE (1024 * 64 * 4)
+#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
+#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
+
#define ASRC_ASRCTR_REG 0x00
@@ -187,7 +199,7 @@ enum asrc_error_status {
struct dma_block {
unsigned int index;
unsigned int length;
- unsigned char *dma_vaddr;
+ void *dma_vaddr;
dma_addr_t dma_paddr;
struct list_head queue;
};
@@ -218,6 +230,11 @@ struct asrc_pair_params {
struct dma_async_tx_descriptor *desc_in;
struct dma_async_tx_descriptor *desc_out;
struct work_struct task_output_work;
+ unsigned int input_sg_nodes;
+ unsigned int output_sg_nodes;
+ struct scatterlist input_sg[4], output_sg[4];
+ enum asrc_word_width input_word_width;
+ enum asrc_word_width output_word_width;
};
struct asrc_data {