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_ERR("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_ERR("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_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 if (*count < pdm_count) {
189 (*count)++;
190 mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, dai_dmic_read(
191 dmic, dmic_base[source_pdm] + MIC_CONTROL));
192 if (stereo)
193 dmic->enable[source_pdm] = 0x3; /* PDMi MIC A and B */
194 else
195 dmic->enable[source_pdm] = mic_swap ? 0x2 : 0x1; /* PDMi MIC B or MIC A */
196 }
197
198 return 0;
199 }
200
dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic * dmic,const int clock_source)201 static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic, const int clock_source)
202 {
203 bool stereo_pdm;
204 int source_pdm;
205 int first_pdm;
206 int num_pdm;
207 int ret;
208 int n;
209 uint32_t outcontrol_val = dai_dmic_read(dmic, dmic->dai_config_params.dai_index *
210 PDM_CHANNEL_REGS_SIZE + OUTCONTROL);
211
212 switch (FIELD_GET(OUTCONTROL_OF, outcontrol_val)) {
213 case 0:
214 case 1:
215 dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE;
216 dmic->dai_config_params.word_size = 16;
217 break;
218 case 2:
219 dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE;
220 dmic->dai_config_params.word_size = 32;
221 break;
222 default:
223 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal OF bit field");
224 return -EINVAL;
225 }
226
227 num_pdm = FIELD_GET(OUTCONTROL_IPM, outcontrol_val);
228 if (num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
229 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM PDM controllers count %d",
230 num_pdm);
231 return -EINVAL;
232 }
233
234 stereo_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, outcontrol_val);
235
236 dmic->dai_config_params.channels = (stereo_pdm + 1) * num_pdm;
237 for (n = 0; n < CONFIG_DAI_DMIC_HW_CONTROLLERS; n++)
238 dmic->enable[n] = 0;
239
240 n = 0;
241 source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, outcontrol_val);
242 first_pdm = source_pdm;
243 ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
244 if (ret) {
245 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_1");
246 return -EINVAL;
247 }
248
249 source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, outcontrol_val);
250 ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
251 if (ret) {
252 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_2");
253 return -EINVAL;
254 }
255
256 source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, outcontrol_val);
257 ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
258 if (ret) {
259 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_3");
260 return -EINVAL;
261 }
262
263 source_pdm = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, outcontrol_val);
264 ret = dai_ipm_source_to_enable(dmic, &n, num_pdm, stereo_pdm, source_pdm);
265 if (ret) {
266 LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_4");
267 return -EINVAL;
268 }
269
270 return dai_nhlt_update_rate(dmic, clock_source, first_pdm);
271 }
272
273
274 /*
275 * @brief Set clock source used by device
276 *
277 * @param source Clock source index
278 */
dai_dmic_clock_select_set(const struct dai_intel_dmic * dmic,uint32_t source)279 static inline void dai_dmic_clock_select_set(const struct dai_intel_dmic *dmic, uint32_t source)
280 {
281 uint32_t val;
282 #ifdef CONFIG_SOC_INTEL_ACE20_LNL /* Ace 2.0 */
283 val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET);
284 val &= ~DMICLVSCTL_MLCS;
285 val |= FIELD_PREP(DMICLVSCTL_MLCS, source);
286 sys_write32(val, dmic->vshim_base + DMICLVSCTL_OFFSET);
287 #else
288 val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET);
289 val &= ~DMICLCTL_MLCS;
290 val |= FIELD_PREP(DMICLCTL_MLCS, source);
291 sys_write32(val, dmic->shim_base + DMICLCTL_OFFSET);
292 #endif
293 }
294
295 /*
296 * @brief Get clock source used by device
297 *
298 * @return Clock source index
299 */
dai_dmic_clock_select_get(const struct dai_intel_dmic * dmic)300 static inline uint32_t dai_dmic_clock_select_get(const struct dai_intel_dmic *dmic)
301 {
302 uint32_t val;
303 #ifdef CONFIG_SOC_INTEL_ACE20_LNL /* Ace 2.0 */
304 val = sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET);
305 return FIELD_GET(DMICLVSCTL_MLCS, val);
306 #else
307 val = sys_read32(dmic->shim_base + DMICLCTL_OFFSET);
308 return FIELD_GET(DMICLCTL_MLCS, val);
309 #endif
310 }
311
312 /*
313 * @brief Set clock source used by device
314 *
315 * @param source Clock source index
316 */
dai_dmic_set_clock(const struct dai_intel_dmic * dmic,const uint8_t clock_source)317 static int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source)
318 {
319 LOG_DBG("%s(): clock_source = %u", __func__, clock_source);
320
321 if (!adsp_clock_source_is_supported(clock_source)) {
322 return -ENOTSUP;
323 }
324
325 #ifndef CONFIG_SOC_INTEL_ACE20_LNL /* Ace 2.0 */
326 if (clock_source && !(sys_read32(dmic->shim_base + DMICLCAP_OFFSET) & DMICLCAP_MLCS)) {
327 return -ENOTSUP;
328 }
329 #endif
330
331 dai_dmic_clock_select_set(dmic, clock_source);
332 return 0;
333 }
334 #else
dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic * dmic)335 static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic)
336 {
337 uint32_t outcontrol;
338 uint32_t fir_control[2];
339 uint32_t mic_control[2];
340
341 int fir_stereo[2];
342 int mic_swap;
343
344 outcontrol = dai_dmic_read(dmic, dmic->dai_config_params.dai_index * PDM_CHANNEL_REGS_SIZE +
345 OUTCONTROL);
346
347 switch (FIELD_GET(OUTCONTROL_OF, outcontrol)) {
348 case 0:
349 case 1:
350 dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE;
351 break;
352 case 2:
353 dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE;
354 break;
355 default:
356 LOG_ERR("Illegal OF bit field");
357 return -EINVAL;
358 }
359
360 fir_control[0] = dai_dmic_read(dmic, dmic_base[0] +
361 dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE +
362 FIR_CONTROL);
363
364 fir_control[1] = dai_dmic_read(dmic, dmic_base[1] +
365 dmic->dai_config_params.dai_index * FIR_CHANNEL_REGS_SIZE +
366 FIR_CONTROL);
367
368 mic_control[0] = dai_dmic_read(dmic, dmic_base[0] + MIC_CONTROL);
369 mic_control[1] = dai_dmic_read(dmic, dmic_base[1] + MIC_CONTROL);
370
371 switch (FIELD_GET(OUTCONTROL_IPM, outcontrol)) {
372 case 0:
373 fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]);
374 if (fir_stereo[0]) {
375 dmic->dai_config_params.channels = 2;
376 dmic->enable[0] = 0x3; /* PDM0 MIC A and B */
377 dmic->enable[1] = 0x0; /* PDM1 none */
378
379 } else {
380 dmic->dai_config_params.channels = 1;
381 mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[0]);
382 dmic->enable[0] = mic_swap ? 0x2 : 0x1; /* PDM0 MIC B or MIC A */
383 dmic->enable[1] = 0x0; /* PDM1 */
384 }
385 break;
386 case 1:
387 fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]);
388 if (fir_stereo[1]) {
389 dmic->dai_config_params.channels = 2;
390 dmic->enable[0] = 0x0; /* PDM0 none */
391 dmic->enable[1] = 0x3; /* PDM1 MIC A and B */
392 } else {
393 dmic->dai_config_params.channels = 1;
394 dmic->enable[0] = 0x0; /* PDM0 none */
395 mic_swap = FIELD_GET(MIC_CONTROL_CLK_EDGE, mic_control[1]);
396 dmic->enable[1] = mic_swap ? 0x2 : 0x1; /* PDM1 MIC B or MIC A */
397 }
398 break;
399 case 2:
400 fir_stereo[0] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[0]);
401 fir_stereo[1] = FIELD_GET(FIR_CONTROL_STEREO, fir_control[1]);
402 if (fir_stereo[0] == fir_stereo[1]) {
403 dmic->dai_config_params.channels = 4;
404 dmic->enable[0] = 0x3; /* PDM0 MIC A and B */
405 dmic->enable[1] = 0x3; /* PDM1 MIC A and B */
406 LOG_INF("set 4ch pdm0 and pdm1");
407 } else {
408 LOG_ERR("Illegal 4ch configuration");
409 return -EINVAL;
410 }
411 break;
412 default:
413 LOG_ERR("Illegal OF bit field");
414 return -EINVAL;
415 }
416
417 return dai_nhlt_update_rate(dmic, 0, 0);
418 }
419
dai_dmic_set_clock(const struct dai_intel_dmic * dmic,const uint8_t clock_source)420 static inline int dai_dmic_set_clock(const struct dai_intel_dmic *dmic, const uint8_t clock_source)
421 {
422 return 0;
423 }
424 #endif
425
print_outcontrol(uint32_t val)426 static int print_outcontrol(uint32_t val)
427 {
428 int bf1, bf2, bf3, bf4, bf5, bf6, bf7, bf8;
429 #ifdef CONFIG_SOC_SERIES_INTEL_ACE
430 int bf9, bf10, bf11, bf12, bf13;
431 #endif
432 uint32_t ref;
433
434 bf1 = FIELD_GET(OUTCONTROL_TIE, val);
435 bf2 = FIELD_GET(OUTCONTROL_SIP, val);
436 bf3 = FIELD_GET(OUTCONTROL_FINIT, val);
437 bf4 = FIELD_GET(OUTCONTROL_FCI, val);
438 bf5 = FIELD_GET(OUTCONTROL_BFTH, val);
439 bf6 = FIELD_GET(OUTCONTROL_OF, val);
440 bf7 = FIELD_GET(OUTCONTROL_IPM, val);
441 bf8 = FIELD_GET(OUTCONTROL_TH, val);
442 LOG_INF("OUTCONTROL = %08x", val);
443 LOG_INF(" tie=%d, sip=%d, finit=%d, fci=%d", bf1, bf2, bf3, bf4);
444 LOG_INF(" bfth=%d, of=%d, ipm=%d, th=%d", bf5, bf6, bf7, bf8);
445 if (bf5 > OUTCONTROL_BFTH_MAX) {
446 LOG_WRN("illegal BFTH value %d", bf5);
447 return -EINVAL;
448 }
449
450 #ifdef CONFIG_SOC_SERIES_INTEL_ACE
451 bf9 = FIELD_GET(OUTCONTROL_IPM_SOURCE_1, val);
452 bf10 = FIELD_GET(OUTCONTROL_IPM_SOURCE_2, val);
453 bf11 = FIELD_GET(OUTCONTROL_IPM_SOURCE_3, val);
454 bf12 = FIELD_GET(OUTCONTROL_IPM_SOURCE_4, val);
455 bf13 = FIELD_GET(OUTCONTROL_IPM_SOURCE_MODE, val);
456 LOG_INF(" ipms1=%d, ipms2=%d, ipms3=%d, ipms4=%d", bf9, bf10, bf11, bf12);
457 LOG_INF(" ipms_mode=%d", bf13);
458 ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) |
459 FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) |
460 FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) |
461 FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_IPM_SOURCE_1, bf9) |
462 FIELD_PREP(OUTCONTROL_IPM_SOURCE_2, bf10) |
463 FIELD_PREP(OUTCONTROL_IPM_SOURCE_3, bf11) |
464 FIELD_PREP(OUTCONTROL_IPM_SOURCE_4, bf12) | FIELD_PREP(OUTCONTROL_TH, bf8) |
465 FIELD_PREP(OUTCONTROL_IPM_SOURCE_MODE, bf13);
466 #else
467 ref = FIELD_PREP(OUTCONTROL_TIE, bf1) | FIELD_PREP(OUTCONTROL_SIP, bf2) |
468 FIELD_PREP(OUTCONTROL_FINIT, bf3) | FIELD_PREP(OUTCONTROL_FCI, bf4) |
469 FIELD_PREP(OUTCONTROL_BFTH, bf5) | FIELD_PREP(OUTCONTROL_OF, bf6) |
470 FIELD_PREP(OUTCONTROL_IPM, bf7) | FIELD_PREP(OUTCONTROL_TH, bf8);
471 #endif
472 if (ref != val) {
473 LOG_WRN("Some reserved bits are set in OUTCONTROL = 0x%08x", val);
474 }
475
476 return 0;
477 }
478
print_cic_control(uint32_t val)479 static void print_cic_control(uint32_t val)
480 {
481 int bf1, bf2, bf3, bf4, bf5, bf6, bf7;
482 uint32_t ref;
483
484 bf1 = FIELD_GET(CIC_CONTROL_SOFT_RESET, val);
485 bf2 = FIELD_GET(CIC_CONTROL_CIC_START_B, val);
486 bf3 = FIELD_GET(CIC_CONTROL_CIC_START_A, val);
487 bf4 = FIELD_GET(CIC_CONTROL_MIC_B_POLARITY, val);
488 bf5 = FIELD_GET(CIC_CONTROL_MIC_A_POLARITY, val);
489 bf6 = FIELD_GET(CIC_CONTROL_MIC_MUTE, val);
490 #ifndef CONFIG_SOC_SERIES_INTEL_ACE
491 bf7 = FIELD_GET(CIC_CONTROL_STEREO_MODE, val);
492 #else
493 bf7 = -1;
494 #endif
495 LOG_DBG("CIC_CONTROL = %08x", val);
496 LOG_DBG(" soft_reset=%d, cic_start_b=%d, cic_start_a=%d",
497 bf1, bf2, bf3);
498 LOG_DBG(" mic_b_polarity=%d, mic_a_polarity=%d, mic_mute=%d",
499 bf4, bf5, bf6);
500 ref = FIELD_PREP(CIC_CONTROL_SOFT_RESET, bf1) |
501 FIELD_PREP(CIC_CONTROL_CIC_START_B, bf2) |
502 FIELD_PREP(CIC_CONTROL_CIC_START_A, bf3) |
503 FIELD_PREP(CIC_CONTROL_MIC_B_POLARITY, bf4) |
504 FIELD_PREP(CIC_CONTROL_MIC_A_POLARITY, bf5) |
505 FIELD_PREP(CIC_CONTROL_MIC_MUTE, bf6)
506 #ifndef CONFIG_SOC_SERIES_INTEL_ACE
507 | FIELD_PREP(CIC_CONTROL_STEREO_MODE, bf7)
508 #endif
509 ;
510 LOG_DBG(" stereo_mode=%d", bf7);
511 if (ref != val) {
512 LOG_WRN("Some reserved bits are set in CIC_CONTROL = 0x%08x", val);
513 }
514 }
515
print_fir_control(uint32_t val)516 static void print_fir_control(uint32_t val)
517 {
518 int bf1, bf2, bf3, bf4, bf5, bf6;
519 uint32_t ref;
520
521 bf1 = FIELD_GET(FIR_CONTROL_START, val);
522 bf2 = FIELD_GET(FIR_CONTROL_ARRAY_START_EN, val);
523 #ifdef CONFIG_SOC_SERIES_INTEL_ACE
524 bf3 = FIELD_GET(FIR_CONTROL_PERIODIC_START_EN, val);
525 #else
526 bf3 = -1;
527 #endif
528 bf4 = FIELD_GET(FIR_CONTROL_DCCOMP, val);
529 bf5 = FIELD_GET(FIR_CONTROL_MUTE, val);
530 bf6 = FIELD_GET(FIR_CONTROL_STEREO, val);
531 LOG_DBG("FIR_CONTROL = %08x", val);
532 LOG_DBG(" start=%d, array_start_en=%d, periodic_start_en=%d",
533 bf1, bf2, bf3);
534 LOG_DBG(" dccomp=%d, mute=%d, stereo=%d", bf4, bf5, bf6);
535 ref = FIELD_PREP(FIR_CONTROL_START, bf1) |
536 FIELD_PREP(FIR_CONTROL_ARRAY_START_EN, bf2) |
537 #ifdef CONFIG_SOC_SERIES_INTEL_ACE
538 FIELD_PREP(FIR_CONTROL_PERIODIC_START_EN, bf3) |
539 #endif
540 FIELD_PREP(FIR_CONTROL_DCCOMP, bf4) |
541 FIELD_PREP(FIR_CONTROL_MUTE, bf5) |
542 FIELD_PREP(FIR_CONTROL_STEREO, bf6);
543
544 if (ref != val) {
545 LOG_WRN("Some reserved bits are set in FIR_CONTROL = 0x%08x", val);
546 }
547 }
548
print_pdm_ctrl(const struct nhlt_pdm_ctrl_cfg * pdm_cfg)549 static void print_pdm_ctrl(const struct nhlt_pdm_ctrl_cfg *pdm_cfg)
550 {
551 int bf1, bf2, bf3, bf4, bf5;
552 uint32_t val;
553
554 LOG_DBG("CIC_CONTROL = %08x", pdm_cfg->cic_control);
555
556 val = pdm_cfg->cic_config;
557 bf1 = FIELD_GET(CIC_CONFIG_CIC_SHIFT, val);
558 bf2 = FIELD_GET(CIC_CONFIG_COMB_COUNT, val);
559 LOG_DBG("CIC_CONFIG = %08x", val);
560 LOG_DBG(" cic_shift=%d, comb_count=%d", bf1, bf2);
561
562 val = pdm_cfg->mic_control;
563
564 #ifndef CONFIG_SOC_SERIES_INTEL_ACE
565 bf1 = FIELD_GET(MIC_CONTROL_PDM_SKEW, val);
566 #else
567 bf1 = -1;
568 #endif
569 bf2 = FIELD_GET(MIC_CONTROL_CLK_EDGE, val);
570 bf3 = FIELD_GET(MIC_CONTROL_PDM_EN_B, val);
571 bf4 = FIELD_GET(MIC_CONTROL_PDM_EN_A, val);
572 bf5 = FIELD_GET(MIC_CONTROL_PDM_CLKDIV, val);
573 LOG_DBG("MIC_CONTROL = %08x", val);
574 LOG_DBG(" clkdiv=%d, skew=%d, clk_edge=%d", bf5, bf1, bf2);
575 LOG_DBG(" en_b=%d, en_a=%d", bf3, bf4);
576 }
577
print_fir_config(const struct nhlt_pdm_ctrl_fir_cfg * fir_cfg)578 static void print_fir_config(const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg)
579 {
580 uint32_t val;
581 int fir_decimation, fir_shift, fir_length;
582
583 val = fir_cfg->fir_config;
584 fir_length = FIELD_GET(FIR_CONFIG_FIR_LENGTH, val);
585 fir_decimation = FIELD_GET(FIR_CONFIG_FIR_DECIMATION, val);
586 fir_shift = FIELD_GET(FIR_CONFIG_FIR_SHIFT, val);
587 LOG_DBG("FIR_CONFIG = %08x", val);
588 LOG_DBG(" fir_decimation=%d, fir_shift=%d, fir_length=%d",
589 fir_decimation, fir_shift, fir_length);
590
591 print_fir_control(fir_cfg->fir_control);
592
593 /* Use DC_OFFSET and GAIN as such */
594 LOG_DBG("DC_OFFSET_LEFT = %08x", fir_cfg->dc_offset_left);
595 LOG_DBG("DC_OFFSET_RIGHT = %08x", fir_cfg->dc_offset_right);
596 LOG_DBG("OUT_GAIN_LEFT = %08x", fir_cfg->out_gain_left);
597 LOG_DBG("OUT_GAIN_RIGHT = %08x", fir_cfg->out_gain_right);
598 }
599
configure_fir(struct dai_intel_dmic * dmic,const uint32_t base,const struct nhlt_pdm_ctrl_fir_cfg * fir_cfg)600 static void configure_fir(struct dai_intel_dmic *dmic, const uint32_t base,
601 const struct nhlt_pdm_ctrl_fir_cfg *fir_cfg)
602 {
603 uint32_t val;
604
605 print_fir_config(fir_cfg);
606
607 /* Use FIR_CONFIG as such */
608 val = fir_cfg->fir_config;
609 dai_dmic_write(dmic, base + FIR_CONFIG, val);
610
611 val = fir_cfg->fir_control;
612 print_fir_control(val);
613
614 /* Clear START, set MUTE */
615 val = (val & ~FIR_CONTROL_START) | FIR_CONTROL_MUTE;
616 dai_dmic_write(dmic, base + FIR_CONTROL, val);
617 LOG_DBG("FIR_CONTROL = %08x", val);
618
619 /* Use DC_OFFSET and GAIN as such */
620 dai_dmic_write(dmic, base + DC_OFFSET_LEFT, fir_cfg->dc_offset_left);
621 dai_dmic_write(dmic, base + DC_OFFSET_RIGHT, fir_cfg->dc_offset_right);
622 dai_dmic_write(dmic, base + OUT_GAIN_LEFT, fir_cfg->out_gain_left);
623 dai_dmic_write(dmic, base + OUT_GAIN_RIGHT, fir_cfg->out_gain_right);
624
625 dmic->gain_left = fir_cfg->out_gain_left;
626 dmic->gain_right = fir_cfg->out_gain_right;
627 }
628
dai_dmic_set_config_nhlt(struct dai_intel_dmic * dmic,const void * bespoke_cfg)629 int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *bespoke_cfg)
630 {
631 const struct nhlt_pdm_ctrl_cfg *pdm_cfg;
632 struct nhlt_dmic_channel_ctrl_mask *dmic_cfg;
633
634 uint32_t channel_ctrl_mask;
635 uint32_t pdm_ctrl_mask;
636 uint32_t pdm_base;
637 int pdm_idx;
638 uint32_t val;
639 uint32_t outcontrol;
640 const uint8_t *p = bespoke_cfg;
641 int num_fifos;
642 int num_pdm;
643 int n;
644 int ret;
645
646 const uint32_t *fir_coeffs;
647
648 /* Array of pointers to pdm coefficient data. Used to reuse coefficient from another pdm. */
649 const uint32_t *pdm_coeff_ptr[DMIC_HW_CONTROLLERS_MAX] = { 0 };
650
651 if (dmic->dai_config_params.dai_index >= DMIC_HW_FIFOS_MAX) {
652 LOG_ERR("dmic_set_config_nhlt(): illegal DAI index %d",
653 dmic->dai_config_params.dai_index);
654 return -EINVAL;
655 }
656
657 /* Skip not used headers */
658 p += sizeof(struct nhlt_dmic_gateway_attributes);
659 p += sizeof(struct nhlt_dmic_ts_group);
660 p += sizeof(struct nhlt_dmic_global_config);
661
662 /* Channel_ctlr_mask bits indicate the FIFOs enabled*/
663 dmic_cfg = (struct nhlt_dmic_channel_ctrl_mask *)p;
664 channel_ctrl_mask = dmic_cfg->channel_ctrl_mask;
665 num_fifos = POPCOUNT(channel_ctrl_mask); /* Count set bits */
666 p += sizeof(struct nhlt_dmic_channel_ctrl_mask);
667 LOG_DBG("dmic_set_config_nhlt(): channel_ctrl_mask = %d", channel_ctrl_mask);
668
669 /* Configure clock source */
670 ret = dai_dmic_set_clock(dmic, dmic_cfg->clock_source);
671 if (ret)
672 return ret;
673
674 /* Get OUTCONTROLx configuration */
675 if (num_fifos < 1 || num_fifos > DMIC_HW_FIFOS_MAX) {
676 LOG_ERR("dmic_set_config_nhlt(): illegal number of FIFOs %d", num_fifos);
677 return -EINVAL;
678 }
679
680 for (n = 0; n < DMIC_HW_FIFOS_MAX; n++) {
681 if (!(channel_ctrl_mask & (1 << n)))
682 continue;
683
684 val = *(uint32_t *)p;
685 ret = print_outcontrol(val);
686 if (ret) {
687 return ret;
688 }
689
690 if (dmic->dai_config_params.dai_index == n) {
691 /* Write the FIFO control registers. The clear/set of bits is the same for
692 * all DMIC_HW_VERSION
693 */
694 /* Clear TIE, SIP, FCI, set FINIT, the rest of bits as such */
695 outcontrol = (val & ~(OUTCONTROL_TIE | OUTCONTROL_SIP | OUTCONTROL_FCI)) |
696 OUTCONTROL_FINIT;
697
698 dai_dmic_write(dmic, dmic->dai_config_params.dai_index *
699 PDM_CHANNEL_REGS_SIZE + OUTCONTROL, outcontrol);
700
701 LOG_INF("OUTCONTROL%d = %08x", dmic->dai_config_params.dai_index,
702 outcontrol);
703
704 /* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA
705 * configuration
706 */
707 val = FIELD_GET(OUTCONTROL_BFTH, outcontrol);
708 dmic->fifo.depth = 1 << val;
709 }
710
711 p += sizeof(uint32_t);
712 }
713
714 /* Get PDMx registers */
715 pdm_ctrl_mask = ((const struct nhlt_pdm_ctrl_mask *)p)->pdm_ctrl_mask;
716 num_pdm = POPCOUNT(pdm_ctrl_mask); /* Count set bits */
717 p += sizeof(struct nhlt_pdm_ctrl_mask);
718 LOG_DBG("dmic_set_config_nhlt(): pdm_ctrl_mask = %d", pdm_ctrl_mask);
719 if (num_pdm < 1 || num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
720 LOG_ERR("dmic_set_config_nhlt(): illegal number of PDMs %d", num_pdm);
721 return -EINVAL;
722 }
723
724 pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)p;
725
726 for (pdm_idx = 0; pdm_idx < CONFIG_DAI_DMIC_HW_CONTROLLERS; pdm_idx++) {
727 pdm_base = dmic_base[pdm_idx];
728
729 if (!(pdm_ctrl_mask & (1 << pdm_idx))) {
730 /* Set MIC_MUTE bit to unused PDM */
731 dai_dmic_write(dmic, pdm_base + CIC_CONTROL, CIC_CONTROL_MIC_MUTE);
732 continue;
733 }
734
735 LOG_DBG("PDM%d", pdm_idx);
736
737 /* Get CIC configuration */
738 if (dai_dmic_global.active_fifos_mask == 0) {
739 print_pdm_ctrl(pdm_cfg);
740
741 val = pdm_cfg->cic_control;
742 print_cic_control(val);
743
744 /* Clear CIC_START_A and CIC_START_B */
745 val = (val & ~(CIC_CONTROL_CIC_START_A | CIC_CONTROL_CIC_START_B));
746 dai_dmic_write(dmic, pdm_base + CIC_CONTROL, val);
747 LOG_DBG("dmic_set_config_nhlt(): CIC_CONTROL = %08x", val);
748
749 /* Use CIC_CONFIG as such */
750 val = pdm_cfg->cic_config;
751 dai_dmic_write(dmic, pdm_base + CIC_CONFIG, val);
752
753 /* Clear PDM_EN_A and PDM_EN_B */
754 val = pdm_cfg->mic_control;
755 val &= ~(MIC_CONTROL_PDM_EN_A | MIC_CONTROL_PDM_EN_B);
756 dai_dmic_write(dmic, pdm_base + MIC_CONTROL, val);
757 LOG_DBG("dmic_set_config_nhlt(): MIC_CONTROL = %08x", val);
758 }
759
760 configure_fir(dmic, pdm_base +
761 FIR_CHANNEL_REGS_SIZE * dmic->dai_config_params.dai_index,
762 &pdm_cfg->fir_config[dmic->dai_config_params.dai_index]);
763
764
765 /* Configure fir coefficients */
766
767 /* Check if FIR coeffs should be reused */
768 if (pdm_cfg->reuse_fir_from_pdm == 0) {
769 /* get ptr, where FIR coeffs starts */
770 fir_coeffs = pdm_cfg->fir_coeffs;
771
772 /* and save it for future pdms reference */
773 pdm_coeff_ptr[pdm_idx] = fir_coeffs;
774 } else {
775 if (pdm_cfg->reuse_fir_from_pdm > pdm_idx) {
776 LOG_ERR("invalid reuse fir index %u", pdm_cfg->reuse_fir_from_pdm);
777 return -EINVAL;
778 }
779
780 /* get FIR coeffs from another pdm */
781 fir_coeffs = pdm_coeff_ptr[pdm_cfg->reuse_fir_from_pdm - 1];
782
783 if (!fir_coeffs) {
784 LOG_ERR("unable to reuse fir from %u", pdm_cfg->reuse_fir_from_pdm);
785 return -EINVAL;
786 }
787 }
788
789 fir_coeffs = dai_dmic_configure_coeff(dmic, pdm_cfg, pdm_base, fir_coeffs);
790
791 /* Update pdm_cfg ptr for next PDM Ctrl. */
792 if (pdm_cfg->reuse_fir_from_pdm) {
793 /* fir_coeffs array is empty if reusing previous coeffs */
794 pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)&pdm_cfg->fir_coeffs;
795 } else {
796 pdm_cfg = (const struct nhlt_pdm_ctrl_cfg *)fir_coeffs;
797 }
798 }
799
800 #ifdef CONFIG_SOC_SERIES_INTEL_ACE
801 ret = dai_nhlt_dmic_dai_params_get(dmic, dmic_cfg->clock_source);
802 #else
803 ret = dai_nhlt_dmic_dai_params_get(dmic);
804 #endif
805 if (ret)
806 return ret;
807
808 LOG_INF("dmic_set_config_nhlt(): enable0 %u, enable1 %u",
809 dmic->enable[0], dmic->enable[1]);
810 return 0;
811 }
812