if (!info->chmap) return -EINVAL;
substream = mchp_pdmc_chmap_substream(info, idx); if (!substream) return -ENODEV;
memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no); if (!substream->runtime) return 0; /* no channels set */
map = mchp_pdmc_chmap_get(substream, info); if (!map) return -EINVAL;
for (i = 0; i < map->channels; i++) { int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
map->map[i] - SNDRV_CHMAP_FL;
/* make sure the reported channel map is the real one, so write the map */ if (dd->channel_mic_map[map_idx].ds_pos)
cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); if (dd->channel_mic_map[map_idx].clk_edge)
cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
/* configure IP for the desired channel map */ if (dd->channel_mic_map[map_idx].ds_pos)
cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); if (dd->channel_mic_map[map_idx].clk_edge)
cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
}
if (channels > dd->mic_no) {
dev_err(comp->dev, "more channels %u than microphones %d\n",
channels, dd->mic_no); return -EINVAL;
}
dd->pdmcen = 0; for (i = 0; i < channels; i++) {
dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i); if (dd->channel_mic_map[i].ds_pos)
cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); if (dd->channel_mic_map[i].clk_edge)
cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
}
/* * from these point forward, we consider the controller busy, so the * audio filter and SINC order can't be changed
*/
atomic_set(&dd->busy_stream, 1); for (osr_start = dd->audio_filter_en ? 64 : 8;
osr_start <= 256 && best_diff_rate; osr_start *= 2) { long round_rate; unsignedlong diff_rate;
/* CLK is enabled by runtime PM. */
clk_disable_unprepare(dd->gclk);
/* set the rate */
ret = clk_set_rate(dd->gclk, gclk_rate);
clk_prepare_enable(dd->gclk); if (ret) {
dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n",
gclk_rate, ret); return ret;
}
/* * PDMC doesn't wait for microphones' startup time thus the acquisition * may start before the microphones are ready leading to poc noises at * the beginning of capture. To avoid this, we need to wait 50ms (in * normal startup procedure) or 150 ms (worst case after resume from sleep * states) after microphones are enabled and then clear the FIFOs (by * reading the RHR 16 times) and possible interrupts before continuing. * Also, for this to work the DMA needs to be started after interrupts * are enabled.
*/
usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5);
while (steps--)
regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp);
switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
MCHP_PDMC_MR_PDMCEN_MASK,
dd->pdmcen);
if (pending & MCHP_PDMC_IR_RXUDR) {
dev_warn(dd->dev, "underrun detected\n");
regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR);
ret = IRQ_HANDLED;
} if (pending & MCHP_PDMC_IR_RXOVR) {
dev_warn(dd->dev, "overrun detected\n");
regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR);
ret = IRQ_HANDLED;
}
return ret;
}
/* regmap configuration */ staticbool mchp_pdmc_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MCHP_PDMC_MR: case MCHP_PDMC_CFGR: case MCHP_PDMC_IMR: case MCHP_PDMC_ISR: case MCHP_PDMC_RHR: case MCHP_PDMC_VER: returntrue; default: returnfalse;
}
}
staticbool mchp_pdmc_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MCHP_PDMC_CR: case MCHP_PDMC_MR: case MCHP_PDMC_CFGR: case MCHP_PDMC_IER: case MCHP_PDMC_IDR: returntrue; default: returnfalse;
}
}
staticbool mchp_pdmc_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MCHP_PDMC_ISR: case MCHP_PDMC_RHR: returntrue; default: returnfalse;
}
}
staticbool mchp_pdmc_precious_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MCHP_PDMC_RHR: case MCHP_PDMC_ISR: returntrue; default: returnfalse;
}
}
/* * by default, we consider the order of microphones in * microchip,mic-pos to be the same with the channel mapping; * 1st microphone channel 0, 2nd microphone channel 1, etc.
*/ for (i = 0; i < dd->mic_no; i++) { int ds; int edge;
ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2,
&ds); if (ret) {
dev_err(dd->dev, "failed to get value no %d value from microchip,mic-pos: %d",
i * 2, ret); return ret;
} if (ds >= MCHP_PDMC_DS_NO) {
dev_err(dd->dev, "invalid DS index in microchip,mic-pos array: %d",
ds); return -EINVAL;
}
ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1,
&edge); if (ret) {
dev_err(dd->dev, "failed to get value no %d value from microchip,mic-pos: %d",
i * 2 + 1, ret); return ret;
}
if (edge != MCHP_PDMC_CLK_POSITIVE &&
edge != MCHP_PDMC_CLK_NEGATIVE) {
dev_err(dd->dev, "invalid edge in microchip,mic-pos array: %d", edge); return -EINVAL;
} if (mic_ch[ds][edge]) {
dev_err(dd->dev, "duplicated mic (DS %d, edge %d) in microchip,mic-pos array",
ds, edge); return -EINVAL;
}
mic_ch[ds][edge] = true;
dd->channel_mic_map[i].ds_pos = ds;
dd->channel_mic_map[i].clk_edge = edge;
}
ret = clk_prepare_enable(dd->pclk); if (ret) {
dev_err(dd->dev, "failed to enable the peripheral clock: %d\n", ret); return ret;
}
ret = clk_prepare_enable(dd->gclk); if (ret) {
dev_err(dd->dev, "failed to enable generic clock: %d\n", ret); goto disable_pclk;
}
regcache_cache_only(dd->regmap, false);
regcache_mark_dirty(dd->regmap);
ret = regcache_sync(dd->regmap); if (ret) {
regcache_cache_only(dd->regmap, true);
clk_disable_unprepare(dd->gclk);
disable_pclk:
clk_disable_unprepare(dd->pclk);
}
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); if (!dd) return -ENOMEM;
dd->dev = &pdev->dev;
ret = mchp_pdmc_dt_init(dd); if (ret < 0) return ret;
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
dd->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dd->pclk)) {
ret = PTR_ERR(dd->pclk);
dev_err(dev, "failed to get peripheral clock: %d\n", ret); return ret;
}
dd->gclk = devm_clk_get(dev, "gclk"); if (IS_ERR(dd->gclk)) {
ret = PTR_ERR(dd->gclk);
dev_err(dev, "failed to get GCK: %d\n", ret); return ret;
}
io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(io_base)) {
ret = PTR_ERR(io_base);
dev_err(dev, "failed to remap register memory: %d\n", ret); return ret;
}
dd->regmap = devm_regmap_init_mmio(dev, io_base,
&mchp_pdmc_regmap_config); if (IS_ERR(dd->regmap)) {
ret = PTR_ERR(dd->regmap);
dev_err(dev, "failed to init register map: %d\n", ret); return ret;
}
ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0,
dev_name(&pdev->dev), dd); if (ret < 0) {
dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
irq, ret); return ret;
}
/* by default audio filter is enabled and the SINC Filter order * will be set to the recommended value, 3
*/
dd->audio_filter_en = true;
dd->sinc_order = 3;
pm_runtime_enable(dd->dev); if (!pm_runtime_enabled(dd->dev)) {
ret = mchp_pdmc_runtime_resume(dd->dev); if (ret) return ret;
}
/* register platform */
ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0); if (ret) {
dev_err(dev, "could not register platform: %d\n", ret); goto pm_runtime_suspend;
}
ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component,
&mchp_pdmc_dai, 1); if (ret) {
dev_err(dev, "could not register CPU DAI: %d\n", ret); goto pm_runtime_suspend;
}
/* print IP version */
regmap_read(dd->regmap, MCHP_PDMC_VER, &version);
dev_info(dd->dev, "hw version: %#lx\n",
version & MCHP_PDMC_VER_VERSION);
return 0;
pm_runtime_suspend: if (!pm_runtime_status_suspended(dd->dev))
mchp_pdmc_runtime_suspend(dd->dev);
pm_runtime_disable(dd->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.