/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(¶ms->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, ¶ms->input_done_queue); params->input_queue_empty++; mcf_edma_enable_transfer(params->input_dma_channel); } params->input_counter++; wake_up_interruptible(¶ms->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(¶ms->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, ¶ms->output_done_queue); params->output_queue_empty++; mcf_edma_enable_transfer(params->output_dma_channel); } params->output_counter++; wake_up_interruptible(¶ms->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(¶ms->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(¶ms->input_queue); INIT_LIST_HEAD(¶ms->input_done_queue); INIT_LIST_HEAD(¶ms->output_queue); INIT_LIST_HEAD(¶ms->output_done_queue); init_waitqueue_head(¶ms->input_wait_queue); init_waitqueue_head(¶ms->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(¶ms->input_dma[buf.index]. queue, ¶ms->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, ¶ms->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(¶ms->output_dma[buf.index]. queue, ¶ms->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, ¶ms->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(¶ms->input_queue)) list_del(params->input_queue.next); while (!list_empty(¶ms->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(¶ms->output_queue)) list_del(params->output_queue.next); while (!list_empty(¶ms->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(¶ms->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");