summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJingchang Lu <b35083@freescale.com>2012-12-05 13:19:53 +0800
committerEd Nash <enash@enash-desktop.(none)>2012-12-12 14:46:31 -0500
commit6dedc932b4d43948145f8d3a0e56f7b3e7b65354 (patch)
tree9c9ab5bdd0f223523f6067b7420c15fa9b4ca0ec /drivers
parent3e67deec905904e2786129bfbffea1e20b18a8fe (diff)
ENGR00216087-1: ASRC support for Vybrid
The ASRC driver supports stereo audio streams resample, the ASRCK1 serial clock using audio external clock source can not support 44.1K sample rate due to divider and prescaler restriction. Signed-off-by: Jingchang Lu <b35083@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mxc/asrc/Kconfig2
-rw-r--r--drivers/mxc/asrc/Makefile4
-rw-r--r--drivers/mxc/asrc/mvf_asrc.c1941
3 files changed, 1946 insertions, 1 deletions
diff --git a/drivers/mxc/asrc/Kconfig b/drivers/mxc/asrc/Kconfig
index 017042126f8d..743b6cdbbc63 100644
--- a/drivers/mxc/asrc/Kconfig
+++ b/drivers/mxc/asrc/Kconfig
@@ -6,7 +6,7 @@ menu "MXC Asynchronous Sample Rate Converter support"
config MXC_ASRC
tristate "ASRC support"
- depends on ARCH_MX35 || ARCH_MX53 || ARCH_MX6
+ depends on ARCH_MX35 || ARCH_MX53 || ARCH_MX6 || ARCH_MVF
---help---
Say Y to get the ASRC service.
diff --git a/drivers/mxc/asrc/Makefile b/drivers/mxc/asrc/Makefile
index 7e9aba3abb56..afc7be8e0a7d 100644
--- a/drivers/mxc/asrc/Makefile
+++ b/drivers/mxc/asrc/Makefile
@@ -1,4 +1,8 @@
#
# Makefile for the kernel Asynchronous Sample Rate Converter driver
#
+ifdef CONFIG_ARCH_MVF
+obj-$(CONFIG_MXC_ASRC) += mvf_asrc.o
+else
obj-$(CONFIG_MXC_ASRC) += mxc_asrc.o
+endif
diff --git a/drivers/mxc/asrc/mvf_asrc.c b/drivers/mxc/asrc/mvf_asrc.c
new file mode 100644
index 000000000000..fc3f5dfa4f9a
--- /dev/null
+++ b/drivers/mxc/asrc/mvf_asrc.c
@@ -0,0 +1,1941 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Vybrid ASRC driver based on mxc_asrc.c
+ *
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxc_asrc.h>
+#include <linux/fsl_devices.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/memory.h>
+#include <mach/mvf.h>
+#include <mach/dma.h>
+#include <mach/mxc_asrc.h>
+#include <mach/mcf_edma.h>
+
+static int asrc_major;
+static struct class *asrc_class;
+#define ASRC_PROC_PATH "driver/asrc"
+
+#define ASRC_RATIO_DECIMAL_DEPTH 26
+
+DEFINE_SPINLOCK(data_lock);
+DEFINE_SPINLOCK(input_int_lock);
+DEFINE_SPINLOCK(output_int_lock);
+
+#if 1
+#define DBG(x...)
+#else
+#define DBG(x...) printk(x)
+#endif
+
+/* ASRC control register bits */
+/* pair C/B/A automatic selection for processing options */
+#define ASRCTR_ATSC (0x01 << 22)
+#define ASRCTR_ATSB (0x01 << 21)
+#define ASRCTR_ATSA (0x01 << 20)
+#define ASRCTR_USRC (0x01 << 18) /* use ratio for pair C */
+#define ASRCTR_IDRC (0x01 << 17) /* use ideal ratio for pair C */
+#define ASRCTR_USRB (0x01 << 16) /* use ratio for pair B */
+#define ASRCTR_IDRB (0x01 << 15) /* use ideal ratio for pair B */
+#define ASRCTR_USRA (0x01 << 14) /* use ratio for pair A */
+#define ASRCTR_IDRA (0x01 << 13) /* use ideal ratio for pair A */
+#define ASRCTR_SRST (0x01 << 4) /* software reset */
+#define ASRCTR_ASREC (0x01 << 3) /* ASRC enable C */
+#define ASRCTR_ASREB (0x01 << 2) /* ASRC enable B */
+#define ASRCTR_ASREA (0x01 << 1) /* ASRC enable A */
+#define ASRCTR_ASRCEN (0x01 << 0) /* ASRC enable */
+
+/* clock divider register bits */
+#define AICPA 0 /* Input Clock Divider A Offset */
+#define AICDA 3 /* Input Clock Prescaler A Offset */
+#define AICPB 6 /* Input Clock Divider B Offset */
+#define AICDB 9 /* Input Clock Prescaler B Offset */
+#define AOCPA 12 /* Output Clock Divider A Offset */
+#define AOCDA 15 /* Output Clock Prescaler A Offset */
+#define AOCPB 18 /* Output Clock Divider B Offset */
+#define AOCDB 21 /* Output Clock Prescaler B Offset */
+#define AICPC 0 /* Input Clock Divider C Offset */
+#define AICDC 3 /* Input Clock Prescaler C Offset */
+#define AOCDC 6 /* Output Clock Prescaler C Offset */
+#define AOCPC 9 /* Output Clock Divider C Offset */
+
+char *asrc_pair_id[] = {
+ [0] = "ASRC RX PAIR A",
+ [1] = "ASRC TX PAIR A",
+ [2] = "ASRC RX PAIR B",
+ [3] = "ASRC TX PAIR B",
+ [4] = "ASRC RX PAIR C",
+ [5] = "ASRC TX PAIR C",
+};
+
+enum asrc_status {
+ ASRC_ASRSTR_AIDEA = 0x01,
+ ASRC_ASRSTR_AIDEB = 0x02,
+ ASRC_ASRSTR_AIDEC = 0x04,
+ ASRC_ASRSTR_AODFA = 0x08,
+ ASRC_ASRSTR_AODFB = 0x10,
+ ASRC_ASRSTR_AODFC = 0x20,
+ ASRC_ASRSTR_AOLE = 0x40,
+ ASRC_ASRSTR_FPWT = 0x80,
+ ASRC_ASRSTR_AIDUA = 0x100,
+ ASRC_ASRSTR_AIDUB = 0x200,
+ ASRC_ASRSTR_AIDUC = 0x400,
+ ASRC_ASRSTR_AODOA = 0x800,
+ ASRC_ASRSTR_AODOB = 0x1000,
+ ASRC_ASRSTR_AODOC = 0x2000,
+ ASRC_ASRSTR_AIOLA = 0x4000,
+ ASRC_ASRSTR_AIOLB = 0x8000,
+ ASRC_ASRSTR_AIOLC = 0x10000,
+ ASRC_ASRSTR_AOOLA = 0x20000,
+ ASRC_ASRSTR_AOOLB = 0x40000,
+ ASRC_ASRSTR_AOOLC = 0x80000,
+ ASRC_ASRSTR_ATQOL = 0x100000,
+ ASRC_ASRSTR_DSLCNT = 0x200000,
+};
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const unsigned char asrc_process_table[][8][2] = {
+ /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 128kHz 192kHz */
+/*8kHz*/
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*11025Hz*/
+ {{0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*16kHz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*22050Hz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*32kHz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},
+/*44.1kHz*/
+ {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},
+/*48kHz*/
+ {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},
+/*64kHz*/
+ {{0, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},
+/*88.2kHz*/
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*96kHz*/
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*128kHz*/
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*192kHz*/
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},
+};
+
+static struct asrc_data *g_asrc_data;
+static struct proc_dir_entry *proc_asrc;
+static unsigned long asrc_vrt_base_addr;
+static unsigned long asrc_phy_base_addr;
+static struct imx_asrc_platform_data *mxc_asrc_data;
+struct dma_async_tx_descriptor *desc_in;
+struct dma_async_tx_descriptor *desc_out;
+static int asrc_dmarx[3];
+static int asrc_dmatx[3];
+#ifdef CONFIG_ARCH_MVF
+static struct dma_async_tx_descriptor dummy_desc;
+#endif
+
+/* The following tables map the relationship between asrc_inclk/asrc_outclk in
+ * mxc_asrc.h and the registers of ASRCSR
+ */
+static unsigned char input_clk_map_v1[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char output_clk_map_v1[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char input_clk_map_v2[] = {
+ 0, 1, 2, 3, 4, 5, 0xf, 0xf, 0xf, 8, 9, 0xa, 0xb, 0xc, 0xf, 0xd,
+};
+
+static unsigned char output_clk_map_v2[] = {
+ 8, 9, 0xa, 0, 0xc, 0x5, 0xf, 0xf, 0, 1, 2, 0xf, 0xf, 4, 0xf, 0xd,
+};
+
+static unsigned char input_clk_map_v3[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 0, 2, 3, 0xf, 5, 6, 1, 4, 7, 0xd, 0x0f, 9, 0xa, 8, 0xf, 0xc,
+};
+
+static unsigned char output_clk_map_v3[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 7, 0xd, 0xf, 0xf, 0xa, 6, 8, 1, 0, 2, 3, 4, 5, 9, 0xf, 0xc,
+};
+
+static unsigned char *input_clk_map, *output_clk_map;
+
+#ifdef CONFIG_ARCH_MVF
+static struct dma_async_tx_descriptor *imx_asrc_dma_config(u32 ch, u32 dma_addr,
+ void *buf_addr, u32 buf_len, int in);
+#endif
+/* Ideal Ratio calc */
+static int asrc_set_clock_ratio(enum asrc_pair_index index,
+ int input_sample_rate, int output_sample_rate)
+{
+ int i;
+ int integ = 0;
+ unsigned long reg_val = 0;
+
+ DBG("%s:in/out sample rate = %d/%d\n", __func__,
+ input_sample_rate, output_sample_rate);
+
+ if (output_sample_rate == 0)
+ return -1;
+ while (input_sample_rate >= output_sample_rate) {
+ input_sample_rate -= output_sample_rate;
+ integ++;
+ }
+ reg_val |= (integ << 26);
+
+ for (i = 1; i <= ASRC_RATIO_DECIMAL_DEPTH; i++) {
+ if ((input_sample_rate * 2) >= output_sample_rate) {
+ reg_val |= (1 << (ASRC_RATIO_DECIMAL_DEPTH - i));
+ input_sample_rate =
+ input_sample_rate * 2 - output_sample_rate;
+ } else
+ input_sample_rate = input_sample_rate << 1;
+
+ if (input_sample_rate == 0)
+ break;
+ }
+
+ __raw_writel(reg_val,
+ (asrc_vrt_base_addr + ASRC_ASRIDRLA_REG + (index << 3)));
+ __raw_writel((reg_val >> 24),
+ (asrc_vrt_base_addr + ASRC_ASRIDRHA_REG + (index << 3)));
+ return 0;
+}
+
+static int asrc_set_process_configuration(enum asrc_pair_index index,
+ int input_sample_rate,
+ int output_sample_rate)
+{
+ int i = 0, j = 0;
+ unsigned long reg;
+
+ DBG("%s:in/out sample rate = %d/%d\n", __func__,
+ input_sample_rate, output_sample_rate);
+
+ switch (input_sample_rate) {
+ case 8000:
+ i = 0;
+ break;
+ case 11025:
+ i = 1;
+ break;
+ case 16000:
+ i = 2;
+ break;
+ case 22050:
+ i = 3;
+ break;
+ case 32000:
+ i = 4;
+ break;
+ case 44100:
+ i = 5;
+ break;
+ case 48000:
+ i = 6;
+ break;
+ case 64000:
+ i = 7;
+ break;
+ case 88200:
+ i = 8;
+ break;
+ case 96000:
+ i = 9;
+ break;
+ case 128000:
+ i = 10;
+ break;
+ case 192000:
+ i = 11;
+ break;
+ default:
+ return -1;
+ }
+
+ switch (output_sample_rate) {
+ case 32000:
+ j = 0;
+ break;
+ case 44100:
+ j = 1;
+ break;
+ case 48000:
+ j = 2;
+ break;
+ case 64000:
+ j = 3;
+ break;
+ case 88200:
+ j = 4;
+ break;
+ case 96000:
+ j = 5;
+ break;
+ case 128000:
+ j = 6;
+ break;
+ case 192000:
+ j = 7;
+ break;
+ default:
+ return -1;
+ }
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+ reg &= ~(0x0f << (6 + (index << 2)));
+ reg |=
+ ((asrc_process_table[i][j][0] << (6 + (index << 2))) |
+ (asrc_process_table[i][j][1] << (8 + (index << 2))));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+
+ return 0;
+}
+
+static int asrc_get_asrck_clock_divider(int samplerate)
+{
+ unsigned int prescaler, divider;
+ unsigned int i;
+ unsigned int ratio, ra;
+ unsigned long bitclk = clk_get_rate(mxc_asrc_data->asrc_audio_clk);
+
+ DBG("%s:bitclk/sample rate = %lu/%d\n", __func__,
+ bitclk, samplerate);
+
+ ra = bitclk/samplerate;
+ ratio = ra;
+ /*calculate the prescaler*/
+ i = 0;
+ while (ratio > 8) {
+ i++;
+ ratio = ratio >> 1;
+ }
+ prescaler = i;
+ /*calculate the divider*/
+ if (i >= 1)
+ divider = ((ra + (1 << (i - 1)) - 1) >> i) - 1;
+ else
+ divider = ra - 1;
+
+ /*the totally divider is (2^prescaler)*divider*/
+ return (divider << 3) + prescaler;
+}
+
+int asrc_req_pair(int chn_num, enum asrc_pair_index *index)
+{
+ int err = 0;
+ unsigned long lock_flags;
+ spin_lock_irqsave(&data_lock, lock_flags);
+
+ if (chn_num > 2) {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active
+ || (chn_num > g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_max))
+ err = -EBUSY;
+ else {
+ *index = ASRC_PAIR_C;
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_num = chn_num;
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].active = 1;
+ }
+ } else {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active ||
+ (g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_max == 0)) {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+ active
+ || (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+ chn_max == 0))
+ err = -EBUSY;
+ else {
+ *index = ASRC_PAIR_B;
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].chn_num = 2;
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].active = 1;
+ }
+ } else {
+ *index = ASRC_PAIR_A;
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_num = 2;
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].active = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return err;
+}
+EXPORT_SYMBOL(asrc_req_pair);
+
+void asrc_release_pair(enum asrc_pair_index index)
+{
+ unsigned long reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+ g_asrc_data->asrc_pair[index].active = 0;
+ g_asrc_data->asrc_pair[index].overload_error = 0;
+ /********Disable PAIR*************/
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg &= ~(1 << (index + 1));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+}
+EXPORT_SYMBOL(asrc_release_pair);
+
+int asrc_config_pair(struct asrc_config *config)
+{
+ int err = 0;
+ int reg, tmp, channel_num;
+ unsigned long lock_flags;
+
+ DBG("%s\n", __func__);
+
+ /* Set the channel number */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ spin_lock_irqsave(&data_lock, lock_flags);
+ g_asrc_data->asrc_pair[config->pair].chn_num = config->channel_num;
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ reg &=
+ ~((0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits)) <<
+ (mxc_asrc_data->channel_bits * config->pair));
+ if (mxc_asrc_data->channel_bits > 3)
+ channel_num = config->channel_num;
+ else
+ channel_num = (config->channel_num + 1) / 2;
+ tmp = channel_num << (mxc_asrc_data->channel_bits * config->pair);
+ reg |= tmp;
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+#if 0
+ /* Set processing options */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ /* disable automatic selection for processing options */
+ reg &= ~(1 << (20 + config->pair));
+ /* enable idea ratio for pair */
+ reg |= (0x3 << (13 + (config->pair << 1)));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+#endif
+ /* Set the clock source */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+ tmp = ~(0x0f << (config->pair << 2));
+ reg &= tmp;
+ tmp = ~(0x0f << (12 + (config->pair << 2)));
+ reg &= tmp;
+ reg |=
+ ((input_clk_map[config->inclk] << (config->pair << 2)) |
+ (output_clk_map[config->outclk] << (12 + (config->pair << 2))));
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+
+ /* config Misc Control Register 1 for Pair X
+ * all FIFO data align LSB without sign extension
+ */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRMCR1A_REG +
+ config->pair * 4);
+ reg &= ~((0x0F << 8) | 0x06);
+ if (config->word_width == 16) {
+ /* 16-bits output */
+ reg |= 0x01;
+ /* 16-bits input */
+ reg |= (0x01 << 9);
+ } else {
+ /* 24-bits output */
+ reg &= ~0x01;
+ /* 24-bits input */
+ }
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRMCR1A_REG +
+ config->pair * 4);
+
+ /* Default Clock Divider Setting */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ if (config->pair == ASRC_PAIR_A) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ reg &= 0xfc0fc0;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+ reg |= 7 << AICPA;
+ else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+ reg |= 6 << AICPA;
+ else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPA;
+ } else if ((config->inclk & 0x0F) == INCLK_NONE) {
+ /* do nothing, will use ideal ratio for convert */
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPA;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPA;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+ reg |= 7 << AOCPA;
+ else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+ reg |= 6 << AOCPA;
+ else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPA;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPA;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPA;
+ else
+ err = -EFAULT;
+ }
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+ } else if (config->pair == ASRC_PAIR_B) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ reg &= 0x03f03f;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+ reg |= 7 << AICPB;
+ else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+ reg |= 6 << AICPB;
+ else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPB;
+ } else if ((config->inclk & 0x0F) == INCLK_NONE) {
+ /* pass through */
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPB;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPB;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+ reg |= 7 << AOCPB;
+ else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+ reg |= 6 << AOCPB;
+ else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPB;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPB;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPB;
+ else
+ err = -EFAULT;
+ }
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+ } else {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+ reg &= 0;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+ reg |= 7 << AICPC;
+ else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+ reg |= 6 << AICPC;
+ else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPC;
+ } else if ((config->inclk & 0x0F) == INCLK_NONE) {
+ /* pass through */
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPC;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPC;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+ reg |= 7 << AOCPC;
+ else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+ reg |= 6 << AOCPC;
+ else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPC;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPC;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPC;
+ else
+ err = -EFAULT;
+ }
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+
+ }
+
+ /* check whether ideal ratio is a must */
+ if ((config->inclk & 0x0f) == INCLK_NONE) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ /* clear ATSC */
+ reg &= ~(1 << (20 + config->pair));
+ reg |= (0x03 << (13 + (config->pair << 1)));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ err = asrc_set_clock_ratio(config->pair,
+ config->input_sample_rate,
+ config->output_sample_rate);
+ if (err < 0)
+ return err;
+
+ err = asrc_set_process_configuration(config->pair,
+ config->input_sample_rate,
+ config->
+ output_sample_rate);
+ if (err < 0)
+ return err;
+
+ } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ if (config->input_sample_rate < 24000) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ if (config->input_sample_rate == 44100
+ || config->input_sample_rate == 88200) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ } else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ if (config->output_sample_rate < 24000) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ if (config->output_sample_rate == 44100
+ || config->output_sample_rate == 88200) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(asrc_config_pair);
+
+void asrc_start_conv(enum asrc_pair_index index)
+{
+ int reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+
+ __raw_writel(0x40, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ reg |= ASRCTR_ASRCEN; /* enable the ASRC */
+ reg |= ASRCTR_ASREA << index; /* enable pair A or B or C convert */
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+
+ return;
+}
+EXPORT_SYMBOL(asrc_start_conv);
+
+void asrc_stop_conv(enum asrc_pair_index index)
+{
+ unsigned long reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+
+ __raw_writel(0, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg &= ~(0x1 << (1 + index));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return;
+}
+EXPORT_SYMBOL(asrc_stop_conv);
+
+/*!
+ * @brief asrc interrupt handler
+ */
+static irqreturn_t asrc_isr(int irq, void *dev_id)
+{
+ unsigned long status;
+
+ status = __raw_readl(asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ } else if (g_asrc_data->asrc_pair[ASRC_PAIR_B].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ } else if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ }
+
+ /* try to clean the overload error */
+ __raw_writel(status, asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+ return IRQ_HANDLED;
+}
+
+void asrc_get_status(struct asrc_status_flags *flags)
+{
+ unsigned long lock_flags;
+ enum asrc_pair_index index;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+ index = flags->index;
+ flags->overload_error = g_asrc_data->asrc_pair[index].overload_error;
+
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return;
+}
+EXPORT_SYMBOL(asrc_get_status);
+
+static int mxc_init_asrc(void)
+{
+ DBG("%s\n", __func__);
+
+ __raw_writel(ASRCTR_ASRCEN, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ /* do a soft reset for ASRC on Faraday */
+ __raw_writel(ASRCTR_SRST | ASRCTR_ASRCEN,
+ asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ /* wait for reset finished */
+ while (__raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG) & ASRCTR_SRST)
+ ;
+
+ /* Disable all interrupt */
+ __raw_writel(0x00, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+
+ /* Default 6: 2: 2 channel assignment for pair C, B, A */
+ __raw_writel((0x06 << mxc_asrc_data->channel_bits *
+ 2) | (0x02 << mxc_asrc_data->channel_bits) | 0x02,
+ asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+ /* Parameter Registers recommended settings */
+ __raw_writel(0x7fffff, asrc_vrt_base_addr + ASRC_ASRPM1_REG);
+ __raw_writel(0x255555, asrc_vrt_base_addr + ASRC_ASRPM2_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM3_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM4_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM5_REG);
+
+ __raw_writel(0x001f00, asrc_vrt_base_addr + ASRC_ASRTFR1);
+
+ /* 132M ipg bus clock */
+ __raw_writel(0x6C8, asrc_vrt_base_addr + ASRC_ASR76K_REG);
+ __raw_writel(0x935, asrc_vrt_base_addr + ASRC_ASR56K_REG);
+
+ return 0;
+}
+
+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 >> 5) << 5;
+
+ return outbuffer_size;
+}
+
+static irqreturn_t asrc_input_dma_callback(int ch, void *data)
+{
+ struct asrc_pair_params *params;
+ struct dma_block *block;
+ u32 dma_addr;
+ void *buf_addr;
+
+ params = (struct asrc_pair_params *)data;
+
+ params->input_queue_empty--;
+ if (!list_empty(&params->input_queue)) {
+ block =
+ list_entry(params->input_queue.next,
+ struct dma_block, queue);
+ dma_addr =
+ (asrc_phy_base_addr + ASRC_ASRDIA_REG +
+ (params->index << 3));
+ buf_addr = block->dma_vaddr;
+ desc_in = imx_asrc_dma_config(
+ params->input_dma_channel,
+ dma_addr, buf_addr,
+ block->length, 1);
+ if (!desc_in)
+ printk(KERN_DEBUG "%s:%d failed to config dma\n",
+ __func__, __LINE__);
+ list_del(params->input_queue.next);
+ list_add_tail(&block->queue, &params->input_done_queue);
+ params->input_queue_empty++;
+
+ mcf_edma_enable_transfer(params->input_dma_channel);
+
+ }
+ params->input_counter++;
+ wake_up_interruptible(&params->input_wait_queue);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t asrc_output_dma_callback(int ch, void *data)
+{
+ struct asrc_pair_params *params;
+ struct dma_block *block;
+ u32 dma_addr;
+ void *buf_addr;
+
+ params = (struct asrc_pair_params *)data;
+
+ params->output_queue_empty--;
+
+ if (!list_empty(&params->output_queue)) {
+ block =
+ list_entry(params->output_queue.next,
+ struct dma_block, queue);
+
+ dma_addr =
+ (asrc_phy_base_addr + ASRC_ASRDOA_REG +
+ (params->index << 3));
+ buf_addr = block->dma_vaddr;
+ desc_out = imx_asrc_dma_config(
+ params->output_dma_channel,
+ dma_addr, buf_addr,
+ block->length, 0);
+ if (!desc_out)
+ printk(KERN_DEBUG "%s:%d failed to config dma\n",
+ __func__, __LINE__);
+
+ list_del(params->output_queue.next);
+ list_add_tail(&block->queue, &params->output_done_queue);
+ params->output_queue_empty++;
+
+ mcf_edma_enable_transfer(params->output_dma_channel);
+
+ }
+ params->output_counter++;
+
+ wake_up_interruptible(&params->output_wait_queue);
+ return IRQ_HANDLED;
+}
+
+static void mxc_free_dma_buf(struct asrc_pair_params *params)
+{
+ int i;
+
+ if (params->input_dma[0].dma_vaddr != NULL) {
+ kfree(params->input_dma[0].dma_vaddr);
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->input_dma[i].dma_vaddr = NULL;
+ params->input_dma[i].dma_paddr = 0;
+ }
+ }
+ if (params->output_dma[0].dma_vaddr != NULL) {
+ kfree(params->output_dma[0].dma_vaddr);
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->output_dma[i].dma_vaddr = NULL;
+ params->output_dma[i].dma_paddr = 0;
+ }
+ }
+
+ return;
+}
+
+static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
+{
+ int i;
+
+ params->input_dma[0].dma_vaddr = NULL;
+ params->input_dma[0].dma_vaddr =
+ kzalloc(PAGE_ALIGN(params->input_buffer_size) *
+ ASRC_DMA_BUFFER_NUM,
+ GFP_DMA/*GFP_KERNEL*/);
+
+ if (!params->input_dma[0].dma_vaddr) {
+ mxc_free_dma_buf(params);
+ pr_info("can't allocate buff\n");
+ return -ENOBUFS;
+ }
+
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->input_dma[i].dma_vaddr =
+ (unsigned char *)
+ ((unsigned long)params->input_dma[0].dma_vaddr +
+ i * PAGE_ALIGN(params->input_buffer_size));
+ params->input_dma[i].dma_paddr =
+ virt_to_dma(NULL, params->input_dma[i].dma_vaddr);
+ if (params->input_dma[i].dma_vaddr == NULL) {
+ mxc_free_dma_buf(params);
+ pr_info("can't allocate input buff\n");
+ return -ENOBUFS;
+ }
+ }
+
+ params->output_dma[0].dma_vaddr = NULL;
+ params->output_dma[0].dma_vaddr =
+ kzalloc(PAGE_ALIGN(params->output_buffer_size) *
+ ASRC_DMA_BUFFER_NUM,
+ GFP_DMA/*GFP_KERNEL*/);
+ if (!params->output_dma[0].dma_vaddr) {
+ mxc_free_dma_buf(params);
+ pr_info("can't allocate buff\n");
+ return -ENOBUFS;
+ }
+
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->output_dma[i].dma_vaddr =
+ (unsigned char *)
+ ((unsigned long)params->output_dma[0].dma_vaddr +
+ i * PAGE_ALIGN(params->output_buffer_size));
+ params->output_dma[i].dma_paddr =
+ virt_to_dma(NULL, params->output_dma[i].dma_vaddr);
+ if (params->output_dma[i].dma_vaddr == NULL) {
+ mxc_free_dma_buf(params);
+ pr_info("can't allocate output buff\n");
+ return -ENOBUFS;
+ }
+ }
+ return 0;
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+
+ chan->private = param;
+ return true;
+}
+
+
+#ifdef CONFIG_ARCH_MVF
+static struct dma_async_tx_descriptor *imx_asrc_dma_config(u32 ch,
+ u32 dma_addr, void *buf_addr,
+ u32 buf_len, int in)
+{
+ struct scatterlist sg;
+ u32 src, dst;
+ int ret;
+
+ if (buf_len <= 0) {
+ printk(KERN_INFO " asrc config dma size error %d\n", buf_len);
+ return NULL;
+ }
+ /* make sure the eDMA channel is stopped */
+ mcf_edma_stop_transfer(ch);
+
+ sg_init_one(&sg, buf_addr, buf_len);
+ ret = dma_map_sg(NULL, &sg, 1, in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (ret != 1)
+ return NULL;
+ if (in) { /* input: buffer to engine */
+ dst = dma_addr; /* reg IN */
+ src = (u32)sg_phys(&sg);
+ mcf_edma_set_tcd_params(ch,
+ src, dst,
+ MCF_EDMA_TCD_ATTR_SSIZE_32BIT |
+ MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
+ 4, /* soff */
+ 4 * 32, /* nbyte */
+ 0, /* slast */
+ buf_len/(4 * 32), /* citer */
+ buf_len/(4 * 32), /* biter */
+ 0, 0, /* doff, dlast */
+ 1, 1, 0); /* major int, disable request, sg */
+ /*DBG("config dma(%d) IN, cnt=%d, buf=%#x\n", ch, buf_len/4, src);*/
+
+ } else { /* output: engine to buffer */
+ dst = (u32)sg_phys(&sg);
+ src = dma_addr;
+ mcf_edma_set_tcd_params(ch,
+ src, dst,
+ MCF_EDMA_TCD_ATTR_SSIZE_32BIT |
+ MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
+ 0,
+ 4 * 32,
+ 0,
+ buf_len/(4 * 32),
+ buf_len/(4 * 32),
+ 4, 0,
+ 1, 1, 0);
+ /*DBG("config dma(%d) OUT, cnt=%d, buf=%#x\n", ch, buf_len/4, dst);*/
+ }
+
+ return &dummy_desc;
+}
+#endif
+
+/*!
+ * asrc interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param cmd unsigned int
+ *
+ * @param arg unsigned long
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static long asrc_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct asrc_pair_params *params;
+ params = file->private_data;
+ if (down_interruptible(&params->busy_lock))
+ return -EBUSY;
+ switch (cmd) {
+ case ASRC_REQ_PAIR:
+ {
+ struct asrc_req req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct asrc_req))) {
+ err = -EFAULT;
+ break;
+ }
+ err = asrc_req_pair(req.chn_num, &req.index);
+ if (err < 0)
+ break;
+ params->pair_hold = 1;
+ params->index = req.index;
+ if (copy_to_user
+ ((void __user *)arg, &req, sizeof(struct asrc_req)))
+ err = -EFAULT;
+
+ break;
+ }
+ case ASRC_CONFIG_PAIR:
+ {
+ struct asrc_config config;
+ u32 rx_id, tx_id;
+ char *rx_name, *tx_name;
+ if (copy_from_user
+ (&config, (void __user *)arg,
+ sizeof(struct asrc_config))) {
+ err = -EFAULT;
+ break;
+ }
+ err = asrc_config_pair(&config);
+ if (err < 0)
+ break;
+ params->output_buffer_size =
+ asrc_get_output_buffer_size(config.
+ dma_buffer_size,
+ config.
+ input_sample_rate,
+ config.
+ output_sample_rate);
+ params->input_buffer_size = config.dma_buffer_size;
+ if (config.buffer_num > ASRC_DMA_BUFFER_NUM)
+ params->buffer_num = ASRC_DMA_BUFFER_NUM;
+ else
+ params->buffer_num = config.buffer_num;
+ err = mxc_allocate_dma_buf(params);
+ if (err < 0)
+ break;
+
+ /* TBD - need to update when new SDMA interface ready */
+ if (config.pair == ASRC_PAIR_A) {
+ rx_id = asrc_dmarx[ASRC_PAIR_A];
+ tx_id = asrc_dmatx[ASRC_PAIR_A];
+ rx_name = asrc_pair_id[0];
+ tx_name = asrc_pair_id[1];
+ } else if (config.pair == ASRC_PAIR_B) {
+ rx_id = asrc_dmarx[ASRC_PAIR_B];
+ tx_id = asrc_dmatx[ASRC_PAIR_B];
+ rx_name = asrc_pair_id[2];
+ tx_name = asrc_pair_id[3];
+ } else {
+ rx_id = asrc_dmarx[ASRC_PAIR_C];
+ tx_id = asrc_dmatx[ASRC_PAIR_C];
+ rx_name = asrc_pair_id[4];
+ tx_name = asrc_pair_id[5];
+ }
+
+ params->input_dma_channel = mcf_edma_request_channel(
+ tx_id,
+ asrc_input_dma_callback, NULL,
+ 6, params, NULL, tx_name);
+
+ params->output_dma_channel = mcf_edma_request_channel(
+ rx_id,
+ asrc_output_dma_callback, NULL,
+ 6, params, NULL, rx_name);
+
+ params->input_queue_empty = 0;
+ params->output_queue_empty = 0;
+ INIT_LIST_HEAD(&params->input_queue);
+ INIT_LIST_HEAD(&params->input_done_queue);
+ INIT_LIST_HEAD(&params->output_queue);
+ INIT_LIST_HEAD(&params->output_done_queue);
+ init_waitqueue_head(&params->input_wait_queue);
+ init_waitqueue_head(&params->output_wait_queue);
+
+ if (copy_to_user
+ ((void __user *)arg, &config,
+ sizeof(struct asrc_config)))
+ err = -EFAULT;
+ break;
+ }
+ case ASRC_QUERYBUF:
+ {
+ struct asrc_querybuf buffer;
+ if (copy_from_user
+ (&buffer, (void __user *)arg,
+ sizeof(struct asrc_querybuf))) {
+ err = -EFAULT;
+ break;
+ }
+ buffer.input_offset =
+ (unsigned long)params->input_dma[buffer.
+ buffer_index].
+ dma_paddr;
+ buffer.input_length = params->input_buffer_size;
+ buffer.output_offset =
+ (unsigned long)params->output_dma[buffer.
+ buffer_index].
+ 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;
+ if (copy_from_user
+ (&index, (void __user *)arg,
+ sizeof(enum asrc_pair_index))) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (params->input_dma_channel)
+ mcf_edma_free_channel(
+ params->input_dma_channel, params);
+ if (params->output_dma_channel)
+ mcf_edma_free_channel(
+ params->output_dma_channel, params);
+
+ mxc_free_dma_buf(params);
+ asrc_release_pair(index);
+ params->pair_hold = 0;
+ break;
+ }
+ case ASRC_Q_INBUF: /* queue input buffer */
+ {
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ u32 dma_addr;
+ void *buf_addr;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_dma[buf.index].index = buf.index;
+ params->input_dma[buf.index].length = buf.length;
+ list_add_tail(&params->input_dma[buf.index].
+ queue, &params->input_queue);
+ if (params->input_queue_empty == 0) {
+ block =
+ list_entry(params->input_queue.next,
+ struct dma_block, queue);
+ dma_addr =
+ (asrc_phy_base_addr + ASRC_ASRDIA_REG +
+ (params->index << 3));
+ buf_addr = block->dma_vaddr;
+ spin_unlock_irqrestore(&input_int_lock,
+ lock_flags);
+ desc_in = imx_asrc_dma_config(
+ params->input_dma_channel,
+ dma_addr, buf_addr,
+ block->length, 1);
+
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_queue_empty++;
+ list_del(params->input_queue.next);
+ list_add_tail(&block->queue,
+ &params->input_done_queue);
+ if (params->asrc_active)
+ mcf_edma_enable_transfer(
+ params->input_dma_channel);
+ }
+
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ break;
+ }
+ case ASRC_DQ_INBUF:{ /* dequeue input buffer */
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ unsigned long lock_flags;
+ 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) {
+ 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--;
+ block =
+ list_entry(params->input_done_queue.next,
+ struct dma_block, queue);
+ list_del(params->input_done_queue.next);
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ 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;
+
+ break;
+ }
+ case ASRC_Q_OUTBUF:{ /* queue output buffer */
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ u32 dma_addr;
+ void *buf_addr;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_dma[buf.index].index = buf.index;
+ params->output_dma[buf.index].length = buf.length;
+ list_add_tail(&params->output_dma[buf.index].
+ queue, &params->output_queue);
+ if (params->output_queue_empty == 0) {
+ block =
+ list_entry(params->output_queue.
+ next, struct dma_block, queue);
+ dma_addr =
+ (asrc_phy_base_addr + ASRC_ASRDOA_REG +
+ (params->index << 3));
+ buf_addr = block->dma_vaddr;
+ spin_unlock_irqrestore(&output_int_lock,
+ lock_flags);
+
+ desc_out = imx_asrc_dma_config(
+ params->output_dma_channel,
+ dma_addr, buf_addr, block->length, 0);
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+
+ list_del(params->output_queue.next);
+ list_add_tail(&block->queue,
+ &params->output_done_queue);
+ params->output_queue_empty++;
+
+ if (params->asrc_active)
+ mcf_edma_enable_transfer(
+ params->output_dma_channel);
+ }
+
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+ break;
+ }
+ case ASRC_DQ_OUTBUF:{
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ unsigned long lock_flags;
+ 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;
+ 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--;
+ block =
+ list_entry(params->output_done_queue.next,
+ struct dma_block, queue);
+ list_del(params->output_done_queue.next);
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+ 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;
+
+ 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))) {
+ err = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ if (params->input_queue_empty == 0) {
+ err = -EFAULT;
+ pr_info
+ ("ASRC_START_CONV - no block available\n");
+ break;
+ }
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ params->asrc_active = 1;
+
+ mcf_edma_enable_transfer(params->output_dma_channel);
+ mcf_edma_enable_transfer(params->input_dma_channel);
+
+ asrc_start_conv(index);
+ break;
+ }
+ case ASRC_STOP_CONV:{
+ enum asrc_pair_index index;
+
+ if (copy_from_user
+ (&index, (void __user *)arg,
+ sizeof(enum asrc_pair_index))) {
+ err = -EFAULT;
+ break;
+ }
+ mcf_edma_stop_transfer(params->input_dma_channel);
+ mcf_edma_stop_transfer(params->output_dma_channel);
+ asrc_stop_conv(index);
+ params->asrc_active = 0;
+ break;
+ }
+ case ASRC_STATUS:{
+ struct asrc_status_flags flags;
+ if (copy_from_user
+ (&flags, (void __user *)arg,
+ sizeof(struct asrc_status_flags))) {
+ err = -EFAULT;
+ break;
+ }
+ asrc_get_status(&flags);
+ if (copy_to_user
+ ((void __user *)arg, &flags,
+ sizeof(struct asrc_status_flags)))
+ err = -EFAULT;
+ break;
+ }
+ case ASRC_FLUSH:{
+ /* flush input dma buffer */
+ unsigned long lock_flags;
+ u32 rx_id, tx_id;
+ char *rx_name, *tx_name;
+
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ while (!list_empty(&params->input_queue))
+ list_del(params->input_queue.next);
+ while (!list_empty(&params->input_done_queue))
+ list_del(params->input_done_queue.next);
+ params->input_counter = 0;
+ params->input_queue_empty = 0;
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+
+ /* flush output dma buffer */
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ while (!list_empty(&params->output_queue))
+ list_del(params->output_queue.next);
+ while (!list_empty(&params->output_done_queue))
+ list_del(params->output_done_queue.next);
+ params->output_counter = 0;
+ params->output_queue_empty = 0;
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+
+ /* release DMA and request again */
+ mcf_edma_free_channel(params->input_dma_channel,
+ params);
+ mcf_edma_free_channel(params->output_dma_channel,
+ params);
+
+ if (params->index == ASRC_PAIR_A) {
+ rx_id = asrc_dmarx[ASRC_PAIR_A];
+ tx_id = asrc_dmatx[ASRC_PAIR_A];
+ rx_name = asrc_pair_id[0];
+ tx_name = asrc_pair_id[1];
+ } else if (params->index == ASRC_PAIR_B) {
+ rx_id = asrc_dmarx[ASRC_PAIR_B];
+ tx_id = asrc_dmatx[ASRC_PAIR_B];
+ rx_name = asrc_pair_id[2];
+ tx_name = asrc_pair_id[3];
+ } else {
+ rx_id = asrc_dmarx[ASRC_PAIR_C];
+ tx_id = asrc_dmatx[ASRC_PAIR_C];
+ rx_name = asrc_pair_id[4];
+ tx_name = asrc_pair_id[5];
+ }
+ params->input_dma_channel = mcf_edma_request_channel(
+ tx_id, asrc_input_dma_callback, NULL,
+ 6, params, NULL, NULL);
+
+ if (params->input_dma_channel == 0) {
+ pr_err("unable to get input channel %d\n",
+ tx_id);
+ err = -EBUSY;
+ }
+
+ params->output_dma_channel = mcf_edma_request_channel(
+ rx_id, asrc_output_dma_callback, NULL,
+ 6, params, NULL, NULL);
+
+ if (params->output_dma_channel == 0) {
+ pr_err("unable to get output channel %d\n",
+ rx_id);
+ err = -EBUSY;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ up(&params->busy_lock);
+ return err;
+}
+
+/*!
+ * asrc interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENOBUFS failed to allocate buffer, ERESTARTSYS interrupted by user
+ */
+static int mxc_asrc_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct asrc_pair_params *pair_params;
+
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+ if (signal_pending(current))
+ return -EINTR;
+ pair_params = kzalloc(sizeof(struct asrc_pair_params), GFP_KERNEL);
+ if (pair_params == NULL) {
+ pr_debug("Failed to allocate pair_params\n");
+ err = -ENOBUFS;
+ }
+
+ sema_init(&pair_params->busy_lock, 1);
+ file->private_data = pair_params;
+
+ return err;
+}
+
+/*!
+ * asrc interface - close function
+ *
+ * @param inode struct inode *
+ * @param file structure file *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_close(struct inode *inode, struct file *file)
+{
+ struct asrc_pair_params *pair_params;
+ pair_params = file->private_data;
+
+ if (pair_params->asrc_active == 1) {
+ asrc_stop_conv(pair_params->index);
+ wake_up_interruptible(&pair_params->input_wait_queue);
+ wake_up_interruptible(&pair_params->output_wait_queue);
+ }
+ if (pair_params->pair_hold == 1) {
+ mcf_edma_free_channel(pair_params->input_dma_channel,
+ pair_params);
+ mcf_edma_free_channel(pair_params->output_dma_channel,
+ pair_params);
+ mxc_free_dma_buf(pair_params);
+ asrc_release_pair(pair_params->index);
+ }
+
+ kfree(pair_params);
+ file->private_data = NULL;
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+
+ return 0;
+}
+
+/*!
+ * asrc interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size;
+ int res = 0;
+ size = vma->vm_end - vma->vm_start;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot))
+ return -ENOBUFS;
+
+ vma->vm_flags &= ~VM_IO;
+ return res;
+}
+
+static const struct file_operations asrc_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = asrc_ioctl,
+ .mmap = mxc_asrc_mmap,
+ .open = mxc_asrc_open,
+ .release = mxc_asrc_close,
+};
+
+static int asrc_read_proc_attr(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long reg;
+ int len = 0;
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+
+ len += sprintf(page, "ANCA: %d\n",
+ (int)(reg &
+ (0xFFFFFFFF >>
+ (32 - mxc_asrc_data->channel_bits))));
+ len +=
+ sprintf(page + len, "ANCB: %d\n",
+ (int)((reg >> mxc_asrc_data->
+ channel_bits) & (0xFFFFFFFF >> (32 -
+ mxc_asrc_data->
+ channel_bits))));
+ len +=
+ sprintf(page + len, "ANCC: %d\n",
+ (int)((reg >> (mxc_asrc_data->channel_bits * 2)) &
+ (0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits))));
+
+ if (off > len)
+ return 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return min(count, len - (int)off);
+}
+
+static int asrc_write_proc_attr(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char buf[50];
+ unsigned long reg;
+ int na, nb, nc;
+ int total;
+ if (count > 48)
+ return -EINVAL;
+ if (copy_from_user(buf, buffer, count)) {
+ pr_debug("Attr proc write, Failed to copy buffer from user\n");
+ return -EFAULT;
+ }
+
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+ sscanf(buf, "ANCA: %d\nANCB: %d\nANCC: %d", &na, &nb, &nc);
+ if (mxc_asrc_data->channel_bits > 3)
+ total = 10;
+ else
+ total = 5;
+ if ((na + nb + nc) != total) {
+ pr_info("Wrong ASRCNR settings\n");
+ return -EFAULT;
+ }
+ reg = na | (nb << mxc_asrc_data->
+ channel_bits) | (nc << (mxc_asrc_data->channel_bits * 2));
+
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+
+ return count;
+}
+
+static void asrc_proc_create(void)
+{
+ struct proc_dir_entry *proc_attr;
+ proc_asrc = proc_mkdir(ASRC_PROC_PATH, NULL);
+ if (proc_asrc) {
+ proc_attr = create_proc_entry("ChSettings",
+ S_IFREG | S_IRUGO |
+ S_IWUSR, proc_asrc);
+ if (proc_attr) {
+ proc_attr->read_proc = asrc_read_proc_attr;
+ proc_attr->write_proc = asrc_write_proc_attr;
+ proc_attr->size = 48;
+ proc_attr->uid = proc_attr->gid = 0;
+ } else {
+ remove_proc_entry(ASRC_PROC_PATH, NULL);
+ pr_info("Failed to create proc attribute entry\n");
+ }
+ } else {
+ pr_info("ASRC: Failed to create proc entry %s\n",
+ ASRC_PROC_PATH);
+ }
+}
+
+/*!
+ * Entry point for the asrc device
+ *
+ * @param pdev Pionter to the registered platform device
+ * @return Error code indicating success or failure
+ */
+static int mxc_asrc_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ struct device *temp_class;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ g_asrc_data = kzalloc(sizeof(struct asrc_data), GFP_KERNEL);
+
+ if (g_asrc_data == NULL) {
+ pr_info("Failed to allocate g_asrc_data\n");
+ return -ENOMEM;
+ }
+
+ g_asrc_data->asrc_pair[0].chn_max = 2;
+ g_asrc_data->asrc_pair[1].chn_max = 2;
+ g_asrc_data->asrc_pair[2].chn_max = 6;
+ g_asrc_data->asrc_pair[0].overload_error = 0;
+ g_asrc_data->asrc_pair[1].overload_error = 0;
+ g_asrc_data->asrc_pair[2].overload_error = 0;
+
+ asrc_major = register_chrdev(asrc_major, "mxc_asrc", &asrc_fops);
+ if (asrc_major < 0) {
+ pr_info("Unable to register asrc device\n");
+ err = -EBUSY;
+ goto error;
+ }
+
+ asrc_class = class_create(THIS_MODULE, "mxc_asrc");
+ if (IS_ERR(asrc_class)) {
+ err = PTR_ERR(asrc_class);
+ goto err_out_chrdev;
+ }
+
+ temp_class = device_create(asrc_class, NULL, MKDEV(asrc_major, 0),
+ NULL, "mxc_asrc");
+ if (IS_ERR(temp_class)) {
+ err = PTR_ERR(temp_class);
+ goto err_out_class;
+ }
+
+ asrc_phy_base_addr = res->start;
+ asrc_vrt_base_addr =
+ (unsigned long)ioremap(res->start, res->end - res->start + 1);
+
+ mxc_asrc_data =
+ (struct imx_asrc_platform_data *)pdev->dev.platform_data;
+
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+
+ switch (mxc_asrc_data->clk_map_ver) {
+ case 1:
+ input_clk_map = &input_clk_map_v1[0];
+ output_clk_map = &output_clk_map_v1[0];
+ break;
+ case 3:
+ input_clk_map = &input_clk_map_v3[0];
+ output_clk_map = &output_clk_map_v3[0];
+ break;
+ case 2:
+ default:
+ input_clk_map = &input_clk_map_v2[0];
+ output_clk_map = &output_clk_map_v2[0];
+ break;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx1");
+ if (res)
+ asrc_dmatx[0] = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx1");
+ if (res)
+ asrc_dmarx[0] = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx2");
+ if (res)
+ asrc_dmatx[1] = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx2");
+ if (res)
+ asrc_dmarx[1] = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx3");
+ if (res)
+ asrc_dmatx[2] = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx3");
+ if (res)
+ asrc_dmarx[2] = res->start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (request_irq(irq, asrc_isr, 0, "asrc", NULL))
+ return -1;
+
+ asrc_proc_create();
+ err = mxc_init_asrc();
+ if (err < 0)
+ goto err_out_class;
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+ goto out;
+
+err_out_class:
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+ device_destroy(asrc_class, MKDEV(asrc_major, 0));
+ class_destroy(asrc_class);
+err_out_chrdev:
+ unregister_chrdev(asrc_major, "mxc_asrc");
+error:
+ kfree(g_asrc_data);
+out:
+ pr_info("MVF ASRC registered\n");
+
+ return err;
+}
+
+/*!
+ * Exit asrc device
+ *
+ * @param pdev Pionter to the registered platform device
+ * @return Error code indicating success or failure
+ */
+static int mxc_asrc_remove(struct platform_device *pdev)
+{
+ int irq = platform_get_irq(pdev, 0);
+ free_irq(irq, NULL);
+ kfree(g_asrc_data);
+ mxc_asrc_data = NULL;
+ iounmap((unsigned long __iomem *)asrc_vrt_base_addr);
+ remove_proc_entry("ChSettings", proc_asrc);
+ remove_proc_entry(ASRC_PROC_PATH, NULL);
+ device_destroy(asrc_class, MKDEV(asrc_major, 0));
+ class_destroy(asrc_class);
+ unregister_chrdev(asrc_major, "mxc_asrc");
+ return 0;
+}
+
+/*! mxc asrc driver definition
+ *
+ */
+static struct platform_driver mxc_asrc_driver = {
+ .driver = {
+ .name = "mxc_asrc",
+ },
+ .probe = mxc_asrc_probe,
+ .remove = mxc_asrc_remove,
+};
+
+/*!
+ * Register asrc driver
+ *
+ */
+static __init int asrc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&mxc_asrc_driver);
+ return ret;
+}
+
+/*!
+ * Exit and free the asrc data
+ *
+ */
+static void __exit asrc_exit(void)
+{
+ platform_driver_unregister(&mxc_asrc_driver);
+ return;
+}
+
+module_init(asrc_init);
+module_exit(asrc_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Asynchronous Sample Rate Converter");
+MODULE_LICENSE("GPL");