staticint micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); unsignedint *item = ucontrol->value.enumerated.item; int val = snd_soc_enum_item_to_val(e, item[0]); int i = 0, ret = 0;
u32 reg_val = 0;
if (val < 0 || val > 3) return -EINVAL;
micfil->dc_remover = val;
/* Calculate total value for all channels */ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
reg_val |= val << MICFIL_DC_CHX_SHIFT(i);
/* Update DC Remover mode for all channels */
ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL,
MICFIL_DC_CTRL_CONFIG, reg_val); if (ret < 0) return ret;
ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val); if (ret < 0) return ret;
dev_dbg(dev, "VERID: 0x%016X\n", val);
micfil->verid.version = val &
(MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK);
micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT;
micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK;
ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val); if (ret < 0) return ret;
dev_dbg(dev, "PARAM: 0x%016X\n", val);
micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >>
MICFIL_PARAM_NUM_HWVAD_SHIFT;
micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD;
micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE;
micfil->param.hwvad = val & MICFIL_PARAM_HWVAD;
micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS;
micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS;
micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER;
micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH;
micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >>
MICFIL_PARAM_FIFO_PTRWID_SHIFT;
micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >>
MICFIL_PARAM_NPAIR_SHIFT;
return 0;
}
/* The SRES is a self-negated bit which provides the CPU with the * capability to initialize the PDM Interface module through the * slave-bus interface. This bit always reads as zero, and this * bit is only effective when MDIS is cleared
*/ staticint fsl_micfil_reset(struct device *dev)
{ struct fsl_micfil *micfil = dev_get_drvdata(dev); int ret;
ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_MDIS); if (ret) return ret;
ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_SRES); if (ret) return ret;
/* * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined * as non-volatile register, so SRES still remain in regmap * cache after set, that every update of REG_MICFIL_CTRL1, * software reset happens. so clear it explicitly.
*/
ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_SRES); if (ret) return ret;
/* * Set SRES should clear CHnF flags, But even add delay here * the CHnF may not be cleared sometimes, so clear CHnF explicitly.
*/
ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF); if (ret) return ret;
/* * Hardware Voice Active Detection: The HWVAD takes data from the input * of a selected PDM microphone to detect if there is any * voice activity. When a voice activity is detected, an interrupt could * be delivered to the system. Initialization in section 8.4: * Can work in two modes: * -> Eneveope-based mode (section 8.4.1) * -> Energy-based mode (section 8.4.2) * * It is important to remark that the HWVAD detector could be enabled * or reset only when the MICFIL isn't running i.e. when the BSY_FIL * bit in STAT register is cleared
*/ staticint fsl_micfil_hwvad_enable(struct fsl_micfil *micfil)
{ int ret;
micfil->vad_detected = 0;
/* envelope-based specific initialization */ if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE)
ret = fsl_micfil_init_hwvad_envelope_mode(micfil); else
ret = fsl_micfil_init_hwvad_energy_mode(micfil); if (ret) return ret;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = fsl_micfil_reset(dev); if (ret) {
dev_err(dev, "failed to soft reset\n"); return ret;
}
/* Enable the module */
ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN); if (ret) return ret;
if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_enable(micfil);
break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_disable(micfil);
/* Disable the module */
ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN); if (ret) return ret;
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_DISEL,
FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); if (ret) return ret; break; default: return -EINVAL;
} return 0;
}
/* Disable clock first, for it was enabled by pm_runtime */
fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk,
micfil->pll11k_clk, ratio);
ret = clk_prepare_enable(clk); if (ret) return ret;
return 0;
}
staticint fsl_micfil_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); unsignedint channels = params_channels(params);
snd_pcm_format_t format = params_format(params); unsignedint rate = params_rate(params); int clk_div = 8, mclk_rate, div_multiply_k; int osr = MICFIL_OSR_DEFAULT; int ret;
/* 1. Disable the module */
ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
MICFIL_CTRL1_PDMIEN); if (ret) return ret;
/* enable channels */
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
0xFF, ((1 << channels) - 1)); if (ret) return ret;
ret = fsl_micfil_reparent_rootclk(micfil, rate); if (ret) return ret;
micfil->mclk_flag = true;
/* floor(K * CLKDIV) */ switch (micfil->quality) { case QUALITY_HIGH:
div_multiply_k = clk_div >> 1; break; case QUALITY_LOW: case QUALITY_VLOW1:
div_multiply_k = clk_div << 1; break; case QUALITY_VLOW2:
div_multiply_k = clk_div << 2; break; case QUALITY_MEDIUM: case QUALITY_VLOW0: default:
div_multiply_k = clk_div; break;
}
/* set default gain to 2 */
regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222);
/* set DC Remover in bypass mode*/ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i);
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL,
MICFIL_DC_CTRL_CONFIG, val); if (ret) {
dev_err(dev, "failed to set DC Remover mode bits\n"); return ret;
}
micfil->dc_remover = MICFIL_DC_BYPASS;
switch (reg) { case REG_MICFIL_CTRL1: case REG_MICFIL_CTRL2: case REG_MICFIL_STAT: case REG_MICFIL_FIFO_CTRL: case REG_MICFIL_FIFO_STAT: case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: case REG_MICFIL_VAD0_SCONFIG: case REG_MICFIL_VAD0_NCONFIG: case REG_MICFIL_VAD0_NDATA: case REG_MICFIL_VAD0_ZCD: returntrue; case REG_MICFIL_FSYNC_CTRL: case REG_MICFIL_VERID: case REG_MICFIL_PARAM: if (micfil->soc->use_verid) returntrue;
fallthrough; default: returnfalse;
}
}
switch (reg) { case REG_MICFIL_CTRL1: case REG_MICFIL_CTRL2: case REG_MICFIL_STAT: /* Write 1 to Clear */ case REG_MICFIL_FIFO_CTRL: case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ case REG_MICFIL_VAD0_SCONFIG: case REG_MICFIL_VAD0_NCONFIG: case REG_MICFIL_VAD0_ZCD: returntrue; case REG_MICFIL_FSYNC_CTRL: if (micfil->soc->use_verid) returntrue;
fallthrough; default: returnfalse;
}
}
switch (reg) { case REG_MICFIL_STAT: case REG_MICFIL_FIFO_STAT: case REG_MICFIL_OUT_STAT: case REG_MICFIL_VERID: case REG_MICFIL_PARAM: case REG_MICFIL_VAD0_STAT: case REG_MICFIL_VAD0_NDATA: returntrue; default: returnfalse;
}
}
/* Channel 0-7 Output Data Flags */ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { if (stat_reg & MICFIL_STAT_CHXF(i))
dev_dbg(&pdev->dev, "Data available in Data Channel %d\n", i); /* if DMA is not enabled, field must be written with 1 * to clear
*/ if (!dma_enabled)
regmap_write_bits(micfil->regmap,
REG_MICFIL_STAT,
MICFIL_STAT_CHXF(i),
MICFIL_STAT_CHXF(i));
}
for (i = 0; i < MICFIL_FIFO_NUM; i++) { if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i))
dev_dbg(&pdev->dev, "FIFO Overflow Exception flag for channel %d\n",
i);
if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i))
dev_dbg(&pdev->dev, "FIFO Underflow Exception flag for channel %d\n",
i);
}
/* * The only difference between MICFIL_VAD0_STAT_EF and * MICFIL_VAD0_STAT_IF is that the former requires Write * 1 to Clear. Since both flags are set, it is enough * to only read one of them
*/ if (vad0_reg & MICFIL_VAD0_STAT_IF) { /* Write 1 to clear */
regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT,
MICFIL_VAD0_STAT_IF,
MICFIL_VAD0_STAT_IF);
micfil->vad_detected = 1;
}
ret = fsl_micfil_hwvad_disable(micfil); if (ret)
dev_err(dev, "Failed to disable hwvad\n");
/* ipg_clk is used to control the registers * ipg_clk_app is used to operate the filter
*/
micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); if (IS_ERR(micfil->mclk)) {
dev_err(&pdev->dev, "failed to get core clock: %ld\n",
PTR_ERR(micfil->mclk)); return PTR_ERR(micfil->mclk);
}
micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); if (IS_ERR(micfil->busclk)) {
dev_err(&pdev->dev, "failed to get ipg clock: %ld\n",
PTR_ERR(micfil->busclk)); return PTR_ERR(micfil->busclk);
}
pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) {
ret = fsl_micfil_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;
/* Get micfil version */
ret = fsl_micfil_use_verid(&pdev->dev); if (ret < 0)
dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret);
ret = pm_runtime_put_sync(&pdev->dev); if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync;
regcache_cache_only(micfil->regmap, true);
/* * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime().
*/
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) {
dev_err(&pdev->dev, "failed to pcm register\n"); goto err_pm_disable;
}
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.