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)); }
/** * Configure input and output thresholds */ static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out) { struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), ASRMCRi_EXTTHRSHi_MASK | ASRMCRi_INFIFO_THRESHOLD_MASK | ASRMCRi_OUTFIFO_THRESHOLD_MASK, ASRMCRi_EXTTHRSHi | ASRMCRi_INFIFO_THRESHOLD(in) | ASRMCRi_OUTFIFO_THRESHOLD(out)); }
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); }
/** * 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); }