1 /*
2  * Copyright (c) 2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <errno.h>
9 #include <zephyr/spinlock.h>
10 
11 #define LOG_DOMAIN dai_intel_dmic_nhlt
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(LOG_DOMAIN);
14 
15 #include <zephyr/drivers/dai.h>
16 #include <adsp_clk.h>
17 #include "dmic.h"
18 #include <dmic_regs.h>
19 #include "dmic_nhlt.h"
20 
21 extern struct dai_dmic_global_shared dai_dmic_global;
22 
23 /* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */
24 static const uint32_t dmic_base[4] = {PDM0, PDM1, PDM2, PDM3};
25 
dai_dmic_write(const struct dai_intel_dmic * dmic,uint32_t reg,uint32_t val)26 static inline void dai_dmic_write(const struct dai_intel_dmic *dmic,
27 				  uint32_t reg, uint32_t val)
28 {
29 	sys_write32(val, dmic->reg_base + reg);
30 }
31 
dai_dmic_read(const struct dai_intel_dmic * dmic,uint32_t reg)32 static inline uint32_t dai_dmic_read(const struct dai_intel_dmic *dmic, uint32_t reg)
33 {
34 	return sys_read32(dmic->reg_base + reg);
35 }
36 
37 /*
38  * @brief Move pointer to next coefficient data
39  *
40  * @return Returns pointer right after coefficient data
41  */
dai_dmic_skip_coeff(const uint32_t * coeff,const int length,const bool packed)42 static const uint32_t *dai_dmic_skip_coeff(const uint32_t *coeff, const int length,
43 					   const bool packed)
44 {
45 	if (!packed) {
46 		coeff += length;
47 	} else {
48 		coeff += ROUND_UP(3 * length, sizeof(uint32_t)) / sizeof(uint32_t);
49 	}
50 
51 	return coeff;
52 }
53 
54 /*
55  * @brief Write the fir coefficients in the PDMs' RAM
56  */
dai_dmic_write_coeff(const struct dai_intel_dmic * dmic,uint32_t base,const uint32_t * coeff,int length,const bool packed)57 static void dai_dmic_write_coeff(const struct dai_intel_dmic *dmic, uint32_t base,
58 				 const uint32_t *coeff, int length, const bool packed)
59 {
60 	const uint8_t *coeff_in_bytes;
61 	uint32_t coeff_val;
62 
63 	if (!packed) {
64 		while (length--) {
65 			dai_dmic_write(dmic, base, *coeff++);
66 			base += sizeof(uint32_t);
67 		}
68 	} else {
69 		coeff_in_bytes = (const uint8_t *)coeff;
70 
71 		while (length--) {
72 			coeff_val = coeff_in_bytes[0] +
73 				(coeff_in_bytes[1] << 8) +
74 				(coeff_in_bytes[2] << 16);
75 
76 			dai_dmic_write(dmic, base, coeff_val);
77 			base += sizeof(uint32_t);
78 
79 			coeff_in_bytes += 3;
80 		}
81 	}
82 }
83 
84 /*
85  * @brief Configures the fir coefficients in the PDMs' RAM
86  *
87  * @return Returns pointer right after coefficients data
88  */
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)89 static const uint32_t *dai_dmic_configure_coeff(const struct dai_intel_dmic *dmic,
90 						const struct nhlt_pdm_ctrl_cfg * const pdm_cfg,
91 						const uint32_t pdm_base,
92 						const uint32_t *coeffs)
93 {
94 	int fir_length_a, fir_length_b;
95 	bool packed = false;
96 	const uint32_t *coeffs_b;
97 
98 	fir_length_a = FIELD_GET(FIR_CONFIG_FIR_LENGTH, pdm_cfg->fir_config[0].fir_config) + 1;
99 	fir_length_b = FIELD_GET(FIR_CONFIG_FIR_LENGTH, pdm_cfg->fir_config[1].fir_config) + 1;
100 
101 	if (fir_length_a > 256 || fir_length_b > 256) {
102 		LOG_ERR("invalid coeff length! %d %d", fir_length_a, fir_length_b);
103 		return NULL;
104 	}
105 
106 	if (*coeffs == FIR_COEFFS_PACKED_TO_24_BITS) {
107 		packed = true;
108 
109 		/* First dword is not included into length_0 and length_1 - skip it. */
110 		coeffs++;
111 	}
112 
113 	coeffs_b = dai_dmic_skip_coeff(coeffs, fir_length_a, packed);
114 
115 	LOG_INF("fir_length_a = %d, fir_length_b = %d, packed = %d", fir_length_a, fir_length_b,
116 		packed);
117 
118 	if (dmic->dai_config_params.dai_index == 0) {
119 		dai_dmic_write_coeff(dmic, pdm_base + PDM_COEFFICIENT_A, coeffs, fir_length_a,
120 				     packed);
121 	} else {
122 		dai_dmic_write_coeff(dmic, pdm_base + PDM_COEFFICIENT_B, coeffs_b, fir_length_b,
123 				     packed);
124 	}
125 
126 	return dai_dmic_skip_coeff(coeffs_b, fir_length_b, packed);
127 }
128 
dai_nhlt_get_clock_div(const struct dai_intel_dmic * dmic,const int pdm)129 static int dai_nhlt_get_clock_div(const struct dai_intel_dmic *dmic, const int pdm)
130 {
131 	uint32_t val;
132 	int p_mcic, p_clkdiv, p_mfir, rate_div;
133 
134 	val = dai_dmic_read(dmic, dmic_base[pdm] + CIC_CONFIG);
135 	p_mcic = FIELD_GET(CIC_CONFIG_COMB_COUNT, val) + 1;
136 
137 	val = dai_dmic_read(dmic, dmic_base[pdm] + MIC_CONTROL);
138 	p_clkdiv = FIELD_GET(MIC_CONTROL_PDM_CLKDIV, val) + 2;
139 
140 	val = dai_dmic_read(dmic, dmic_base[pdm] +
141 			    FIR_CHANNEL_REGS_SIZE * dmic->dai_config_params.dai_index + FIR_CONFIG);
142 	LOG_INF("pdm = %d, FIR_CONFIG = 0x%08X", pdm, val);
143 
144 	p_mfir = FIELD_GET(FIR_CONFIG_FIR_DECIMATION, val) + 1;
145 
146 	rate_div = p_clkdiv * p_mcic * p_mfir;
147 	LOG_INF("dai_index = %d, rate_div = %d, p_clkdiv = %d, p_mcic = %d, p_mfir = %d",
148 		dmic->dai_config_params.dai_index, rate_div, p_clkdiv, p_mcic, p_mfir);
149 
150 	if (!rate_div) {
151 		LOG_ERR("zero clock divide or decimation factor");
152 		return -EINVAL;
153 	}
154 
155 	return rate_div;
156 }
157 
dai_nhlt_update_rate(struct dai_intel_dmic * dmic,const int clock_source,const int pdm)158 static int dai_nhlt_update_rate(struct dai_intel_dmic *dmic, const int clock_source, const int pdm)
159 {
160 	int rate_div;
161 
162 	rate_div = dai_nhlt_get_clock_div(dmic, pdm);
163 	if (rate_div < 0) {
164 		return rate_div;
165 	}
166 
167 	dmic->dai_config_params.rate = adsp_clock_source_frequency(clock_source) /
168 		rate_div;
169 
170 	LOG_INF("rate = %d, channels = %d, format = %d",
171 		dmic->dai_config_params.rate, dmic->dai_config_params.channels,
172 		dmic->dai_config_params.format);
173 
174 	LOG_INF("io_clk %u, rate_div %d", adsp_clock_source_frequency(clock_source), rate_div);
175 	return 0;
176 }
177 
178 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
dai_ipm_source_to_enable(struct dai_intel_dmic * dmic,int * count,int pdm_count,int stereo,int source_pdm)179 static int dai_ipm_source_to_enable(struct dai_intel_dmic *dmic,
180 				    int *count, int pdm_count, int stereo,
181 				    int source_pdm)
182 {
183 	int mic_swap;
184 
185 	if (source_pdm >= CONFIG_DAI_DMIC_HW_CONTROLLERS) {
186 		return -EINVAL;
187 	}
188 
189 	if (*count < pdm_count) {
190 		(*count)++;
191 		mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, dai_dmic_read(
192 						dmic, dmic_base[source_pdm] + MIC_CONTROL));
193 		if (stereo) {
194 			dmic->enable[source_pdm] = 0x3; /* PDMi MIC A and B */
195 		} else {
196 			dmic->enable[source_pdm] = mic_swap ? 0x2 : 0x1; /* PDMi MIC B or MIC A */
197 		}
198 	}
199 
200 	return 0;
201 }
202 
dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic * dmic,const int clock_source)203 static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic, const int clock_source)
204 {
205 	bool stereo_pdm;
206 	int source_pdm;
207 	int first_pdm;
208 	int num_pdm;
209 	int ret;
210 	int n;
211 	uint32_t outcontrol_val = dai_dmic_read(dmic, dmic->dai_config_params.dai_index *
212 						PDM_CHANNEL_REGS_SIZE + OUTCONTROL);
213 
214 	switch (FIELD_GET(OUTCONTROL_OF, outcontrol_val)) {
215 	case 0:
216 	case 1:
217 		dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE;
218 		dmic->dai_config_params.word_size = 16;
219 		break;
220 	case 2:
221 		dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE;
222 		dmic->dai_config_params.word_size = 32;
223 		break;
224 	default:
225 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal OF bit field");
226 		return -EINVAL;
227 	}
228 
229 	num_pdm = FIELD_GET(OUTCONTROL_IPM, outcontrol_val);
230 	if (num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
231 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM PDM controllers count %d",
232 			num_pdm);
233 		return -EINVAL;
234 	}
235 
236 	stereo_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, outcontrol_val);
237 
238 	dmic->dai_config_params.channels = (stereo_pdm + 1) * num_pdm;
239 	for (n = 0; n < CONFIG_DAI_DMIC_HW_CONTROLLERS; n++) {
240 		dmic->enable[n] = 0;
241 	}
242 
243 	n = 0;
244 	source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, outcontrol_val);
245 	first_pdm = source_pdm;
246 	ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
247 	if (ret) {
248 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_1");
249 		return -EINVAL;
250 	}
251 
252 	source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, outcontrol_val);
253 	ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
254 	if (ret) {
255 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_2");
256 		return -EINVAL;
257 	}
258 
259 	source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, outcontrol_val);
260 	ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
261 	if (ret) {
262 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_3");
263 		return -EINVAL;
264 	}
265 
266 	source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, outcontrol_val);
267 	ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
268 	if (ret) {
269 		LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_4");
270 		return -EINVAL;
271 	}
272 
273 	return dai_nhlt_update_rate(dmic, clock_source, first_pdm);
274 }
275 
276 
277 /*
278  * @brief Set clock source used by device
279  *
280  * @param source Clock source index
281  */
dai_dmic_clock_select_set(const struct dai_intel_dmic * dmic,uint32_t source)282 static inline void dai_dmic_clock_select_set(const struct dai_intel_dmic *dmic, uint32_t source)
283 {
284 	uint32_t val;
285 #if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30) /* ACE 2.0,3.0 */
286 	val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET);
287 	val &= ~DMICLVSCTL_MLCS;
288 	val |= FIELD_PREP(DMICLVSCTL_MLCS, source);
289 	sys_write32(val, dmic->vshim_base + DMICLVSCTL_OFFSET);
290 #else
291 	val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET);
292 	val &= ~DMICLCTL_MLCS;
293 	val |= FIELD_PREP(DMICLCTL_MLCS, source);
294 	sys_write32(val, dmic->shim_base + DMICLCTL_OFFSET);
295 #endif
296 }
297 
298 /*
299  * @brief Get clock source used by device
300  *
301  * @return Clock source index
302  */
dai_dmic_clock_select_get(const struct dai_intel_dmic * dmic)303 static inline uint32_t dai_dmic_clock_select_get(const struct dai_intel_dmic *dmic)
304 {
305 	uint32_t val;
306 #if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30) /* ACE 2.0,3.0 */
307 	val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET);
308 	return FIELD_GET(DMICLVSCTL_MLCS, val);
309 #else
310 	val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET);
311 	return FIELD_GET(DMICLCTL_MLCS, val);
312 #endif
313 }
314 
315 /*
316  * @brief Set clock source used by device
317  *
318  * @param source Clock source index
319  */
dai_dmic_set_clock(const struct dai_intel_dmic * dmic,const uint8_t clock_source)320 static int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source)
321 {
322 	LOG_DBG("%s(): clock_source = %u", __func__, clock_source);
323 
324 	if (!adsp_clock_source_is_supported(clock_source)) {
325 		return -ENOTSUP;
326 	}
327 
328 #if defined(CONFIG_SOC_INTEL_ACE15_MTPM)
329 	if (clock_source && !(sys_read32(dmic->shim_base + DMICLCAP_OFFSET) & DMICLCAP_MLCS)) {
330 		return -ENOTSUP;
331 	}
332 #endif
333 
334 	dai_dmic_clock_select_set(dmic, clock_source);
335 	return 0;
336 }
337 #else
dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic * dmic)338 static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic)
339 {
340 	uint32_t outcontrol;
341 	uint32_t fir_control[2];
342 	uint32_t mic_control[2];
343 
344 	int fir_stereo[2];
345 	int mic_swap;
346 
347 	outcontrol = dai_dmic_read(dmic, dmic->dai_config_params.dai_index * PDM_CHANNEL_REGS_SIZE +
348 				   OUTCONTROL);
349 
350 	switch (FIELD_GET(OUTCONTROL_OF, outcontrol)) {
351 	case 0:
352 	case 1:
353 		dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE;
354 		break;
355 	case 2:
356 		dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE;
357 		break;
358 	default:
359 		LOG_ERR("Illegal OF bit field");
360 		return -EINVAL;
361 	}
362 
363 	fir_control[0] = dai_dmic_read(dmic, dmic_base[0] +
364 				       dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE +
365 				       FIR_CONTROL);
366 
367 	fir_control[1] = dai_dmic_read(dmic, dmic_base[1] +
368 				       dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE +
369 				       FIR_CONTROL);
370 
371 	mic_control[0] = dai_dmic_read(dmic, dmic_base[0] + MIC_CONTROL);
372 	mic_control[1] = dai_dmic_read(dmic, dmic_base[1] + MIC_CONTROL);
373 
374 	switch (FIELD_GET(OUTCONTROL_IPM, outcontrol)) {
375 	case 0:
376 		fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]);
377 		if (fir_stereo[0]) {
378 			dmic->dai_config_params.channels = 2;
379 			dmic->enable[0] = 0x3; /* PDM0 MIC A and B */
380 			dmic->enable[1] = 0x0;	/* PDM1 none */
381 
382 		} else {
383 			dmic->dai_config_params.channels = 1;
384 			mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[0]);
385 			dmic->enable[0] = mic_swap ? 0x2 : 0x1; /* PDM0 MIC B or MIC A */
386 			dmic->enable[1] = 0x0;	/* PDM1 */
387 		}
388 		break;
389 	case 1:
390 		fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]);
391 		if (fir_stereo[1]) {
392 			dmic->dai_config_params.channels = 2;
393 			dmic->enable[0] = 0x0; /* PDM0 none */
394 			dmic->enable[1] = 0x3;	/* PDM1 MIC A and B */
395 		} else {
396 			dmic->dai_config_params.channels = 1;
397 			dmic->enable[0] = 0x0; /* PDM0 none */
398 			mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[1]);
399 			dmic->enable[1] = mic_swap ? 0x2 : 0x1; /* PDM1 MIC B or MIC A */
400 		}
401 		break;
402 	case 2:
403 		fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]);
404 		fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]);
405 		if (fir_stereo[0] == fir_stereo[1]) {
406 			dmic->dai_config_params.channels = 4;
407 			dmic->enable[0] = 0x3; /* PDM0 MIC A and B */
408 			dmic->enable[1] = 0x3;	/* PDM1 MIC A and B */
409 			LOG_INF("set 4ch pdm0 and pdm1");
410 		} else {
411 			LOG_ERR("Illegal 4ch configuration");
412 			return -EINVAL;
413 		}
414 		break;
415 	default:
416 		LOG_ERR("Illegal OF bit field");
417 		return -EINVAL;
418 	}
419 
420 	return dai_nhlt_update_rate(dmic, 0, 0);
421 }
422 
dai_dmic_set_clock(const struct dai_intel_dmic * dmic,const uint8_t clock_source)423 static inline int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source)
424 {
425 	return 0;
426 }
427 #endif
428 
print_outcontrol(uint32_t val)429 static int print_outcontrol(uint32_t val)
430 {
431 	int bf1, bf2, bf3, bf4, bf5, bf6, bf7, bf8;
432 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
433 	int bf9, bf10, bf11, bf12, bf13;
434 #endif
435 	uint32_t ref;
436 
437 	bf1 = FIELD_GET(OUTCONTROL_TIE, val);
438 	bf2 = FIELD_GET(OUTCONTROL_SIP, val);
439 	bf3 = FIELD_GET(OUTCONTROL_FINIT, val);
440 	bf4 = FIELD_GET(OUTCONTROL_FCI, val);
441 	bf5 = FIELD_GET(OUTCONTROL_BFTH, val);
442 	bf6 = FIELD_GET(OUTCONTROL_OF, val);
443 	bf7 = FIELD_GET(OUTCONTROL_IPM, val);
444 	bf8 = FIELD_GET(OUTCONTROL_TH, val);
445 	LOG_INF("OUTCONTROL = %08x", val);
446 	LOG_INF("  tie=%d, sip=%d, finit=%d, fci=%d", bf1, bf2, bf3, bf4);
447 	LOG_INF("  bfth=%d, of=%d, ipm=%d, th=%d", bf5, bf6, bf7, bf8);
448 	if (bf5 > OUTCONTROL_BFTH_MAX) {
449 		LOG_WRN("illegal BFTH value %d", bf5);
450 		return -EINVAL;
451 	}
452 
453 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
454 	bf9 = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, val);
455 	bf10 = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, val);
456 	bf11 = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, val);
457 	bf12 = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, val);
458 	bf13 = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, val);
459 	LOG_INF("  ipms1=%d, ipms2=%d, ipms3=%d, ipms4=%d", bf9, bf10, bf11, bf12);
460 	LOG_INF("  ipms_mode=%d", bf13);
461 	ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) |
462 		FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) |
463 		FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) |
464 		FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_1, bf9) |
465 		FIELD_PREP(OUTCONTROL_IPM_SOURCE_2, bf10) |
466 		FIELD_PREP(OUTCONTROL_IPM_SOURCE_3, bf11) |
467 		FIELD_PREP(OUTCONTROL_IPM_SOURCE_4, bf12) | FIELD_PREP(OUTCONTROL_TH, bf8) |
468 		FIELD_PREP(OUTCONTROL_IPM_SOURCE_MODE, bf13);
469 #else
470 	ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) |
471 		FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) |
472 		FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) |
473 		FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_TH, bf8);
474 #endif
475 	if (ref != val) {
476 		LOG_WRN("Some reserved bits are set in OUTCONTROL = 0x%08x", val);
477 	}
478 
479 	return 0;
480 }
481 
print_cic_control(uint32_t val)482 static void print_cic_control(uint32_t val)
483 {
484 	int bf1, bf2, bf3, bf4, bf5, bf6, bf7;
485 	uint32_t ref;
486 
487 	bf1 = FIELD_GET(CIC_CONTROL_SOFT_RESET, val);
488 	bf2 = FIELD_GET(CIC_CONTROL_CIC_START_B, val);
489 	bf3 = FIELD_GET(CIC_CONTROL_CIC_START_A, val);
490 	bf4 = FIELD_GET(CIC_CONTROL_MIC_B_POLARITY, val);
491 	bf5 = FIELD_GET(CIC_CONTROL_MIC_A_POLARITY, val);
492 	bf6 = FIELD_GET(CIC_CONTROL_MIC_MUTE, val);
493 #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
494 	bf7 = FIELD_GET(CIC_CONTROL_STEREO_MODE, val);
495 #else
496 	bf7 = -1;
497 #endif
498 	LOG_DBG("CIC_CONTROL = %08x", val);
499 	LOG_DBG("  soft_reset=%d, cic_start_b=%d, cic_start_a=%d",
500 		bf1, bf2, bf3);
501 	LOG_DBG("  mic_b_polarity=%d, mic_a_polarity=%d, mic_mute=%d",
502 		bf4, bf5, bf6);
503 	ref = FIELD_PREP(CIC_CONTROL_SOFT_RESET, bf1) |
504 		FIELD_PREP(CIC_CONTROL_CIC_START_B, bf2) |
505 		FIELD_PREP(CIC_CONTROL_CIC_START_A, bf3) |
506 		FIELD_PREP(CIC_CONTROL_MIC_B_POLARITY, bf4) |
507 		FIELD_PREP(CIC_CONTROL_MIC_A_POLARITY, bf5) |
508 		FIELD_PREP(CIC_CONTROL_MIC_MUTE, bf6)
509 #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
510 		| FIELD_PREP(CIC_CONTROL_STEREO_MODE, bf7)
511 #endif
512 		;
513 	LOG_DBG("  stereo_mode=%d", bf7);
514 	if (ref != val) {
515 		LOG_WRN("Some reserved bits are set in CIC_CONTROL = 0x%08x", val);
516 	}
517 }
518 
print_fir_control(uint32_t val)519 static void print_fir_control(uint32_t val)
520 {
521 	int bf1, bf2, bf3, bf4, bf5, bf6;
522 	uint32_t ref;
523 
524 	bf1 = FIELD_GET(FIR_CONTROL_START, val);
525 	bf2 = FIELD_GET(FIR_CONTROL_ARRAY_START_EN, val);
526 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
527 	bf3 = FIELD_GET(FIR_CONTROL_PERIODIC_START_EN, val);
528 #else
529 	bf3 = -1;
530 #endif
531 	bf4 = FIELD_GET(FIR_CONTROL_DCCOMP, val);
532 	bf5 = FIELD_GET(FIR_CONTROL_MUTE, val);
533 	bf6 = FIELD_GET(FIR_CONTROL_STEREO, val);
534 	LOG_DBG("FIR_CONTROL = %08x", val);
535 	LOG_DBG("  start=%d, array_start_en=%d, periodic_start_en=%d",
536 		bf1, bf2, bf3);
537 	LOG_DBG("  dccomp=%d, mute=%d, stereo=%d", bf4, bf5, bf6);
538 	ref = FIELD_PREP(FIR_CONTROL_START, bf1) |
539 		FIELD_PREP(FIR_CONTROL_ARRAY_START_EN, bf2) |
540 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
541 		FIELD_PREP(FIR_CONTROL_PERIODIC_START_EN, bf3) |
542 #endif
543 		FIELD_PREP(FIR_CONTROL_DCCOMP, bf4) |
544 		FIELD_PREP(FIR_CONTROL_MUTE, bf5) |
545 		FIELD_PREP(FIR_CONTROL_STEREO, bf6);
546 
547 	if (ref != val) {
548 		LOG_WRN("Some reserved bits are set in FIR_CONTROL = 0x%08x", val);
549 	}
550 }
551 
print_pdm_ctrl(const struct nhlt_pdm_ctrl_cfg * pdm_cfg)552 static void print_pdm_ctrl(const struct nhlt_pdm_ctrl_cfg *pdm_cfg)
553 {
554 	int bf1, bf2, bf3, bf4, bf5;
555 	uint32_t val;
556 
557 	LOG_DBG("CIC_CONTROL = %08x", pdm_cfg->cic_control);
558 
559 	val = pdm_cfg->cic_config;
560 	bf1 = FIELD_GET(CIC_CONFIG_CIC_SHIFT, val);
561 	bf2 = FIELD_GET(CIC_CONFIG_COMB_COUNT, val);
562 	LOG_DBG("CIC_CONFIG = %08x", val);
563 	LOG_DBG("  cic_shift=%d, comb_count=%d", bf1, bf2);
564 
565 	val = pdm_cfg->mic_control;
566 
567 #ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
568 	bf1 = FIELD_GET(MIC_CONTROL_PDM_SKEW, val);
569 #else
570 	bf1 = -1;
571 #endif
572 	bf2 = FIELD_GET(MIC_CONTROL_CLK_EDGE, val);
573 	bf3 = FIELD_GET(MIC_CONTROL_PDM_EN_B, val);
574 	bf4 = FIELD_GET(MIC_CONTROL_PDM_EN_A, val);
575 	bf5 = FIELD_GET(MIC_CONTROL_PDM_CLKDIV, val);
576 	LOG_DBG("MIC_CONTROL = %08x", val);
577 	LOG_DBG("  clkdiv=%d, skew=%d, clk_edge=%d", bf5, bf1, bf2);
578 	LOG_DBG("  en_b=%d, en_a=%d", bf3, bf4);
579 }
580 
print_fir_config(const struct nhlt_pdm_ctrl_fir_cfg * fir_cfg)581 static void print_fir_config(const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg)
582 {
583 	uint32_t val;
584 	int fir_decimation, fir_shift, fir_length;
585 
586 	val = fir_cfg->fir_config;
587 	fir_length = FIELD_GET(FIR_CONFIG_FIR_LENGTH, val);
588 	fir_decimation = FIELD_GET(FIR_CONFIG_FIR_DECIMATION, val);
589 	fir_shift = FIELD_GET(FIR_CONFIG_FIR_SHIFT, val);
590 	LOG_DBG("FIR_CONFIG = %08x", val);
591 	LOG_DBG("  fir_decimation=%d, fir_shift=%d, fir_length=%d",
592 		fir_decimation, fir_shift, fir_length);
593 
594 	print_fir_control(fir_cfg->fir_control);
595 
596 	/* Use DC_OFFSET and GAIN as such */
597 	LOG_DBG("DC_OFFSET_LEFT = %08x", fir_cfg->dc_offset_left);
598 	LOG_DBG("DC_OFFSET_RIGHT = %08x", fir_cfg->dc_offset_right);
599 	LOG_DBG("OUT_GAIN_LEFT = %08x", fir_cfg->out_gain_left);
600 	LOG_DBG("OUT_GAIN_RIGHT = %08x", fir_cfg->out_gain_right);
601 }
602 
configure_fir(struct dai_intel_dmic * dmic,const uint32_t base,const struct nhlt_pdm_ctrl_fir_cfg * fir_cfg)603 static void configure_fir(struct dai_intel_dmic *dmic, const uint32_t base,
604 			  const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg)
605 {
606 	uint32_t val;
607 
608 	print_fir_config(fir_cfg);
609 
610 	/* Use FIR_CONFIG as such */
611 	val = fir_cfg->fir_config;
612 	dai_dmic_write(dmic, base + FIR_CONFIG, val);
613 
614 	val = fir_cfg->fir_control;
615 	print_fir_control(val);
616 
617 	/* Clear START, set MUTE */
618 	val = (val & ~FIR_CONTROL_START) | FIR_CONTROL_MUTE;
619 	dai_dmic_write(dmic, base + FIR_CONTROL, val);
620 	LOG_DBG("FIR_CONTROL = %08x", val);
621 
622 	/* Use DC_OFFSET and GAIN as such */
623 	dai_dmic_write(dmic, base + DC_OFFSET_LEFT, fir_cfg->dc_offset_left);
624 	dai_dmic_write(dmic, base + DC_OFFSET_RIGHT, fir_cfg->dc_offset_right);
625 	dai_dmic_write(dmic, base + OUT_GAIN_LEFT, fir_cfg->out_gain_left);
626 	dai_dmic_write(dmic, base + OUT_GAIN_RIGHT, fir_cfg->out_gain_right);
627 
628 	dmic->gain_left = fir_cfg->out_gain_left;
629 	dmic->gain_right = fir_cfg->out_gain_right;
630 }
631 
dai_dmic_set_config_nhlt(struct dai_intel_dmic * dmic,const void * bespoke_cfg)632 int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *bespoke_cfg)
633 {
634 	const struct nhlt_pdm_ctrl_cfg *pdm_cfg;
635 	struct nhlt_dmic_channel_ctrl_mask *dmic_cfg;
636 
637 	uint32_t channel_ctrl_mask;
638 	uint32_t pdm_ctrl_mask;
639 	uint32_t pdm_base;
640 	int pdm_idx;
641 	uint32_t val;
642 	uint32_t outcontrol;
643 	const uint8_t *p = bespoke_cfg;
644 	int num_fifos;
645 	int num_pdm;
646 	int n;
647 	int ret;
648 
649 	const uint32_t *fir_coeffs;
650 
651 	/* Array of pointers to pdm coefficient data. Used to reuse coefficient from another pdm. */
652 	const uint32_t *pdm_coeff_ptr[DMIC_HW_CONTROLLERS_MAX] = { 0 };
653 
654 	if (dmic->dai_config_params.dai_index >= DMIC_HW_FIFOS_MAX) {
655 		LOG_ERR("dmic_set_config_nhlt(): illegal DAI index %d",
656 			 dmic->dai_config_params.dai_index);
657 		return -EINVAL;
658 	}
659 
660 	/* Skip not used headers */
661 	p += sizeof(struct nhlt_dmic_gateway_attributes);
662 	p += sizeof(struct nhlt_dmic_ts_group);
663 	p += sizeof(struct nhlt_dmic_global_config);
664 
665 	/* Channel_ctlr_mask bits indicate the FIFOs enabled*/
666 	dmic_cfg = (struct nhlt_dmic_channel_ctrl_mask *)p;
667 	channel_ctrl_mask = dmic_cfg->channel_ctrl_mask;
668 	num_fifos = POPCOUNT(channel_ctrl_mask); /* Count set bits */
669 	p += sizeof(struct nhlt_dmic_channel_ctrl_mask);
670 	LOG_DBG("dmic_set_config_nhlt(): channel_ctrl_mask = %d", channel_ctrl_mask);
671 
672 	/* Configure clock source */
673 	ret = dai_dmic_set_clock(dmic, dmic_cfg->clock_source);
674 	if (ret) {
675 		return ret;
676 	}
677 
678 	/* Get OUTCONTROLx configuration */
679 	if (num_fifos < 1 || num_fifos > DMIC_HW_FIFOS_MAX) {
680 		LOG_ERR("dmic_set_config_nhlt(): illegal number of FIFOs %d", num_fifos);
681 		return -EINVAL;
682 	}
683 
684 	for (n = 0; n < DMIC_HW_FIFOS_MAX; n++) {
685 		if (!(channel_ctrl_mask & (1 << n))) {
686 			continue;
687 		}
688 
689 		val = *(uint32_t *)p;
690 		ret = print_outcontrol(val);
691 		if (ret) {
692 			return ret;
693 		}
694 
695 		if (dmic->dai_config_params.dai_index == n) {
696 			/* Write the FIFO control registers. The clear/set of bits is the same for
697 			 * all DMIC_HW_VERSION
698 			 */
699 			/* Clear TIE, SIP, FCI, set FINIT, the rest of bits as such */
700 			outcontrol = (val & ~(OUTCONTROL_TIE | OUTCONTROL_SIP | OUTCONTROL_FCI)) |
701 				OUTCONTROL_FINIT;
702 
703 			dai_dmic_write(dmic, dmic->dai_config_params.dai_index *
704 				       PDM_CHANNEL_REGS_SIZE + OUTCONTROL, outcontrol);
705 
706 			LOG_INF("OUTCONTROL%d = %08x", dmic->dai_config_params.dai_index,
707 				outcontrol);
708 
709 			/* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA
710 			 * configuration
711 			 */
712 			val = FIELD_GET(OUTCONTROL_BFTH, outcontrol);
713 			dmic->fifo.depth = 1 << val;
714 		}
715 
716 		p += sizeof(uint32_t);
717 	}
718 
719 	/* Get PDMx registers */
720 	pdm_ctrl_mask = ((const struct nhlt_pdm_ctrl_mask *)p)->pdm_ctrl_mask;
721 	num_pdm = POPCOUNT(pdm_ctrl_mask); /* Count set bits */
722 	p += sizeof(struct nhlt_pdm_ctrl_mask);
723 	LOG_DBG("dmic_set_config_nhlt(): pdm_ctrl_mask = %d", pdm_ctrl_mask);
724 	if (num_pdm < 1 || num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
725 		LOG_ERR("dmic_set_config_nhlt(): illegal number of PDMs %d", num_pdm);
726 		return -EINVAL;
727 	}
728 
729 	pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)p;
730 
731 	for (pdm_idx = 0; pdm_idx < CONFIG_DAI_DMIC_HW_CONTROLLERS; pdm_idx++) {
732 		pdm_base = dmic_base[pdm_idx];
733 
734 		if (!(pdm_ctrl_mask & (1 << pdm_idx))) {
735 			/* Set MIC_MUTE bit to unused PDM */
736 			dai_dmic_write(dmic, pdm_base + CIC_CONTROL, CIC_CONTROL_MIC_MUTE);
737 			continue;
738 		}
739 
740 		LOG_DBG("PDM%d", pdm_idx);
741 
742 		/* Get CIC configuration */
743 		if (dai_dmic_global.active_fifos_mask == 0) {
744 			print_pdm_ctrl(pdm_cfg);
745 
746 			val = pdm_cfg->cic_control;
747 			print_cic_control(val);
748 
749 			/* Clear CIC_START_A and CIC_START_B */
750 			val = (val & ~(CIC_CONTROL_CIC_START_A | CIC_CONTROL_CIC_START_B));
751 			dai_dmic_write(dmic, pdm_base + CIC_CONTROL, val);
752 			LOG_DBG("dmic_set_config_nhlt(): CIC_CONTROL = %08x", val);
753 
754 			/* Use CIC_CONFIG as such */
755 			val = pdm_cfg->cic_config;
756 			dai_dmic_write(dmic, pdm_base + CIC_CONFIG, val);
757 
758 			/* Clear PDM_EN_A and PDM_EN_B */
759 			val = pdm_cfg->mic_control;
760 			val &= ~(MIC_CONTROL_PDM_EN_A | MIC_CONTROL_PDM_EN_B);
761 			dai_dmic_write(dmic, pdm_base + MIC_CONTROL, val);
762 			LOG_DBG("dmic_set_config_nhlt(): MIC_CONTROL = %08x", val);
763 		}
764 
765 		configure_fir(dmic, pdm_base +
766 			      FIR_CHANNEL_REGS_SIZE * dmic->dai_config_params.dai_index,
767 			      &pdm_cfg->fir_config[dmic->dai_config_params.dai_index]);
768 
769 
770 		/* Configure fir coefficients */
771 
772 		/* Check if FIR coeffs should be reused */
773 		if (pdm_cfg->reuse_fir_from_pdm == 0) {
774 			/* get ptr, where FIR coeffs starts */
775 			fir_coeffs = pdm_cfg->fir_coeffs;
776 
777 			/* and save it for future pdms reference */
778 			pdm_coeff_ptr[pdm_idx] = fir_coeffs;
779 		} else {
780 			if (pdm_cfg->reuse_fir_from_pdm > pdm_idx) {
781 				LOG_ERR("invalid reuse fir index %u", pdm_cfg->reuse_fir_from_pdm);
782 				return -EINVAL;
783 			}
784 
785 			/* get FIR coeffs from another pdm */
786 			fir_coeffs = pdm_coeff_ptr[pdm_cfg->reuse_fir_from_pdm - 1];
787 
788 			if (!fir_coeffs) {
789 				LOG_ERR("unable to reuse fir from %u", pdm_cfg->reuse_fir_from_pdm);
790 				return -EINVAL;
791 			}
792 		}
793 
794 		fir_coeffs = dai_dmic_configure_coeff(dmic, pdm_cfg, pdm_base, fir_coeffs);
795 
796 		/* Update pdm_cfg ptr for next PDM Ctrl. */
797 		if (pdm_cfg->reuse_fir_from_pdm) {
798 			/* fir_coeffs array is empty if reusing previous coeffs */
799 			pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)&pdm_cfg->fir_coeffs;
800 		} else {
801 			pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)fir_coeffs;
802 		}
803 	}
804 
805 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
806 	ret = dai_nhlt_dmic_dai_params_get(dmic, dmic_cfg->clock_source);
807 #else
808 	ret = dai_nhlt_dmic_dai_params_get(dmic);
809 #endif
810 	if (ret) {
811 		return ret;
812 	}
813 
814 	LOG_INF("dmic_set_config_nhlt(): enable0 %u, enable1 %u",
815 		dmic->enable[0], dmic->enable[1]);
816 	return 0;
817 }
818