% prm = dmic_init(prm) % % Create PDM microphones interface configuration % SPDX-License-Identifier: BSD-3-Clause % % Copyright (c) 2019, Intel Corporation. All rights reserved. % % Author: Seppo Ingalsuo function prm = dmic_init(prm) hw.controllers = 4; hw.bits_cic = 26; hw.bits_fir_coef = 20; hw.bits_fir_gain = 20; hw.bits_fir_input = 22; hw.bits_fir_output = 24; hw.bits_fir_internal = 26; hw.bits_gain_output = 22; hw.cic_shift_right_range = [-8 4]; hw.fir_shift_right_range = [0 8]; hw.fir_max_length = 250; hw.version = 1; hw.number_of_pdm_controllers = 2; hw.ioclk = 19.2e6; spec.scale = 0.95; spec.linear_phase = 1; spec.rp = 0.1; spec.cp = 0.4375; spec.cs = 0.5100; spec.rs = 95; if (prm.fifo_a_fs == 0) && (prm.fifo_b_fs == 0) fprintf(1,'Error: At least one FIFO needs non-zero Fs!\n'); return; end %% Match modes [a_clkdiv, a_mcic, a_mfir] = find_modes(prm, hw, prm.fifo_a_fs); [b_clkdiv, b_mcic, b_mfir] = find_modes(prm, hw, prm.fifo_b_fs); [common_clkdiv_list,common_mcic_list,a_mfir_list,b_mfir_list] = ... match_modes(a_clkdiv, a_mcic, a_mfir, b_clkdiv, b_mcic, b_mfir); if isempty(common_clkdiv_list) error('No compatible settings were found!\n'); end %% Set basic configuration data cfg = get_cfg(prm, hw); %% Done, print 1st modes combination fprintf('Selected fifo_a_fs=%d, fifo_b_fs=%d: ', prm.fifo_a_fs, prm.fifo_b_fs); cfg = select_mode(common_mcic_list, a_mfir_list, b_mfir_list, common_clkdiv_list, cfg); cfg = get_cic_config(cfg, hw); prm = get_fir_config(prm, cfg, hw, spec); end %% Functions %% Get FIR filters function prm = get_fir_config(prm, cfg, hw, spec) if prm.fifo_a_fs > 0 fir = get_fir(cfg.mfir_a, cfg, hw, spec); dmic_fir_export(fir, 'include'); prm.vfn = dmic_fir_export_alsa(fir, 'include_alsa_utils', prm.vfn); end if prm.fifo_b_fs > 0 fir = get_fir(cfg.mfir_b, cfg, hw, spec); dmic_fir_export(fir, 'include'); prm.vfn = dmic_fir_export_alsa(fir, 'include_alsa_utils', prm.vfn); end end function fir = get_fir(mfir, cfg, hw, spec) fs_fir = cfg.mic_clk/cfg.mcic; fs = fs_fir/mfir; passhz = spec.cp * fs; stophz = spec.cs * fs; max_length = min(hw.fir_max_length, floor(hw.ioclk/fs/2-5)); [ coef, shift, bw, sb, rs, got_pb, got_sb, passed] = ... dmic_fir(spec.rp, spec.rs, passhz, stophz, cfg.mic_clk, fs_fir, max_length, spec.linear_phase); if passed == 0 fprintf(1, 'Warning: Filter specification was reduced.\n'); if (got_pb > 1) || (got_sb > -60) error('The design is erroneous.'); end end coef32 = round(2^31 * coef); fir.length = length(coef32); fir.coef = coef32; fir.shift = shift; cp = bw/fs; cs = sb/fs; fir.cp = cp; fir.cs = cs; fir.rp = spec.rp; fir.rs = rs; fir.m = mfir; end %% Select one mode from possible combinations function cfg = select_mode(common_mcic_list, a_mfir_list, b_mfir_list, common_clkdiv_list, cfg) cfg.mcic = 0; cfg.mfir_a = 0; cfg.mfir_b = 0; cfg.clk_div = 0; % Order of preference for FIR decimation factors, prime numbers % even if low value, are less preferable due to incompatibility % with other FIR decimation factor. mpref = [2 4 6 8 3 5 7 9 10 11 12 13 14 15]; % Find common mode with lowest FIR decimation ratio. If there are many % select one with lowest mic clock rate. Lowest rates or highest dividers % are in the end of list. if ~isempty(common_mcic_list) for mtry = mpref idx = find(a_mfir_list == mtry); if ~isempty(idx) n = idx(end); break; end end cfg.mcic = common_mcic_list(n); cfg.clk_div = common_clkdiv_list(n); if a_mfir_list(1) > 0 cfg.mfir_a = a_mfir_list(n); end if b_mfir_list(1) > 0 cfg.mfir_b = b_mfir_list(n); end fprintf(1, 'clk_div=%d, cic=%d, fir_a=%d, fir_b=%d\n', ... cfg.clk_div, cfg.mcic, cfg.mfir_a, cfg.mfir_b); end end %% Compute CIC filter settings function cfg = get_cic_config(cfg, hw) cfg.mic_clk = hw.ioclk/cfg.clk_div; g_cic = cfg.mcic^5; bitsneeded = floor(log(g_cic)/log(2)+1)+1; cfg.cic_shift = bitsneeded - hw.bits_fir_input; if hw.bits_cic < bitsneeded fprintf(1,'Error: Needed CIC word length is exceeded %d\n', bitsneeded); end if cfg.cic_shift < hw.cic_shift_right_range(1) fprintf(1,'Warning: Limited CIC shift right from %d', cfg.cic_shift); cfg.cic_shift = hw.cic_shift_right_range(1); end if cfg.cic_shift > hw.cic_shift_right_range(2) fprintf(1,'Error: Limited CIC shift right from %d', cfg.cic_shift); cfg.cic_shift = hw.cic_shift_right_range(2); end % Compute how much gain is left for FIR from scaling to full scale cfg.remain_gain_to_fir = 2^(hw.bits_fir_input-1)/(g_cic/2^cfg.cic_shift); end %% Find modes those are possible and exist in setup database function [clkdiv, mcic, mfir] = find_modes(prm, hw, pcm_fs) if pcm_fs < 1 clkdiv = 0; mcic = 0; mfir = 0; return; end osr_min = 50; if pcm_fs > 48e3 osr_min = floor(3.0e6 / pcm_fs); end mcic_min = 5; mcic_max = 31; % 8 bits reg but CIC gain and 26 bits implementation limits this mfir_min = 2; mfir_max = 15; clkdiv_min = ceil(hw.ioclk/prm.pdmclk_max); clkdiv_max = floor(hw.ioclk/prm.pdmclk_min); n = 1; clkdiv = []; mcic = []; mfir = []; % Highest to lowest PDM clock, prefer best quality in range for clkdiv_test = clkdiv_min:clkdiv_max if clkdiv_test > 4 % Limitation in cAVS1.5-2.0 c1 = floor(clkdiv_test/2); c2 = clkdiv_test - c1; du_min = 100*c1/clkdiv_test; du_max = 100*c2/clkdiv_test; pdmclk = hw.ioclk/clkdiv_test; osr = round(pdmclk/pcm_fs); % Lowest FIR decimation to highest, prefer low FIR % decimation ratios for mfir_test = mfir_min:osr mcic_test = floor(osr/mfir_test); if (abs(pcm_fs*mfir_test*mcic_test -pdmclk) < 1) ... && (osr >= osr_min) ... && (mcic_test >= mcic_min) ... && (mcic_test <= mcic_max) ... && (mfir_test <= mfir_max) ... && (du_min >= prm.duty_min) ... && (du_max <= prm.duty_max) sfir = sprintf('FIR %d x %d x %dHz', ... mcic_test, mfir_test, round(pcm_fs)); %fprintf('Found: %s\n',sfir); clkdiv(n) = clkdiv_test; mcic(n) = mcic_test; mfir(n) = mfir_test; n=n+1; end end end end end %% Match found modes for common clkdiv and mcic, a DMIC hardware constraint function [common_clkdiv_list,common_mcic_list,a_mfir_list,b_mfir_list] = ... match_modes(a_clkdiv, a_mcic, a_mfir, b_clkdiv, b_mcic, b_mfir) if b_clkdiv == 0 common_clkdiv_list = a_clkdiv; common_mcic_list = a_mcic; a_mfir_list = a_mfir; b_mfir_list = 0; return; end common_clkdiv_list = []; common_mcic_list = []; a_mfir_list = []; b_mfir_list = []; n_list = 1; for n=1:length(a_clkdiv) idx = find(b_clkdiv == a_clkdiv(n)); for m=1:length(idx) if b_mcic(idx(m)) == a_mcic(n) common_clkdiv = a_clkdiv(n); common_mcic = a_mcic(n); common_clkdiv_list(n_list) = common_clkdiv; common_mcic_list(n_list) = common_mcic; a_mfir_list(n_list) = a_mfir(n); b_mfir_list(n_list) = b_mfir(idx(m)); fprintf('Option %d: div=%d, mcic=%d, mfira=%d, mfirb=%d\n', ... n_list, common_clkdiv, common_mcic, ... a_mfir_list(n_list), b_mfir_list(n_list)); n_list = n_list+1; end end end end %% Misc blob details needed function cfg = get_cfg(prm, hw) % if FIFO A or B is disabled, append a dummy blob to keep modes matching % code happy with identical A and B request if (prm.fifo_a_fs == 0) && (prm.fifo_b_fs == 0) fprintf('Error: FIFO A or B need to be assigned a nonzero samplerate\n'); return; end cfg.hw_version = hw.version; cfg.fifo_a_fs = prm.fifo_a_fs; cfg.fifo_b_fs = prm.fifo_b_fs; cfg.pdm01_provided = (prm.pdm(1).enable_mic_a | prm.pdm(1).enable_mic_b) ... + (prm.pdm(2).enable_mic_a | prm.pdm(2).enable_mic_b)*2; cfg.ch01_provided = (prm.fifo_a_fs > 0) + (prm.fifo_b_fs > 0)*2; if prm.fifo_a_fs > 0 cfg.a_nchannels = prm.pdm(1).enable_mic_a + prm.pdm(1).enable_mic_b ... + prm.pdm(2).enable_mic_a + prm.pdm(2).enable_mic_b; else cfg.a_nchannels = 0; end if prm.fifo_b_fs > 0 cfg.b_nchannels = prm.pdm(1).enable_mic_a + prm.pdm(1).enable_mic_b ... + prm.pdm(2).enable_mic_a + prm.pdm(2).enable_mic_b; else cfg.b_nchannels = 0; end end