summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen Liangjun <b36089@freescale.com>2012-10-31 11:11:26 +0800
committerChen Liangjun <b36089@freescale.com>2012-11-06 13:43:41 +0800
commitf10e0cf7a83b920cede6e8fd933b66b454040a72 (patch)
tree1818f55c2135e35ae8b6ae7650a6f53455587fc4
parente49af26851c3f5a899b69682189f7849da45452b (diff)
ENGR00231773-5 ASRC: use poll mode to receive last period of ASRC data
ASRC driver use DMA to transfer data from ASRC output FIFO to memory. However, DMA way require the data number in ASRC output FIFO being larger than watermark level. Thus a dma request can trigger a DMA burst. For the last period of output data, its number is possiblely less than output FIFO watermark level. In this case, the output DMA would pending for the last period of output data until timeout. In this patch: 1 divide expected output data length into 2 parts: DMA part and poll part. Using DMA to get the DMA part data and poll mode to get the poll part. 2 to prevent user from processing these 2 parts above, kernel buffers would be untouchable. User application only need send its data buffer address to driver instead of query the kernel buffer. Signed-off-by: Chen Liangjun <b36089@freescale.com>
-rw-r--r--drivers/mxc/asrc/mxc_asrc.c211
-rw-r--r--include/linux/mxc_asrc.h19
2 files changed, 187 insertions, 43 deletions
diff --git a/drivers/mxc/asrc/mxc_asrc.c b/drivers/mxc/asrc/mxc_asrc.c
index 95677ea5de6c..d9888a483de2 100644
--- a/drivers/mxc/asrc/mxc_asrc.c
+++ b/drivers/mxc/asrc/mxc_asrc.c
@@ -41,6 +41,8 @@
#include <asm/memory.h>
#include <mach/dma.h>
#include <mach/mxc_asrc.h>
+#include <linux/delay.h>
+
#define ASRC_PROC_PATH "driver/asrc"
@@ -861,32 +863,106 @@ static void asrc_input_dma_callback(void *data)
params->input_counter++;
wake_up_interruptible(&params->input_wait_queue);
spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ schedule_work(&params->task_output_work);
return;
}
static void asrc_output_dma_callback(void *data)
{
struct asrc_pair_params *params;
- 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);
- spin_unlock_irqrestore(&output_int_lock, lock_flags);
-
- schedule_work(&params->task_output_work);
return;
}
+static unsigned int asrc_get_output_FIFO_size(enum asrc_pair_index index)
+{
+ u32 reg;
+
+ reg = __raw_readl(g_asrc->vaddr + ASRC_ASRFSTA_REG + (index << 3));
+ return (reg & ASRC_ASRFSTX_OUTPUT_FIFO_MASK)
+ >> ASRC_ASRFSTX_OUTPUT_FIFO_OFFSET;
+}
+
+static u32 asrc_read_one_from_output_FIFO(enum asrc_pair_index index)
+{
+ return __raw_readl(g_asrc->vaddr + ASRC_ASRDOA_REG + (index << 3));
+}
+
+static void asrc_read_output_FIFO_S16(struct asrc_pair_params *params)
+{
+ u32 size, i, j, reg, t_size;
+ u16 *index = params->output_last_period.dma_vaddr;
+
+ size = asrc_get_output_FIFO_size(params->index);
+ while (size) {
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < params->channel_nums; j++) {
+ reg = asrc_read_one_from_output_FIFO(
+ params->index);
+ *(index) = (u16)reg;
+ index++;
+ }
+ }
+ t_size += size;
+ mdelay(1);
+ size = asrc_get_output_FIFO_size(params->index);
+ }
+
+ params->output_last_period.length = t_size * params->channel_nums * 2;
+}
+
+static void asrc_read_output_FIFO_S24(struct asrc_pair_params *params)
+{
+ u32 size, i, j, reg, t_size;
+ u32 *index = params->output_last_period.dma_vaddr;
+
+ t_size = 0;
+ size = asrc_get_output_FIFO_size(params->index);
+ while (size) {
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < params->channel_nums; j++) {
+ reg = asrc_read_one_from_output_FIFO(
+ params->index);
+ *(index) = reg;
+ index++;
+ }
+ }
+ t_size += size;
+ mdelay(1);
+ size = asrc_get_output_FIFO_size(params->index);
+ }
+
+ params->output_last_period.length = t_size * params->channel_nums * 4;
+}
+
+
static void asrc_output_task_worker(struct work_struct *w)
{
struct asrc_pair_params *params =
container_of(w, struct asrc_pair_params, task_output_work);
+ unsigned long lock_flags;
/* asrc output work struct */
+ switch (params->output_word_width) {
+ case ASRC_WIDTH_24_BIT:
+ asrc_read_output_FIFO_S24(params);
+ break;
+ case ASRC_WIDTH_16_BIT:
+ case ASRC_WIDTH_8_BIT:
+ asrc_read_output_FIFO_S16(params);
+ break;
+ default:
+ pr_err("%s: error word width\n", __func__);
+ }
+
+ /* finish receiving all output data */
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_counter++;
+ wake_up_interruptible(&params->output_wait_queue);
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
}
static void mxc_free_dma_buf(struct asrc_pair_params *params)
@@ -901,15 +977,23 @@ static void mxc_free_dma_buf(struct asrc_pair_params *params)
params->output_dma_total.dma_vaddr = NULL;
}
+ if (params->output_last_period.dma_vaddr) {
+ dma_free_coherent(
+ g_asrc->dev, 4096,
+ params->output_last_period.dma_vaddr,
+ params->output_last_period.dma_paddr);
+ params->output_last_period.dma_vaddr = NULL;
+ }
+
return;
}
static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
{
- struct dma_block *input_a, *output_a;
-
+ struct dma_block *input_a, *output_a, *last_period;
input_a = &params->input_dma_total;
output_a = &params->output_dma_total;
+ last_period = &params->output_last_period;
input_a->dma_vaddr = kzalloc(input_a->length, GFP_KERNEL);
if (!input_a->dma_vaddr) {
@@ -925,6 +1009,12 @@ static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
}
output_a->dma_paddr = virt_to_dma(NULL, output_a->dma_vaddr);
+ last_period->dma_vaddr =
+ dma_alloc_coherent(NULL,
+ 4096,
+ &last_period->dma_paddr,
+ GFP_KERNEL);
+
return 0;
exit:
@@ -1031,7 +1121,8 @@ static int imx_asrc_dma_config(
buf_len - ASRC_MAX_BUFFER_SIZE * sg_index);
break;
default:
- pr_err("Error Input DMA nodes number!");
+ pr_err("Error Input DMA nodes number[%d]!", sg_nent);
+ return -EINVAL;
}
ret = dma_map_sg(NULL, sg, sg_nent, slave_config.direction);
@@ -1062,7 +1153,34 @@ static int imx_asrc_dma_config(
}
-int mxc_asrc_prepare_input_buffer(struct asrc_pair_params *params,
+static int asrc_get_output_buffer_size(int input_buffer_size,
+ int input_sample_rate,
+ int output_sample_rate)
+{
+ int i = 0;
+ int outbuffer_size = 0;
+ int outsample = output_sample_rate;
+ while (outsample >= input_sample_rate) {
+ ++i;
+ outsample -= input_sample_rate;
+ }
+ outbuffer_size = i * input_buffer_size;
+ i = 1;
+ while (((input_buffer_size >> i) > 2) && (outsample != 0)) {
+ if (((outsample << 1) - input_sample_rate) >= 0) {
+ outsample = (outsample << 1) - input_sample_rate;
+ outbuffer_size += (input_buffer_size >> i);
+ } else {
+ outsample = outsample << 1;
+ }
+ i++;
+ }
+ outbuffer_size = (outbuffer_size >> 3) << 3;
+ return outbuffer_size;
+}
+
+
+static int mxc_asrc_prepare_input_buffer(struct asrc_pair_params *params,
struct asrc_convert_buffer *pbuf)
{
u32 word_size;
@@ -1109,10 +1227,38 @@ int mxc_asrc_prepare_input_buffer(struct asrc_pair_params *params,
}
-int mxc_asrc_prepare_output_buffer(struct asrc_pair_params *params,
+static 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;
+ u32 word_size;
+
+ pbuf->output_buffer_length = asrc_get_output_buffer_size(
+ pbuf->input_buffer_length,
+ params->input_sample_rate, params->output_sample_rate);
+ switch (params->output_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 word size!\n");
+ return -EINVAL;
+ }
+
+ if (pbuf->output_buffer_length <
+ ASRC_OUTPUT_LAST_SAMPLE * word_size * params->channel_nums) {
+ pr_err("output buffer size[%d] is wrong.\n",
+ pbuf->output_buffer_length);
+ return -EINVAL;
+ }
+
+ params->output_dma_total.length =
+ pbuf->output_buffer_length -
+ ASRC_OUTPUT_LAST_SAMPLE * word_size * params->channel_nums ;
+
params->output_sg_nodes =
params->output_dma_total.length / ASRC_MAX_BUFFER_SIZE + 1;
@@ -1146,11 +1292,20 @@ int mxc_asrc_process_output_buffer(struct asrc_pair_params *params,
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;
+ pbuf->output_buffer_length += params->output_last_period.length;
+
+ if (copy_to_user((void __user *)(pbuf->output_buffer_vaddr +
+ params->output_dma_total.length),
+ params->output_last_period.dma_vaddr,
+ params->output_last_period.length))
+ return -EFAULT;
+
return 0;
}
@@ -1250,6 +1405,9 @@ static long asrc_ioctl(struct file *file,
params->input_word_width = config.input_word_width;
params->output_word_width = config.output_word_width;
+ params->input_sample_rate = config.input_sample_rate;
+ params->output_sample_rate = config.output_sample_rate;
+
err = mxc_allocate_dma_buf(params);
if (err < 0)
break;
@@ -1297,32 +1455,6 @@ static long asrc_ioctl(struct file *file,
err = -EFAULT;
break;
}
- case ASRC_QUERYBUF:
- {
- struct asrc_querybuf buffer;
- unsigned int index_n;
- if (copy_from_user
- (&buffer, (void __user *)arg,
- sizeof(struct asrc_querybuf))) {
- err = -EFAULT;
- break;
- }
- index_n = buffer.buffer_index;
-
- buffer.input_offset = (unsigned long)
- params->input_dma[index_n].dma_paddr;
- buffer.input_length = params->input_buffer_size;
-
- buffer.output_offset = (unsigned long)
- params->output_dma[index_n].dma_paddr;
- buffer.output_length = params->output_buffer_size;
-
- if (copy_to_user
- ((void __user *)arg, &buffer,
- sizeof(struct asrc_querybuf)))
- err = -EFAULT;
- break;
- }
case ASRC_RELEASE_PAIR:
{
enum asrc_pair_index index;
@@ -1341,6 +1473,7 @@ static long asrc_ioctl(struct file *file,
mxc_free_dma_buf(params);
asrc_release_pair(index);
asrc_finish_conv(index);
+ params->asrc_active = 0;
params->pair_hold = 0;
break;
}
diff --git a/include/linux/mxc_asrc.h b/include/linux/mxc_asrc.h
index 28137a0ce104..d1b313699477 100644
--- a/include/linux/mxc_asrc.h
+++ b/include/linux/mxc_asrc.h
@@ -27,7 +27,6 @@
#define ASRC_REQ_PAIR _IOWR(ASRC_IOC_MAGIC, 0, struct asrc_req)
#define ASRC_CONFIG_PAIR _IOWR(ASRC_IOC_MAGIC, 1, struct asrc_config)
#define ASRC_RELEASE_PAIR _IOW(ASRC_IOC_MAGIC, 2, enum asrc_pair_index)
-#define ASRC_QUERYBUF _IOWR(ASRC_IOC_MAGIC, 3, struct asrc_buffer)
#define ASRC_Q_INBUF _IOW(ASRC_IOC_MAGIC, 4, struct asrc_buffer)
#define ASRC_DQ_INBUF _IOW(ASRC_IOC_MAGIC, 5, struct asrc_buffer)
#define ASRC_Q_OUTBUF _IOW(ASRC_IOC_MAGIC, 6, struct asrc_buffer)
@@ -146,11 +145,11 @@ enum asrc_error_status {
#include <linux/scatterlist.h>
#define ASRC_DMA_BUFFER_NUM 2
-#define ASRC_INPUTFIFO_THRESHOLD 32
-#define ASRC_OUTPUTFIFO_THRESHOLD 32
+#define ASRC_INPUTFIFO_THRESHOLD 4
+#define ASRC_OUTPUTFIFO_THRESHOLD 2
#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
-
+#define ASRC_OUTPUT_LAST_SAMPLE 8
#define ASRC_ASRCTR_REG 0x00
@@ -196,6 +195,15 @@ enum asrc_error_status {
#define ASRC_ASRMCR1C_REG 0xC8
+#define ASRC_ASRFSTX_INPUT_FIFO_WIDTH 7
+#define ASRC_ASRFSTX_INPUT_FIFO_OFFSET 0
+#define ASRC_ASRFSTX_INPUT_FIFO_MASK 0x7F
+
+#define ASRC_ASRFSTX_OUTPUT_FIFO_WIDTH 7
+#define ASRC_ASRFSTX_OUTPUT_FIFO_OFFSET 12
+#define ASRC_ASRFSTX_OUTPUT_FIFO_MASK (0x7F << ASRC_ASRFSTX_OUTPUT_FIFO_OFFSET)
+
+
struct dma_block {
unsigned int index;
unsigned int length;
@@ -227,6 +235,7 @@ struct asrc_pair_params {
struct dma_block input_dma[ASRC_DMA_BUFFER_NUM];
struct dma_block output_dma_total;
struct dma_block output_dma[ASRC_DMA_BUFFER_NUM];
+ struct dma_block output_last_period;
struct dma_async_tx_descriptor *desc_in;
struct dma_async_tx_descriptor *desc_out;
struct work_struct task_output_work;
@@ -235,6 +244,8 @@ struct asrc_pair_params {
struct scatterlist input_sg[4], output_sg[4];
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
+ u32 input_sample_rate;
+ u32 output_sample_rate;
};
struct asrc_data {