/* * Check if the divider is available for internal ratio mode
*/ staticbool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
{
u32 rem, i;
u64 n;
if (div)
*div = 0;
if (clk_rate == 0 || rate == 0) returnfalse;
n = clk_rate;
rem = do_div(n, rate);
if (div)
*div = n;
if (rem != 0) returnfalse;
for (i = 0; i < DIVIDER_NUM; i++) { if (n == asrc_clk_divider[i]) break;
}
if (i == DIVIDER_NUM) returnfalse;
returntrue;
}
/** * fsl_asrc_sel_proc - Select the pre-processing and post-processing options * @inrate: input sample rate * @outrate: output sample rate * @pre_proc: return value for pre-processing option * @post_proc: return value for post-processing option * * Make sure to exclude following unsupported cases before * calling this function: * 1) inrate > 8.125 * outrate * 2) inrate > 16.125 * outrate *
*/ staticvoid fsl_asrc_sel_proc(int inrate, int outrate, int *pre_proc, int *post_proc)
{ bool post_proc_cond2; bool post_proc_cond0;
/** * fsl_asrc_request_pair - Request ASRC pair * @channels: number of channels * @pair: pointer to pair * * It assigns pair by the order of A->C->B because allocation of pair B, * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A * while pair A and pair C are comparatively independent.
*/ staticint fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{ enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc = pair->asrc; struct device *dev = &asrc->pdev->dev; unsignedlong lock_flags; int i, ret = 0;
spin_lock_irqsave(&asrc->lock, lock_flags);
for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { if (asrc->pair[i] != NULL) continue;
index = i;
if (i != ASRC_PAIR_B) break;
}
if (index == ASRC_INVALID_PAIR) {
dev_err(dev, "all pairs are busy now\n");
ret = -EBUSY;
} elseif (asrc->channel_avail < channels) {
dev_err(dev, "can't afford required channels: %d\n", channels);
ret = -EINVAL;
} else {
asrc->channel_avail -= channels;
asrc->pair[index] = pair;
pair->channels = channels;
pair->index = index;
}
spin_unlock_irqrestore(&asrc->lock, lock_flags);
return ret;
}
/** * fsl_asrc_release_pair - Release ASRC pair * @pair: pair to release * * It clears the resource from asrc and releases the occupied channels.
*/ staticvoid fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{ struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; unsignedlong lock_flags;
/* Make sure the pair is disabled */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), 0);
/** * fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate * @pair: pointer to pair * @div: divider * * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
*/ static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
{
u32 ps;
/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */ for (ps = 0; div > 8; ps++)
div >>= 1;
return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
}
/** * fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only * @pair: pointer to pair * @inrate: input rate * @outrate: output rate * * The ratio is a 32-bit fixed point value with 26 fractional bits.
*/ staticint fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, int inrate, int outrate)
{ struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; unsignedlong 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->regmap, REG_ASRIDRL(index), ratio);
regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
return 0;
}
/** * fsl_asrc_config_pair - Configure the assigned ASRC pair * @pair: pointer to pair * @use_ideal_rate: boolean configuration * * 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. * * Note: * The ideal ratio configuration can work with a flexible clock rate setting. * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC. * For a regular audio playback, the clock rate should not be slower than an * clock rate aligning with the output sample rate; For a use case requiring * faster conversion, set use_ideal_rate to have the faster speed.
*/ staticint fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{ struct fsl_asrc_pair_priv *pair_priv = pair->private; struct asrc_config *config = pair_priv->config; struct fsl_asrc *asrc = pair->asrc; struct fsl_asrc_priv *asrc_priv = asrc->private; enum asrc_pair_index index = pair->index; enum asrc_word_width input_word_width; enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
u32 clk_index[2], div[2];
u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; bool ideal, div_avail;
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;
}
switch (snd_pcm_format_width(config->input_format)) { case 8:
input_word_width = ASRC_WIDTH_8_BIT; break; case 16:
input_word_width = ASRC_WIDTH_16_BIT; break; case 24:
input_word_width = ASRC_WIDTH_24_BIT; break; default:
pair_err("does not support this input format, %d\n",
config->input_format); return -EINVAL;
}
switch (snd_pcm_format_width(config->output_format)) { case 16:
output_word_width = ASRC_WIDTH_16_BIT; break; case 24:
output_word_width = ASRC_WIDTH_24_BIT; break; default:
pair_err("does not support this output format, %d\n",
config->output_format); return -EINVAL;
}
/* * The divider range is [1, 1024], defined by the hardware. For non- * ideal ratio configuration, clock rate has to be strictly aligned * with the sample rate. For ideal ratio configuration, clock rates * only result in different converting speeds. So remainder does not * matter, as long as we keep the divider within its valid range.
*/ if (div[IN] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]); return -EINVAL;
}
/* Output divider has the same limitation as the input one */ if (div[OUT] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]); return -EINVAL;
}
div[OUT] = min_t(u32, 1024, div[OUT]);
/* Set the channel number */
channels = config->channel_num;
if (asrc_priv->soc->channel_bits < 4)
channels /= 2;
/* Update channels for current pair */
regmap_update_bits(asrc->regmap, REG_ASRCNCR,
ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
/** * fsl_asrc_start_pair - Start the assigned ASRC pair * @pair: pointer to pair * * It enables the assigned pair and makes it stopped at the stall level.
*/ staticvoid fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{ struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; int reg, retry = INIT_RETRY_NUM, i;
/* Enable the current pair */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
/* Wait for status of initialization */ do {
udelay(5);
regmap_read(asrc->regmap, REG_ASRCFG, ®);
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
/* NOTE: Doesn't treat initialization timeout as an error */ if (!retry)
pair_warn("initialization isn't finished\n");
/* Make the input fifo to ASRC STALL level */
regmap_read(asrc->regmap, REG_ASRCNCR, ®); for (i = 0; i < pair->channels * 4; i++)
regmap_write(asrc->regmap, REG_ASRDI(index), 0);
/* Odd channel number is not valid for older ASRC (channel_bits==3) */ if (asrc_priv->soc->channel_bits == 3)
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
/* Select proper clock source for internal ratio mode */ staticvoid fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv, struct fsl_asrc_pair *pair, int in_rate, int out_rate)
{ struct fsl_asrc_pair_priv *pair_priv = pair->private; struct asrc_config *config = pair_priv->config; int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */ int clk_rate, clk_index; int i, j;
rate[IN] = in_rate;
rate[OUT] = out_rate;
/* Select proper clock source for internal ratio mode */ for (j = 0; j < 2; j++) { for (i = 0; i < ASRC_CLK_MAP_LEN; i++) {
clk_index = asrc_priv->clk_map[j][i];
clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]); /* Only match a perfect clock source with no remainder */ if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL)) break;
}
select_clk[j] = i;
}
/* Switch to ideal ratio mode if there is no proper clock source */ if (select_clk[IN] == ASRC_CLK_MAP_LEN || select_clk[OUT] == ASRC_CLK_MAP_LEN) {
select_clk[IN] = INCLK_NONE;
select_clk[OUT] = OUTCLK_ASRCK1_CLK;
}
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
fsl_asrc_start_pair(pair); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
fsl_asrc_stop_pair(pair); break; default: return -EINVAL;
}
staticbool fsl_asrc_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_ASRCTR: case REG_ASRIER: case REG_ASRCNCR: case REG_ASRCFG: case REG_ASRCSR: case REG_ASRCDR1: case REG_ASRCDR2: case REG_ASRSTR: case REG_ASRPM1: case REG_ASRPM2: case REG_ASRPM3: case REG_ASRPM4: case REG_ASRPM5: case REG_ASRTFR1: case REG_ASRCCR: case REG_ASRDOA: case REG_ASRDOB: case REG_ASRDOC: case REG_ASRIDRHA: case REG_ASRIDRLA: case REG_ASRIDRHB: case REG_ASRIDRLB: case REG_ASRIDRHC: case REG_ASRIDRLC: case REG_ASR76K: case REG_ASR56K: case REG_ASRMCRA: case REG_ASRFSTA: case REG_ASRMCRB: case REG_ASRFSTB: case REG_ASRMCRC: case REG_ASRFSTC: case REG_ASRMCR1A: case REG_ASRMCR1B: case REG_ASRMCR1C: returntrue; default: returnfalse;
}
}
staticbool fsl_asrc_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_ASRSTR: case REG_ASRDIA: case REG_ASRDIB: case REG_ASRDIC: case REG_ASRDOA: case REG_ASRDOB: case REG_ASRDOC: case REG_ASRFSTA: case REG_ASRFSTB: case REG_ASRFSTC: case REG_ASRCFG: returntrue; default: returnfalse;
}
}
staticbool fsl_asrc_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_ASRCTR: case REG_ASRIER: case REG_ASRCNCR: case REG_ASRCFG: case REG_ASRCSR: case REG_ASRCDR1: case REG_ASRCDR2: case REG_ASRSTR: case REG_ASRPM1: case REG_ASRPM2: case REG_ASRPM3: case REG_ASRPM4: case REG_ASRPM5: case REG_ASRTFR1: case REG_ASRCCR: case REG_ASRDIA: case REG_ASRDIB: case REG_ASRDIC: case REG_ASRIDRHA: case REG_ASRIDRLA: case REG_ASRIDRHB: case REG_ASRIDRLB: case REG_ASRIDRHC: case REG_ASRIDRLC: case REG_ASR76K: case REG_ASR56K: case REG_ASRMCRA: case REG_ASRMCRB: case REG_ASRMCRC: case REG_ASRMCR1A: case REG_ASRMCR1B: case REG_ASRMCR1C: returntrue; default: returnfalse;
}
}
/* Base address for task queue FIFO. Set to 0x7C */
regmap_update_bits(asrc->regmap, REG_ASRTFR1,
ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
/* * Set the period of the 76KHz and 56KHz sampling clocks based on * the ASRC processing clock. * On iMX6, ipg_clk = 133MHz, REG_ASR76K = 0x06D6, REG_ASR56K = 0x0947
*/
ipg_rate = clk_get_rate(asrc->ipg_clk);
regmap_write(asrc->regmap, REG_ASR76K, ipg_rate / 76000); return regmap_write(asrc->regmap, REG_ASR56K, ipg_rate / 56000);
}
/* * We here use dev_dbg() for all exceptions because ASRC itself does * not care if FIFO overflowed or underrun while a warning in the * interrupt would result a ridged conversion.
*/ for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) { if (!asrc->pair[index]) continue;
pair_priv->config = &config;
ret = fsl_asrc_config_pair(pair, true); if (ret) {
dev_err(dev, "failed to config pair: %d\n", ret); return ret;
}
pair->first_convert = 1;
return 0;
}
staticint fsl_asrc_m2m_start(struct fsl_asrc_pair *pair)
{ if (pair->first_convert) {
fsl_asrc_start_pair(pair);
pair->first_convert = 0;
} /* * Clear DMA request during the stall state of ASRC: * During STALL state, the remaining in input fifo would never be * smaller than the input threshold while the output fifo would not * be bigger than output one. Thus the DMA request would be cleared.
*/
fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
ASRC_FIFO_THRESHOLD_MAX);
/* Update the real input threshold to raise DMA request */
fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML,
ASRC_M2M_OUTPUTFIFO_WML);
asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL); if (!asrc) return -ENOMEM;
asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL); if (!asrc_priv) return -ENOMEM;
asrc->pdev = pdev;
asrc->private = asrc_priv;
/* Get the addresses and IRQ */
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs);
asrc->paddr = res->start;
asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config); if (IS_ERR(asrc->regmap)) {
dev_err(&pdev->dev, "failed to init regmap\n"); return PTR_ERR(asrc->regmap);
}
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
dev_name(&pdev->dev), asrc); if (ret) {
dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); return ret;
}
asrc->mem_clk = devm_clk_get(&pdev->dev, "mem"); if (IS_ERR(asrc->mem_clk)) {
dev_err(&pdev->dev, "failed to get mem clock\n"); return PTR_ERR(asrc->mem_clk);
}
asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(asrc->ipg_clk)) {
dev_err(&pdev->dev, "failed to get ipg clock\n"); return PTR_ERR(asrc->ipg_clk);
}
asrc->spba_clk = devm_clk_get(&pdev->dev, "spba"); if (IS_ERR(asrc->spba_clk))
dev_warn(&pdev->dev, "failed to get spba clock\n");
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); if (IS_ERR(asrc_priv->asrck_clk[i])) {
dev_err(&pdev->dev, "failed to get %s clock\n", tmp); return PTR_ERR(asrc_priv->asrck_clk[i]);
}
}
ret = of_property_read_u32(np, "fsl,asrc-rate",
&asrc->asrc_rate); if (ret) {
dev_err(&pdev->dev, "failed to get output rate\n"); return ret;
}
ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt; if (ret) {
ret = of_property_read_u32(np, "fsl,asrc-width", &width); if (ret) {
dev_err(&pdev->dev, "failed to decide output format\n"); return ret;
}
switch (width) { case 16:
asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE; break; case 24:
asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE; break; default:
dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE; break;
}
}
if (!(FSL_ASRC_FORMATS & pcm_format_to_bits(asrc->asrc_format))) {
dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
}
platform_set_drvdata(pdev, asrc);
spin_lock_init(&asrc->lock);
pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) {
ret = fsl_asrc_runtime_resume(&pdev->dev); if (ret) goto err_pm_disable;
}
ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) goto err_pm_get_sync;
ret = fsl_asrc_init(asrc); if (ret) {
dev_err(&pdev->dev, "failed to init asrc %d\n", ret); goto err_pm_get_sync;
}
ret = pm_runtime_put_sync(&pdev->dev); if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync;
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
&fsl_asrc_dai, 1); if (ret) {
dev_err(&pdev->dev, "failed to register ASoC DAI\n"); goto err_pm_get_sync;
}
ret = fsl_asrc_m2m_init(asrc); if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); return ret;
}
return 0;
err_pm_get_sync: if (!pm_runtime_status_suspended(&pdev->dev))
fsl_asrc_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev); return ret;
}
pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev))
fsl_asrc_runtime_suspend(&pdev->dev);
}
staticint fsl_asrc_runtime_resume(struct device *dev)
{ struct fsl_asrc *asrc = dev_get_drvdata(dev); struct fsl_asrc_priv *asrc_priv = asrc->private; int reg, retry = INIT_RETRY_NUM; int i, ret;
u32 asrctr;
ret = clk_prepare_enable(asrc->mem_clk); if (ret) return ret;
ret = clk_prepare_enable(asrc->ipg_clk); if (ret) goto disable_mem_clk; if (!IS_ERR(asrc->spba_clk)) {
ret = clk_prepare_enable(asrc->spba_clk); if (ret) goto disable_ipg_clk;
} for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
ret = clk_prepare_enable(asrc_priv->asrck_clk[i]); if (ret) goto disable_asrck_clk;
}
/* Wait for status of initialization for all enabled pairs */ do {
udelay(5);
regmap_read(asrc->regmap, REG_ASRCFG, ®);
reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
} while ((reg != ((asrctr >> ASRCTR_ASRCEi_SHIFT(0)) & 0x7)) && --retry);
/* * NOTE: Doesn't treat initialization timeout as an error * Some of the pairs may success, then still can continue.
*/ if (!retry) { for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { if ((asrctr & ASRCTR_ASRCEi_MASK(i)) && !(reg & (1 << i)))
dev_warn(dev, "Pair %c initialization isn't finished\n", 'A' + i);
}
}
return 0;
disable_asrck_clk: for (i--; i >= 0; i--)
clk_disable_unprepare(asrc_priv->asrck_clk[i]); if (!IS_ERR(asrc->spba_clk))
clk_disable_unprepare(asrc->spba_clk);
disable_ipg_clk:
clk_disable_unprepare(asrc->ipg_clk);
disable_mem_clk:
clk_disable_unprepare(asrc->mem_clk); return ret;
}
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]); if (!IS_ERR(asrc->spba_clk))
clk_disable_unprepare(asrc->spba_clk);
clk_disable_unprepare(asrc->ipg_clk);
clk_disable_unprepare(asrc->mem_clk);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.