aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/codecs/arizona.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/arizona.c')
-rw-r--r--sound/soc/codecs/arizona.c488
1 files changed, 322 insertions, 166 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 657808ba141..29e198f57d4 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -53,6 +53,14 @@
#define ARIZONA_AIF_RX_ENABLES 0x1A
#define ARIZONA_AIF_FORCE_WRITE 0x1B
+#define ARIZONA_FLL_VCO_CORNER 141900000
+#define ARIZONA_FLL_MAX_FREF 13500000
+#define ARIZONA_FLL_MIN_FVCO 90000000
+#define ARIZONA_FLL_MAX_FRATIO 16
+#define ARIZONA_FLL_MAX_REFDIV 8
+#define ARIZONA_FLL_MIN_OUTDIV 2
+#define ARIZONA_FLL_MAX_OUTDIV 7
+
#define arizona_fll_err(_fll, fmt, ...) \
dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define arizona_fll_warn(_fll, fmt, ...) \
@@ -93,7 +101,7 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (!priv->spk_ena && manual_ena) {
- snd_soc_write(codec, 0x4f5, 0x25a);
+ regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
priv->spk_ena_pending = true;
}
break;
@@ -105,12 +113,13 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
return -EBUSY;
}
- snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1,
- 1 << w->shift, 1 << w->shift);
+ regmap_update_bits_async(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
if (priv->spk_ena_pending) {
msleep(75);
- snd_soc_write(codec, 0x4f5, 0xda);
+ regmap_write_async(arizona->regmap, 0x4f5, 0xda);
priv->spk_ena_pending = false;
priv->spk_ena++;
}
@@ -119,16 +128,19 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
if (manual_ena) {
priv->spk_ena--;
if (!priv->spk_ena)
- snd_soc_write(codec, 0x4f5, 0x25a);
+ regmap_write_async(arizona->regmap,
+ 0x4f5, 0x25a);
}
- snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1,
- 1 << w->shift, 0);
+ regmap_update_bits_async(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 0);
break;
case SND_SOC_DAPM_POST_PMD:
if (manual_ena) {
if (!priv->spk_ena)
- snd_soc_write(codec, 0x4f5, 0x0da);
+ regmap_write_async(arizona->regmap,
+ 0x4f5, 0x0da);
}
break;
}
@@ -292,6 +304,10 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"AIF1RX8",
"AIF2RX1",
"AIF2RX2",
+ "AIF2RX3",
+ "AIF2RX4",
+ "AIF2RX5",
+ "AIF2RX6",
"AIF3RX1",
"AIF3RX2",
"SLIMRX1",
@@ -395,6 +411,10 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x27,
0x28, /* AIF2RX1 */
0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
0x30, /* AIF3RX1 */
0x31,
0x38, /* SLIMRX1 */
@@ -486,6 +506,22 @@ const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
EXPORT_SYMBOL_GPL(arizona_rate_val);
+const struct soc_enum arizona_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_1,
+ ARIZONA_ISRC1_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_1,
+ ARIZONA_ISRC2_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_1,
+ ARIZONA_ISRC3_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsh);
+
const struct soc_enum arizona_isrc_fsl[] = {
SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2,
ARIZONA_ISRC1_FSL_SHIFT, 0xf,
@@ -502,64 +538,90 @@ const struct soc_enum arizona_isrc_fsl[] = {
};
EXPORT_SYMBOL_GPL(arizona_isrc_fsl);
+const struct soc_enum arizona_asrc_rate1 =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ASRC_RATE1,
+ ARIZONA_ASRC_RATE1_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE - 1,
+ arizona_rate_text, arizona_rate_val);
+EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
+
static const char *arizona_vol_ramp_text[] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"15ms/6dB", "30ms/6dB",
};
-const struct soc_enum arizona_in_vd_ramp =
- SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
- ARIZONA_IN_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
-const struct soc_enum arizona_in_vi_ramp =
- SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
- ARIZONA_IN_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
-const struct soc_enum arizona_out_vd_ramp =
- SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
- ARIZONA_OUT_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
-const struct soc_enum arizona_out_vi_ramp =
- SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
- ARIZONA_OUT_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
static const char *arizona_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
-const struct soc_enum arizona_lhpf1_mode =
- SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2,
- arizona_lhpf_mode_text);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode,
+ ARIZONA_HPLPF1_1,
+ ARIZONA_LHPF1_MODE_SHIFT,
+ arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf1_mode);
-const struct soc_enum arizona_lhpf2_mode =
- SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2,
- arizona_lhpf_mode_text);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode,
+ ARIZONA_HPLPF2_1,
+ ARIZONA_LHPF2_MODE_SHIFT,
+ arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf2_mode);
-const struct soc_enum arizona_lhpf3_mode =
- SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2,
- arizona_lhpf_mode_text);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode,
+ ARIZONA_HPLPF3_1,
+ ARIZONA_LHPF3_MODE_SHIFT,
+ arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf3_mode);
-const struct soc_enum arizona_lhpf4_mode =
- SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2,
- arizona_lhpf_mode_text);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
+ ARIZONA_HPLPF4_1,
+ ARIZONA_LHPF4_MODE_SHIFT,
+ arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
static const char *arizona_ng_hold_text[] = {
"30ms", "120ms", "250ms", "500ms",
};
-const struct soc_enum arizona_ng_hold =
- SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
- 4, arizona_ng_hold_text);
+SOC_ENUM_SINGLE_DECL(arizona_ng_hold,
+ ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_HOLD_SHIFT,
+ arizona_ng_hold_text);
EXPORT_SYMBOL_GPL(arizona_ng_hold);
+static const char * const arizona_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
+ ARIZONA_HPF_CONTROL,
+ ARIZONA_IN_HPF_CUT_SHIFT,
+ arizona_in_hpf_cut_text);
+EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
+
static const char * const arizona_in_dmic_osr_text[] = {
"1.536MHz", "3.072MHz", "6.144MHz",
};
@@ -669,6 +731,7 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
int event)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(w->codec);
+ struct arizona *arizona = priv->arizona;
unsigned int mask = 1 << w->shift;
unsigned int val;
@@ -691,7 +754,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
if (priv->arizona->hpdet_magic)
val = 0;
- snd_soc_update_bits(w->codec, ARIZONA_OUTPUT_ENABLES_1, mask, val);
+ regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1,
+ mask, val);
return arizona_out_ev(w, kcontrol, event);
}
@@ -846,6 +910,8 @@ EXPORT_SYMBOL_GPL(arizona_set_sysclk);
static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
int lrclk, bclk, mode, base;
base = dai->driver->base;
@@ -902,17 +968,19 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
- ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR,
- bclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL,
- ARIZONA_AIF1TX_LRCLK_INV |
- ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL,
- ARIZONA_AIF1RX_LRCLK_INV |
- ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT,
- ARIZONA_AIF1_FMT_MASK, mode);
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_INV |
+ ARIZONA_AIF1_BCLK_MSTR,
+ bclk);
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_TX_PIN_CTRL,
+ ARIZONA_AIF1TX_LRCLK_INV |
+ ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_PIN_CTRL,
+ ARIZONA_AIF1RX_LRCLK_INV |
+ ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
+ regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FORMAT,
+ ARIZONA_AIF1_FMT_MASK, mode);
return 0;
}
@@ -1164,18 +1232,22 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
if (ret != 0)
return ret;
- snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
- ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE,
- ARIZONA_AIF1TX_BCPF_MASK, lrclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE,
- ARIZONA_AIF1RX_BCPF_MASK, lrclk);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1,
- ARIZONA_AIF1TX_WL_MASK |
- ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2,
- ARIZONA_AIF1RX_WL_MASK |
- ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+ 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);
return 0;
}
@@ -1322,74 +1394,147 @@ struct arizona_fll_cfg {
int gain;
};
-static int arizona_calc_fll(struct arizona_fll *fll,
- struct arizona_fll_cfg *cfg,
- unsigned int Fref,
- unsigned int Fout)
+static int arizona_validate_fll(struct arizona_fll *fll,
+ unsigned int Fref,
+ unsigned int Fout)
{
- unsigned int target, div, gcd_fll;
- int i, ratio;
+ unsigned int Fvco_min;
+
+ 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;
+}
+
+static int arizona_find_fratio(unsigned int 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;
+}
- arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);
+static int arizona_calc_fratio(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int target,
+ unsigned int Fref, bool sync)
+{
+ int init_ratio, ratio;
+ int refdiv, div;
- /* Fref must be <=13.5MHz */
+ /* Fref must be <=13.5MHz, find initial refdiv */
div = 1;
cfg->refdiv = 0;
- while ((Fref / div) > 13500000) {
+ while (Fref > ARIZONA_FLL_MAX_FREF) {
div *= 2;
+ Fref /= 2;
cfg->refdiv++;
- if (div > 8) {
- arizona_fll_err(fll,
- "Can't scale %dMHz in to <=13.5MHz\n",
- Fref);
+ 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 WM5110:
+ if (fll->arizona->rev < 3 || sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+
+ cfg->fratio = init_ratio - 1;
+
+ /* Adjust FRATIO/refdiv to avoid integer mode if possible */
+ refdiv = cfg->refdiv;
+
+ while (div <= ARIZONA_FLL_MAX_REFDIV) {
+ for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
+ ratio++) {
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ for (ratio = init_ratio - 1; ratio >= 0; ratio--) {
+ if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) <
+ Fref)
+ break;
+
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
}
+
+ div *= 2;
+ Fref /= 2;
+ refdiv++;
+ init_ratio = arizona_find_fratio(Fref, NULL);
}
- /* Apply the division for our remaining calculations */
- Fref /= div;
+ arizona_fll_warn(fll, "Falling back to integer mode operation\n");
+ return cfg->fratio + 1;
+}
+
+static int arizona_calc_fll(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int Fref, bool sync)
+{
+ unsigned int target, div, gcd_fll;
+ int i, ratio;
+
+ arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout);
/* Fvco should be over the targt; don't check the upper bound */
- div = 1;
- while (Fout * div < 90000000 * fll->vco_mult) {
+ div = ARIZONA_FLL_MIN_OUTDIV;
+ while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) {
div++;
- if (div > 7) {
- arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
- Fout);
+ if (div > ARIZONA_FLL_MAX_OUTDIV)
return -EINVAL;
- }
}
- target = Fout * div / fll->vco_mult;
+ target = fll->fout * div / fll->vco_mult;
cfg->outdiv = div;
arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
- /* Find an appropraite FLL_FRATIO and factor it out of the target */
- for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
- if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
- cfg->fratio = fll_fratios[i].fratio;
- ratio = fll_fratios[i].ratio;
- break;
- }
- }
- if (i == ARRAY_SIZE(fll_fratios)) {
- arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
- Fref);
- return -EINVAL;
- }
+ /* Find an appropriate FLL_FRATIO and refdiv */
+ ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync);
+ if (ratio < 0)
+ return ratio;
- 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;
- }
+ /* Apply the division for our remaining calculations */
+ Fref = Fref / (1 << cfg->refdiv);
cfg->n = target / (ratio * Fref);
@@ -1414,6 +1559,18 @@ static int arizona_calc_fll(struct arizona_fll *fll,
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;
+ }
+
arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
cfg->n, cfg->theta, cfg->lambda);
arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
@@ -1428,31 +1585,35 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
struct arizona_fll_cfg *cfg, int source,
bool sync)
{
- regmap_update_bits(arizona->regmap, base + 3,
- ARIZONA_FLL1_THETA_MASK, cfg->theta);
- regmap_update_bits(arizona->regmap, base + 4,
- ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
- regmap_update_bits(arizona->regmap, base + 5,
- ARIZONA_FLL1_FRATIO_MASK,
- cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
- regmap_update_bits(arizona->regmap, base + 6,
- ARIZONA_FLL1_CLK_REF_DIV_MASK |
- ARIZONA_FLL1_CLK_REF_SRC_MASK,
- cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
- source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
-
- if (sync)
+ regmap_update_bits_async(arizona->regmap, base + 3,
+ ARIZONA_FLL1_THETA_MASK, cfg->theta);
+ regmap_update_bits_async(arizona->regmap, base + 4,
+ ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
+ regmap_update_bits_async(arizona->regmap, base + 5,
+ ARIZONA_FLL1_FRATIO_MASK,
+ cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
+ regmap_update_bits_async(arizona->regmap, base + 6,
+ ARIZONA_FLL1_CLK_REF_DIV_MASK |
+ ARIZONA_FLL1_CLK_REF_SRC_MASK,
+ cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
+ source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
+
+ if (sync) {
regmap_update_bits(arizona->regmap, base + 0x7,
ARIZONA_FLL1_GAIN_MASK,
cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
- else
+ } else {
+ regmap_update_bits(arizona->regmap, base + 0x5,
+ ARIZONA_FLL1_OUTDIV_MASK,
+ cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
regmap_update_bits(arizona->regmap, base + 0x9,
ARIZONA_FLL1_GAIN_MASK,
cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ }
- regmap_update_bits(arizona->regmap, base + 2,
- ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
- ARIZONA_FLL1_CTRL_UPD | cfg->n);
+ regmap_update_bits_async(arizona->regmap, base + 2,
+ ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
+ ARIZONA_FLL1_CTRL_UPD | cfg->n);
}
static bool arizona_is_enabled_fll(struct arizona_fll *fll)
@@ -1471,37 +1632,38 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll)
return reg & ARIZONA_FLL1_ENA;
}
-static void arizona_enable_fll(struct arizona_fll *fll,
- struct arizona_fll_cfg *ref,
- struct arizona_fll_cfg *sync)
+static void arizona_enable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
int ret;
+ bool use_sync = false;
+ struct arizona_fll_cfg cfg;
/*
* If we have both REFCLK and SYNCCLK then enable both,
* otherwise apply the SYNCCLK settings to REFCLK.
*/
- if (fll->ref_src >= 0 && fll->ref_src != fll->sync_src) {
- regmap_update_bits(arizona->regmap, fll->base + 5,
- ARIZONA_FLL1_OUTDIV_MASK,
- ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+ if (fll->ref_src >= 0 && fll->ref_freq &&
+ fll->ref_src != fll->sync_src) {
+ arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
- arizona_apply_fll(arizona, fll->base, ref, fll->ref_src,
+ arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
false);
- if (fll->sync_src >= 0)
- arizona_apply_fll(arizona, fll->base + 0x10, sync,
+ if (fll->sync_src >= 0) {
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, true);
+
+ arizona_apply_fll(arizona, fll->base + 0x10, &cfg,
fll->sync_src, true);
+ use_sync = true;
+ }
} else if (fll->sync_src >= 0) {
- regmap_update_bits(arizona->regmap, fll->base + 5,
- ARIZONA_FLL1_OUTDIV_MASK,
- sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, false);
- arizona_apply_fll(arizona, fll->base, sync,
+ arizona_apply_fll(arizona, fll->base, &cfg,
fll->sync_src, false);
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA, 0);
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0);
} else {
arizona_fll_err(fll, "No clocks provided\n");
return;
@@ -1511,12 +1673,13 @@ static void arizona_enable_fll(struct arizona_fll *fll,
* Increase the bandwidth if we're not using a low frequency
* sync source.
*/
- if (fll->sync_src >= 0 && fll->sync_freq > 100000)
- regmap_update_bits(arizona->regmap, fll->base + 0x17,
- ARIZONA_FLL1_SYNC_BW, 0);
+ 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(arizona->regmap, fll->base + 0x17,
- ARIZONA_FLL1_SYNC_BW, ARIZONA_FLL1_SYNC_BW);
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW,
+ ARIZONA_FLL1_SYNC_BW);
if (!arizona_is_enabled_fll(fll))
pm_runtime_get(arizona->dev);
@@ -1524,13 +1687,14 @@ static void arizona_enable_fll(struct arizona_fll *fll,
/* Clear any pending completions */
try_wait_for_completion(&fll->ok);
- regmap_update_bits(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
- if (fll->ref_src >= 0 && fll->sync_src >= 0 &&
- fll->ref_src != fll->sync_src)
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA,
- ARIZONA_FLL1_SYNC_ENA);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
+ if (use_sync)
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA,
+ ARIZONA_FLL1_SYNC_ENA);
ret = wait_for_completion_timeout(&fll->ok,
msecs_to_jiffies(250));
@@ -1543,6 +1707,8 @@ static void arizona_disable_fll(struct arizona_fll *fll)
struct arizona *arizona = fll->arizona;
bool change;
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
regmap_update_bits_check(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_ENA, 0, &change);
regmap_update_bits(arizona->regmap, fll->base + 0x11,
@@ -1555,30 +1721,22 @@ static void arizona_disable_fll(struct arizona_fll *fll)
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout)
{
- struct arizona_fll_cfg ref, sync;
int ret;
if (fll->ref_src == source && fll->ref_freq == Fref)
return 0;
if (fll->fout && Fref > 0) {
- ret = arizona_calc_fll(fll, &ref, Fref, fll->fout);
+ ret = arizona_validate_fll(fll, Fref, fll->fout);
if (ret != 0)
return ret;
-
- if (fll->sync_src >= 0) {
- ret = arizona_calc_fll(fll, &sync, fll->sync_freq,
- fll->fout);
- if (ret != 0)
- return ret;
- }
}
fll->ref_src = source;
fll->ref_freq = Fref;
if (fll->fout && Fref > 0) {
- arizona_enable_fll(fll, &ref, &sync);
+ arizona_enable_fll(fll);
}
return 0;
@@ -1588,7 +1746,6 @@ EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout)
{
- struct arizona_fll_cfg ref, sync;
int ret;
if (fll->sync_src == source &&
@@ -1597,13 +1754,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
if (Fout) {
if (fll->ref_src >= 0) {
- ret = arizona_calc_fll(fll, &ref, fll->ref_freq,
- Fout);
+ ret = arizona_validate_fll(fll, fll->ref_freq, Fout);
if (ret != 0)
return ret;
}
- ret = arizona_calc_fll(fll, &sync, Fref, Fout);
+ ret = arizona_validate_fll(fll, Fref, Fout);
if (ret != 0)
return ret;
}
@@ -1613,7 +1769,7 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
fll->fout = Fout;
if (Fout) {
- arizona_enable_fll(fll, &ref, &sync);
+ arizona_enable_fll(fll);
} else {
arizona_disable_fll(fll);
}