switch (event) { case SND_SOC_DAPM_POST_PMU:
val = snd_soc_component_read(component,
ARIZONA_INTERRUPT_RAW_STATUS_3); if (val & ARIZONA_SPK_OVERHEAT_STS) {
dev_crit(arizona->dev, "Speaker not enabled due to temperature\n"); return -EBUSY;
}
ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1); if (ret != 0) return ret;
switch (arizona->type) { case WM8997: case CS47L24: case WM1831: break; default:
ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); if (ret != 0) return ret; break;
}
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_spk);
int arizona_init_spk_irqs(struct arizona *arizona)
{ int ret;
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, "Thermal warning", arizona_thermal_warn,
arizona); if (ret != 0)
dev_err(arizona->dev, "Failed to get thermal warning IRQ: %d\n",
ret);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, "Thermal shutdown", arizona_thermal_shutdown,
arizona); if (ret != 0)
dev_err(arizona->dev, "Failed to get thermal shutdown IRQ: %d\n",
ret);
for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { if (arizona->pdata.out_mono[i])
snd_soc_dapm_add_routes(dapm,
&arizona_mono_routes[i], 1);
}
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_mono);
int arizona_init_gpio(struct snd_soc_component *component)
{ struct arizona_priv *priv = snd_soc_component_get_drvdata(component); struct arizona *arizona = priv->arizona; int i;
switch (arizona->type) { case WM5110: case WM8280:
snd_soc_component_disable_pin(component, "DRC2 Signal Activity"); break; default: break;
}
snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
snd_soc_component_enable_pin(component, "DRC1 Signal Activity"); break; case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
snd_soc_component_enable_pin(component, "DRC2 Signal Activity"); break; default: break;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
int arizona_init_common(struct arizona *arizona)
{ struct arizona_pdata *pdata = &arizona->pdata; unsignedint val, mask; int i;
BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { /* Default is 0 so noop with defaults */ if (pdata->out_mono[i])
val = ARIZONA_OUT1_MONO; else
val = 0;
regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
ARIZONA_OUT1_MONO, val);
}
for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { if (pdata->spk_mute[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
ARIZONA_SPK1_MUTE_ENDIAN_MASK |
ARIZONA_SPK1_MUTE_SEQ1_MASK,
pdata->spk_mute[i]);
if (pdata->spk_fmt[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
ARIZONA_SPK1_FMT_MASK,
pdata->spk_fmt[i]);
}
for (i = 0; i < ARIZONA_MAX_INPUT; i++) { /* Default for both is 0 so noop with defaults */
val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT; if (pdata->inmode[i] & ARIZONA_INMODE_DMIC)
val |= 1 << ARIZONA_IN1_MODE_SHIFT;
switch (arizona->type) { case WM8998: case WM1814:
regmap_update_bits(arizona->regmap,
ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
ARIZONA_IN1L_SRC_SE_MASK,
(pdata->inmode[i] & ARIZONA_INMODE_SE)
<< ARIZONA_IN1L_SRC_SE_SHIFT);
int arizona_init_vol_limit(struct arizona *arizona)
{ int i;
for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) { if (arizona->pdata.out_vol_limit[i])
regmap_update_bits(arizona->regmap,
ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4,
ARIZONA_OUT1L_VOL_LIM_MASK,
arizona->pdata.out_vol_limit[i]);
}
switch (event) { case SND_SOC_DAPM_PRE_PMU:
priv->in_pending++; break; case SND_SOC_DAPM_POST_PMU:
snd_soc_component_update_bits(component, reg,
ARIZONA_IN1L_MUTE, 0);
/* If this is the last input pending then allow VU */
priv->in_pending--; if (priv->in_pending == 0) {
msleep(1);
arizona_in_set_vu(component, 1);
} break; case SND_SOC_DAPM_PRE_PMD:
snd_soc_component_update_bits(component, reg,
ARIZONA_IN1L_MUTE | ARIZONA_IN_VU,
ARIZONA_IN1L_MUTE | ARIZONA_IN_VU); break; case SND_SOC_DAPM_POST_PMD: /* Disable volume updates if no inputs are enabled */
reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES); if (reg == 0)
arizona_in_set_vu(component, 0); break; default: break;
}
switch (event) { case SND_SOC_DAPM_PRE_PMU: switch (w->shift) { case ARIZONA_OUT1L_ENA_SHIFT: case ARIZONA_OUT1R_ENA_SHIFT: case ARIZONA_OUT2L_ENA_SHIFT: case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_up_pending++;
priv->out_up_delay += 17000; break; case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_up_pending++; switch (arizona->type) { case WM5102: case WM8997: break; default:
priv->out_up_delay += 10000; break;
} break; default: break;
} break; case SND_SOC_DAPM_POST_PMU: switch (w->shift) { case ARIZONA_OUT1L_ENA_SHIFT: case ARIZONA_OUT1R_ENA_SHIFT: case ARIZONA_OUT2L_ENA_SHIFT: case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_up_pending--; if (!priv->out_up_pending && priv->out_up_delay) {
dev_dbg(component->dev, "Power up delay: %d\n",
priv->out_up_delay);
fsleep(priv->out_up_delay);
priv->out_up_delay = 0;
} break;
default: break;
} break; case SND_SOC_DAPM_PRE_PMD: switch (w->shift) { case ARIZONA_OUT1L_ENA_SHIFT: case ARIZONA_OUT1R_ENA_SHIFT: case ARIZONA_OUT2L_ENA_SHIFT: case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_down_pending++;
priv->out_down_delay += 1000; break; case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_down_pending++; switch (arizona->type) { case WM5102: case WM8997: break; case WM8998: case WM1814:
priv->out_down_delay += 5000; break; default:
priv->out_down_delay += 1000; break;
} break; default: break;
} break; case SND_SOC_DAPM_POST_PMD: switch (w->shift) { case ARIZONA_OUT1L_ENA_SHIFT: case ARIZONA_OUT1R_ENA_SHIFT: case ARIZONA_OUT2L_ENA_SHIFT: case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_down_pending--; if (!priv->out_down_pending && priv->out_down_delay) {
dev_dbg(component->dev, "Power down delay: %d\n",
priv->out_down_delay);
fsleep(priv->out_down_delay);
priv->out_down_delay = 0;
} break; default: break;
} break; default: break;
}
switch (event) { case SND_SOC_DAPM_POST_PMU:
val = mask; break; case SND_SOC_DAPM_PRE_PMD:
val = 0; break; case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_POST_PMD: return arizona_out_ev(w, kcontrol, event); default: return -EINVAL;
}
/* Store the desired state for the HP outputs */
priv->arizona->hp_ena &= ~mask;
priv->arizona->hp_ena |= val;
/* Force off if HPDET clamp is active */ if (priv->arizona->hpdet_clamp)
val = 0;
int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event)
{ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct arizona_priv *priv = snd_soc_component_get_drvdata(component); int ret = 0;
mutex_lock(&priv->dvfs_lock);
switch (event) { case SND_SOC_DAPM_POST_PMU: if (priv->dvfs_reqs)
ret = arizona_dvfs_enable(component);
priv->dvfs_cached = false; break; case SND_SOC_DAPM_PRE_PMD: /* We must ensure DVFS is disabled before the codec goes into * suspend so that we are never in an illegal state of DVFS * enabled without enough DCVDD
*/
priv->dvfs_cached = true;
if (priv->dvfs_reqs)
ret = arizona_dvfs_disable(component); break; default: break;
}
for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
rates[ref] <= refclk; ref++) {
div = 1; while (rates[ref] / div >= freq && div < 32) { if (rates[ref] / div == freq) {
dev_dbg(component->dev, "Configured %dHz OPCLK\n",
freq);
snd_soc_component_update_bits(component, reg,
ARIZONA_OPCLK_DIV_MASK |
ARIZONA_OPCLK_SEL_MASK,
(div <<
ARIZONA_OPCLK_DIV_SHIFT) |
ref); return 0;
}
div++;
}
}
dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq); return -EINVAL;
}
int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event)
{ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct arizona *arizona = dev_get_drvdata(component->dev->parent); unsignedint val; int clk_idx; int ret;
ret = regmap_read(arizona->regmap, w->reg, &val); if (ret) {
dev_err(component->dev, "Failed to check clock source: %d\n", ret); return ret;
}
val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
switch (val) { case ARIZONA_CLK_SRC_MCLK1:
clk_idx = ARIZONA_MCLK1; break; case ARIZONA_CLK_SRC_MCLK2:
clk_idx = ARIZONA_MCLK2; break; default: return 0;
}
switch (event) { case SND_SOC_DAPM_PRE_PMU: return clk_prepare_enable(arizona->mclk[clk_idx]); case SND_SOC_DAPM_POST_PMD:
clk_disable_unprepare(arizona->mclk[clk_idx]); return 0; default: return 0;
}
}
EXPORT_SYMBOL_GPL(arizona_clk_ev);
int arizona_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsignedint freq, int dir)
{ struct arizona_priv *priv = snd_soc_component_get_drvdata(component); struct arizona *arizona = priv->arizona; char *name; unsignedint reg; unsignedint mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; unsignedint val = source << ARIZONA_SYSCLK_SRC_SHIFT; int *clk;
switch (clk_id) { case ARIZONA_CLK_SYSCLK:
name = "SYSCLK";
reg = ARIZONA_SYSTEM_CLOCK_1;
clk = &priv->sysclk;
mask |= ARIZONA_SYSCLK_FRAC; break; case ARIZONA_CLK_ASYNCCLK:
name = "ASYNCCLK";
reg = ARIZONA_ASYNC_CLOCK_1;
clk = &priv->asyncclk; break; case ARIZONA_CLK_OPCLK: case ARIZONA_CLK_ASYNC_OPCLK: return arizona_set_opclk(component, clk_id, freq); default: return -EINVAL;
}
switch (freq) { case 5644800: case 6144000: break; case 11289600: case 12288000:
val |= ARIZONA_CLK_12MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 22579200: case 24576000:
val |= ARIZONA_CLK_24MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 45158400: case 49152000:
val |= ARIZONA_CLK_49MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 67737600: case 73728000:
val |= ARIZONA_CLK_73MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 90316800: case 98304000:
val |= ARIZONA_CLK_98MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 135475200: case 147456000:
val |= ARIZONA_CLK_147MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; break; case 0:
dev_dbg(arizona->dev, "%s cleared\n", name);
*clk = freq; return 0; default: return -EINVAL;
}
*clk = freq;
if (freq % 6144000)
val |= ARIZONA_SYSCLK_FRAC;
dev_dbg(arizona->dev, "%s set to %uHz", name, freq);
staticint arizona_hw_params_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ struct snd_soc_component *component = dai->component; struct arizona_priv *priv = snd_soc_component_get_drvdata(component); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; int base = dai->driver->base; int i, sr_val, ret;
/* * We will need to be more flexible than this in future, * currently we use a single sample rate for SYSCLK.
*/ for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) if (arizona_sr_vals[i] == params_rate(params)) break; if (i == ARRAY_SIZE(arizona_sr_vals)) {
arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
params_rate(params)); return -EINVAL;
}
sr_val = i;
switch (priv->arizona->type) { case WM5102: case WM8997: if (arizona_sr_vals[sr_val] >= 88200)
ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ); else
ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ);
if (ret) {
arizona_aif_err(dai, "Failed to change DVFS %d\n", ret); return ret;
} break; default: break;
}
switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: switch (priv->arizona->type) { case WM5102:
arizona_wm5102_set_dac_comp(component,
params_rate(params)); break; default: break;
}
snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
ARIZONA_SAMPLE_RATE_1_MASK,
sr_val); if (base)
snd_soc_component_update_bits(component,
base + ARIZONA_AIF_RATE_CTRL,
ARIZONA_AIF1_RATE_MASK, 0); break; case ARIZONA_CLK_ASYNCCLK:
snd_soc_component_update_bits(component,
ARIZONA_ASYNC_SAMPLE_RATE_1,
ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
sr_val); if (base)
snd_soc_component_update_bits(component,
base + ARIZONA_AIF_RATE_CTRL,
ARIZONA_AIF1_RATE_MASK,
8 << ARIZONA_AIF1_RATE_SHIFT); break; default:
arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); return -EINVAL;
}
return 0;
}
staticbool arizona_aif_cfg_changed(struct snd_soc_component *component, int base, int bclk, int lrclk, int frame)
{ int val;
val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL); if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK)) returntrue;
val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE); if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK)) returntrue;
val = snd_soc_component_read(component, base + ARIZONA_AIF_FRAME_CTRL_1); if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
ARIZONA_AIF1TX_SLOT_LEN_MASK))) returntrue;
returnfalse;
}
staticint arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ struct snd_soc_component *component = dai->component; struct arizona_priv *priv = snd_soc_component_get_drvdata(component); struct arizona *arizona = priv->arizona; int base = dai->driver->base; constint *rates; int i, ret, val; int channels = params_channels(params); int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; int tdm_width = arizona->tdm_width[dai->id - 1]; int tdm_slots = arizona->tdm_slots[dai->id - 1]; int bclk, lrclk, wl, frame, bclk_target; bool reconfig; unsignedint aif_tx_state, aif_rx_state;
if (reconfig) { /* Save AIF TX/RX state */
aif_tx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_TX_ENABLES);
aif_rx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_RX_ENABLES); /* Disable AIF TX/RX before reconfiguring it */
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_TX_ENABLES,
0xff, 0x0);
regmap_update_bits(arizona->regmap,
base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
}
ret = arizona_hw_params_rate(substream, params, dai); if (ret != 0) goto restore_aif;
if (reconfig) {
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_BCLK_CTRL,
ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_TX_BCLK_RATE,
ARIZONA_AIF1TX_BCPF_MASK, lrclk);
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_RX_BCLK_RATE,
ARIZONA_AIF1RX_BCPF_MASK, lrclk);
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_FRAME_CTRL_1,
ARIZONA_AIF1TX_WL_MASK |
ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
regmap_update_bits(arizona->regmap,
base + ARIZONA_AIF_FRAME_CTRL_2,
ARIZONA_AIF1RX_WL_MASK |
ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
}
restore_aif: if (reconfig) { /* Restore AIF TX/RX state */
regmap_update_bits_async(arizona->regmap,
base + ARIZONA_AIF_TX_ENABLES,
0xff, aif_tx_state);
regmap_update_bits(arizona->regmap,
base + ARIZONA_AIF_RX_ENABLES,
0xff, aif_rx_state);
} return ret;
}
staticconstchar *arizona_dai_clk_str(int clk_id)
{ switch (clk_id) { case ARIZONA_CLK_SYSCLK: return"SYSCLK"; case ARIZONA_CLK_ASYNCCLK: return"ASYNCCLK"; default: return"Unknown clock";
}
}
if (fll->fout && Fout != fll->fout) {
arizona_fll_err(fll, "Can't change output on active FLL\n"); return -EINVAL;
}
if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) {
arizona_fll_err(fll, "Can't scale %dMHz in to <=13.5MHz\n",
Fref); return -EINVAL;
}
Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult; if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) {
arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
Fout); return -EINVAL;
}
return 0;
}
staticint arizona_find_fratio(unsignedint Fref, int *fratio)
{ int i;
/* Find an appropriate FLL_FRATIO */ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { if (fratio)
*fratio = fll_fratios[i].fratio; return fll_fratios[i].ratio;
}
}
return -EINVAL;
}
staticint arizona_calc_fratio(struct arizona_fll *fll, struct arizona_fll_cfg *cfg, unsignedint target, unsignedint Fref, bool sync)
{ int init_ratio, ratio; int refdiv, div;
/* Fref must be <=13.5MHz, find initial refdiv */
div = 1;
cfg->refdiv = 0; while (Fref > ARIZONA_FLL_MAX_FREF) {
div *= 2;
Fref /= 2;
cfg->refdiv++;
if (div > ARIZONA_FLL_MAX_REFDIV) return -EINVAL;
}
/* Find an appropriate FLL_FRATIO */
init_ratio = arizona_find_fratio(Fref, &cfg->fratio); if (init_ratio < 0) {
arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
Fref); return init_ratio;
}
switch (fll->arizona->type) { case WM5102: case WM8997: return init_ratio; case WM5110: case WM8280: if (fll->arizona->rev < 3 || sync) return init_ratio; break; default: if (sync) return init_ratio; break;
}
cfg->fratio = init_ratio - 1;
/* Adjust FRATIO/refdiv to avoid integer mode if possible */
refdiv = cfg->refdiv;
while (div <= ARIZONA_FLL_MAX_REFDIV) { /* start from init_ratio because this may already give a * fractional N.K
*/ for (ratio = init_ratio; ratio > 0; ratio--) { if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
arizona_fll_dbg(fll, "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
Fref, refdiv, div, ratio); return ratio;
}
}
for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO;
ratio++) { if ((ARIZONA_FLL_VCO_CORNER / 2) /
(fll->vco_mult * ratio) < Fref) {
arizona_fll_dbg(fll, "pseudo: hit VCO corner\n"); break;
}
if (Fref > pseudo_fref_max[ratio - 1]) {
arizona_fll_dbg(fll, "pseudo: exceeded max fref(%u) for ratio=%u\n",
pseudo_fref_max[ratio - 1],
ratio); break;
}
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
arizona_fll_dbg(fll, "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
Fref, refdiv, div, ratio); return ratio;
}
}
/* Fvco should be over the targt; don't check the upper bound */
div = ARIZONA_FLL_MIN_OUTDIV; while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) {
div++; if (div > ARIZONA_FLL_MAX_OUTDIV) return -EINVAL;
}
target = fll->fout * div / fll->vco_mult;
cfg->outdiv = div;
arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
/* Find an appropriate FLL_FRATIO and refdiv */
ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); if (ratio < 0) return ratio;
/* Apply the division for our remaining calculations */
Fref = Fref / (1 << cfg->refdiv);
cfg->n = target / (ratio * Fref);
if (target % (ratio * Fref)) {
gcd_fll = gcd(target, ratio * Fref);
arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
/* Round down to 16bit range with cost of accuracy lost. * Denominator must be bigger than numerator so we only * take care of it.
*/ while (cfg->lambda >= (1 << 16)) {
cfg->theta >>= 1;
cfg->lambda >>= 1;
}
for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
cfg->gain = fll_gains[i].gain; break;
}
} if (i == ARRAY_SIZE(fll_gains)) {
arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
Fref); return -EINVAL;
}
/* * If we have both REFCLK and SYNCCLK then enable both, * otherwise apply the SYNCCLK settings to REFCLK.
*/ if (fll->ref_src >= 0 && fll->ref_freq &&
fll->ref_src != fll->sync_src) {
arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
/* Ref path hardcodes lambda to 65536 when sync is on */ if (fll->sync_src >= 0 && cfg.lambda)
cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda;
if (already_enabled && !!sync_enabled != use_sync)
arizona_fll_warn(fll, "Synchroniser changed on active FLL\n");
/* * Increase the bandwidth if we're not using a low frequency * sync source.
*/ if (use_sync && fll->sync_freq > 100000)
regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
ARIZONA_FLL1_SYNC_BW, 0); else
regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
ARIZONA_FLL1_SYNC_BW,
ARIZONA_FLL1_SYNC_BW);
if (!already_enabled)
pm_runtime_get_sync(arizona->dev);
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.