1% success = dmic_init(prm)
2%
3% Create PDM microphones interface configuration
4
5% SPDX-License-Identifier: BSD-3-Clause
6%
7% Copyright (c) 2019, Intel Corporation. All rights reserved.
8%
9% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
10
11function success = dmic_init(prm)
12
13hw.controllers = 4;
14hw.bits_cic = 26;
15hw.bits_fir_coef = 20;
16hw.bits_fir_gain = 20;
17hw.bits_fir_input = 22;
18hw.bits_fir_output = 24;
19hw.bits_fir_internal = 26;
20hw.bits_gain_output = 22;
21hw.fir_length_max = 250;
22hw.cic_shift_right_range = [-8 4];
23hw.fir_shift_right_range  = [0 8];
24hw.fir_max_length = 250;
25hw.version = 1;
26hw.number_of_pdm_controllers = 2;
27hw.ioclk = 19.2e6;
28spec.scale = 0.95;
29spec.linear_phase = 1;
30spec.rp = 0.1;
31spec.cp = 0.4375;
32spec.cs = 0.5100;
33spec.rs = 95;
34success = 0;
35
36if (prm.fifo_a_fs == 0) && (prm.fifo_b_fs == 0)
37        fprintf(1,'Error: At least one FIFO needs non-zero Fs!\n');
38        return;
39end
40
41%% Match modes
42[a_clkdiv, a_mcic, a_mfir] = find_modes(prm, hw, prm.fifo_a_fs);
43[b_clkdiv, b_mcic, b_mfir] = find_modes(prm, hw, prm.fifo_b_fs);
44[common_clkdiv_list,common_mcic_list,a_mfir_list,b_mfir_list] = ...
45        match_modes(a_clkdiv, a_mcic, a_mfir, b_clkdiv, b_mcic, b_mfir);
46
47if isempty(common_clkdiv_list)
48        error('No compatible settings were found!\n');
49end
50
51%% Set basic configuration data
52cfg = get_cfg(prm, hw);
53
54%% Done, print 1st modes combination
55fprintf('Selected fifo_a_fs=%d, fifo_b_fs=%d: ', prm.fifo_a_fs, prm.fifo_b_fs);
56cfg = select_mode(common_mcic_list, a_mfir_list, b_mfir_list, common_clkdiv_list, cfg);
57cfg = get_cic_config(prm, cfg, hw);
58cfg = get_fir_config(prm, cfg, hw, spec);
59
60end
61
62%% Functions
63
64%% Get FIR filters
65function cfg = get_fir_config(prm, cfg, hw, spec)
66
67if prm.fifo_a_fs > 0
68        [coef, shift] = get_fir(cfg.mfir_a, prm, cfg, hw, spec);
69
70        [cfg.fir_coef_a, cfg.fir_shift_a, cfg.fir_length_a] = ...
71                scale_fir_coef(coef, shift, spec.scale, cfg.remain_gain_to_fir, hw.bits_fir_coef);
72        cfg.fir_gain_a = 1;
73else
74        cfg.fir_gain_a = 0;
75        cfg.fir_coef_a = 0;
76        cfg.fir_shift_a = 0;
77        cfg.fir_length_a = 1;
78end
79
80if prm.fifo_b_fs > 0
81        [coef, shift] = get_fir(cfg.mfir_b, prm, cfg, hw, spec);
82
83        [cfg.fir_coef_b, cfg.fir_shift_b, cfg.fir_length_b] = ...
84		scale_fir_coef(coef, shift, spec.scale, cfg.remain_gain_to_fir, hw.bits_fir_coef);
85        cfg.fir_gain_b = 1;
86else
87        cfg.fir_gain_b = 0;
88        cfg.fir_coef_b = 0;
89        cfg.fir_shift_b = 0;
90        cfg.fir_length_b = 1;
91end
92
93end
94
95function [coefq, shiftq, len] = scale_fir_coef(coef32, shift32, hw_sens, add_gain, bits)
96
97out_scale = 2^(bits-1);
98q31_scale = 2^31;
99coef = coef32/q31_scale * 2^(-shift32) * hw_sens * add_gain;
100max_abs_coef = max(abs(coef));
101scale = 0.9999/max_abs_coef;
102shiftq = floor(log(scale)/log(2));
103c = 2^shiftq;
104coefq = round(out_scale * coef * c);
105len = length(coefq);
106
107end
108
109% This function becomes table lookup in C
110function [coef32, shift] = get_fir(mfir, prm, cfg, hw, spec)
111
112fs_fir = cfg.mic_clk/cfg.mcic;
113fs = fs_fir/mfir;
114passhz = spec.cp * fs;
115stophz = spec.cs * fs;
116max_length = min(hw.fir_max_length, floor(hw.ioclk/fs/2-5));
117
118[ coef, shift, bw, sb, rs, got_pb, got_sb, passed] = ...
119	dmic_fir(spec.rp, spec.rs, passhz, stophz, cfg.mic_clk, fs_fir, max_length, spec.linear_phase);
120
121if passed == 0
122        fprintf(1, 'Warning: Filter specification was reduced.\n');
123        if (got_pb > 1) || (got_sb > -60)
124                error('The design is erroneous.');
125        end
126end
127
128coef32 = round(2^31 * coef);
129fir.length = length(coef32);
130fir.coef = coef32;
131fir.shift = shift;
132cp = bw/fs;
133cs = sb/fs;
134fir.cp = cp;
135fir.cs = cs;
136fir.rp = spec.rp;
137fir.rs = rs;
138fir.m = mfir;
139dmic_fir_export(fir, 'include');
140
141end
142
143%% Select one mode from possible combinations
144function cfg = select_mode(common_mcic_list, a_mfir_list, b_mfir_list, common_clkdiv_list, cfg)
145
146cfg.mcic = 0;
147cfg.mfir_a = 0;
148cfg.mfir_b = 0;
149cfg.clk_div = 0;
150
151% Order of preference for FIR decimation factors, prime numbers
152% even if low value, are less preferable due to incompatibility
153% with other FIR decimation factor.
154mpref = [2 4 6 8 3 5 7 9 10 11 12 13 14 15];
155
156% Find common mode with lowest FIR decimation ratio. If there are many
157% select one with lowest mic clock rate. Lowest rates or highest dividers
158% are in the end of list.
159if length(common_mcic_list) > 0
160	for mtry = mpref
161		idx = find(a_mfir_list == mtry);
162		if ~isempty(idx)
163			n = idx(end);
164			break;
165		end
166	end
167        cfg.mcic = common_mcic_list(n);
168        cfg.clk_div = common_clkdiv_list(n);
169        if a_mfir_list(1) > 0
170                cfg.mfir_a = a_mfir_list(n);
171        end
172        if b_mfir_list(1) > 0
173                cfg.mfir_b = b_mfir_list(n);
174        end
175        fprintf(1, 'clk_div=%d, cic=%d, fir_a=%d, fir_b=%d\n', ...
176		cfg.clk_div, cfg.mcic, cfg.mfir_a, cfg.mfir_b);
177end
178
179end
180
181%% Compute CIC filter settings
182function cfg = get_cic_config(prm, cfg, hw)
183
184cfg.mic_clk = hw.ioclk/cfg.clk_div;
185g_cic = cfg.mcic^5;
186bitsneeded = floor(log(g_cic)/log(2)+1)+1;
187cfg.cic_shift = bitsneeded - hw.bits_fir_input;
188
189if hw.bits_cic < bitsneeded
190        fprintf(1,'Error: Needed CIC word length is exceeded %d\n', bitsneeded);
191end
192
193if cfg.cic_shift < hw.cic_shift_right_range(1);
194        fprintf(1,'Warning: Limited CIC shift right from %d', cfg.cic_shift);
195        cfg.cic_shift = hw.cic_shift_right_range(1);
196end
197
198if cfg.cic_shift > hw.cic_shift_right_range(2);
199        fprintf(1,'Error: Limited CIC shift right from %d', cfg.cic_shift);
200        cfg.cic_shift = hw.cic_shift_right_range(2);
201end
202
203% Compute how much gain is left for FIR from scaling to full scale
204cfg.remain_gain_to_fir = 2^(hw.bits_fir_input-1)/(g_cic/2^cfg.cic_shift);
205
206end
207
208%% Find modes those are possible and exist in setup database
209function [clkdiv, mcic, mfir] = find_modes(prm, hw, pcm_fs)
210
211if pcm_fs < 1
212        clkdiv = 0;
213        mcic = 0;
214        mfir = 0;
215        return;
216end
217osr_min = 50;
218if pcm_fs > 48e3
219        osr_min = floor(3.0e6 / pcm_fs);
220end
221mcic_min = 5;
222mcic_max = 31; % 8 bits reg but CIC gain and 26 bits implementation limits this
223mfir_min = 2;
224mfir_max = 15;
225clkdiv_min = ceil(hw.ioclk/prm.pdmclk_max);
226clkdiv_max = floor(hw.ioclk/prm.pdmclk_min);
227n = 1;
228clkdiv = [];
229mcic = [];
230mfir = [];
231% Highest to lowest PDM clock, prefer best quality in range
232for clkdiv_test = clkdiv_min:clkdiv_max
233        if clkdiv_test > 4 % Limitation in cAVS1.5-2.0
234                c1 = floor(clkdiv_test/2);
235                c2 = clkdiv_test - c1;
236                du_min = 100*c1/clkdiv_test;
237                du_max = 100*c2/clkdiv_test;
238                pdmclk = hw.ioclk/clkdiv_test;
239                osr = round(pdmclk/pcm_fs);
240                % Lowest FIR decimation to highest, prefer low FIR
241                % decimation ratios
242                for mfir_test = mfir_min:osr
243                        mcic_test = floor(osr/mfir_test);
244                        if (abs(pcm_fs*mfir_test*mcic_test -pdmclk) < 1)  ...
245                                        && (osr >= osr_min) ...
246                                        && (mcic_test >= mcic_min) ...
247                                        && (mcic_test <= mcic_max) ...
248                                        && (mfir_test <= mfir_max) ...
249                                        && (du_min >= prm.duty_min) ...
250                                        && (du_max <= prm.duty_max)
251                                sfir = sprintf('FIR %d x %d x %dHz', ...
252                                        mcic_test, mfir_test, round(pcm_fs));
253                                %fprintf('Found: %s\n',sfir);
254                                clkdiv(n) = clkdiv_test;
255                                mcic(n) = mcic_test;
256                                mfir(n) = mfir_test;
257                                n=n+1;
258                        end
259                end
260        end
261end
262end
263
264
265%% Match found modes for common clkdiv and mcic, a DMIC hardware constraint
266function [common_clkdiv_list,common_mcic_list,a_mfir_list,b_mfir_list] = ...
267        match_modes(a_clkdiv, a_mcic, a_mfir, b_clkdiv, b_mcic, b_mfir)
268
269if b_clkdiv == 0
270        common_clkdiv_list = a_clkdiv;
271        common_mcic_list = a_mcic;
272        a_mfir_list = a_mfir;
273        b_mfir_list = 0;
274        return;
275end
276common_clkdiv_list = [];
277common_mcic_list = [];
278a_mfir_list = [];
279b_mfir_list = [];
280n_list = 1;
281for n=1:length(a_clkdiv)
282        idx = find(b_clkdiv == a_clkdiv(n));
283        for m=1:length(idx)
284                if b_mcic(idx(m)) == a_mcic(n)
285                        common_clkdiv = a_clkdiv(n);
286                        common_mcic = a_mcic(n);
287                        common_clkdiv_list(n_list) = common_clkdiv;
288                        common_mcic_list(n_list) = common_mcic;
289                        a_mfir_list(n_list) = a_mfir(n);
290                        b_mfir_list(n_list) = b_mfir(idx(m));
291                        fprintf('Option %d: div=%d, mcic=%d, mfira=%d, mfirb=%d\n', ...
292                                n_list, common_clkdiv, common_mcic, ...
293                                a_mfir_list(n_list), b_mfir_list(n_list));
294                        n_list = n_list+1;
295                end
296        end
297end
298end
299
300%% Misc blob details needed
301function cfg = get_cfg(prm, hw)
302
303% if FIFO A or B is disabled, append a dummy blob to keep modes matching
304% code happy with identical A and B request
305if (prm.fifo_a_fs == 0) && (prm.fifo_b_fs == 0)
306        fprintf('Error: FIFO A or B need to be assigned a nonzero samplerate\n');
307        return;
308end
309
310cfg.hw_version = hw.version;
311
312cfg.fifo_a_fs = prm.fifo_a_fs;
313cfg.fifo_b_fs = prm.fifo_b_fs;
314
315cfg.pdm01_provided = (prm.pdm(1).enable_mic_a | prm.pdm(1).enable_mic_b) ...
316        + (prm.pdm(2).enable_mic_a | prm.pdm(2).enable_mic_b)*2;
317cfg.ch01_provided = (prm.fifo_a_fs > 0) + (prm.fifo_b_fs > 0)*2;
318
319if prm.fifo_a_fs > 0
320        cfg.a_nchannels = prm.pdm(1).enable_mic_a + prm.pdm(1).enable_mic_b ...
321                + prm.pdm(2).enable_mic_a + prm.pdm(2).enable_mic_b;
322else
323        cfg.a_nchannels = 0;
324end
325if prm.fifo_b_fs > 0
326        cfg.b_nchannels = prm.pdm(1).enable_mic_a + prm.pdm(1).enable_mic_b ...
327                + prm.pdm(2).enable_mic_a + prm.pdm(2).enable_mic_b;
328else
329        cfg.b_nchannels = 0;
330end
331
332end
333