static long asrc_ioctl_flush(struct asrc_pair_params *params, void __user *user) { enum asrc_pair_index index = params->index; init_completion(¶ms->input_complete); init_completion(¶ms->output_complete); init_completion(¶ms->lastperiod_complete); /* Release DMA and request again */ dma_release_channel(params->input_dma_channel); dma_release_channel(params->output_dma_channel); params->input_dma_channel = imx_asrc_get_dma_channel(index, true); if (params->input_dma_channel == NULL) { pair_err("failed to request input task dma channel\n"); return -EBUSY; } params->output_dma_channel = imx_asrc_get_dma_channel(index, false); if (params->output_dma_channel == NULL) { pair_err("failed to request output task dma channel\n"); return -EBUSY; } return 0; }
/** * Configure the assigned ASRC pair * * It configures those ASRC registers according to a configuration instance * of struct asrc_config which includes in/output sample rate, width, channel * and clock settings. */ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) { struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; u32 inrate, outrate, indiv, outdiv; u32 clk_index[2], div[2]; int in, out, channels; struct clk *clk; bool ideal; if (!config) { pair_err("invalid pair config\n"); return -EINVAL; } /* Validate channels */ if (config->channel_num < 1 || config->channel_num > 10) { pair_err("does not support %d channels\n", config->channel_num); return -EINVAL; } /* Validate output width */ if (config->output_word_width == ASRC_WIDTH_8_BIT) { pair_err("does not support 8bit width output\n"); return -EINVAL; } inrate = config->input_sample_rate; outrate = config->output_sample_rate; ideal = config->inclk == INCLK_NONE; /* Validate input and output sample rates */ for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) if (inrate == supported_input_rate[in]) break; if (in == ARRAY_SIZE(supported_input_rate)) { pair_err("unsupported input sample rate: %dHz\n", inrate); return -EINVAL; } for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++) if (outrate == supported_asrc_rate[out]) break; if (out == ARRAY_SIZE(supported_asrc_rate)) { pair_err("unsupported output sample rate: %dHz\n", outrate); return -EINVAL; } if ((outrate > 8000 && outrate < 30000) && (outrate/inrate > 24 || inrate/outrate > 8)) { pair_err("exceed supported ratio range [1/24, 8] for \ inrate/outrate: %d/%d\n", inrate, outrate); return -EINVAL; }
int mxc_asrc_process_io_buffer(struct asrc_pair_params *params, struct asrc_convert_buffer *pbuf, bool in) { void *last_vaddr = params->output_last_period.dma_vaddr; unsigned int *last_len = ¶ms->output_last_period.length; enum asrc_pair_index index = params->index; unsigned int dma_len, *buf_len; struct completion *complete; void __user *buf_vaddr; void *dma_vaddr; if (in) { dma_vaddr = params->input_dma_total.dma_vaddr; dma_len = params->input_dma_total.length; buf_len = &pbuf->input_buffer_length; complete = ¶ms->input_complete; buf_vaddr = (void __user *)pbuf->input_buffer_vaddr; } else { dma_vaddr = params->output_dma_total.dma_vaddr; dma_len = params->output_dma_total.length; buf_len = &pbuf->output_buffer_length; complete = ¶ms->lastperiod_complete; buf_vaddr = (void __user *)pbuf->output_buffer_vaddr; } if (!wait_for_completion_interruptible_timeout(complete, 10 * HZ)) { pair_err("%s task timeout\n", in ? "input dma" : "last period"); return -ETIME; } else if (signal_pending(current)) { pair_err("%sput task forcibly aborted\n", in ? "in" : "out"); return -ERESTARTSYS; } init_completion(complete); *buf_len = dma_len; /* Only output need return data to user space */ if (!in) { if (copy_to_user(buf_vaddr, dma_vaddr, dma_len)) return -EFAULT; *buf_len += *last_len; if (copy_to_user(buf_vaddr + dma_len, last_vaddr, *last_len)) return -EFAULT; } return 0; }
static int mxc_asrc_prepare_buffer(struct asrc_pair_params *params, struct asrc_convert_buffer *pbuf) { enum asrc_pair_index index = params->index; int ret; ret = mxc_asrc_prepare_io_buffer(params, pbuf, true); if (ret) { pair_err("failed to prepare input buffer: %d\n", ret); return ret; } ret = mxc_asrc_prepare_io_buffer(params, pbuf, false); if (ret) { pair_err("failed to prepare output buffer: %d\n", ret); return ret; } return 0; }
int asrc_set_watermark(enum asrc_pair_index index, u32 in_wm, u32 out_wm) { if (in_wm > ASRC_FIFO_THRESHOLD_MAX || out_wm > ASRC_FIFO_THRESHOLD_MAX) { pair_err("invalid watermark!\n"); return -EINVAL; } return regmap_update_bits(asrc->regmap, REG_ASRMCR(index), ASRMCRx_EXTTHRSHx_MASK | ASRMCRx_INFIFO_THRESHOLD_MASK | ASRMCRx_OUTFIFO_THRESHOLD_MASK, ASRMCRx_EXTTHRSHx | ASRMCRx_INFIFO_THRESHOLD(in_wm) | ASRMCRx_OUTFIFO_THRESHOLD(out_wm)); }
static long asrc_ioctl_convert(struct asrc_pair_params *params, void __user *user) { enum asrc_pair_index index = params->index; struct asrc_convert_buffer buf; long ret; ret = copy_from_user(&buf, user, sizeof(buf)); if (ret) { pair_err("failed to get buf from user space: %ld\n", ret); return ret; } ret = mxc_asrc_prepare_buffer(params, &buf); if (ret) { pair_err("failed to prepare buffer: %ld\n", ret); return ret; } #ifdef ASRC_POLLING_WITHOUT_DMA asrc_polling_debug(params); #else mxc_asrc_submit_dma(params); #endif ret = mxc_asrc_process_buffer(params, &buf); if (ret) { pair_err("failed to process buffer: %ld\n", ret); return ret; } ret = copy_to_user(user, &buf, sizeof(buf)); if (ret) { pair_err("failed to send buf to user space: %ld\n", ret); return ret; } return 0; }
static int mxc_allocate_dma_buf(struct asrc_pair_params *params) { struct dma_block *input_a, *output_a, *last_period; enum asrc_pair_index index = params->index; input_a = ¶ms->input_dma_total; output_a = ¶ms->output_dma_total; last_period = ¶ms->output_last_period; input_a->dma_vaddr = kzalloc(input_a->length, GFP_KERNEL); if (!input_a->dma_vaddr) { pair_err("failed to allocate input dma buffer\n"); goto exit; } input_a->dma_paddr = virt_to_dma(NULL, input_a->dma_vaddr); output_a->dma_vaddr = kzalloc(output_a->length, GFP_KERNEL); if (!output_a->dma_vaddr) { pair_err("failed to allocate output dma buffer\n"); goto exit; } output_a->dma_paddr = virt_to_dma(NULL, output_a->dma_vaddr); last_period->dma_vaddr = dma_alloc_coherent(asrc->dev, 1024 * params->last_period_sample, &last_period->dma_paddr, GFP_KERNEL); if (!last_period->dma_vaddr) { pair_err("failed to allocate last period buffer\n"); goto exit; } return 0; exit: mxc_free_dma_buf(params); return -ENOBUFS; }
static long asrc_ioctl_status(struct asrc_pair_params *params, void __user *user) { enum asrc_pair_index index = params->index; struct asrc_status_flags flags; long ret; ret = copy_from_user(&flags, user, sizeof(flags)); if (ret) { pair_err("failed to get flags from user space: %ld\n", ret); return ret; } asrc_get_status(&flags); ret = copy_to_user(user, &flags, sizeof(flags)); if (ret) { pair_err("failed to send flags to user space: %ld\n", ret); return ret; } return 0; }
/** * Calculate and set the ratio for Ideal Ratio mode only * * The ratio is a 32-bit fixed point value with 26 fractional bits. */ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, int inrate, int outrate) { struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; unsigned long ratio; int i; if (!outrate) { pair_err("output rate should not be zero\n"); return -EINVAL; } /* Calculate the intergal part of the ratio */ ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH; /* ... and then the 26 depth decimal part */ inrate %= outrate; for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) { inrate <<= 1; if (inrate < outrate) continue; ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i); inrate -= outrate; if (!inrate) break; } regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio); regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24); return 0; }
static void asrc_output_task_worker(struct work_struct *w) { struct asrc_pair_params *params = container_of(w, struct asrc_pair_params, task_output_work); enum asrc_pair_index index = params->index; unsigned long lock_flags; if (!wait_for_completion_interruptible_timeout(¶ms->output_complete, HZ / 10)) { pair_err("output dma task timeout\n"); return; } init_completion(¶ms->output_complete); spin_lock_irqsave(&pair_lock, lock_flags); if (!params->pair_hold) { spin_unlock_irqrestore(&pair_lock, lock_flags); return; } asrc_read_output_FIFO(params); spin_unlock_irqrestore(&pair_lock, lock_flags); complete(¶ms->lastperiod_complete); }
static int mxc_asrc_prepare_io_buffer(struct asrc_pair_params *params, struct asrc_convert_buffer *pbuf, bool in) { enum asrc_pair_index index = params->index; struct dma_chan *dma_channel; enum asrc_word_width width; unsigned int *dma_len, *sg_nodes, buf_len, wm; void __user *buf_vaddr; void *dma_vaddr; u32 word_size, fifo_addr; if (in) { dma_channel = params->input_dma_channel; dma_vaddr = params->input_dma_total.dma_vaddr; dma_len = ¶ms->input_dma_total.length; width = params->input_word_width; sg_nodes = ¶ms->input_sg_nodes; wm = params->input_wm; buf_vaddr = (void __user *)pbuf->input_buffer_vaddr; buf_len = pbuf->input_buffer_length; } else { dma_channel = params->output_dma_channel; dma_vaddr = params->output_dma_total.dma_vaddr; dma_len = ¶ms->output_dma_total.length; width = params->output_word_width; sg_nodes = ¶ms->output_sg_nodes; wm = params->last_period_sample; buf_vaddr = (void __user *)pbuf->output_buffer_vaddr; buf_len = pbuf->output_buffer_length; } switch (width) { case ASRC_WIDTH_24_BIT: word_size = 4; break; case ASRC_WIDTH_16_BIT: case ASRC_WIDTH_8_BIT: word_size = 2; break; default: pair_err("invalid %sput word size!\n", in ? "in" : "out"); return -EINVAL; } if (buf_len < word_size * params->channel_nums * wm) { pair_err("%sput buffer size[%d] is too small!\n", in ? "in" : "out", buf_len); return -EINVAL; } /* Copy origin data into input buffer */ if (in && copy_from_user(dma_vaddr, buf_vaddr, buf_len)) return -EFAULT; *dma_len = buf_len; if (!in) *dma_len -= wm * word_size * params->channel_nums; *sg_nodes = *dma_len / ASRC_MAX_BUFFER_SIZE + 1; fifo_addr = asrc_get_per_addr(params->index, in); return imx_asrc_dma_config(params, dma_channel, fifo_addr, dma_vaddr, *dma_len, in, width); }
static int imx_asrc_dma_config(struct asrc_pair_params *params, struct dma_chan *chan, u32 dma_addr, void *buf_addr, u32 buf_len, bool in, enum asrc_word_width word_width) { enum asrc_pair_index index = params->index; struct dma_async_tx_descriptor *desc; struct dma_slave_config slave_config; enum dma_slave_buswidth buswidth; struct scatterlist *sg; unsigned int sg_nent, i; int ret; if (in) { sg = params->input_sg; sg_nent = params->input_sg_nodes; desc = params->desc_in; } else { sg = params->output_sg; sg_nent = params->output_sg_nodes; desc = params->desc_out; } switch (word_width) { case ASRC_WIDTH_16_BIT: buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case ASRC_WIDTH_24_BIT: buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: pair_err("invalid word width\n"); return -EINVAL; } slave_config.dma_request0 = 0; slave_config.dma_request1 = 0; if (in) { slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = dma_addr; slave_config.dst_addr_width = buswidth; slave_config.dst_maxburst = params->input_wm * params->channel_nums / buswidth; } else { slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = dma_addr; slave_config.src_addr_width = buswidth; slave_config.src_maxburst = params->output_wm * params->channel_nums / buswidth; } ret = dmaengine_slave_config(chan, &slave_config); if (ret) { pair_err("failed to config dmaengine for %sput task: %d\n", in ? "in" : "out", ret); return -EINVAL; } sg_init_table(sg, sg_nent); switch (sg_nent) { case 1: sg_init_one(sg, buf_addr, buf_len); break; case 2: case 3: case 4: for (i = 0; i < (sg_nent - 1); i++) sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE, ASRC_MAX_BUFFER_SIZE); sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE, buf_len - ASRC_MAX_BUFFER_SIZE * i); break; default: pair_err("invalid input DMA nodes number: %d\n", sg_nent); return -EINVAL; } ret = dma_map_sg(NULL, sg, sg_nent, slave_config.direction); if (ret != sg_nent) { pair_err("failed to map dma sg for %sput task\n", in ? "in" : "out"); return -EINVAL; } desc = dmaengine_prep_slave_sg(chan, sg, sg_nent, slave_config.direction, DMA_PREP_INTERRUPT); if (!desc) { pair_err("failed to prepare slave sg for %sput task\n", in ? "in" : "out"); return -EINVAL; } if (in) { params->desc_in = desc; params->desc_in->callback = asrc_input_dma_callback; } else { params->desc_out = desc; params->desc_out->callback = asrc_output_dma_callback; } desc->callback = ASRC_xPUT_DMA_CALLBACK(in); desc->callback_param = params; return 0; }
int asrc_config_pair(struct asrc_config *config) { u32 inrate = config->input_sample_rate, indiv; u32 outrate = config->output_sample_rate, outdiv; int ret, channels, index = config->pair; unsigned long lock_flags; /* Set the channel number */ spin_lock_irqsave(&data_lock, lock_flags); asrc->asrc_pair[index].chn_num = config->channel_num; spin_unlock_irqrestore(&data_lock, lock_flags); if (asrc->channel_bits > 3) channels = config->channel_num; else channels = (config->channel_num + 1) / 2; /* Update channel number of current pair */ regmap_update_bits(asrc->regmap, REG_ASRCNCR, ASRCNCR_ANCx_MASK(index, asrc->channel_bits), ASRCNCR_ANCx_set(index, channels, asrc->channel_bits)); /* Set the clock source */ regmap_update_bits(asrc->regmap, REG_ASRCSR, ASRCSR_AICSx_MASK(index) | ASRCSR_AOCSx_MASK(index), ASRCSR_AICS(index, input_clk_map[config->inclk]) | ASRCSR_AOCS(index, output_clk_map[config->outclk])); /* Default setting: Automatic selection for processing mode */ regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ATSx_MASK(index), ASRCTR_ATS(index)); regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_USRx_MASK(index), 0); /* Default Input Clock Divider Setting */ switch (config->inclk & ASRCSR_AxCSx_MASK) { case INCLK_SPDIF_RX: indiv = ASRC_PRESCALER_SPDIF_RX; break; case INCLK_SPDIF_TX: indiv = ASRC_PRESCALER_SPDIF_TX; break; case INCLK_ASRCK1_CLK: indiv = asrc_get_asrck_clock_divider(inrate); break; default: switch (config->input_word_width) { case ASRC_WIDTH_16_BIT: indiv = ASRC_PRESCALER_I2S_16BIT; break; case ASRC_WIDTH_24_BIT: indiv = ASRC_PRESCALER_I2S_24BIT; break; default: pair_err("unsupported input word width %d\n", config->input_word_width); return -EINVAL; } break; } /* Default Output Clock Divider Setting */ switch (config->outclk & ASRCSR_AxCSx_MASK) { case OUTCLK_SPDIF_RX: outdiv = ASRC_PRESCALER_SPDIF_RX; break; case OUTCLK_SPDIF_TX: outdiv = ASRC_PRESCALER_SPDIF_TX; break; case OUTCLK_ASRCK1_CLK: if ((config->inclk & ASRCSR_AxCSx_MASK) == INCLK_NONE) outdiv = ASRC_PRESCALER_IDEAL_RATIO; else outdiv = asrc_get_asrck_clock_divider(outrate); break; default: switch (config->output_word_width) { case ASRC_WIDTH_16_BIT: outdiv = ASRC_PRESCALER_I2S_16BIT; break; case ASRC_WIDTH_24_BIT: outdiv = ASRC_PRESCALER_I2S_24BIT; break; default: pair_err("unsupported output word width %d\n", config->input_word_width); return -EINVAL; } break; } /* indiv and outdiv'd include prescaler's value, so add its MASK too */ regmap_update_bits(asrc->regmap, REG_ASRCDR(index), ASRCDRx_AOCPx_MASK(index) | ASRCDRx_AICPx_MASK(index) | ASRCDRx_AOCDx_MASK(index) | ASRCDRx_AICDx_MASK(index), ASRCDRx_AOCP(index, outdiv) | ASRCDRx_AICP(index, indiv)); /* Check whether ideal ratio is a must */ switch (config->inclk & ASRCSR_AxCSx_MASK) { case INCLK_NONE: /* Clear ASTSx bit to use ideal ratio */ regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ATSx_MASK(index), 0); regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_IDRx_MASK(index) | ASRCTR_USRx_MASK(index), ASRCTR_IDR(index) | ASRCTR_USR(index)); ret = asrc_set_clock_ratio(index, inrate, outrate); if (ret) return ret; ret = asrc_set_process_configuration(index, inrate, outrate); if (ret) return ret; break; case INCLK_ASRCK1_CLK: /* This case and default are both remained for v1 */ if (inrate == 44100 || inrate == 88200) { pair_err("unsupported sample rate %d by selected clock\n", inrate); return -EINVAL; } break; default: if ((config->outclk & ASRCSR_AxCSx_MASK) != OUTCLK_ASRCK1_CLK) break; if (outrate == 44100 || outrate == 88200) { pair_err("unsupported sample rate %d by selected clock\n", outrate); return -EINVAL; } break; } /* Config input and output wordwidth */ if (config->output_word_width == ASRC_WIDTH_8_BIT) { pair_err("unsupported wordwidth for output: 8bit\n"); pair_err("output only support: 16bit or 24bit\n"); return -EINVAL; } regmap_update_bits(asrc->regmap, REG_ASRMCR1(index), ASRMCR1x_OW16_MASK | ASRMCR1x_IWD_MASK, ASRMCR1x_OW16(config->output_word_width) | ASRMCR1x_IWD(config->input_word_width)); /* Enable BUFFER STALL */ regmap_update_bits(asrc->regmap, REG_ASRMCR(index), ASRMCRx_BUFSTALLx_MASK, ASRMCRx_BUFSTALLx); /* Set Threshold for input and output FIFO */ return asrc_set_watermark(index, ASRC_INPUTFIFO_THRESHOLD, ASRC_INPUTFIFO_THRESHOLD); }
static long asrc_ioctl_config_pair(struct asrc_pair_params *params, void __user *user) { struct asrc_config config; enum asrc_pair_index index; long ret; ret = copy_from_user(&config, user, sizeof(config)); if (ret) { dev_err(asrc->dev, "failed to get config from user space: %ld\n", ret); return ret; } index = config.pair; ret = asrc_config_pair(&config); if (ret) { pair_err("failed to config pair: %ld\n", ret); return ret; } params->input_wm = 4; params->output_wm = 2; ret = asrc_set_watermark(index, params->input_wm, params->output_wm); if (ret) return ret; params->output_buffer_size = config.dma_buffer_size; 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; params->input_dma_total.length = ASRC_DMA_BUFFER_SIZE; params->output_dma_total.length = ASRC_DMA_BUFFER_SIZE; params->input_word_width = config.input_word_width; params->output_word_width = config.output_word_width; params->input_sample_rate = config.input_sample_rate; params->output_sample_rate = config.output_sample_rate; params->last_period_sample = ASRC_OUTPUT_LAST_SAMPLE_DEFAULT; ret = mxc_allocate_dma_buf(params); if (ret) { pair_err("failed to allocate dma buffer: %ld\n", ret); return ret; } /* Request DMA channel for both input and output */ params->input_dma_channel = imx_asrc_get_dma_channel(index, true); if (params->input_dma_channel == NULL) { pair_err("failed to request input task dma channel\n"); return -EBUSY; } params->output_dma_channel = imx_asrc_get_dma_channel(index, false); if (params->output_dma_channel == NULL) { pair_err("failed to request output task dma channel\n"); return -EBUSY; } init_completion(¶ms->input_complete); init_completion(¶ms->output_complete); init_completion(¶ms->lastperiod_complete); /* Add work struct to receive last period of output data */ INIT_WORK(¶ms->task_output_work, asrc_output_task_worker); ret = copy_to_user(user, &config, sizeof(config)); if (ret) { pair_err("failed to send config to user space: %ld\n", ret); return ret; } return 0; }
/** * Configure the assigned ASRC pair * * It configures those ASRC registers according to a configuration instance * of struct asrc_config which includes in/output sample rate, width, channel * and clock settings. */ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) { struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; u32 inrate, outrate, indiv, outdiv; u32 clk_index[2], div[2]; int in, out, channels; struct clk *clk; bool ideal; if (!config) { pair_err("invalid pair config\n"); return -EINVAL; } /* Validate channels */ if (config->channel_num < 1 || config->channel_num > 10) { pair_err("does not support %d channels\n", config->channel_num); return -EINVAL; } /* Validate output width */ if (config->output_word_width == ASRC_WIDTH_8_BIT) { pair_err("does not support 8bit width output\n"); return -EINVAL; } inrate = config->input_sample_rate; outrate = config->output_sample_rate; ideal = config->inclk == INCLK_NONE; /* Validate input and output sample rates */ for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) if (inrate == supported_input_rate[in]) break; if (in == ARRAY_SIZE(supported_input_rate)) { pair_err("unsupported input sample rate: %dHz\n", inrate); return -EINVAL; } for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++) if (outrate == supported_asrc_rate[out]) break; if (out == ARRAY_SIZE(supported_asrc_rate)) { pair_err("unsupported output sample rate: %dHz\n", outrate); return -EINVAL; } /* Validate input and output clock sources */ clk_index[IN] = clk_map[IN][config->inclk]; clk_index[OUT] = clk_map[OUT][config->outclk]; /* We only have output clock for ideal ratio mode */ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; div[IN] = clk_get_rate(clk) / inrate; if (div[IN] == 0) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; } clk = asrc_priv->asrck_clk[clk_index[OUT]]; /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ if (ideal) div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; else div[OUT] = clk_get_rate(clk) / outrate; if (div[OUT] == 0) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; } /* Set the channel number */ channels = config->channel_num; if (asrc_priv->channel_bits < 4) channels /= 2; /* Update channels for current pair */ regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR, ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits), ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits)); /* Default setting: Automatic selection for processing mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index)); regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, ASRCTR_USRi_MASK(index), 0); /* Set the input and output clock sources */ regmap_update_bits(asrc_priv->regmap, REG_ASRCSR, ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index), ASRCSR_AICS(index, clk_index[IN]) | ASRCSR_AOCS(index, clk_index[OUT])); /* Calculate the input clock divisors */ indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]); outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]); /* Suppose indiv and outdiv includes prescaler, so add its MASK too */ regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index), ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) | ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index), ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv)); /* Implement word_width configurations */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index), ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK, ASRMCR1i_OW16(config->output_word_width) | ASRMCR1i_IWD(config->input_word_width)); /* Enable BUFFER STALL */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi); /* Set default thresholds for input and output FIFO */ fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD, ASRC_INPUTFIFO_THRESHOLD); /* Configure the followings only for Ideal Ratio mode */ if (!ideal) return 0; /* Clear ASTSx bit to use Ideal Ratio mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ATSi_MASK(index), 0); /* Enable Ideal Ratio mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index), ASRCTR_IDR(index) | ASRCTR_USR(index)); /* Apply configurations for pre- and post-processing */ regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index), ASRCFG_PREMOD(index, process_option[in][out][0]) | ASRCFG_POSTMOD(index, process_option[in][out][1])); return fsl_asrc_set_ideal_ratio(pair, inrate, outrate); }