diff options
-rw-r--r-- | drivers/mxc/asrc/mxc_asrc.c | 211 | ||||
-rw-r--r-- | include/linux/mxc_asrc.h | 19 |
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(¶ms->input_wait_queue); spin_unlock_irqrestore(&input_int_lock, lock_flags); + schedule_work(¶ms->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(¶ms->output_wait_queue); - spin_unlock_irqrestore(&output_int_lock, lock_flags); - - schedule_work(¶ms->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(¶ms->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 = ¶ms->input_dma_total; output_a = ¶ms->output_dma_total; + last_period = ¶ms->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 { |