struct wm8995_priv { struct regmap *regmap; int sysclk[2]; int mclk[2]; int aifclk[2]; struct fll_config fll[2], fll_suspend[2]; struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; struct snd_soc_component *component;
};
/* * We can't use the same notifier block for more than one supply and * there's no way I can see to get from a callback to the caller * except container_of().
*/ #define WM8995_REGULATOR_EVENT(n) \ staticint wm8995_regulator_event_##n(struct notifier_block *nb, \ unsignedlong event, void *data) \
{ \ struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \
regcache_mark_dirty(wm8995->regmap); \
} \ return 0; \
}
staticvoid wm8995_update_class_w(struct snd_soc_component *component)
{ int enable = 1; int source = 0; /* GCC flow analysis can't track enable */ int reg, reg_r;
/* We also need the same setting for L/R and only one path */
reg = snd_soc_component_read(component, WM8995_DAC1_LEFT_MIXER_ROUTING); switch (reg) { case WM8995_AIF2DACL_TO_DAC1L:
dev_dbg(component->dev, "Class W source AIF2DAC\n");
source = 2 << WM8995_CP_DYN_SRC_SEL_SHIFT; break; case WM8995_AIF1DAC2L_TO_DAC1L:
dev_dbg(component->dev, "Class W source AIF1DAC2\n");
source = 1 << WM8995_CP_DYN_SRC_SEL_SHIFT; break; case WM8995_AIF1DAC1L_TO_DAC1L:
dev_dbg(component->dev, "Class W source AIF1DAC1\n");
source = 0 << WM8995_CP_DYN_SRC_SEL_SHIFT; break; default:
dev_dbg(component->dev, "DAC mixer setting: %x\n", reg);
enable = 0; break;
}
reg_r = snd_soc_component_read(component, WM8995_DAC1_RIGHT_MIXER_ROUTING); if (reg_r != reg) {
dev_dbg(component->dev, "Left and right DAC mixers different\n");
enable = 0;
}
if (enable) {
dev_dbg(component->dev, "Class W enabled\n");
snd_soc_component_update_bits(component, WM8995_CLASS_W_1,
WM8995_CP_DYN_PWR_MASK |
WM8995_CP_DYN_SRC_SEL_MASK,
source | WM8995_CP_DYN_PWR);
} else {
dev_dbg(component->dev, "Class W disabled\n");
snd_soc_component_update_bits(component, WM8995_CLASS_W_1,
WM8995_CP_DYN_PWR_MASK, 0);
}
}
/* Bring up the AIF clocks first */
configure_aif_clock(component, 0);
configure_aif_clock(component, 1);
/* * Then switch CLK_SYS over to the higher of them; a change * can only happen as a result of a clocking change which can * only be made outside of DAPM so we can safely redo the * clocking.
*/
/* If they're equal it doesn't matter which is used */ if (wm8995->aifclk[0] == wm8995->aifclk[1]) return 0;
if (wm8995->aifclk[0] < wm8995->aifclk[1]) new = WM8995_SYSCLK_SRC; else new = 0;
change = snd_soc_component_update_bits(component, WM8995_CLOCKING_1,
WM8995_SYSCLK_SRC_MASK, new); if (!change) return 0;
staticbool wm8995_readable(struct device *dev, unsignedint reg)
{ switch (reg) { case WM8995_SOFTWARE_RESET: case WM8995_POWER_MANAGEMENT_1: case WM8995_POWER_MANAGEMENT_2: case WM8995_POWER_MANAGEMENT_3: case WM8995_POWER_MANAGEMENT_4: case WM8995_POWER_MANAGEMENT_5: case WM8995_LEFT_LINE_INPUT_1_VOLUME: case WM8995_RIGHT_LINE_INPUT_1_VOLUME: case WM8995_LEFT_LINE_INPUT_CONTROL: case WM8995_DAC1_LEFT_VOLUME: case WM8995_DAC1_RIGHT_VOLUME: case WM8995_DAC2_LEFT_VOLUME: case WM8995_DAC2_RIGHT_VOLUME: case WM8995_OUTPUT_VOLUME_ZC_1: case WM8995_MICBIAS_1: case WM8995_MICBIAS_2: case WM8995_LDO_1: case WM8995_LDO_2: case WM8995_ACCESSORY_DETECT_MODE1: case WM8995_ACCESSORY_DETECT_MODE2: case WM8995_HEADPHONE_DETECT1: case WM8995_HEADPHONE_DETECT2: case WM8995_MIC_DETECT_1: case WM8995_MIC_DETECT_2: case WM8995_CHARGE_PUMP_1: case WM8995_CLASS_W_1: case WM8995_DC_SERVO_1: case WM8995_DC_SERVO_2: case WM8995_DC_SERVO_3: case WM8995_DC_SERVO_5: case WM8995_DC_SERVO_6: case WM8995_DC_SERVO_7: case WM8995_DC_SERVO_READBACK_0: case WM8995_ANALOGUE_HP_1: case WM8995_ANALOGUE_HP_2: case WM8995_CHIP_REVISION: case WM8995_CONTROL_INTERFACE_1: case WM8995_CONTROL_INTERFACE_2: case WM8995_WRITE_SEQUENCER_CTRL_1: case WM8995_WRITE_SEQUENCER_CTRL_2: case WM8995_AIF1_CLOCKING_1: case WM8995_AIF1_CLOCKING_2: case WM8995_AIF2_CLOCKING_1: case WM8995_AIF2_CLOCKING_2: case WM8995_CLOCKING_1: case WM8995_CLOCKING_2: case WM8995_AIF1_RATE: case WM8995_AIF2_RATE: case WM8995_RATE_STATUS: case WM8995_FLL1_CONTROL_1: case WM8995_FLL1_CONTROL_2: case WM8995_FLL1_CONTROL_3: case WM8995_FLL1_CONTROL_4: case WM8995_FLL1_CONTROL_5: case WM8995_FLL2_CONTROL_1: case WM8995_FLL2_CONTROL_2: case WM8995_FLL2_CONTROL_3: case WM8995_FLL2_CONTROL_4: case WM8995_FLL2_CONTROL_5: case WM8995_AIF1_CONTROL_1: case WM8995_AIF1_CONTROL_2: case WM8995_AIF1_MASTER_SLAVE: case WM8995_AIF1_BCLK: case WM8995_AIF1ADC_LRCLK: case WM8995_AIF1DAC_LRCLK: case WM8995_AIF1DAC_DATA: case WM8995_AIF1ADC_DATA: case WM8995_AIF2_CONTROL_1: case WM8995_AIF2_CONTROL_2: case WM8995_AIF2_MASTER_SLAVE: case WM8995_AIF2_BCLK: case WM8995_AIF2ADC_LRCLK: case WM8995_AIF2DAC_LRCLK: case WM8995_AIF2DAC_DATA: case WM8995_AIF2ADC_DATA: case WM8995_AIF1_ADC1_LEFT_VOLUME: case WM8995_AIF1_ADC1_RIGHT_VOLUME: case WM8995_AIF1_DAC1_LEFT_VOLUME: case WM8995_AIF1_DAC1_RIGHT_VOLUME: case WM8995_AIF1_ADC2_LEFT_VOLUME: case WM8995_AIF1_ADC2_RIGHT_VOLUME: case WM8995_AIF1_DAC2_LEFT_VOLUME: case WM8995_AIF1_DAC2_RIGHT_VOLUME: case WM8995_AIF1_ADC1_FILTERS: case WM8995_AIF1_ADC2_FILTERS: case WM8995_AIF1_DAC1_FILTERS_1: case WM8995_AIF1_DAC1_FILTERS_2: case WM8995_AIF1_DAC2_FILTERS_1: case WM8995_AIF1_DAC2_FILTERS_2: case WM8995_AIF1_DRC1_1: case WM8995_AIF1_DRC1_2: case WM8995_AIF1_DRC1_3: case WM8995_AIF1_DRC1_4: case WM8995_AIF1_DRC1_5: case WM8995_AIF1_DRC2_1: case WM8995_AIF1_DRC2_2: case WM8995_AIF1_DRC2_3: case WM8995_AIF1_DRC2_4: case WM8995_AIF1_DRC2_5: case WM8995_AIF1_DAC1_EQ_GAINS_1: case WM8995_AIF1_DAC1_EQ_GAINS_2: case WM8995_AIF1_DAC1_EQ_BAND_1_A: case WM8995_AIF1_DAC1_EQ_BAND_1_B: case WM8995_AIF1_DAC1_EQ_BAND_1_PG: case WM8995_AIF1_DAC1_EQ_BAND_2_A: case WM8995_AIF1_DAC1_EQ_BAND_2_B: case WM8995_AIF1_DAC1_EQ_BAND_2_C: case WM8995_AIF1_DAC1_EQ_BAND_2_PG: case WM8995_AIF1_DAC1_EQ_BAND_3_A: case WM8995_AIF1_DAC1_EQ_BAND_3_B: case WM8995_AIF1_DAC1_EQ_BAND_3_C: case WM8995_AIF1_DAC1_EQ_BAND_3_PG: case WM8995_AIF1_DAC1_EQ_BAND_4_A: case WM8995_AIF1_DAC1_EQ_BAND_4_B: case WM8995_AIF1_DAC1_EQ_BAND_4_C: case WM8995_AIF1_DAC1_EQ_BAND_4_PG: case WM8995_AIF1_DAC1_EQ_BAND_5_A: case WM8995_AIF1_DAC1_EQ_BAND_5_B: case WM8995_AIF1_DAC1_EQ_BAND_5_PG: case WM8995_AIF1_DAC2_EQ_GAINS_1: case WM8995_AIF1_DAC2_EQ_GAINS_2: case WM8995_AIF1_DAC2_EQ_BAND_1_A: case WM8995_AIF1_DAC2_EQ_BAND_1_B: case WM8995_AIF1_DAC2_EQ_BAND_1_PG: case WM8995_AIF1_DAC2_EQ_BAND_2_A: case WM8995_AIF1_DAC2_EQ_BAND_2_B: case WM8995_AIF1_DAC2_EQ_BAND_2_C: case WM8995_AIF1_DAC2_EQ_BAND_2_PG: case WM8995_AIF1_DAC2_EQ_BAND_3_A: case WM8995_AIF1_DAC2_EQ_BAND_3_B: case WM8995_AIF1_DAC2_EQ_BAND_3_C: case WM8995_AIF1_DAC2_EQ_BAND_3_PG: case WM8995_AIF1_DAC2_EQ_BAND_4_A: case WM8995_AIF1_DAC2_EQ_BAND_4_B: case WM8995_AIF1_DAC2_EQ_BAND_4_C: case WM8995_AIF1_DAC2_EQ_BAND_4_PG: case WM8995_AIF1_DAC2_EQ_BAND_5_A: case WM8995_AIF1_DAC2_EQ_BAND_5_B: case WM8995_AIF1_DAC2_EQ_BAND_5_PG: case WM8995_AIF2_ADC_LEFT_VOLUME: case WM8995_AIF2_ADC_RIGHT_VOLUME: case WM8995_AIF2_DAC_LEFT_VOLUME: case WM8995_AIF2_DAC_RIGHT_VOLUME: case WM8995_AIF2_ADC_FILTERS: case WM8995_AIF2_DAC_FILTERS_1: case WM8995_AIF2_DAC_FILTERS_2: case WM8995_AIF2_DRC_1: case WM8995_AIF2_DRC_2: case WM8995_AIF2_DRC_3: case WM8995_AIF2_DRC_4: case WM8995_AIF2_DRC_5: case WM8995_AIF2_EQ_GAINS_1: case WM8995_AIF2_EQ_GAINS_2: case WM8995_AIF2_EQ_BAND_1_A: case WM8995_AIF2_EQ_BAND_1_B: case WM8995_AIF2_EQ_BAND_1_PG: case WM8995_AIF2_EQ_BAND_2_A: case WM8995_AIF2_EQ_BAND_2_B: case WM8995_AIF2_EQ_BAND_2_C: case WM8995_AIF2_EQ_BAND_2_PG: case WM8995_AIF2_EQ_BAND_3_A: case WM8995_AIF2_EQ_BAND_3_B: case WM8995_AIF2_EQ_BAND_3_C: case WM8995_AIF2_EQ_BAND_3_PG: case WM8995_AIF2_EQ_BAND_4_A: case WM8995_AIF2_EQ_BAND_4_B: case WM8995_AIF2_EQ_BAND_4_C: case WM8995_AIF2_EQ_BAND_4_PG: case WM8995_AIF2_EQ_BAND_5_A: case WM8995_AIF2_EQ_BAND_5_B: case WM8995_AIF2_EQ_BAND_5_PG: case WM8995_DAC1_MIXER_VOLUMES: case WM8995_DAC1_LEFT_MIXER_ROUTING: case WM8995_DAC1_RIGHT_MIXER_ROUTING: case WM8995_DAC2_MIXER_VOLUMES: case WM8995_DAC2_LEFT_MIXER_ROUTING: case WM8995_DAC2_RIGHT_MIXER_ROUTING: case WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING: case WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING: case WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING: case WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING: case WM8995_DAC_SOFTMUTE: case WM8995_OVERSAMPLING: case WM8995_SIDETONE: case WM8995_GPIO_1: case WM8995_GPIO_2: case WM8995_GPIO_3: case WM8995_GPIO_4: case WM8995_GPIO_5: case WM8995_GPIO_6: case WM8995_GPIO_7: case WM8995_GPIO_8: case WM8995_GPIO_9: case WM8995_GPIO_10: case WM8995_GPIO_11: case WM8995_GPIO_12: case WM8995_GPIO_13: case WM8995_GPIO_14: case WM8995_PULL_CONTROL_1: case WM8995_PULL_CONTROL_2: case WM8995_INTERRUPT_STATUS_1: case WM8995_INTERRUPT_STATUS_2: case WM8995_INTERRUPT_RAW_STATUS_2: case WM8995_INTERRUPT_STATUS_1_MASK: case WM8995_INTERRUPT_STATUS_2_MASK: case WM8995_INTERRUPT_CONTROL: case WM8995_LEFT_PDM_SPEAKER_1: case WM8995_RIGHT_PDM_SPEAKER_1: case WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE: case WM8995_LEFT_PDM_SPEAKER_2: case WM8995_RIGHT_PDM_SPEAKER_2: case WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE: returntrue; default: returnfalse;
}
}
staticbool wm8995_volatile(struct device *dev, unsignedint reg)
{ switch (reg) { case WM8995_SOFTWARE_RESET: case WM8995_DC_SERVO_READBACK_0: case WM8995_INTERRUPT_STATUS_1: case WM8995_INTERRUPT_STATUS_2: case WM8995_INTERRUPT_CONTROL: case WM8995_ACCESSORY_DETECT_MODE1: case WM8995_ACCESSORY_DETECT_MODE2: case WM8995_HEADPHONE_DETECT1: case WM8995_HEADPHONE_DETECT2: case WM8995_RATE_STATUS: returntrue; default: returnfalse;
}
}
staticint wm8995_aif_mute(struct snd_soc_dai *dai, int mute, int direction)
{ struct snd_soc_component *component = dai->component; int mute_reg;
switch (dai->id) { case 0:
mute_reg = WM8995_AIF1_DAC1_FILTERS_1; break; case 1:
mute_reg = WM8995_AIF2_DAC_FILTERS_1; break; default: return -EINVAL;
}
staticint wm8995_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ struct snd_soc_component *component; struct wm8995_priv *wm8995; int aif1_reg; int bclk_reg; int lrclk_reg; int rate_reg; int bclk_rate; int aif1; int lrclk, bclk; int i, rate_val, best, best_val, cur_val;
bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) return bclk_rate;
aif1 = 0; switch (params_width(params)) { case 16: break; case 20:
aif1 |= (0x1 << WM8995_AIF1_WL_SHIFT); break; case 24:
aif1 |= (0x2 << WM8995_AIF1_WL_SHIFT); break; case 32:
aif1 |= (0x3 << WM8995_AIF1_WL_SHIFT); break; default:
dev_err(dai->dev, "Unsupported word length %u\n",
params_width(params)); return -EINVAL;
}
/* try to find a suitable sample rate */ for (i = 0; i < ARRAY_SIZE(srs); ++i) if (srs[i] == params_rate(params)) break; if (i == ARRAY_SIZE(srs)) {
dev_err(dai->dev, "Sample rate %d is not supported\n",
params_rate(params)); return -EINVAL;
}
rate_val = i << WM8995_AIF1_SR_SHIFT;
dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i]);
dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
dai->id + 1, wm8995->aifclk[dai->id], bclk_rate);
/* AIFCLK/fs ratio; look for a close match in either direction */
best = 1;
best_val = abs((fs_ratios[1] * params_rate(params))
- wm8995->aifclk[dai->id]); for (i = 2; i < ARRAY_SIZE(fs_ratios); i++) {
cur_val = abs((fs_ratios[i] * params_rate(params))
- wm8995->aifclk[dai->id]); if (cur_val >= best_val) continue;
best = i;
best_val = cur_val;
}
rate_val |= best;
/* * We may not get quite the right frequency if using * approximate clocks so look for the closest match that is * higher than the target (we need to ensure that there enough * BCLKs to clock out the samples).
*/
best = 0;
bclk = 0; for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
cur_val = (wm8995->aifclk[dai->id] * 10 / bclk_divs[i]) - bclk_rate; if (cur_val < 0) /* BCLK table is sorted */ break;
best = i;
}
bclk |= best << WM8995_AIF1_BCLK_DIV_SHIFT;
bclk_rate = wm8995->aifclk[dai->id] * 10 / bclk_divs[best];
dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
bclk_divs[best], bclk_rate);
lrclk = bclk_rate / params_rate(params);
dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
lrclk, bclk_rate / lrclk);
switch (id) { case WM8995_FLL1:
reg_offset = 0;
id = 0; break; case WM8995_FLL2:
reg_offset = 0x20;
id = 1; break; default: return -EINVAL;
}
switch (src) { case 0: /* Allow no source specification when stopping */ if (freq_out) return -EINVAL; break; case WM8995_FLL_SRC_MCLK1: case WM8995_FLL_SRC_MCLK2: case WM8995_FLL_SRC_LRCLK: case WM8995_FLL_SRC_BCLK: break; default: return -EINVAL;
}
/* Are we changing anything? */ if (wm8995->fll[id].src == src &&
wm8995->fll[id].in == freq_in && wm8995->fll[id].out == freq_out) return 0;
/* If we're stopping the FLL redo the old config - no * registers will actually be written but we avoid GCC flow * analysis bugs spewing warnings.
*/ if (freq_out)
ret = wm8995_get_fll_config(&fll, freq_in, freq_out); else
ret = wm8995_get_fll_config(&fll, wm8995->fll[id].in,
wm8995->fll[id].out); if (ret < 0) return ret;
/* Gate the AIF clocks while we reclock */
snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1,
WM8995_AIF1CLK_ENA_MASK, 0);
snd_soc_component_update_bits(component, WM8995_AIF2_CLOCKING_1,
WM8995_AIF2CLK_ENA_MASK, 0);
/* We always need to disable the FLL while reconfiguring */
snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_1 + reg_offset,
WM8995_FLL1_ENA_MASK, 0);
wm8995 = snd_soc_component_get_drvdata(component); switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies); if (ret) return ret;
ret = regcache_sync(wm8995->regmap); if (ret) {
dev_err(component->dev, "Failed to sync cache: %d\n", ret); return ret;
}
/* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
ret = devm_regulator_register_notifier(
wm8995->supplies[i].consumer,
&wm8995->disable_nb[i]); if (ret) {
dev_err(component->dev, "Failed to register regulator notifier: %d\n",
ret);
}
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies); if (ret) {
dev_err(component->dev, "Failed to enable supplies: %d\n", ret); return ret;
}
ret = snd_soc_component_read(component, WM8995_SOFTWARE_RESET); if (ret < 0) {
dev_err(component->dev, "Failed to read device ID: %d\n", ret); goto err_reg_enable;
}
if (ret != 0x8995) {
dev_err(component->dev, "Invalid device ID: %#x\n", ret);
ret = -EINVAL; goto err_reg_enable;
}
ret = snd_soc_component_write(component, WM8995_SOFTWARE_RESET, 0); if (ret < 0) {
dev_err(component->dev, "Failed to issue reset: %d\n", ret); goto err_reg_enable;
}
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.