/* * Copyright (c) 2022 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #define LOG_DOMAIN dai_intel_dmic_nhlt #include LOG_MODULE_REGISTER(LOG_DOMAIN); #include #include #include "dmic.h" #include #include "dmic_nhlt.h" extern struct dai_dmic_global_shared dai_dmic_global; /* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ static const uint32_t dmic_base[4] = {PDM0, PDM1, PDM2, PDM3}; static inline void dai_dmic_write(const struct dai_intel_dmic *dmic, uint32_t reg, uint32_t val) { sys_write32(val, dmic->reg_base + reg); } static inline uint32_t dai_dmic_read(const struct dai_intel_dmic *dmic, uint32_t reg) { return sys_read32(dmic->reg_base + reg); } /* * @brief Move pointer to next coefficient data * * @return Returns pointer right after coefficient data */ static const uint32_t *dai_dmic_skip_coeff(const uint32_t *coeff, const int length, const bool packed) { if (!packed) { coeff += length; } else { coeff += ROUND_UP(3 * length, sizeof(uint32_t)) / sizeof(uint32_t); } return coeff; } /* * @brief Write the fir coefficients in the PDMs' RAM */ static void dai_dmic_write_coeff(const struct dai_intel_dmic *dmic, uint32_t base, const uint32_t *coeff, int length, const bool packed) { const uint8_t *coeff_in_bytes; uint32_t coeff_val; if (!packed) { while (length--) { dai_dmic_write(dmic, base, *coeff++); base += sizeof(uint32_t); } } else { coeff_in_bytes = (const uint8_t *)coeff; while (length--) { coeff_val = coeff_in_bytes[0] + (coeff_in_bytes[1] << 8) + (coeff_in_bytes[2] << 16); dai_dmic_write(dmic, base, coeff_val); base += sizeof(uint32_t); coeff_in_bytes += 3; } } } /* * @brief Configures the fir coefficients in the PDMs' RAM * * @return Returns pointer right after coefficients data */ static const uint32_t *dai_dmic_configure_coeff(const struct dai_intel_dmic *dmic, const struct nhlt_pdm_ctrl_cfg * const pdm_cfg, const uint32_t pdm_base, const uint32_t *coeffs) { int fir_length_a, fir_length_b; bool packed = false; const uint32_t *coeffs_b; fir_length_a = FIELD_GET(FIR_CONFIG_FIR_LENGTH, pdm_cfg->fir_config[0].fir_config) + 1; fir_length_b = FIELD_GET(FIR_CONFIG_FIR_LENGTH, pdm_cfg->fir_config[1].fir_config) + 1; if (fir_length_a > 256 || fir_length_b > 256) { LOG_ERR("invalid coeff length! %d %d", fir_length_a, fir_length_b); return NULL; } if (*coeffs == FIR_COEFFS_PACKED_TO_24_BITS) { packed = true; /* First dword is not included into length_0 and length_1 - skip it. */ coeffs++; } coeffs_b = dai_dmic_skip_coeff(coeffs, fir_length_a, packed); LOG_INF("fir_length_a = %d, fir_length_b = %d, packed = %d", fir_length_a, fir_length_b, packed); if (dmic->dai_config_params.dai_index == 0) { dai_dmic_write_coeff(dmic, pdm_base + PDM_COEFFICIENT_A, coeffs, fir_length_a, packed); } else { dai_dmic_write_coeff(dmic, pdm_base + PDM_COEFFICIENT_B, coeffs_b, fir_length_b, packed); } return dai_dmic_skip_coeff(coeffs_b, fir_length_b, packed); } static int dai_nhlt_get_clock_div(const struct dai_intel_dmic *dmic, const int pdm) { uint32_t val; int p_mcic, p_clkdiv, p_mfir, rate_div; val = dai_dmic_read(dmic, dmic_base[pdm] + CIC_CONFIG); p_mcic = FIELD_GET(CIC_CONFIG_COMB_COUNT, val) + 1; val = dai_dmic_read(dmic, dmic_base[pdm] + MIC_CONTROL); p_clkdiv = FIELD_GET(MIC_CONTROL_PDM_CLKDIV, val) + 2; val = dai_dmic_read(dmic, dmic_base[pdm] + FIR_CHANNEL_REGS_SIZE * dmic->dai_config_params.dai_index + FIR_CONFIG); LOG_INF("pdm = %d, FIR_CONFIG = 0x%08X", pdm, val); p_mfir = FIELD_GET(FIR_CONFIG_FIR_DECIMATION, val) + 1; rate_div = p_clkdiv * p_mcic * p_mfir; LOG_INF("dai_index = %d, rate_div = %d, p_clkdiv = %d, p_mcic = %d, p_mfir = %d", dmic->dai_config_params.dai_index, rate_div, p_clkdiv, p_mcic, p_mfir); if (!rate_div) { LOG_ERR("zero clock divide or decimation factor"); return -EINVAL; } return rate_div; } static int dai_nhlt_update_rate(struct dai_intel_dmic *dmic, const int clock_source, const int pdm) { int rate_div; rate_div = dai_nhlt_get_clock_div(dmic, pdm); if (rate_div < 0) { return rate_div; } dmic->dai_config_params.rate = adsp_clock_source_frequency(clock_source) / rate_div; LOG_INF("rate = %d, channels = %d, format = %d", dmic->dai_config_params.rate, dmic->dai_config_params.channels, dmic->dai_config_params.format); LOG_INF("io_clk %u, rate_div %d", adsp_clock_source_frequency(clock_source), rate_div); return 0; } #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE static int dai_ipm_source_to_enable(struct dai_intel_dmic *dmic, int *count, int pdm_count, int stereo, int source_pdm) { int mic_swap; if (source_pdm >= CONFIG_DAI_DMIC_HW_CONTROLLERS) return -EINVAL; if (*count < pdm_count) { (*count)++; mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, dai_dmic_read( dmic, dmic_base[source_pdm] + MIC_CONTROL)); if (stereo) dmic->enable[source_pdm] = 0x3; /* PDMi MIC A and B */ else dmic->enable[source_pdm] = mic_swap ? 0x2 : 0x1; /* PDMi MIC B or MIC A */ } return 0; } static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic, const int clock_source) { bool stereo_pdm; int source_pdm; int first_pdm; int num_pdm; int ret; int n; uint32_t outcontrol_val = dai_dmic_read(dmic, dmic->dai_config_params.dai_index * PDM_CHANNEL_REGS_SIZE + OUTCONTROL); switch (FIELD_GET(OUTCONTROL_OF, outcontrol_val)) { case 0: case 1: dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE; dmic->dai_config_params.word_size = 16; break; case 2: dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE; dmic->dai_config_params.word_size = 32; break; default: LOG_ERR("nhlt_dmic_dai_params_get(): Illegal OF bit field"); return -EINVAL; } num_pdm = FIELD_GET(OUTCONTROL_IPM, outcontrol_val); if (num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) { LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM PDM controllers count %d", num_pdm); return -EINVAL; } stereo_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, outcontrol_val); dmic->dai_config_params.channels = (stereo_pdm + 1) * num_pdm; for (n = 0; n < CONFIG_DAI_DMIC_HW_CONTROLLERS; n++) dmic->enable[n] = 0; n = 0; source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, outcontrol_val); first_pdm = source_pdm; ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm); if (ret) { LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_1"); return -EINVAL; } source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, outcontrol_val); ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm); if (ret) { LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_2"); return -EINVAL; } source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, outcontrol_val); ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm); if (ret) { LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_3"); return -EINVAL; } source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, outcontrol_val); ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm); if (ret) { LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_4"); return -EINVAL; } return dai_nhlt_update_rate(dmic, clock_source, first_pdm); } /* * @brief Set clock source used by device * * @param source Clock source index */ static inline void dai_dmic_clock_select_set(const struct dai_intel_dmic *dmic, uint32_t source) { uint32_t val; #if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30_PTL) /* ACE 2.0,3.0 */ val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET); val &= ~DMICLVSCTL_MLCS; val |= FIELD_PREP(DMICLVSCTL_MLCS, source); sys_write32(val, dmic->vshim_base + DMICLVSCTL_OFFSET); #else val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET); val &= ~DMICLCTL_MLCS; val |= FIELD_PREP(DMICLCTL_MLCS, source); sys_write32(val, dmic->shim_base + DMICLCTL_OFFSET); #endif } /* * @brief Get clock source used by device * * @return Clock source index */ static inline uint32_t dai_dmic_clock_select_get(const struct dai_intel_dmic *dmic) { uint32_t val; #if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30_PTL) /* ACE 2.0,3.0 */ val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET); return FIELD_GET(DMICLVSCTL_MLCS, val); #else val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET); return FIELD_GET(DMICLCTL_MLCS, val); #endif } /* * @brief Set clock source used by device * * @param source Clock source index */ static int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source) { LOG_DBG("%s(): clock_source = %u", __func__, clock_source); if (!adsp_clock_source_is_supported(clock_source)) { return -ENOTSUP; } #if defined(CONFIG_SOC_INTEL_ACE15_MTPM) if (clock_source && !(sys_read32(dmic->shim_base + DMICLCAP_OFFSET) & DMICLCAP_MLCS)) { return -ENOTSUP; } #endif dai_dmic_clock_select_set(dmic, clock_source); return 0; } #else static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic) { uint32_t outcontrol; uint32_t fir_control[2]; uint32_t mic_control[2]; int fir_stereo[2]; int mic_swap; outcontrol = dai_dmic_read(dmic, dmic->dai_config_params.dai_index * PDM_CHANNEL_REGS_SIZE + OUTCONTROL); switch (FIELD_GET(OUTCONTROL_OF, outcontrol)) { case 0: case 1: dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE; break; case 2: dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE; break; default: LOG_ERR("Illegal OF bit field"); return -EINVAL; } fir_control[0] = dai_dmic_read(dmic, dmic_base[0] + dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE + FIR_CONTROL); fir_control[1] = dai_dmic_read(dmic, dmic_base[1] + dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE + FIR_CONTROL); mic_control[0] = dai_dmic_read(dmic, dmic_base[0] + MIC_CONTROL); mic_control[1] = dai_dmic_read(dmic, dmic_base[1] + MIC_CONTROL); switch (FIELD_GET(OUTCONTROL_IPM, outcontrol)) { case 0: fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]); if (fir_stereo[0]) { dmic->dai_config_params.channels = 2; dmic->enable[0] = 0x3; /* PDM0 MIC A and B */ dmic->enable[1] = 0x0; /* PDM1 none */ } else { dmic->dai_config_params.channels = 1; mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[0]); dmic->enable[0] = mic_swap ? 0x2 : 0x1; /* PDM0 MIC B or MIC A */ dmic->enable[1] = 0x0; /* PDM1 */ } break; case 1: fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]); if (fir_stereo[1]) { dmic->dai_config_params.channels = 2; dmic->enable[0] = 0x0; /* PDM0 none */ dmic->enable[1] = 0x3; /* PDM1 MIC A and B */ } else { dmic->dai_config_params.channels = 1; dmic->enable[0] = 0x0; /* PDM0 none */ mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[1]); dmic->enable[1] = mic_swap ? 0x2 : 0x1; /* PDM1 MIC B or MIC A */ } break; case 2: fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]); fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]); if (fir_stereo[0] == fir_stereo[1]) { dmic->dai_config_params.channels = 4; dmic->enable[0] = 0x3; /* PDM0 MIC A and B */ dmic->enable[1] = 0x3; /* PDM1 MIC A and B */ LOG_INF("set 4ch pdm0 and pdm1"); } else { LOG_ERR("Illegal 4ch configuration"); return -EINVAL; } break; default: LOG_ERR("Illegal OF bit field"); return -EINVAL; } return dai_nhlt_update_rate(dmic, 0, 0); } static inline int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source) { return 0; } #endif static int print_outcontrol(uint32_t val) { int bf1, bf2, bf3, bf4, bf5, bf6, bf7, bf8; #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE int bf9, bf10, bf11, bf12, bf13; #endif uint32_t ref; bf1 = FIELD_GET(OUTCONTROL_TIE, val); bf2 = FIELD_GET(OUTCONTROL_SIP, val); bf3 = FIELD_GET(OUTCONTROL_FINIT, val); bf4 = FIELD_GET(OUTCONTROL_FCI, val); bf5 = FIELD_GET(OUTCONTROL_BFTH, val); bf6 = FIELD_GET(OUTCONTROL_OF, val); bf7 = FIELD_GET(OUTCONTROL_IPM, val); bf8 = FIELD_GET(OUTCONTROL_TH, val); LOG_INF("OUTCONTROL = %08x", val); LOG_INF(" tie=%d, sip=%d, finit=%d, fci=%d", bf1, bf2, bf3, bf4); LOG_INF(" bfth=%d, of=%d, ipm=%d, th=%d", bf5, bf6, bf7, bf8); if (bf5 > OUTCONTROL_BFTH_MAX) { LOG_WRN("illegal BFTH value %d", bf5); return -EINVAL; } #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE bf9 = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, val); bf10 = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, val); bf11 = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, val); bf12 = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, val); bf13 = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, val); LOG_INF(" ipms1=%d, ipms2=%d, ipms3=%d, ipms4=%d", bf9, bf10, bf11, bf12); LOG_INF(" ipms_mode=%d", bf13); ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) | FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) | FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) | FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_1, bf9) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_2, bf10) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_3, bf11) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_4, bf12) | FIELD_PREP(OUTCONTROL_TH, bf8) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_MODE, bf13); #else ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) | FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) | FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) | FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_TH, bf8); #endif if (ref != val) { LOG_WRN("Some reserved bits are set in OUTCONTROL = 0x%08x", val); } return 0; } static void print_cic_control(uint32_t val) { int bf1, bf2, bf3, bf4, bf5, bf6, bf7; uint32_t ref; bf1 = FIELD_GET(CIC_CONTROL_SOFT_RESET, val); bf2 = FIELD_GET(CIC_CONTROL_CIC_START_B, val); bf3 = FIELD_GET(CIC_CONTROL_CIC_START_A, val); bf4 = FIELD_GET(CIC_CONTROL_MIC_B_POLARITY, val); bf5 = FIELD_GET(CIC_CONTROL_MIC_A_POLARITY, val); bf6 = FIELD_GET(CIC_CONTROL_MIC_MUTE, val); #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE bf7 = FIELD_GET(CIC_CONTROL_STEREO_MODE, val); #else bf7 = -1; #endif LOG_DBG("CIC_CONTROL = %08x", val); LOG_DBG(" soft_reset=%d, cic_start_b=%d, cic_start_a=%d", bf1, bf2, bf3); LOG_DBG(" mic_b_polarity=%d, mic_a_polarity=%d, mic_mute=%d", bf4, bf5, bf6); ref = FIELD_PREP(CIC_CONTROL_SOFT_RESET, bf1) | FIELD_PREP(CIC_CONTROL_CIC_START_B, bf2) | FIELD_PREP(CIC_CONTROL_CIC_START_A, bf3) | FIELD_PREP(CIC_CONTROL_MIC_B_POLARITY, bf4) | FIELD_PREP(CIC_CONTROL_MIC_A_POLARITY, bf5) | FIELD_PREP(CIC_CONTROL_MIC_MUTE, bf6) #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE | FIELD_PREP(CIC_CONTROL_STEREO_MODE, bf7) #endif ; LOG_DBG(" stereo_mode=%d", bf7); if (ref != val) { LOG_WRN("Some reserved bits are set in CIC_CONTROL = 0x%08x", val); } } static void print_fir_control(uint32_t val) { int bf1, bf2, bf3, bf4, bf5, bf6; uint32_t ref; bf1 = FIELD_GET(FIR_CONTROL_START, val); bf2 = FIELD_GET(FIR_CONTROL_ARRAY_START_EN, val); #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE bf3 = FIELD_GET(FIR_CONTROL_PERIODIC_START_EN, val); #else bf3 = -1; #endif bf4 = FIELD_GET(FIR_CONTROL_DCCOMP, val); bf5 = FIELD_GET(FIR_CONTROL_MUTE, val); bf6 = FIELD_GET(FIR_CONTROL_STEREO, val); LOG_DBG("FIR_CONTROL = %08x", val); LOG_DBG(" start=%d, array_start_en=%d, periodic_start_en=%d", bf1, bf2, bf3); LOG_DBG(" dccomp=%d, mute=%d, stereo=%d", bf4, bf5, bf6); ref = FIELD_PREP(FIR_CONTROL_START, bf1) | FIELD_PREP(FIR_CONTROL_ARRAY_START_EN, bf2) | #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE FIELD_PREP(FIR_CONTROL_PERIODIC_START_EN, bf3) | #endif FIELD_PREP(FIR_CONTROL_DCCOMP, bf4) | FIELD_PREP(FIR_CONTROL_MUTE, bf5) | FIELD_PREP(FIR_CONTROL_STEREO, bf6); if (ref != val) { LOG_WRN("Some reserved bits are set in FIR_CONTROL = 0x%08x", val); } } static void print_pdm_ctrl(const struct nhlt_pdm_ctrl_cfg *pdm_cfg) { int bf1, bf2, bf3, bf4, bf5; uint32_t val; LOG_DBG("CIC_CONTROL = %08x", pdm_cfg->cic_control); val = pdm_cfg->cic_config; bf1 = FIELD_GET(CIC_CONFIG_CIC_SHIFT, val); bf2 = FIELD_GET(CIC_CONFIG_COMB_COUNT, val); LOG_DBG("CIC_CONFIG = %08x", val); LOG_DBG(" cic_shift=%d, comb_count=%d", bf1, bf2); val = pdm_cfg->mic_control; #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE bf1 = FIELD_GET(MIC_CONTROL_PDM_SKEW, val); #else bf1 = -1; #endif bf2 = FIELD_GET(MIC_CONTROL_CLK_EDGE, val); bf3 = FIELD_GET(MIC_CONTROL_PDM_EN_B, val); bf4 = FIELD_GET(MIC_CONTROL_PDM_EN_A, val); bf5 = FIELD_GET(MIC_CONTROL_PDM_CLKDIV, val); LOG_DBG("MIC_CONTROL = %08x", val); LOG_DBG(" clkdiv=%d, skew=%d, clk_edge=%d", bf5, bf1, bf2); LOG_DBG(" en_b=%d, en_a=%d", bf3, bf4); } static void print_fir_config(const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg) { uint32_t val; int fir_decimation, fir_shift, fir_length; val = fir_cfg->fir_config; fir_length = FIELD_GET(FIR_CONFIG_FIR_LENGTH, val); fir_decimation = FIELD_GET(FIR_CONFIG_FIR_DECIMATION, val); fir_shift = FIELD_GET(FIR_CONFIG_FIR_SHIFT, val); LOG_DBG("FIR_CONFIG = %08x", val); LOG_DBG(" fir_decimation=%d, fir_shift=%d, fir_length=%d", fir_decimation, fir_shift, fir_length); print_fir_control(fir_cfg->fir_control); /* Use DC_OFFSET and GAIN as such */ LOG_DBG("DC_OFFSET_LEFT = %08x", fir_cfg->dc_offset_left); LOG_DBG("DC_OFFSET_RIGHT = %08x", fir_cfg->dc_offset_right); LOG_DBG("OUT_GAIN_LEFT = %08x", fir_cfg->out_gain_left); LOG_DBG("OUT_GAIN_RIGHT = %08x", fir_cfg->out_gain_right); } static void configure_fir(struct dai_intel_dmic *dmic, const uint32_t base, const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg) { uint32_t val; print_fir_config(fir_cfg); /* Use FIR_CONFIG as such */ val = fir_cfg->fir_config; dai_dmic_write(dmic, base + FIR_CONFIG, val); val = fir_cfg->fir_control; print_fir_control(val); /* Clear START, set MUTE */ val = (val & ~FIR_CONTROL_START) | FIR_CONTROL_MUTE; dai_dmic_write(dmic, base + FIR_CONTROL, val); LOG_DBG("FIR_CONTROL = %08x", val); /* Use DC_OFFSET and GAIN as such */ dai_dmic_write(dmic, base + DC_OFFSET_LEFT, fir_cfg->dc_offset_left); dai_dmic_write(dmic, base + DC_OFFSET_RIGHT, fir_cfg->dc_offset_right); dai_dmic_write(dmic, base + OUT_GAIN_LEFT, fir_cfg->out_gain_left); dai_dmic_write(dmic, base + OUT_GAIN_RIGHT, fir_cfg->out_gain_right); dmic->gain_left = fir_cfg->out_gain_left; dmic->gain_right = fir_cfg->out_gain_right; } int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *bespoke_cfg) { const struct nhlt_pdm_ctrl_cfg *pdm_cfg; struct nhlt_dmic_channel_ctrl_mask *dmic_cfg; uint32_t channel_ctrl_mask; uint32_t pdm_ctrl_mask; uint32_t pdm_base; int pdm_idx; uint32_t val; uint32_t outcontrol; const uint8_t *p = bespoke_cfg; int num_fifos; int num_pdm; int n; int ret; const uint32_t *fir_coeffs; /* Array of pointers to pdm coefficient data. Used to reuse coefficient from another pdm. */ const uint32_t *pdm_coeff_ptr[DMIC_HW_CONTROLLERS_MAX] = { 0 }; if (dmic->dai_config_params.dai_index >= DMIC_HW_FIFOS_MAX) { LOG_ERR("dmic_set_config_nhlt(): illegal DAI index %d", dmic->dai_config_params.dai_index); return -EINVAL; } /* Skip not used headers */ p += sizeof(struct nhlt_dmic_gateway_attributes); p += sizeof(struct nhlt_dmic_ts_group); p += sizeof(struct nhlt_dmic_global_config); /* Channel_ctlr_mask bits indicate the FIFOs enabled*/ dmic_cfg = (struct nhlt_dmic_channel_ctrl_mask *)p; channel_ctrl_mask = dmic_cfg->channel_ctrl_mask; num_fifos = POPCOUNT(channel_ctrl_mask); /* Count set bits */ p += sizeof(struct nhlt_dmic_channel_ctrl_mask); LOG_DBG("dmic_set_config_nhlt(): channel_ctrl_mask = %d", channel_ctrl_mask); /* Configure clock source */ ret = dai_dmic_set_clock(dmic, dmic_cfg->clock_source); if (ret) return ret; /* Get OUTCONTROLx configuration */ if (num_fifos < 1 || num_fifos > DMIC_HW_FIFOS_MAX) { LOG_ERR("dmic_set_config_nhlt(): illegal number of FIFOs %d", num_fifos); return -EINVAL; } for (n = 0; n < DMIC_HW_FIFOS_MAX; n++) { if (!(channel_ctrl_mask & (1 << n))) continue; val = *(uint32_t *)p; ret = print_outcontrol(val); if (ret) { return ret; } if (dmic->dai_config_params.dai_index == n) { /* Write the FIFO control registers. The clear/set of bits is the same for * all DMIC_HW_VERSION */ /* Clear TIE, SIP, FCI, set FINIT, the rest of bits as such */ outcontrol = (val & ~(OUTCONTROL_TIE | OUTCONTROL_SIP | OUTCONTROL_FCI)) | OUTCONTROL_FINIT; dai_dmic_write(dmic, dmic->dai_config_params.dai_index * PDM_CHANNEL_REGS_SIZE + OUTCONTROL, outcontrol); LOG_INF("OUTCONTROL%d = %08x", dmic->dai_config_params.dai_index, outcontrol); /* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA * configuration */ val = FIELD_GET(OUTCONTROL_BFTH, outcontrol); dmic->fifo.depth = 1 << val; } p += sizeof(uint32_t); } /* Get PDMx registers */ pdm_ctrl_mask = ((const struct nhlt_pdm_ctrl_mask *)p)->pdm_ctrl_mask; num_pdm = POPCOUNT(pdm_ctrl_mask); /* Count set bits */ p += sizeof(struct nhlt_pdm_ctrl_mask); LOG_DBG("dmic_set_config_nhlt(): pdm_ctrl_mask = %d", pdm_ctrl_mask); if (num_pdm < 1 || num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) { LOG_ERR("dmic_set_config_nhlt(): illegal number of PDMs %d", num_pdm); return -EINVAL; } pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)p; for (pdm_idx = 0; pdm_idx < CONFIG_DAI_DMIC_HW_CONTROLLERS; pdm_idx++) { pdm_base = dmic_base[pdm_idx]; if (!(pdm_ctrl_mask & (1 << pdm_idx))) { /* Set MIC_MUTE bit to unused PDM */ dai_dmic_write(dmic, pdm_base + CIC_CONTROL, CIC_CONTROL_MIC_MUTE); continue; } LOG_DBG("PDM%d", pdm_idx); /* Get CIC configuration */ if (dai_dmic_global.active_fifos_mask == 0) { print_pdm_ctrl(pdm_cfg); val = pdm_cfg->cic_control; print_cic_control(val); /* Clear CIC_START_A and CIC_START_B */ val = (val & ~(CIC_CONTROL_CIC_START_A | CIC_CONTROL_CIC_START_B)); dai_dmic_write(dmic, pdm_base + CIC_CONTROL, val); LOG_DBG("dmic_set_config_nhlt(): CIC_CONTROL = %08x", val); /* Use CIC_CONFIG as such */ val = pdm_cfg->cic_config; dai_dmic_write(dmic, pdm_base + CIC_CONFIG, val); /* Clear PDM_EN_A and PDM_EN_B */ val = pdm_cfg->mic_control; val &= ~(MIC_CONTROL_PDM_EN_A | MIC_CONTROL_PDM_EN_B); dai_dmic_write(dmic, pdm_base + MIC_CONTROL, val); LOG_DBG("dmic_set_config_nhlt(): MIC_CONTROL = %08x", val); } configure_fir(dmic, pdm_base + FIR_CHANNEL_REGS_SIZE * dmic->dai_config_params.dai_index, &pdm_cfg->fir_config[dmic->dai_config_params.dai_index]); /* Configure fir coefficients */ /* Check if FIR coeffs should be reused */ if (pdm_cfg->reuse_fir_from_pdm == 0) { /* get ptr, where FIR coeffs starts */ fir_coeffs = pdm_cfg->fir_coeffs; /* and save it for future pdms reference */ pdm_coeff_ptr[pdm_idx] = fir_coeffs; } else { if (pdm_cfg->reuse_fir_from_pdm > pdm_idx) { LOG_ERR("invalid reuse fir index %u", pdm_cfg->reuse_fir_from_pdm); return -EINVAL; } /* get FIR coeffs from another pdm */ fir_coeffs = pdm_coeff_ptr[pdm_cfg->reuse_fir_from_pdm - 1]; if (!fir_coeffs) { LOG_ERR("unable to reuse fir from %u", pdm_cfg->reuse_fir_from_pdm); return -EINVAL; } } fir_coeffs = dai_dmic_configure_coeff(dmic, pdm_cfg, pdm_base, fir_coeffs); /* Update pdm_cfg ptr for next PDM Ctrl. */ if (pdm_cfg->reuse_fir_from_pdm) { /* fir_coeffs array is empty if reusing previous coeffs */ pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)&pdm_cfg->fir_coeffs; } else { pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)fir_coeffs; } } #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE ret = dai_nhlt_dmic_dai_params_get(dmic, dmic_cfg->clock_source); #else ret = dai_nhlt_dmic_dai_params_get(dmic); #endif if (ret) return ret; LOG_INF("dmic_set_config_nhlt(): enable0 %u, enable1 %u", dmic->enable[0], dmic->enable[1]); return 0; }