aclocal.m4
Makefile
Makefile.in
+libtool
+ltmain.sh
+ltconfig
version
autom4te.cache
compile
ABOUT-NLS
*.ok
*.gmo
+*.la
+*.lo
*.o
*~
.deps
AC_PROG_MKDIR_P
AC_PROG_LN_S
AC_PROG_SED
+AM_PROG_LIBTOOL
PKG_PROG_PKG_CONFIG
AM_PATH_ALSA(1.2.5)
if test "x$enable_alsatest" = "xyes"; then
AC_SUBST(CURSES_CFLAGS)
test "x$prefix" = xNONE && prefix=$ac_default_prefix
+test "x$exec_prefix" = xNONE && exec_prefix=$prefix
eval dir="$datadir"
case "$dir" in
[ALSACTL_DAEMONSWITCH="/etc/alsa/state-daemon.conf"])
AC_SUBST(ALSACTL_DAEMONSWITCH)
+dnl pre-process plugin directory
+AC_ARG_WITH(plugindir,
+ AS_HELP_STRING([--with-plugindir=dir],
+ [path where pre-process plugin files are stored]),
+ plugindir="$withval", plugindir="")
+if test -z "$plugindir"; then
+ eval dir="$libdir"
+ echo ${exec_prefix}
+ echo $libdir
+ echo $dir
+ case "$dir" in
+ /*) ;;
+ *) dir="$dir"
+ esac
+ plugindir="$dir/alsa-topology"
+fi
+AC_DEFINE_UNQUOTED(ALSA_TOPOLOGY_PLUGIN_DIR, "$plugindir", [directory containing ALSA topology pre-process plugins])
+ALSA_TOPOLOGY_PLUGIN_DIR="$plugindir"
+AC_SUBST(ALSA_TOPOLOGY_PLUGIN_DIR)
+
AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
alsamixer/Makefile amidi/Makefile amixer/Makefile \
m4/Makefile po/Makefile.in \
alsaconf/alsaconf alsaconf/Makefile \
alsaconf/po/Makefile \
- alsaucm/Makefile topology/Makefile \
+ alsaucm/Makefile topology/Makefile topology/nhlt/Makefile \
bat/Makefile bat/tests/Makefile bat/tests/asound_state/Makefile \
aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \
utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \
echo "EXTRA_DIST = gettext.m4" > m4/Makefile.am
cp Makefile.am.ok Makefile.am
cp configure.ac.ok configure.ac
+touch ltconfig
+libtoolize --force --copy --automake
+aclocal $ACLOCAL_FLAGS
autoheader
automake --foreign --copy --add-missing
touch depcomp # for older automake
+SUBDIRS = nhlt
+
bin_PROGRAMS = \
alsatplg
noinst_HEADERS = topology.h pre-processor.h
AM_CPPFLAGS = \
- -Wall -I$(top_srcdir)/include
+ -Wall -I$(top_srcdir)/include -DALSA_TOPOLOGY_PLUGIN_DIR=\"@ALSA_TOPOLOGY_PLUGIN_DIR@\"
alsatplg_LDADD = $(ALSA_TOPOLOGY_LIBS)
--- /dev/null
+alsatplg_module_nhlt_LTLIBRARIES = libalsatplg_module_nhlt.la
+
+alsatplg_module_nhltdir = @ALSA_TOPOLOGY_PLUGIN_DIR@
+
+AM_CFLAGS = -Wall -fvisibility=hidden -I$(top_srcdir)/include -I$(top_srcdir)/topology
+AM_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined $(LDFLAGS_NOUNDEFINED)
+
+libalsatplg_module_nhlt_la_SOURCES = nhlt-processor.c \
+ intel/intel-nhlt.c \
+ intel/dmic-nhlt.c \
+ intel/dmic/dmic-debug.c intel/dmic/dmic-process.c \
+ intel/ssp-nhlt.c \
+ intel/ssp/ssp-debug.c intel/ssp/ssp-process.c
+
+libalsatplg_module_nhlt_la_LDFLAGS = -shared
+libalsatplg_module_nhlt_la_LIBADD = -lasound
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+#include "dmic-nhlt.h"
+#include "dmic/dmic-process.h"
+
+static int set_dmic_data(struct intel_nhlt_params *nhlt, snd_config_t *dai_cfg, snd_config_t *top)
+{
+ long unmute_ramp_time_ms = 0;
+ long fifo_word_length = 0;
+ long driver_version = 0;
+ long num_pdm_active = 0;
+ long sample_rate = 0;
+ long dai_index_t = 0;
+ long duty_min = 0;
+ long duty_max = 0;
+ long clk_min = 0;
+ long clk_max = 0;
+ long io_clk = 0;
+ int ret;
+
+ struct dai_values dmic_data[] = {
+ { "driver_version", SND_CONFIG_TYPE_INTEGER, NULL, &driver_version, NULL},
+ { "io_clk", SND_CONFIG_TYPE_INTEGER, NULL, &io_clk, NULL},
+ { "dai_index", SND_CONFIG_TYPE_INTEGER, NULL, &dai_index_t, NULL},
+ { "num_pdm_active", SND_CONFIG_TYPE_INTEGER, NULL, &num_pdm_active, NULL},
+ { "fifo_word_length", SND_CONFIG_TYPE_INTEGER, NULL, &fifo_word_length, NULL},
+ { "clk_min", SND_CONFIG_TYPE_INTEGER, NULL, &clk_min, NULL},
+ { "clk_max", SND_CONFIG_TYPE_INTEGER, NULL, &clk_max, NULL},
+ { "duty_min", SND_CONFIG_TYPE_INTEGER, NULL, &duty_min, NULL},
+ { "duty_max", SND_CONFIG_TYPE_INTEGER, NULL, &duty_max, NULL},
+ { "sample_rate", SND_CONFIG_TYPE_INTEGER, NULL, &sample_rate, NULL},
+ { "unmute_ramp_time_ms", SND_CONFIG_TYPE_INTEGER, NULL, &unmute_ramp_time_ms, NULL},
+ };
+
+ ret = find_set_values(&dmic_data[0], ARRAY_SIZE(dmic_data), dai_cfg, top, "Class.Dai.DMIC");
+ if (ret < 0)
+ return ret;
+
+ return dmic_set_params(nhlt, dai_index_t, driver_version, io_clk, num_pdm_active,
+ fifo_word_length, clk_min, clk_max, duty_min, duty_max, sample_rate,
+ unmute_ramp_time_ms);
+}
+
+static int set_pdm_data(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ long mic_a_enable = 0;
+ long mic_b_enable = 0;
+ long polarity_a = 0;
+ long polarity_b = 0;
+ long clk_edge = 0;
+ long ctrl_id = 0;
+ long skew = 0;
+ int ret;
+
+ struct dai_values dmic_pdm_data[] = {
+ { "mic_a_enable", SND_CONFIG_TYPE_INTEGER, NULL, &mic_a_enable, NULL},
+ { "mic_b_enable", SND_CONFIG_TYPE_INTEGER, NULL, &mic_b_enable, NULL},
+ { "polarity_a", SND_CONFIG_TYPE_INTEGER, NULL, &polarity_a, NULL},
+ { "polarity_b", SND_CONFIG_TYPE_INTEGER, NULL, &polarity_b, NULL},
+ { "clk_edge", SND_CONFIG_TYPE_INTEGER, NULL, &clk_edge, NULL},
+ { "ctrl_id", SND_CONFIG_TYPE_INTEGER, NULL, &ctrl_id, NULL},
+ { "skew", SND_CONFIG_TYPE_INTEGER, NULL, &skew, NULL},
+ };
+
+ ret = find_set_values(&dmic_pdm_data[0], ARRAY_SIZE(dmic_pdm_data), cfg, top,
+ "Class.Base.pdm_config");
+ if (ret < 0)
+ return ret;
+
+ return dmic_set_pdm_params(nhlt, ctrl_id, mic_a_enable, mic_b_enable, polarity_a,
+ polarity_b, clk_edge, skew);
+}
+
+static int set_mic_data(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ long sensitivity = 0;
+ long snr = 0;
+ int ret;
+
+ struct dai_values dmic_mic_data[] = {
+ { "snr", SND_CONFIG_TYPE_INTEGER, NULL, &snr, NULL},
+ { "sensitivity", SND_CONFIG_TYPE_INTEGER, NULL, &snr, NULL},
+ };
+
+ ret = find_set_values(&dmic_mic_data[0], ARRAY_SIZE(dmic_mic_data), cfg, top,
+ "Class.Base.mic_extension");
+ if (ret < 0)
+ return ret;
+
+ return dmic_set_ext_params(nhlt, snr, sensitivity);
+}
+
+static int set_vendor_mic_data(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ long speaker_position_distance = 0;
+ long horizontal_angle_begin = 0;
+ long horizontal_angle_end = 0;
+ long vertical_angle_begin = 0;
+ long vertical_angle_end = 0;
+ long frequency_high_band = 0;
+ long frequency_low_band = 0;
+ long horizontal_offset = 0;
+ long vertical_offset = 0;
+ long direction_angle = 0;
+ long elevation_angle = 0;
+ long mic_type = 0;
+ long location = 0;
+ long mic_id = 0;
+ int ret;
+
+ struct dai_values dmic_vendor_data[] = {
+ { "mic_id", SND_CONFIG_TYPE_INTEGER, NULL, &mic_id, NULL},
+ { "mic_type", SND_CONFIG_TYPE_INTEGER, NULL, &mic_type, NULL},
+ { "location", SND_CONFIG_TYPE_INTEGER, NULL, &location, NULL},
+ { "speaker_position_distance", SND_CONFIG_TYPE_INTEGER, NULL,
+ &speaker_position_distance, NULL},
+ { "horizontal_offset", SND_CONFIG_TYPE_INTEGER, NULL, &horizontal_offset, NULL},
+ { "vertical_offset", SND_CONFIG_TYPE_INTEGER, NULL, &vertical_offset, NULL},
+ { "frequency_low_band", SND_CONFIG_TYPE_INTEGER, NULL, &frequency_low_band, NULL},
+ { "frequency_high_band", SND_CONFIG_TYPE_INTEGER, NULL, &frequency_high_band, NULL},
+ { "direction_angle", SND_CONFIG_TYPE_INTEGER, NULL, &direction_angle, NULL},
+ { "elevation_angle", SND_CONFIG_TYPE_INTEGER, NULL, &elevation_angle, NULL},
+ { "vertical_angle_begin", SND_CONFIG_TYPE_INTEGER, NULL, &vertical_angle_begin,
+ NULL},
+ { "vertical_angle_end", SND_CONFIG_TYPE_INTEGER, NULL, &vertical_angle_end, NULL},
+ { "horizontal_angle_begin", SND_CONFIG_TYPE_INTEGER, NULL, &horizontal_angle_begin,
+ NULL},
+ { "horizontal_angle_end", SND_CONFIG_TYPE_INTEGER, NULL, &horizontal_angle_end,
+ NULL},
+ };
+
+ ret = find_set_values(&dmic_vendor_data[0], ARRAY_SIZE(dmic_vendor_data), cfg, top,
+ "Class.Base.vendor_mic_config");
+ if (ret < 0)
+ return ret;
+
+ return dmic_set_mic_params(nhlt, mic_id, mic_type, location, speaker_position_distance,
+ horizontal_offset, vertical_offset, frequency_low_band,
+ frequency_high_band, direction_angle, elevation_angle,
+ vertical_angle_begin, vertical_angle_end, horizontal_angle_begin,
+ horizontal_angle_end);
+}
+
+static int set_bytes_data(struct intel_nhlt_params *nhlt, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *bytes;
+ const char *id;
+
+ if (snd_config_get_id(cfg, &id) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "fir_coeffs"))
+ return 0;
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_string(n, &bytes))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* init dmic parameters, should be called before parsing dais */
+int nhlt_dmic_init_params(struct intel_nhlt_params *nhlt)
+{
+ return dmic_init_params(nhlt);
+}
+
+/* get dmic endpoint count */
+int nhlt_dmic_get_ep_count(struct intel_nhlt_params *nhlt)
+{
+ return dmic_get_vendor_blob_count(nhlt);
+}
+
+int nhlt_dmic_get_ep(struct intel_nhlt_params *nhlt, struct endpoint_descriptor **eps,
+ int index)
+{
+ struct endpoint_descriptor ep;
+ struct mic_array_device_specific_config mic_s_conf;
+ struct mic_array_device_specific_vendor_config mic_v_conf;
+ struct mic_snr_sensitivity_extension mic_ext;
+ struct mic_vendor_config mic_conf;
+ struct formats_config f_conf;
+ struct format_config f_conf1;
+ uint8_t *ep_target;
+ size_t blob_size;
+ int ret;
+ int i;
+
+ size_t mic_config_size;
+ uint32_t sample_rate;
+ uint16_t channel_count;
+ uint32_t bits_per_sample;
+ uint8_t array_type;
+ uint8_t extension;
+ uint8_t num_mics;
+ uint32_t snr;
+ uint32_t sensitivity;
+
+ uint8_t type;
+ uint8_t panel;
+ uint32_t speaker_position_distance;
+ uint32_t horizontal_offset;
+ uint32_t vertical_offset;
+ uint8_t frequency_low_band;
+ uint8_t frequency_high_band;
+ uint16_t direction_angle;
+ uint16_t elevation_angle;
+ uint16_t vertical_angle_begin;
+ uint16_t vertical_angle_end;
+ uint16_t horizontal_angle_begin;
+ uint16_t horizontal_angle_end;
+
+ /*
+ * nhlt dmic structure:
+ *
+ * endpoint_descriptor, sizeof(struct endpoint_descriptor)
+ *
+ * device_specific_config (mic), sizeof(mic_array_device_specific_config)
+ * or
+ * device_specific_config (mic), sizeof(mic_array_device_specific_vendor_config)
+ *
+ * formats_config (formats_count), sizeof(struct formats_config)
+ * format_config (waveex), sizeof(struct format_config)
+ * vendor_blob sizeof(vendor_blob)
+ */
+
+ /* dmic ep */
+ ep.link_type = NHLT_LINK_TYPE_PDM;
+ ep.instance_id = 0;
+ ep.vendor_id = NHLT_VENDOR_ID_INTEL;
+ ep.device_id = NHLT_DEVICE_ID_INTEL_PDM_DMIC;
+ ep.revision_id = 0;
+ ep.subsystem_id = 0;
+ ep.device_type = 0;
+ ep.direction = NHLT_ENDPOINT_DIRECTION_CAPTURE;
+ ep.virtualbus_id = 0;
+
+ ret = dmic_get_params(nhlt, index, &sample_rate, &channel_count, &bits_per_sample,
+ &array_type, &num_mics, &extension, &snr, &sensitivity);
+
+ if (ret) {
+ fprintf(stderr, "nhlt_dmic_get_ep: dmic_get_params failed\n");
+ return ret;
+ }
+
+ if (array_type == NHLT_MIC_ARRAY_TYPE_VENDOR_DEFINED) {
+ mic_v_conf.config.capabilities_size = 4 + num_mics *
+ sizeof(struct mic_vendor_config);
+ mic_v_conf.device_config.virtual_slot = 0; /* always 0 for dmic */
+ mic_v_conf.device_config.config_type = NHLT_DEVICE_CONFIG_TYPE_MICARRAY;
+ mic_v_conf.number_of_microphones = num_mics;
+ mic_v_conf.array_type_ex = array_type;
+ /* precense of extension struct is coded into lower 4 bits of array_type */
+ if (extension) {
+ mic_v_conf.array_type_ex = (array_type & ~0x0F) | (0x01 & 0x0F);
+ mic_v_conf.config.capabilities_size +=
+ sizeof(struct mic_snr_sensitivity_extension);
+ }
+ } else {
+ mic_s_conf.config.capabilities_size = 3;
+ mic_s_conf.device_config.virtual_slot = 0; /* always 0 for dmic */
+ mic_s_conf.device_config.config_type = NHLT_DEVICE_CONFIG_TYPE_MICARRAY;
+ mic_s_conf.array_type_ex = array_type;
+ /* presense of extension struct coded into lower 4 bits of array_type */
+ if (extension) {
+ mic_s_conf.array_type_ex = (array_type & ~0x0F) | (0x01 & 0x0F);
+ mic_s_conf.config.capabilities_size +=
+ sizeof(struct mic_snr_sensitivity_extension);
+ }
+ }
+
+ /* formats_config */
+ f_conf.formats_count = 1;
+
+ /* fill in wave format extensible types */
+ f_conf1.format.wFormatTag = 0xFFFE;
+ f_conf1.format.nSamplesPerSec = sample_rate;
+ f_conf1.format.nChannels = channel_count;
+ f_conf1.format.wBitsPerSample = bits_per_sample;
+ f_conf1.format.nBlockAlign = channel_count * bits_per_sample / 8;
+ f_conf1.format.nAvgBytesPerSec = f_conf1.format.nSamplesPerSec * f_conf1.format.nBlockAlign;
+
+ /* bytes after this value in this struct */
+ f_conf1.format.cbSize = 22;
+ /* actual bits in container */
+ f_conf1.format.wValidBitsPerSample = bits_per_sample;
+ /* channel map not used at this time */
+ f_conf1.format.dwChannelMask = 0;
+ /* WAVE_FORMAT_PCM guid (0x0001) ? */
+ f_conf1.format.SubFormat[0] = 0;
+ f_conf1.format.SubFormat[1] = 0;
+ f_conf1.format.SubFormat[2] = 0;
+ f_conf1.format.SubFormat[3] = 0;
+
+ ret = dmic_get_vendor_blob_size(nhlt, &blob_size);
+ if (ret) {
+ fprintf(stderr, "nhlt_dmic_get_ep: dmic_get_vendor_blob_size failed\n");
+ return ret;
+ }
+
+ f_conf1.vendor_blob.capabilities_size = blob_size;
+
+ if (array_type == NHLT_MIC_ARRAY_TYPE_VENDOR_DEFINED)
+ mic_config_size = sizeof(struct mic_array_device_specific_vendor_config) +
+ num_mics * sizeof(struct mic_vendor_config);
+ else
+ mic_config_size = sizeof(struct mic_array_device_specific_config);
+
+ if (extension)
+ mic_config_size = sizeof(struct mic_snr_sensitivity_extension);
+
+ ep.length = sizeof(struct endpoint_descriptor) +
+ mic_config_size +
+ sizeof(struct formats_config) +
+ sizeof(struct format_config) +
+ blob_size;
+
+ /* allocate the final variable length ep struct */
+ ep_target = calloc(ep.length, sizeof(uint8_t));
+ if (!ep_target)
+ return -ENOMEM;
+
+ *eps = (struct endpoint_descriptor *)ep_target;
+
+ /* copy all parsed sub arrays into the top level array */
+ memcpy(ep_target, &ep, sizeof(struct endpoint_descriptor));
+
+ ep_target += sizeof(struct endpoint_descriptor);
+
+ if (array_type == NHLT_MIC_ARRAY_TYPE_VENDOR_DEFINED) {
+ memcpy(ep_target, &mic_v_conf,
+ sizeof(struct mic_array_device_specific_vendor_config));
+ ep_target += sizeof(struct mic_array_device_specific_vendor_config);
+ for (i = 0; i < num_mics; i++) {
+ ret = dmic_get_mic_params(nhlt, i, &type,
+ &panel, &speaker_position_distance,
+ &horizontal_offset, &vertical_offset,
+ &frequency_low_band, &frequency_high_band,
+ &direction_angle, &elevation_angle,
+ &vertical_angle_begin, &vertical_angle_end,
+ &horizontal_angle_begin, &horizontal_angle_end);
+
+ if (ret) {
+ fprintf(stderr, "nhlt_dmic_get_ep: dmic_get_mic_params failed\n");
+ return ret;
+ }
+
+ mic_conf.type = type;
+ mic_conf.panel = panel;
+ mic_conf.speaker_position_distance = speaker_position_distance;
+ mic_conf.horizontal_offset = horizontal_offset;
+ mic_conf.vertical_offset = vertical_offset;
+ mic_conf.frequency_low_band = frequency_low_band;
+ mic_conf.frequency_high_band = frequency_high_band;
+ mic_conf.direction_angle = direction_angle;
+ mic_conf.elevation_angle = elevation_angle;
+ mic_conf.vertical_angle_begin = vertical_angle_begin;
+ mic_conf.vertical_angle_end = vertical_angle_end;
+ mic_conf.horizontal_angle_begin = horizontal_angle_begin;
+ mic_conf.horizontal_angle_end = horizontal_angle_end;
+
+ memcpy(ep_target, &mic_conf, sizeof(struct mic_vendor_config));
+ ep_target += sizeof(struct mic_vendor_config);
+ }
+ } else {
+ memcpy(ep_target, &mic_s_conf, sizeof(struct mic_array_device_specific_config));
+ ep_target += sizeof(struct mic_array_device_specific_config);
+ }
+
+ if (extension) {
+ mic_ext.snr = snr;
+ mic_ext.sensitivity = sensitivity;
+ memcpy(ep_target, &mic_ext, sizeof(struct mic_snr_sensitivity_extension));
+ ep_target += sizeof(struct mic_snr_sensitivity_extension);
+ }
+
+ memcpy(ep_target, &f_conf, sizeof(struct formats_config));
+ ep_target += sizeof(struct formats_config);
+
+ memcpy(ep_target, &f_conf1, sizeof(struct format_config));
+ ep_target += sizeof(struct format_config);
+
+ ret = dmic_get_vendor_blob(nhlt, ep_target);
+ if (ret) {
+ fprintf(stderr, "nhlt_dmic_get_ep: dmic_get_vendor_blob failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Set dmic parameters from topology for dmic coefficient calculation.
+ *
+ * Coefficients are recalculated in case of multiple DAIs in topology and might affect each other.
+ *
+ * You can see an example of topology v2 config of dmic below. In this example the default
+ * object parameters are spelled out for clarity. General parameters like clk_min are parsed with
+ * set_dmic_data and pdm object data with set_pdm_data. Number of pdm's can vary from 1 to 2. Values
+ * are saved into intermediate structs and the vendor specific blob is calculated at the end of
+ * parsing with dmic_calculate.
+ *
+ * DMIC."0" {
+ * name NoCodec-6
+ * id 6
+ * index 0
+ * driver_version 1
+ * io_clk 38400000
+ * clk_min 500000
+ * clk_max 4800000
+ * duty_min 40
+ * duty_max 60
+ * sample_rate 48000
+ * fifo_word_length 16
+ * unmute_ramp_time_ms 200
+ * num_pdm_active 2
+ *
+ * # PDM controller config
+ * Object.Base.pdm_config."0" {
+ * ctrl_id 0
+ * mic_a_enable 1
+ * mic_b_enable 1
+ * polarity_a 0
+ * polarity_b 0
+ * clk_edge 0
+ * skew 0
+ * }
+ * }
+ */
+int nhlt_dmic_set_params(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ snd_config_t *items;
+ int ret;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+
+ /* set basic dmic data */
+ ret = set_dmic_data(nhlt, cfg, top);
+ if (ret < 0)
+ return ret;
+
+ /* we need to have at least one pdm object */
+ ret = snd_config_search(cfg, "Object.Base.pdm_config", &items);
+ if (ret < 0)
+ return ret;
+
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ ret = set_pdm_data(nhlt, n, top);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* check for microphone parameter configuration */
+ ret = snd_config_search(cfg, "Object.Base.mic_extension", &items);
+ if (!ret) {
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ ret = set_mic_data(nhlt, n, top);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ /* check for microphone parameter configuration */
+ ret = snd_config_search(cfg, "Object.Base.vendor_mic_config", &items);
+ if (!ret) {
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ set_vendor_mic_data(nhlt, n, top);
+ }
+ }
+
+ /* check for optional filter coeffs */
+ ret = snd_config_search(cfg, "Object.Base.data", &items);
+ if (!ret) {
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ set_bytes_data(nhlt, n);
+ }
+ }
+
+ ret = dmic_calculate(nhlt);
+
+ return ret;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __DMIC_NHLT_H
+#define __DMIC_NHLT_H
+
+#include "intel-nhlt.h"
+#include "../nhlt.h"
+
+int nhlt_dmic_init_params(struct intel_nhlt_params *nhlt);
+int nhlt_dmic_set_params(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top);
+int nhlt_dmic_get_ep(struct intel_nhlt_params *nhlt, struct endpoint_descriptor **eps,
+ int index);
+int nhlt_dmic_get_ep_count(struct intel_nhlt_params *nhlt);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdio.h>
+#include <stdint.h>
+#include "dmic-debug.h"
+
+#ifdef NHLT_DEBUG
+
+/* print blob as bytes hex string like: 0x11,0xff,0xff,0xff etc. */
+void dmic_print_bytes_as_hex(uint8_t *src, size_t size)
+{
+ int i, j, lines, remain;
+
+ fprintf(stdout, "printing dmic vendor blob as bytes:\n");
+
+ lines = size / 8;
+ remain = size % 8;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < 8; j++) {
+ fprintf(stdout, "0x%02x,", *src);
+ src++;
+ }
+ fprintf(stdout, "\n");
+ }
+ for (i = 0; i < remain; i++) {
+ fprintf(stdout, "0x%02x,", *src);
+ src++;
+ }
+
+ fprintf(stdout, "\n");
+}
+
+/* print blob as 32 bit integer hex string like: 0xffffffff,0x00000010 etc. */
+void dmic_print_integers_as_hex(uint32_t *src, size_t size)
+{
+ int i, j, lines, remain;
+
+ fprintf(stdout, "printing dmic vendor blob as integers:\n");
+
+ lines = size / 8;
+ remain = size % 8;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < 8; j++) {
+ fprintf(stdout, "0x%08x,", *src);
+ src++;
+ }
+ fprintf(stdout, "\n");
+ }
+ for (i = 0; i < remain; i++) {
+ fprintf(stdout, "0x%08x,", *src);
+ src++;
+ }
+
+ fprintf(stdout, "\n");
+}
+
+void dmic_print_internal(struct intel_dmic_params *dmic)
+{
+ int i, j, line, lines, remain;
+
+ fprintf(stdout, "printing dmic nhlt internal data:\n");
+
+ /* top level struct */
+ fprintf(stdout, "gateway attributes: 0x%08x\n", dmic->dmic_blob.gateway_attributes);
+
+ fprintf(stdout, "ts_group: 0x%08x 0x%08x 0x%08x 0x%08x\n", dmic->dmic_blob.ts_group[0],
+ dmic->dmic_blob.ts_group[1], dmic->dmic_blob.ts_group[2],
+ dmic->dmic_blob.ts_group[3]);
+
+ fprintf(stdout, "clock_on_delay: 0x%08x\n", dmic->dmic_blob.clock_on_delay);
+
+ fprintf(stdout, "channel_ctrl_mask: 0x%08x\n", dmic->dmic_blob.channel_ctrl_mask);
+
+ fprintf(stdout, "chan_ctrl_cfg: 0x%08x 0x%08x\n", dmic->dmic_blob.chan_ctrl_cfg[0],
+ dmic->dmic_blob.chan_ctrl_cfg[1]);
+
+ fprintf(stdout, "channel_pdm_mask: 0x%08x\n", dmic->dmic_blob.channel_pdm_mask);
+
+ /* first pdm struct */
+ fprintf(stdout, "pdm_ctrl_cfg 0\n");
+ fprintf(stdout, "cic_control: 0x%08x\n", dmic->dmic_blob_pdm[0].cic_control);
+ fprintf(stdout, "cic_config: 0x%08x\n", dmic->dmic_blob_pdm[0].cic_config);
+ fprintf(stdout, "mic_control: 0x%08x\n", dmic->dmic_blob_pdm[0].mic_control);
+ fprintf(stdout, "pdmsm: 0x%08x\n", dmic->dmic_blob_pdm[0].pdmsm);
+ fprintf(stdout, "reuse_fir_from_pdm: 0x%08x\n", dmic->dmic_blob_pdm[0].reuse_fir_from_pdm);
+
+ /* first pdm struct, first fir */
+ fprintf(stdout, "fir_config 0\n");
+ fprintf(stdout, "fir_control: 0x%08x\n", dmic->dmic_blob_fir[0][0].fir_control);
+ fprintf(stdout, "fir_config: 0x%08x\n", dmic->dmic_blob_fir[0][0].fir_config);
+ fprintf(stdout, "dc_offset_left: 0x%08x\n", dmic->dmic_blob_fir[0][0].dc_offset_left);
+ fprintf(stdout, "dc_offset_right: 0x%08x\n", dmic->dmic_blob_fir[0][0].dc_offset_right);
+ fprintf(stdout, "out_gain_left: 0x%08x\n", dmic->dmic_blob_fir[0][0].out_gain_left);
+ fprintf(stdout, "out_gain_right: 0x%08x\n", dmic->dmic_blob_fir[0][0].out_gain_right);
+
+ /* first pdm struct, second fir */
+ fprintf(stdout, "fir_config 1\n");
+ fprintf(stdout, "fir_control: 0x%08x\n", dmic->dmic_blob_fir[0][1].fir_control);
+ fprintf(stdout, "fir_config: 0x%08x\n", dmic->dmic_blob_fir[0][1].fir_config);
+ fprintf(stdout, "dc_offset_left: 0x%08x\n", dmic->dmic_blob_fir[0][1].dc_offset_left);
+ fprintf(stdout, "dc_offset_right: 0x%08x\n", dmic->dmic_blob_fir[0][1].dc_offset_right);
+ fprintf(stdout, "out_gain_left: 0x%08x\n", dmic->dmic_blob_fir[0][1].out_gain_left);
+ fprintf(stdout, "out_gain_right: 0x%08x\n", dmic->dmic_blob_fir[0][1].out_gain_right);
+
+ /* first pdm struct, fir coeffs */
+ for (j = 0; j < DMIC_HW_CONTROLLERS; j++) {
+ fprintf(stdout, "fir_coeffs a length %u:\n", dmic->dmic_fir_array.fir_len[0]);
+ lines = dmic->dmic_fir_array.fir_len[0] / 8;
+ remain = dmic->dmic_fir_array.fir_len[0] % 8;
+ for (i = 0; i < lines; i++) {
+ line = i * 8;
+ fprintf(stdout, "%d %d %d %d %d %d %d %d %d\n", i,
+ dmic->dmic_fir_array.fir_coeffs[j][0][line],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 1],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 2],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 3],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 4],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 5],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 6],
+ dmic->dmic_fir_array.fir_coeffs[j][0][line + 7]);
+ }
+ line += 1;
+ for (i = 0; i < remain; i++)
+ fprintf(stdout, "%d ", dmic->dmic_fir_array.fir_coeffs[j][0][line + i]);
+ }
+
+ /* second pdm struct */
+ fprintf(stdout, "pdm_ctrl_cfg 1\n");
+ fprintf(stdout, "cic_control: 0x%08x\n", dmic->dmic_blob_pdm[1].cic_control);
+ fprintf(stdout, "cic_config: 0x%08x\n", dmic->dmic_blob_pdm[1].cic_config);
+ fprintf(stdout, "mic_control: 0x%08x\n", dmic->dmic_blob_pdm[1].mic_control);
+ fprintf(stdout, "pdmsm: 0x%08x\n", dmic->dmic_blob_pdm[1].pdmsm);
+ fprintf(stdout, "reuse_fir_from_pdm: 0x%08x\n", dmic->dmic_blob_pdm[1].reuse_fir_from_pdm);
+
+ /* second pdm struct, first fir */
+ fprintf(stdout, "fir_config 0\n");
+ fprintf(stdout, "fir_control: 0x%08x\n", dmic->dmic_blob_fir[1][0].fir_control);
+ fprintf(stdout, "fir_config: 0x%08x\n", dmic->dmic_blob_fir[1][0].fir_config);
+ fprintf(stdout, "dc_offset_left: 0x%08x\n", dmic->dmic_blob_fir[1][0].dc_offset_left);
+ fprintf(stdout, "dc_offset_right: 0x%08x\n", dmic->dmic_blob_fir[1][0].dc_offset_right);
+ fprintf(stdout, "out_gain_left: 0x%08x\n", dmic->dmic_blob_fir[1][0].out_gain_left);
+ fprintf(stdout, "out_gain_right: 0x%08x\n", dmic->dmic_blob_fir[1][0].out_gain_right);
+
+ /* second pdm struct, second fir */
+ fprintf(stdout, "fir_config 1\n");
+ fprintf(stdout, "fir_control: 0x%08x\n", dmic->dmic_blob_fir[1][1].fir_control);
+ fprintf(stdout, "fir_config: 0x%08x\n", dmic->dmic_blob_fir[1][1].fir_config);
+ fprintf(stdout, "dc_offset_left: 0x%08x\n", dmic->dmic_blob_fir[1][1].dc_offset_left);
+ fprintf(stdout, "dc_offset_right: 0x%08x\n", dmic->dmic_blob_fir[1][1].dc_offset_right);
+ fprintf(stdout, "out_gain_left: 0x%08x\n", dmic->dmic_blob_fir[1][1].out_gain_left);
+ fprintf(stdout, "out_gain_right: 0x%08x\n", dmic->dmic_blob_fir[1][1].out_gain_right);
+
+ for (j = 0; j < DMIC_HW_CONTROLLERS; j++) {
+ fprintf(stdout, "fir_coeffs b length %u:\n", dmic->dmic_fir_array.fir_len[1]);
+ lines = dmic->dmic_fir_array.fir_len[1] / 8;
+ remain = dmic->dmic_fir_array.fir_len[1] % 8;
+ for (i = 0; i < lines; i++) {
+ line = i * 8;
+ fprintf(stdout, "%d %d %d %d %d %d %d %d %d\n", i,
+ dmic->dmic_fir_array.fir_coeffs[j][1][line],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 1],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 2],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 3],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 4],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 5],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 6],
+ dmic->dmic_fir_array.fir_coeffs[j][1][line + 7]);
+ }
+ line += 1;
+ for (i = 0; i < remain; i++)
+ fprintf(stdout, "%d ", dmic->dmic_fir_array.fir_coeffs[j][1][line + i]);
+ }
+
+ fprintf(stdout, "\n");
+}
+
+#else /* NHLT_DEBUG */
+void dmic_print_bytes_as_hex(uint8_t *src, size_t size) {}
+void dmic_print_integers_as_hex(uint32_t *src, size_t size) {}
+void dmic_print_internal(struct intel_dmic_params *dmic) {}
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __DMIC_DEBUG_H
+#define __DMIC_DEBUG_H
+
+#include "dmic-internal.h"
+
+void dmic_print_bytes_as_hex(uint8_t *src, size_t size);
+void dmic_print_integers_as_hex(uint32_t *src, size_t size);
+void dmic_print_internal(struct intel_dmic_params *dmic);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __DMIC_INTEL_H
+#define __DMIC_INTEL_H
+
+#include <stdint.h>
+
+#define DMIC_TS_GROUP_SIZE 4
+
+/* structs for intel dmic nhlt vendor specific blob generation */
+struct dmic_intel_fir_config {
+ uint32_t fir_control;
+ uint32_t fir_config;
+ uint32_t dc_offset_left;
+ uint32_t dc_offset_right;
+ uint32_t out_gain_left;
+ uint32_t out_gain_right;
+ uint32_t reserved2[2];
+ uint32_t fir_coeffs[];
+} __attribute__((packed));
+
+struct dmic_intel_pdm_ctrl_cfg {
+ uint32_t cic_control;
+ uint32_t cic_config;
+ uint32_t reserved0;
+ uint32_t mic_control;
+ uint32_t pdmsm;
+ uint32_t reuse_fir_from_pdm;
+ uint32_t reserved1[2];
+ struct dmic_intel_fir_config fir_config[];
+} __attribute__((packed));
+
+struct dmic_intel_config_data {
+ uint32_t gateway_attributes;
+ uint32_t ts_group[DMIC_TS_GROUP_SIZE];
+ uint32_t clock_on_delay;
+ uint32_t channel_ctrl_mask;
+ uint32_t chan_ctrl_cfg[2];
+ uint32_t channel_pdm_mask;
+ struct dmic_intel_pdm_ctrl_cfg pdm_ctrl_cfg[];
+} __attribute__((packed));
+
+#endif /* __DMIC_INTEL_H */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __DMIC_MACROS_H
+#define __DMIC_MACROS_H
+
+#include "dmic-intel.h"
+
+#define DMIC_HW_CONTROLLERS 2
+#define DMIC_HW_FIFOS 2
+#define DMIC_HW_FIR_LENGTH_MAX 250
+
+/* Get max and min signed integer values for N bits word length */
+#define INT_MAX(N) ((int64_t)((1ULL << ((N) - 1)) - 1))
+
+/* Fractional multiplication with shift and round
+ * Note that the parameters px and py must be cast to (int64_t) if other type.
+ */
+#define Q_MULTSR_32X32(px, py, qx, qy, qp) \
+ ((((px) * (py) >> ((qx) + (qy) - (qp) - 1)) + 1) >> 1)
+
+/* Convert a float number to fractional Qnx.ny format. Note that there is no
+ * check for nx+ny number of bits to fit the word length of int. The parameter
+ * qy must be 31 or less.
+ */
+#define Q_CONVERT_FLOAT(f, qy) \
+ ((int32_t)(((const double)f) * ((int64_t)1 << (const int)qy) + 0.5))
+
+/* Saturation */
+#define SATP_INT32(x) (((x) > INT32_MAX) ? INT32_MAX : (x))
+
+#define DMIC_MAX_MODES 50
+#define DMIC_FIR_PIPELINE_OVERHEAD 5
+
+/* Minimum OSR is always applied for 48 kHz and less sample rates */
+#define DMIC_MIN_OSR 50
+
+/* These are used as guideline for configuring > 48 kHz sample rates. The
+ * minimum OSR can be relaxed down to 40 (use 3.84 MHz clock for 96 kHz).
+ */
+#define DMIC_HIGH_RATE_MIN_FS 64000
+#define DMIC_HIGH_RATE_OSR_MIN 40
+
+/* Used for scaling FIR coefficients for HW */
+#define DMIC_HW_FIR_COEF_MAX ((1 << (DMIC_HW_BITS_FIR_COEF - 1)) - 1)
+#define DMIC_HW_FIR_COEF_Q (DMIC_HW_BITS_FIR_COEF - 1)
+
+/* Internal precision in gains computation, e.g. Q4.28 in int32_t */
+#define DMIC_FIR_SCALE_Q 28
+
+/* Parameters used in modes computation */
+#define DMIC_HW_BITS_CIC 26
+#define DMIC_HW_BITS_FIR_COEF 20
+#define DMIC_HW_BITS_FIR_GAIN 20
+#define DMIC_HW_BITS_FIR_INPUT 22
+#define DMIC_HW_BITS_FIR_OUTPUT 24
+#define DMIC_HW_BITS_FIR_INTERNAL 26
+#define DMIC_HW_BITS_GAIN_OUTPUT 22
+#define DMIC_HW_CIC_SHIFT_MIN -8
+#define DMIC_HW_CIC_SHIFT_MAX 4
+#define DMIC_HW_FIR_SHIFT_MIN 0
+#define DMIC_HW_FIR_SHIFT_MAX 8
+#define DMIC_HW_CIC_DECIM_MIN 5
+#define DMIC_HW_CIC_DECIM_MAX 31 /* Note: Limited by BITS_CIC */
+#define DMIC_HW_FIR_DECIM_MIN 2
+#define DMIC_HW_FIR_DECIM_MAX 20 /* Note: Practical upper limit */
+#define DMIC_HW_SENS_Q28 Q_CONVERT_FLOAT(1.0, 28) /* Q1.28 */
+#define DMIC_HW_PDM_CLK_MIN 100000 /* Note: Practical min value */
+#define DMIC_HW_DUTY_MIN 20 /* Note: Practical min value */
+#define DMIC_HW_DUTY_MAX 80 /* Note: Practical max value */
+
+/* OUTCONTROL0 bits */
+#define OUTCONTROL0_TIE_BIT BIT(27)
+#define OUTCONTROL0_SIP_BIT BIT(26)
+#define OUTCONTROL0_FINIT_BIT BIT(25)
+#define OUTCONTROL0_FCI_BIT BIT(24)
+#define OUTCONTROL0_TIE(x) SET_BIT(27, x)
+#define OUTCONTROL0_SIP(x) SET_BIT(26, x)
+#define OUTCONTROL0_FINIT(x) SET_BIT(25, x)
+#define OUTCONTROL0_FCI(x) SET_BIT(24, x)
+#define OUTCONTROL0_BFTH(x) SET_BITS(23, 20, x)
+#define OUTCONTROL0_OF(x) SET_BITS(19, 18, x)
+#define OUTCONTROL0_TH(x) SET_BITS(5, 0, x)
+
+/* OUTCONTROL1 bits */
+#define OUTCONTROL1_TIE_BIT BIT(27)
+#define OUTCONTROL1_SIP_BIT BIT(26)
+#define OUTCONTROL1_FINIT_BIT BIT(25)
+#define OUTCONTROL1_FCI_BIT BIT(24)
+#define OUTCONTROL1_TIE(x) SET_BIT(27, x)
+#define OUTCONTROL1_SIP(x) SET_BIT(26, x)
+#define OUTCONTROL1_FINIT(x) SET_BIT(25, x)
+#define OUTCONTROL1_FCI(x) SET_BIT(24, x)
+#define OUTCONTROL1_BFTH(x) SET_BITS(23, 20, x)
+#define OUTCONTROL1_OF(x) SET_BITS(19, 18, x)
+#define OUTCONTROL1_TH(x) SET_BITS(5, 0, x)
+
+/* OUTCONTROL0 bits ver1*/
+#define OUTCONTROL0_IPM_VER1(x) SET_BITS(17, 16, x)
+/* OUTCONTROL1 bits ver1 */
+#define OUTCONTROL1_IPM_VER1(x) SET_BITS(17, 16, x)
+
+/* OUTCONTROL0 bits */
+#define OUTCONTROL0_IPM_VER2(x) SET_BITS(17, 15, x)
+#define OUTCONTROL0_IPM_SOURCE_1(x) SET_BITS(14, 13, x)
+#define OUTCONTROL0_IPM_SOURCE_2(x) SET_BITS(12, 11, x)
+#define OUTCONTROL0_IPM_SOURCE_3(x) SET_BITS(10, 9, x)
+#define OUTCONTROL0_IPM_SOURCE_4(x) SET_BITS(8, 7, x)
+#define OUTCONTROL0_IPM_SOURCE_MODE(x) SET_BIT(6, x)
+
+/* OUTCONTROL1 bits */
+#define OUTCONTROL1_IPM_VER2(x) SET_BITS(17, 15, x)
+#define OUTCONTROL1_IPM_SOURCE_1(x) SET_BITS(14, 13, x)
+#define OUTCONTROL1_IPM_SOURCE_2(x) SET_BITS(12, 11, x)
+#define OUTCONTROL1_IPM_SOURCE_3(x) SET_BITS(10, 9, x)
+#define OUTCONTROL1_IPM_SOURCE_4(x) SET_BITS(8, 7, x)
+#define OUTCONTROL1_IPM_SOURCE_MODE(x) SET_BIT(6, x)
+
+#define OUTCONTROLX_IPM_NUMSOURCES 4
+
+/* CIC_CONTROL bits */
+#define CIC_CONTROL_SOFT_RESET_BIT BIT(16)
+#define CIC_CONTROL_CIC_START_B_BIT BIT(15)
+#define CIC_CONTROL_CIC_START_A_BIT BIT(14)
+#define CIC_CONTROL_MIC_B_POLARITY_BIT BIT(3)
+#define CIC_CONTROL_MIC_A_POLARITY_BIT BIT(2)
+#define CIC_CONTROL_MIC_MUTE_BIT BIT(1)
+#define CIC_CONTROL_STEREO_MODE_BIT BIT(0)
+
+#define CIC_CONTROL_SOFT_RESET(x) SET_BIT(16, x)
+#define CIC_CONTROL_CIC_START_B(x) SET_BIT(15, x)
+#define CIC_CONTROL_CIC_START_A(x) SET_BIT(14, x)
+#define CIC_CONTROL_MIC_B_POLARITY(x) SET_BIT(3, x)
+#define CIC_CONTROL_MIC_A_POLARITY(x) SET_BIT(2, x)
+#define CIC_CONTROL_MIC_MUTE(x) SET_BIT(1, x)
+#define CIC_CONTROL_STEREO_MODE(x) SET_BIT(0, x)
+
+/* CIC_CONFIG bits */
+#define CIC_CONFIG_CIC_SHIFT(x) SET_BITS(27, 24, x)
+#define CIC_CONFIG_COMB_COUNT(x) SET_BITS(15, 8, x)
+
+/* CIC_CONFIG masks */
+#define CIC_CONFIG_CIC_SHIFT_MASK MASK(27, 24)
+#define CIC_CONFIG_COMB_COUNT_MASK MASK(15, 8)
+
+/* MIC_CONTROL bits */
+#define MIC_CONTROL_PDM_EN_B_BIT BIT(1)
+#define MIC_CONTROL_PDM_EN_A_BIT BIT(0)
+#define MIC_CONTROL_PDM_CLKDIV(x) SET_BITS(15, 8, x)
+#define MIC_CONTROL_PDM_SKEW(x) SET_BITS(7, 4, x)
+#define MIC_CONTROL_CLK_EDGE(x) SET_BIT(3, x)
+#define MIC_CONTROL_PDM_EN_B(x) SET_BIT(1, x)
+#define MIC_CONTROL_PDM_EN_A(x) SET_BIT(0, x)
+
+/* MIC_CONTROL masks */
+#define MIC_CONTROL_PDM_CLKDIV_MASK MASK(15, 8)
+
+/* FIR_CONTROL_A bits */
+#define FIR_CONTROL_A_START_BIT BIT(7)
+#define FIR_CONTROL_A_ARRAY_START_EN_BIT BIT(6)
+#define FIR_CONTROL_A_MUTE_BIT BIT(1)
+#define FIR_CONTROL_A_START(x) SET_BIT(7, x)
+#define FIR_CONTROL_A_ARRAY_START_EN(x) SET_BIT(6, x)
+#define FIR_CONTROL_A_DCCOMP(x) SET_BIT(4, x)
+#define FIR_CONTROL_A_MUTE(x) SET_BIT(1, x)
+#define FIR_CONTROL_A_STEREO(x) SET_BIT(0, x)
+
+/* FIR_CONFIG_A bits */
+#define FIR_CONFIG_A_FIR_DECIMATION(x) SET_BITS(20, 16, x)
+#define FIR_CONFIG_A_FIR_SHIFT(x) SET_BITS(11, 8, x)
+#define FIR_CONFIG_A_FIR_LENGTH(x) SET_BITS(7, 0, x)
+
+/* DC offset compensation time constants */
+#define DCCOMP_TC0 0
+#define DCCOMP_TC1 1
+#define DCCOMP_TC2 2
+#define DCCOMP_TC3 3
+#define DCCOMP_TC4 4
+#define DCCOMP_TC5 5
+#define DCCOMP_TC6 6
+#define DCCOMP_TC7 7
+
+/* DC_OFFSET_LEFT_A bits */
+#define DC_OFFSET_LEFT_A_DC_OFFS(x) SET_BITS(21, 0, x)
+
+/* DC_OFFSET_RIGHT_A bits */
+#define DC_OFFSET_RIGHT_A_DC_OFFS(x) SET_BITS(21, 0, x)
+
+/* OUT_GAIN_LEFT_A bits */
+#define OUT_GAIN_LEFT_A_GAIN(x) SET_BITS(19, 0, x)
+
+/* OUT_GAIN_RIGHT_A bits */
+#define OUT_GAIN_RIGHT_A_GAIN(x) SET_BITS(19, 0, x)
+
+/* FIR_CONTROL_B bits */
+#define FIR_CONTROL_B_START_BIT BIT(7)
+#define FIR_CONTROL_B_ARRAY_START_EN_BIT BIT(6)
+#define FIR_CONTROL_B_MUTE_BIT BIT(1)
+#define FIR_CONTROL_B_START(x) SET_BIT(7, x)
+#define FIR_CONTROL_B_ARRAY_START_EN(x) SET_BIT(6, x)
+#define FIR_CONTROL_B_DCCOMP(x) SET_BIT(4, x)
+#define FIR_CONTROL_B_MUTE(x) SET_BIT(1, x)
+#define FIR_CONTROL_B_STEREO(x) SET_BIT(0, x)
+
+/* FIR_CONFIG_B bits */
+#define FIR_CONFIG_B_FIR_DECIMATION(x) SET_BITS(20, 16, x)
+#define FIR_CONFIG_B_FIR_SHIFT(x) SET_BITS(11, 8, x)
+#define FIR_CONFIG_B_FIR_LENGTH(x) SET_BITS(7, 0, x)
+
+/* DC_OFFSET_LEFT_B bits */
+#define DC_OFFSET_LEFT_B_DC_OFFS(x) SET_BITS(21, 0, x)
+
+/* DC_OFFSET_RIGHT_B bits */
+#define DC_OFFSET_RIGHT_B_DC_OFFS(x) SET_BITS(21, 0, x)
+
+/* OUT_GAIN_LEFT_B bits */
+#define OUT_GAIN_LEFT_B_GAIN(x) SET_BITS(19, 0, x)
+
+/* OUT_GAIN_RIGHT_B bits */
+#define OUT_GAIN_RIGHT_B_GAIN(x) SET_BITS(19, 0, x)
+
+/* FIR coefficients */
+#define FIR_COEF_A(x) SET_BITS(19, 0, x)
+#define FIR_COEF_B(x) SET_BITS(19, 0, x)
+
+/* structs for dmic internal calculations */
+struct dmic_calc_decim_modes {
+ int16_t clkdiv[DMIC_MAX_MODES];
+ int16_t mcic[DMIC_MAX_MODES];
+ int16_t mfir[DMIC_MAX_MODES];
+ int num_of_modes;
+};
+
+struct dmic_calc_matched_modes {
+ int16_t clkdiv[DMIC_MAX_MODES];
+ int16_t mcic[DMIC_MAX_MODES];
+ int16_t mfir_a[DMIC_MAX_MODES];
+ int16_t mfir_b[DMIC_MAX_MODES];
+ int num_of_modes;
+};
+
+struct dmic_calc_configuration {
+ struct pdm_decim *fir_a;
+ struct pdm_decim *fir_b;
+ int clkdiv;
+ int mcic;
+ int mfir_a;
+ int mfir_b;
+ int cic_shift;
+ int fir_a_shift;
+ int fir_b_shift;
+ int fir_a_length;
+ int fir_b_length;
+ int32_t fir_a_scale;
+ int32_t fir_b_scale;
+};
+
+/* structs for gathering the parameters from topology */
+struct dmic_config_pdm {
+ uint16_t id;
+ uint16_t enable_mic_a;
+ uint16_t enable_mic_b;
+ uint16_t polarity_mic_a;
+ uint16_t polarity_mic_b;
+ uint16_t clk_edge;
+ uint16_t skew;
+};
+
+struct dmic_config_dai {
+ uint32_t driver_version;
+ uint32_t io_clk;
+ uint32_t pdmclk_min;
+ uint32_t pdmclk_max;
+ uint32_t fifo_fs;
+ uint16_t fifo_bits;
+ uint16_t fifo_bits_b;
+ uint16_t duty_min;
+ uint16_t duty_max;
+ uint32_t num_pdm_active;
+ uint32_t wake_up_time;
+ uint32_t min_clock_on_time;
+ uint32_t unmute_ramp_time;
+ struct dmic_config_pdm pdm[DMIC_HW_CONTROLLERS];
+};
+
+/* every pdm controller has separate fir filter for output fifos */
+struct dmic_calc_fir_coeffs_array {
+ uint32_t fir_len[DMIC_HW_CONTROLLERS];
+ int32_t fir_coeffs[DMIC_HW_CONTROLLERS][DMIC_HW_FIFOS][DMIC_HW_FIR_LENGTH_MAX];
+};
+
+struct dmic_config_mic_vendor {
+ uint8_t type;
+ uint8_t panel;
+ uint32_t speaker_position_distance;
+ uint32_t horizontal_offset;
+ uint32_t vertical_offset;
+ uint8_t frequency_low_band;
+ uint8_t frequency_high_band;
+ uint16_t direction_angle;
+ uint16_t elevation_angle;
+ uint16_t vertical_angle_begin;
+ uint16_t vertical_angle_end;
+ uint16_t horizontal_angle_begin;
+ uint16_t horizontal_angle_end;
+};
+
+struct dmic_config_mic {
+ uint8_t num_mics;
+ uint8_t extension;
+ int8_t array_type;
+ uint32_t snr;
+ uint32_t sensitivity;
+ struct dmic_config_mic_vendor vendor[8];
+};
+
+struct intel_dmic_params {
+ /* structs to gather dmic params before calculations */
+ struct dmic_config_dai dmic_prm[DMIC_HW_FIFOS];
+ uint32_t dmic_dai_index;
+ int dmic_count;
+
+ /* dmic vendor blob structs */
+ struct dmic_intel_config_data dmic_blob;
+ struct dmic_intel_pdm_ctrl_cfg dmic_blob_pdm[DMIC_HW_CONTROLLERS];
+ struct dmic_intel_fir_config dmic_blob_fir[DMIC_HW_CONTROLLERS][DMIC_HW_FIFOS];
+ struct dmic_calc_fir_coeffs_array dmic_fir_array;
+ struct dmic_config_mic dmic_mic_config;
+};
+
+#endif /* __DMIC_MACROS_H */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+#include "../intel-nhlt.h"
+#include "dmic-process.h"
+#include "dmic-internal.h"
+#include "pdm-decim-fir.h"
+#include "dmic-debug.h"
+
+/* Note 1: Higher spec filter must be before lower spec filter if there are multiple filters for a
+ * decimation factor. The first filter is skipped if the length is too much vs. overrun limit. If
+ * other order the better filter would be never selected.
+ *
+ * Note 2: The introduction order of FIR decimation factors is the selection preference order.
+ * The decimation factor 5 and 10 (2*5) cause a often less compatible output sample rate for CIC so
+ * they are not used if there other suitable nearby values.
+ *
+ * The naming scheme of coefficients set is:
+ * <type>_<decim factor>_<rel passband>_<rel stopband>_<ripple>_<attenuation>
+ */
+struct pdm_decim *fir_list[] = {
+ &pdm_decim_int32_02_4375_5100_010_095,
+ &pdm_decim_int32_02_4323_5100_010_095,
+ &pdm_decim_int32_03_4375_5100_010_095,
+ &pdm_decim_int32_04_4318_5100_010_095,
+ &pdm_decim_int32_06_4172_5100_010_095,
+ &pdm_decim_int32_05_4325_5100_010_095,
+ &pdm_decim_int32_08_4156_5301_010_090,
+ &pdm_decim_int32_12_4156_5345_010_090,
+ &pdm_decim_int32_10_4156_5345_010_090,
+ NULL, /* This marks the end of coefficients */
+};
+
+/* This is a divide function that returns ceil of the quotient. E.g. ceil_divide(9, 3) returns 3,
+ * ceil_divide(10, 3) returns 4.
+ */
+static int ceil_divide(int a, int b)
+{
+ int c;
+
+ c = a / b;
+
+ if (!((a ^ b) & (1U << ((sizeof(int) * 8) - 1))) && c * b != a)
+ c++;
+
+ return c;
+}
+
+/* This function searches from vec[] (of length vec_length) integer values of n. The indices to
+ * equal values is returned in idx[]. The function returns the number of found matches.
+ * The max_results should be set to 0 (or negative) or vec_length to get all matches. The
+ * max_result can be set to 1 to receive only the first match in ascending order. It avoids need for
+ * an array for idx.
+ */
+static int find_equal_int16(int16_t idx[], int16_t vec[], int n, int vec_length,
+ int max_results)
+{
+ int nresults = 0;
+ int i;
+
+ for (i = 0; i < vec_length; i++) {
+ if (vec[i] == n) {
+ idx[nresults++] = i;
+ if (nresults == max_results)
+ break;
+ }
+ }
+
+ return nresults;
+}
+
+/* Return the largest absolute value found in the vector. Note that smallest negative value need to
+ * be saturated to preset as int32_t.
+ */
+static int32_t find_max_abs_int32(int32_t vec[], int vec_length)
+{
+ int i;
+ int64_t amax = (vec[0] > 0) ? vec[0] : -vec[0];
+
+ for (i = 1; i < vec_length; i++) {
+ amax = (vec[i] > amax) ? vec[i] : amax;
+ amax = (-vec[i] > amax) ? -vec[i] : amax;
+ }
+
+ return SATP_INT32(amax); /* Amax is always a positive value */
+}
+
+/* Count the left shift amount to normalize a 32 bit signed integer value without causing overflow.
+ * Input value 0 will result to 31.
+ */
+static int norm_int32(int32_t val)
+{
+ int c = 0;
+
+ /* count number of bits c that val can be right-shifted arithmetically
+ * until there is -1 (if val is negative) or 0 (if val is positive)
+ * norm of val will be 31-c
+ */
+ for (; val != -1 && val != 0; c++)
+ val >>= 1;
+
+ return 31 - c;
+}
+
+/* This function returns a raw list of potential microphone clock and decimation modes for achieving
+ * requested sample rates. The search is constrained by decimation HW capabililies and setup
+ * parameters. The parameters such as microphone clock min/max and duty cycle requirements need be
+ * checked from used microphone component datasheet.
+ */
+static void find_modes(struct intel_dmic_params *dmic, struct dmic_calc_decim_modes *modes)
+{
+ int di = dmic->dmic_dai_index;
+ uint32_t fs = dmic->dmic_prm[di].fifo_fs;
+ int clkdiv_min;
+ int clkdiv_max;
+ int clkdiv;
+ int c1;
+ int du_min;
+ int du_max;
+ int pdmclk;
+ int osr;
+ int mfir;
+ int mcic;
+ int ioclk_test;
+ int osr_min = DMIC_MIN_OSR;
+ int j;
+ int i = 0;
+
+ /* Defaults, empty result */
+ modes->num_of_modes = 0;
+
+ /* The FIFO is not requested if sample rate is set to zero. Just return in such case with
+ * num_of_modes as zero.
+ */
+ if (fs == 0) {
+ fprintf(stderr, "find_modes(): fs not set\n");
+ return;
+ }
+
+ /* Override DMIC_MIN_OSR for very high sample rates, use as minimum the nominal clock for
+ * the high rates.
+ */
+ if (fs >= DMIC_HIGH_RATE_MIN_FS)
+ osr_min = DMIC_HIGH_RATE_OSR_MIN;
+
+ /* Check for sane pdm clock, min 100 kHz, max ioclk/2 */
+ if (dmic->dmic_prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN ||
+ dmic->dmic_prm[di].pdmclk_max > dmic->dmic_prm[di].io_clk / 2) {
+ fprintf(stderr, "find_modes(): pdm clock max not in range\n");
+ return;
+ }
+ if (dmic->dmic_prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN ||
+ dmic->dmic_prm[di].pdmclk_min > dmic->dmic_prm[di].pdmclk_max) {
+ fprintf(stderr, "find_modes(): pdm clock min not in range\n");
+ return;
+ }
+
+ /* Check for sane duty cycle */
+ if (dmic->dmic_prm[di].duty_min > dmic->dmic_prm[di].duty_max) {
+ fprintf(stderr, "find_modes(): duty cycle min > max\n");
+ return;
+ }
+ if (dmic->dmic_prm[di].duty_min < DMIC_HW_DUTY_MIN ||
+ dmic->dmic_prm[di].duty_min > DMIC_HW_DUTY_MAX) {
+ fprintf(stderr, "find_modes(): pdm clock min not in range\n");
+ return;
+ }
+ if (dmic->dmic_prm[di].duty_max < DMIC_HW_DUTY_MIN ||
+ dmic->dmic_prm[di].duty_max > DMIC_HW_DUTY_MAX) {
+ fprintf(stderr, "find_modes(): pdm clock max not in range\n");
+ return;
+ }
+
+ /* Min and max clock dividers */
+ clkdiv_min = ceil_divide(dmic->dmic_prm[di].io_clk, dmic->dmic_prm[di].pdmclk_max);
+ clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN);
+ clkdiv_max = dmic->dmic_prm[di].io_clk / dmic->dmic_prm[di].pdmclk_min;
+
+ /* Loop possible clock dividers and check based on resulting oversampling ratio that CIC and
+ * FIR decimation ratios are feasible. The ratios need to be integers. Also the mic clock
+ * duty cycle need to be within limits.
+ */
+ for (clkdiv = clkdiv_min; clkdiv <= clkdiv_max; clkdiv++) {
+ /* Calculate duty cycle for this clock divider. Note that odd dividers cause non-50%
+ * duty cycle.
+ */
+ c1 = clkdiv >> 1;
+ du_min = 100 * c1 / clkdiv;
+ du_max = 100 - du_min;
+
+ /* Calculate PDM clock rate and oversampling ratio. */
+ pdmclk = dmic->dmic_prm[di].io_clk / clkdiv;
+ osr = pdmclk / fs;
+
+ /* Check that OSR constraints is met and clock duty cycle does not exceed microphone
+ * specification. If exceed proceed to next clkdiv.
+ */
+ if (osr < osr_min || du_min < dmic->dmic_prm[di].duty_min ||
+ du_max > dmic->dmic_prm[di].duty_max)
+ continue;
+
+ /* Loop FIR decimation factors candidates. If the integer divided decimation factors
+ * and clock dividers as multiplied with sample rate match the IO clock rate the
+ * division was exact and such decimation mode is possible. Then check that CIC
+ * decimation constraints are met. The passed decimation modes are added to array.
+ */
+ for (j = 0; fir_list[j]; j++) {
+ mfir = fir_list[j]->decim_factor;
+
+ /* Skip if previous decimation factor was the same */
+ if (j > 1 && fir_list[j - 1]->decim_factor == mfir)
+ continue;
+
+ mcic = osr / mfir;
+ ioclk_test = fs * mfir * mcic * clkdiv;
+
+ if (ioclk_test == dmic->dmic_prm[di].io_clk &&
+ mcic >= DMIC_HW_CIC_DECIM_MIN &&
+ mcic <= DMIC_HW_CIC_DECIM_MAX &&
+ i < DMIC_MAX_MODES) {
+ modes->clkdiv[i] = clkdiv;
+ modes->mcic[i] = mcic;
+ modes->mfir[i] = mfir;
+ i++;
+ }
+ }
+ }
+
+ modes->num_of_modes = i;
+}
+
+/* The previous raw modes list contains sane configuration possibilities. When there is request for
+ * both FIFOs A and B operation this function returns list of compatible settings.
+ */
+static void match_modes(struct dmic_calc_matched_modes *c, struct dmic_calc_decim_modes *a,
+ struct dmic_calc_decim_modes *b)
+{
+ int16_t idx[DMIC_MAX_MODES];
+ int idx_length;
+ int i;
+ int n;
+ int m;
+
+ /* Check if previous search got results. */
+ c->num_of_modes = 0;
+ if (a->num_of_modes == 0 && b->num_of_modes == 0) {
+ /* Nothing to do */
+ return;
+ }
+
+ /* Ensure that num_of_modes is sane. */
+ if (a->num_of_modes > DMIC_MAX_MODES ||
+ b->num_of_modes > DMIC_MAX_MODES)
+ return;
+
+ /* Check for request only for FIFO A or B. In such case pass list for A or B as such. */
+ if (b->num_of_modes == 0) {
+ c->num_of_modes = a->num_of_modes;
+ for (i = 0; i < a->num_of_modes; i++) {
+ c->clkdiv[i] = a->clkdiv[i];
+ c->mcic[i] = a->mcic[i];
+ c->mfir_a[i] = a->mfir[i];
+ c->mfir_b[i] = 0; /* Mark FIR B as non-used */
+ }
+ return;
+ }
+
+ if (a->num_of_modes == 0) {
+ c->num_of_modes = b->num_of_modes;
+ for (i = 0; i < b->num_of_modes; i++) {
+ c->clkdiv[i] = b->clkdiv[i];
+ c->mcic[i] = b->mcic[i];
+ c->mfir_b[i] = b->mfir[i];
+ c->mfir_a[i] = 0; /* Mark FIR A as non-used */
+ }
+ return;
+ }
+
+ /* Merge a list of compatible modes */
+ i = 0;
+ for (n = 0; n < a->num_of_modes; n++) {
+ /* Find all indices of values a->clkdiv[n] in b->clkdiv[] */
+ idx_length = find_equal_int16(idx, b->clkdiv, a->clkdiv[n],
+ b->num_of_modes, 0);
+ for (m = 0; m < idx_length; m++) {
+ if (b->mcic[idx[m]] == a->mcic[n]) {
+ c->clkdiv[i] = a->clkdiv[n];
+ c->mcic[i] = a->mcic[n];
+ c->mfir_a[i] = a->mfir[n];
+ c->mfir_b[i] = b->mfir[idx[m]];
+ i++;
+ }
+ }
+ c->num_of_modes = i;
+ }
+}
+
+/* Finds a suitable FIR decimation filter from the included set */
+static struct pdm_decim *get_fir(struct intel_dmic_params *dmic,
+ struct dmic_calc_configuration *cfg, int mfir)
+{
+ int i = 0;
+ int fs;
+ int cic_fs;
+ int fir_max_length;
+ struct pdm_decim *fir = NULL;
+ int di = dmic->dmic_dai_index;
+
+ if (mfir <= 0)
+ return fir;
+
+ cic_fs = dmic->dmic_prm[di].io_clk / cfg->clkdiv / cfg->mcic;
+ fs = cic_fs / mfir;
+ /* FIR max. length depends on available cycles and coef RAM length. Exceeding this length
+ * sets HW overrun status and overwrite of other register.
+ */
+ fir_max_length = MIN(DMIC_HW_FIR_LENGTH_MAX,
+ dmic->dmic_prm[di].io_clk / fs / 2 -
+ DMIC_FIR_PIPELINE_OVERHEAD);
+
+ /* Loop until NULL */
+ while (fir_list[i]) {
+ if (fir_list[i]->decim_factor == mfir) {
+ if (fir_list[i]->length <= fir_max_length) {
+ /* Store pointer, break from loop to avoid a possible other mode
+ * with lower FIR length.
+ */
+ fir = fir_list[i];
+ break;
+ }
+ }
+ i++;
+ }
+
+ return fir;
+}
+
+/* Calculate scale and shift to use for FIR coefficients. Scale is applied before write to HW coef
+ * RAM. Shift will be programmed to HW register.
+ */
+static int fir_coef_scale(int32_t *fir_scale, int *fir_shift, int add_shift,
+ const int32_t coef[], int coef_length, int32_t gain)
+{
+ int32_t amax;
+ int32_t new_amax;
+ int32_t fir_gain;
+ int shift;
+
+ /* Multiply gain passed from CIC with output full scale. */
+ fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q28,
+ DMIC_FIR_SCALE_Q, 28, DMIC_FIR_SCALE_Q);
+
+ /* Find the largest FIR coefficient value. */
+ amax = find_max_abs_int32((int32_t *)coef, coef_length);
+
+ /* Scale max. tap value with FIR gain. */
+ new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31,
+ DMIC_FIR_SCALE_Q, DMIC_FIR_SCALE_Q);
+ if (new_amax <= 0)
+ return -EINVAL;
+
+ /* Get left shifts count to normalize the fractional value as 32 bit. We need right shifts
+ * count for scaling so need to invert. The difference of Q31 vs. used Q format is added to
+ * get the correct normalization right shift value.
+ */
+ shift = 31 - DMIC_FIR_SCALE_Q - norm_int32(new_amax);
+
+ /* Add to shift for coef raw Q31 format shift and store to configuration. Ensure range (fail
+ * should not happen with OK coefficient set).
+ */
+ *fir_shift = -shift + add_shift;
+ if (*fir_shift < DMIC_HW_FIR_SHIFT_MIN ||
+ *fir_shift > DMIC_HW_FIR_SHIFT_MAX)
+ return -EINVAL;
+
+ /* Compensate shift into FIR coef scaler and store as Q4.20. */
+ if (shift < 0)
+ *fir_scale = fir_gain << -shift;
+ else
+ *fir_scale = fir_gain >> shift;
+
+ return 0;
+}
+
+/* This function selects with a simple criteria one mode to set up the decimator. For the settings
+ * chosen for FIFOs A and B output a lookup is done for FIR coefficients from the included
+ * coefficients tables. For some decimation factors there may be several length coefficient sets. It
+ * is due to possible restruction of decimation engine cycles per given sample rate. If the
+ * coefficients length is exceeded the lookup continues. Therefore the list of coefficient set must
+ * present the filters for a decimation factor in decreasing length order.
+ *
+ * Note: If there is no filter available an error is returned. The parameters should be reviewed for
+ * such case. If still a filter is missing it should be added into the included set. FIR decimation
+ * with a high factor usually needs compromizes into specifications and is not desirable.
+ */
+static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configuration *cfg,
+ struct dmic_calc_matched_modes *modes)
+{
+ int32_t g_cic;
+ int32_t fir_in_max;
+ int32_t cic_out_max;
+ int32_t gain_to_fir;
+ int16_t idx[DMIC_MAX_MODES];
+ int16_t *mfir;
+ int mcic;
+ int bits_cic;
+ int ret;
+ int n;
+ int found = 0;
+
+ /* If there are more than one possibilities select a mode with a preferred FIR decimation
+ * factor. If there are several select mode with highest ioclk divider to minimize
+ * microphone power consumption. The highest clock divisors are in the end of list so select
+ * the last of list. The minimum OSR criteria used in previous ensures that quality in the
+ * candidates should be sufficient.
+ */
+ if (modes->num_of_modes == 0) {
+ fprintf(stderr, "select_mode(): no modes available\n");
+ return -EINVAL;
+ }
+
+ /* Valid modes presence is indicated with non-zero decimation factor in 1st element. If FIR
+ * A is not used get decimation factors from FIR B instead.
+ */
+ if (modes->mfir_a[0] > 0)
+ mfir = modes->mfir_a;
+ else
+ mfir = modes->mfir_b;
+
+ /* Search fir_list[] decimation factors from start towards end. The found last configuration
+ * entry with searched decimation factor will be used.
+ */
+ for (n = 0; fir_list[n]; n++) {
+ found = find_equal_int16(idx, mfir, fir_list[n]->decim_factor,
+ modes->num_of_modes, 0);
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ fprintf(stderr, "select_mode(): No filter for decimation found\n");
+ return -EINVAL;
+ }
+ n = idx[found - 1]; /* Option with highest clock divisor and lowest mic clock rate */
+
+ /* Get microphone clock and decimation parameters for used mode from the list. */
+ cfg->clkdiv = modes->clkdiv[n];
+ cfg->mfir_a = modes->mfir_a[n];
+ cfg->mfir_b = modes->mfir_b[n];
+ cfg->mcic = modes->mcic[n];
+ cfg->fir_a = NULL;
+ cfg->fir_b = NULL;
+
+ /* Find raw FIR coefficients to match the decimation factors of FIR A and B. */
+ if (cfg->mfir_a > 0) {
+ cfg->fir_a = get_fir(dmic, cfg, cfg->mfir_a);
+ if (!cfg->fir_a) {
+ fprintf(stderr, "select_mode(): can't find FIR coefficients, mfir_a = %d\n",
+ cfg->mfir_a);
+ return -EINVAL;
+ }
+ }
+
+ if (cfg->mfir_b > 0) {
+ cfg->fir_b = get_fir(dmic, cfg, cfg->mfir_b);
+ if (!cfg->fir_b) {
+ fprintf(stderr, "select_mode(): can't find FIR coefficients, mfir_b = %d\n",
+ cfg->mfir_b);
+ return -EINVAL;
+ }
+ }
+
+ /* Calculate CIC shift from the decimation factor specific gain. The gain of HW decimator
+ * equals decimation factor to power of 5.
+ */
+ mcic = cfg->mcic;
+ g_cic = mcic * mcic * mcic * mcic * mcic;
+ if (g_cic < 0) {
+ /* Erroneous decimation factor and CIC gain */
+ fprintf(stderr, "select_mode(): erroneous decimation factor and CIC gain\n");
+ return -EINVAL;
+ }
+
+ bits_cic = 32 - norm_int32(g_cic);
+ cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT;
+
+ /* Calculate remaining gain to FIR in Q format used for gain values. */
+ fir_in_max = INT_MAX(DMIC_HW_BITS_FIR_INPUT);
+ if (cfg->cic_shift >= 0)
+ cic_out_max = g_cic >> cfg->cic_shift;
+ else
+ cic_out_max = g_cic << -cfg->cic_shift;
+
+ gain_to_fir = (int32_t)((((int64_t)fir_in_max) << DMIC_FIR_SCALE_Q) /
+ cic_out_max);
+
+ /* Calculate FIR scale and shift */
+ if (cfg->mfir_a > 0) {
+ cfg->fir_a_length = cfg->fir_a->length;
+ ret = fir_coef_scale(&cfg->fir_a_scale, &cfg->fir_a_shift,
+ cfg->fir_a->shift, cfg->fir_a->coef,
+ cfg->fir_a->length, gain_to_fir);
+ if (ret < 0) {
+ /* Invalid coefficient set found, should not happen. */
+ fprintf(stderr, "select_mode(): invalid coefficient set found\n");
+ return -EINVAL;
+ }
+ } else {
+ cfg->fir_a_scale = 0;
+ cfg->fir_a_shift = 0;
+ cfg->fir_a_length = 0;
+ }
+
+ if (cfg->mfir_b > 0) {
+ cfg->fir_b_length = cfg->fir_b->length;
+ ret = fir_coef_scale(&cfg->fir_b_scale, &cfg->fir_b_shift,
+ cfg->fir_b->shift, cfg->fir_b->coef,
+ cfg->fir_b->length, gain_to_fir);
+ if (ret < 0) {
+ /* Invalid coefficient set found, should not happen. */
+ fprintf(stderr, "select_mode(): invalid coefficient set found\n");
+ return -EINVAL;
+ }
+ } else {
+ cfg->fir_b_scale = 0;
+ cfg->fir_b_shift = 0;
+ cfg->fir_b_length = 0;
+ }
+
+ return 0;
+}
+
+/* The FIFO input packer mode (IPM) settings are somewhat different in HW versions. This helper
+ * function returns a suitable IPM bit field value to use.
+ */
+static void ipm_helper1(struct intel_dmic_params *dmic, int *ipm)
+{
+ int di = dmic->dmic_dai_index;
+ int pdm[DMIC_HW_CONTROLLERS];
+ int i;
+
+ /* Loop number of PDM controllers in the configuration. If mic A or B is enabled then a pdm
+ * controller is marked as active for this DAI.
+ */
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ if (dmic->dmic_prm[di].pdm[i].enable_mic_a ||
+ dmic->dmic_prm[di].pdm[i].enable_mic_b)
+ pdm[i] = 1;
+ else
+ pdm[i] = 0;
+ }
+
+ /* Set IPM to match active pdm controllers. */
+ *ipm = 0;
+
+ if (pdm[0] == 0 && pdm[1] > 0)
+ *ipm = 1;
+
+ if (pdm[0] > 0 && pdm[1] > 0)
+ *ipm = 2;
+}
+
+static void ipm_helper2(struct intel_dmic_params *dmic, int source[], int *ipm)
+{
+ int di = dmic->dmic_dai_index;
+ int pdm[DMIC_HW_CONTROLLERS];
+ int i;
+ int n = 0;
+
+ for (i = 0; i < OUTCONTROLX_IPM_NUMSOURCES; i++)
+ source[i] = 0;
+
+ /* Loop number of PDM controllers in the configuration. If mic A or B is enabled then a pdm
+ * controller is marked as active. The function returns in array source[] the indice of
+ * enabled pdm controllers to be used for IPM configuration.
+ */
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ if (dmic->dmic_prm[di].pdm[i].enable_mic_a ||
+ dmic->dmic_prm[di].pdm[i].enable_mic_b) {
+ pdm[i] = 1;
+ source[n] = i;
+ n++;
+ } else {
+ pdm[i] = 0;
+ }
+ }
+
+ /* IPM bit field is set to count of active pdm controllers. */
+ *ipm = pdm[0];
+ for (i = 1; i < DMIC_HW_CONTROLLERS; i++)
+ *ipm += pdm[i];
+}
+
+/* Loop number of PDM controllers in the configuration. The function checks if the controller should
+ * operate as stereo or mono left (A) or mono right (B) mode. Mono right mode is setup as channel
+ * swapped mono left.
+ */
+static int stereo_helper(struct intel_dmic_params *dmic, int stereo[], int swap[])
+{
+ int cnt;
+ int i;
+ int swap_check;
+ int ret = 0;
+
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ cnt = 0;
+ if (dmic->dmic_prm[0].pdm[i].enable_mic_a ||
+ dmic->dmic_prm[1].pdm[i].enable_mic_a)
+ cnt++;
+
+ if (dmic->dmic_prm[0].pdm[i].enable_mic_b ||
+ dmic->dmic_prm[1].pdm[i].enable_mic_b)
+ cnt++;
+
+ /* Set stereo mode if both mic A anc B are enabled. */
+ cnt >>= 1;
+ stereo[i] = cnt;
+
+ /* Swap channels if only mic B is used for mono processing. */
+ swap[i] = (dmic->dmic_prm[0].pdm[i].enable_mic_b ||
+ dmic->dmic_prm[1].pdm[i].enable_mic_b) && !cnt;
+
+ /* Check that swap does not conflict with other DAI request */
+ swap_check = (dmic->dmic_prm[1].pdm[i].enable_mic_a ||
+ dmic->dmic_prm[0].pdm[i].enable_mic_a);
+
+ if (swap_check && swap[i]) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_configuration *cfg)
+{
+ int stereo[DMIC_HW_CONTROLLERS];
+ int swap[DMIC_HW_CONTROLLERS];
+ uint32_t val = 0;
+ int32_t ci;
+ uint32_t cu;
+ int ipm;
+ int of0;
+ int of1;
+ int fir_decim;
+ int fir_length;
+ int length;
+ int edge;
+ int soft_reset;
+ int cic_mute;
+ int fir_mute;
+ int i;
+ int j;
+ int ret;
+ int mic;
+ int chmap_bits;
+ int di = dmic->dmic_dai_index;
+ int dccomp = 1;
+ int array_a = 0;
+ int array_b = 0;
+ int bfth = 3; /* Should be 3 for 8 entries, 1 is 2 entries */
+ int th = 3; /* Used with TIE=1 */
+ int source[OUTCONTROLX_IPM_NUMSOURCES];
+
+ /*
+ * ts_group value describes which audio channels in the hw fifo are enabled. A 32 bit
+ * value is divided into 8 x 4 bit nibbles corresponding to 8 audio channels. Hex value 0xF
+ * means "not in use", any other value means the channel is enabled. For example 0xFFFFFFFF
+ * means no channels are enabled, 0xFFFFFF10 means channels 1 and 2 are enabled.
+ *
+ * ts_group array index corresponds to dmic hw fifos, that gather audio samples from pdm
+ * controllers. 1 pdm controller can host 2 mono dmics and usually pdm controllers are
+ * connected to 2 hw fifos -> we can for example run the dmics simultaneously with different
+ * sampling rates.
+ *
+ * Currently there is no evidence we would ever have more than 2 hw fifos, so ts_group[2]
+ * and ts_group[3] are not used for anything. Also the nibbles could be used for channel
+ * mapping the pdm channels arbitrarely into hw fifos, however currently it is used as
+ * binary not_enabled/enabled setting.
+ *
+ * if we have 2 dmics (stereo) it means we are using 1 pdm controller with possibly 2 hw
+ * fifos:
+ * mic1 fifo0(2ch)
+ * \ /
+ * pdm0
+ * / \
+ * mic2 fifo1(2ch)
+ *
+ * So in this case it makes only sense to control ts_group indexes 0 and 1 and their last 2
+ * nibbles (as we have only 2 channels).
+ *
+ * if we have 4 dmics, it means we are using 2 pdm controller with possibly 2 x 4 channel hw
+ * fifos:
+ *
+ * mic1 fifo0(4ch)
+ * \ / /
+ * pdm0 /
+ * / \ /
+ * mic2 \/
+ * mic3 /\
+ * \ / \
+ * pdm1 \
+ * / \ \
+ * mic4 fifo1(4ch)
+ *
+ * So it makes sense to control ts_group indexes 0 and 1 and their last 4 nibbles.
+ *
+ * channel_pdm_mask defines which existing pdm controllers will be taken into use. So if
+ * either of mic a or b is enabled -> that particular pdm controller is in use. For example
+ * pdm0 in use/not_in_use is defined by setting bit 0 in channel_pdm_mask to 1/0.
+ *
+ * channel_ctrl_mask defines what mic channels are available in hw for a pdm controller. in
+ * theory pdm controller could have only 1 channel enabled, in practice there's always 2
+ * channels which are both enabled -> set bits 0 and 1.
+ */
+
+ for (i = 0, mic = 0, chmap_bits = 4; i < DMIC_HW_CONTROLLERS; i++) {
+ /* enable fifo channels (ts_group) based on mic_enable in dai definition */
+ if (dmic->dmic_prm[di].pdm[i].enable_mic_a) {
+ dmic->dmic_blob.ts_group[di] &= ~(0xF << (chmap_bits * mic));
+ dmic->dmic_blob.ts_group[di] |= 0x0 << (chmap_bits * mic);
+ }
+ mic++;
+ if (dmic->dmic_prm[di].pdm[i].enable_mic_b) {
+ dmic->dmic_blob.ts_group[di] &= ~(0xF << (chmap_bits * mic));
+ dmic->dmic_blob.ts_group[di] |= 0x1 << (chmap_bits * mic);
+ }
+ mic++;
+ }
+
+ /* set channel_pdm_mask to describe what pdm controllers are in use */
+ for (i = 0; i < dmic->dmic_prm[di].num_pdm_active; i++)
+ dmic->dmic_blob.channel_pdm_mask |= 1 << i;
+
+ /* set always both mic channels enabled */
+ dmic->dmic_blob.channel_ctrl_mask = 0x3;
+
+ /* Normal start sequence */
+ soft_reset = 0;
+ cic_mute = 0;
+ fir_mute = 0;
+
+ /* OUTCONTROL0 and OUTCONTROL1 */
+ of0 = (dmic->dmic_prm[0].fifo_bits == 32) ? 2 : 0;
+ of1 = (dmic->dmic_prm[1].fifo_bits == 32) ? 2 : 0;
+
+ if (dmic->dmic_prm[di].driver_version == 1) {
+ if (di == 0) {
+ ipm_helper1(dmic, &ipm);
+ val = OUTCONTROL0_TIE(0) |
+ OUTCONTROL0_SIP(0) |
+ OUTCONTROL0_FINIT(0) |
+ OUTCONTROL0_FCI(0) |
+ OUTCONTROL0_BFTH(bfth) |
+ OUTCONTROL0_OF(of0) |
+ OUTCONTROL0_IPM_VER1(ipm) |
+ OUTCONTROL0_TH(th);
+ } else {
+ ipm_helper1(dmic, &ipm);
+ val = OUTCONTROL1_TIE(0) |
+ OUTCONTROL1_SIP(0) |
+ OUTCONTROL1_FINIT(0) |
+ OUTCONTROL1_FCI(0) |
+ OUTCONTROL1_BFTH(bfth) |
+ OUTCONTROL1_OF(of1) |
+ OUTCONTROL1_IPM_VER1(ipm) |
+ OUTCONTROL1_TH(th);
+ }
+ }
+
+ if (dmic->dmic_prm[di].driver_version == 2 || dmic->dmic_prm[di].driver_version == 3) {
+ if (di == 0) {
+ ipm_helper2(dmic, source, &ipm);
+ val = OUTCONTROL0_TIE(0) |
+ OUTCONTROL0_SIP(0) |
+ OUTCONTROL0_FINIT(0) |
+ OUTCONTROL0_FCI(0) |
+ OUTCONTROL0_BFTH(bfth) |
+ OUTCONTROL0_OF(of0) |
+ OUTCONTROL0_IPM_VER2(ipm) |
+ OUTCONTROL0_IPM_SOURCE_1(source[0]) |
+ OUTCONTROL0_IPM_SOURCE_2(source[1]) |
+ OUTCONTROL0_IPM_SOURCE_3(source[2]) |
+ OUTCONTROL0_IPM_SOURCE_4(source[3]) |
+ OUTCONTROL0_IPM_SOURCE_MODE(1) |
+ OUTCONTROL0_TH(th);
+ } else {
+ ipm_helper2(dmic, source, &ipm);
+ val = OUTCONTROL1_TIE(0) |
+ OUTCONTROL1_SIP(0) |
+ OUTCONTROL1_FINIT(0) |
+ OUTCONTROL1_FCI(0) |
+ OUTCONTROL1_BFTH(bfth) |
+ OUTCONTROL1_OF(of1) |
+ OUTCONTROL1_IPM_VER2(ipm) |
+ OUTCONTROL1_IPM_SOURCE_1(source[0]) |
+ OUTCONTROL1_IPM_SOURCE_2(source[1]) |
+ OUTCONTROL1_IPM_SOURCE_3(source[2]) |
+ OUTCONTROL1_IPM_SOURCE_4(source[3]) |
+ OUTCONTROL1_IPM_SOURCE_MODE(1) |
+ OUTCONTROL1_TH(th);
+ }
+ }
+
+ dmic->dmic_blob.chan_ctrl_cfg[di] = val;
+
+ ret = stereo_helper(dmic, stereo, swap);
+ if (ret < 0) {
+ fprintf(stderr, "configure_registers(): enable conflict\n");
+ return ret;
+ }
+
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ /* CIC */
+ val = CIC_CONTROL_SOFT_RESET(soft_reset) |
+ CIC_CONTROL_CIC_START_B(1) |
+ CIC_CONTROL_CIC_START_A(1) |
+ CIC_CONTROL_MIC_B_POLARITY(dmic->dmic_prm[di].pdm[i].polarity_mic_b) |
+ CIC_CONTROL_MIC_A_POLARITY(dmic->dmic_prm[di].pdm[i].polarity_mic_a) |
+ CIC_CONTROL_MIC_MUTE(cic_mute) |
+ CIC_CONTROL_STEREO_MODE(stereo[i]);
+ dmic->dmic_blob_pdm[i].cic_control = val;
+
+ val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) |
+ CIC_CONFIG_COMB_COUNT(cfg->mcic - 1);
+ dmic->dmic_blob_pdm[i].cic_config = val;
+
+ /* Mono right channel mic usage requires swap of PDM channels
+ * since the mono decimation is done with only left channel
+ * processing active.
+ */
+ edge = dmic->dmic_prm[di].pdm[i].clk_edge;
+ if (swap[i])
+ edge = !edge;
+
+ val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) |
+ MIC_CONTROL_PDM_SKEW(dmic->dmic_prm[di].pdm[i].skew) |
+ MIC_CONTROL_CLK_EDGE(edge) |
+ MIC_CONTROL_PDM_EN_B(1) |
+ MIC_CONTROL_PDM_EN_A(1);
+ dmic->dmic_blob_pdm[i].mic_control = val;
+
+ /* if cfg->mfir_a */
+ if (di == 0) {
+ /* FIR A */
+ fir_decim = MAX(cfg->mfir_a - 1, 0);
+ fir_length = MAX(cfg->fir_a_length - 1, 0);
+ val = FIR_CONTROL_A_START(1) |
+ FIR_CONTROL_A_ARRAY_START_EN(array_a) |
+ FIR_CONTROL_A_DCCOMP(dccomp) |
+ FIR_CONTROL_A_MUTE(fir_mute) |
+ FIR_CONTROL_A_STEREO(stereo[i]);
+ dmic->dmic_blob_fir[i][di].fir_control = val;
+
+ val = FIR_CONFIG_A_FIR_DECIMATION(fir_decim) |
+ FIR_CONFIG_A_FIR_SHIFT(cfg->fir_a_shift) |
+ FIR_CONFIG_A_FIR_LENGTH(fir_length);
+ dmic->dmic_blob_fir[i][di].fir_config = val;
+
+ val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0);
+ dmic->dmic_blob_fir[i][di].dc_offset_left = val;
+
+ val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0);
+ dmic->dmic_blob_fir[i][di].dc_offset_right = val;
+
+ val = OUT_GAIN_LEFT_A_GAIN(0);
+ dmic->dmic_blob_fir[i][di].out_gain_left = val;
+
+ val = OUT_GAIN_RIGHT_A_GAIN(0);
+ dmic->dmic_blob_fir[i][di].out_gain_right = val;
+
+ /* Write coef RAM A with scaled coefficient in reverse order */
+ length = cfg->fir_a_length;
+ for (j = 0; j < length; j++) {
+ ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_a->coef[j],
+ cfg->fir_a_scale, 31,
+ DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q);
+ cu = FIR_COEF_A(ci);
+ /* blob_pdm[i].fir_coeffs[di][j] = cu; */
+ dmic->dmic_fir_array.fir_coeffs[i][di][j] = cu;
+ }
+ dmic->dmic_fir_array.fir_len[0] = length;
+ dmic->dmic_fir_array.fir_len[1] = 0;
+ }
+
+ if (di == 1) {
+ /* FIR B */
+ fir_decim = MAX(cfg->mfir_b - 1, 0);
+ fir_length = MAX(cfg->fir_b_length - 1, 0);
+ val = FIR_CONTROL_B_START(1) |
+ FIR_CONTROL_B_ARRAY_START_EN(array_b) |
+ FIR_CONTROL_B_DCCOMP(dccomp) |
+ FIR_CONTROL_B_MUTE(fir_mute) |
+ FIR_CONTROL_B_STEREO(stereo[i]);
+ dmic->dmic_blob_fir[i][di].fir_control = val;
+
+ val = FIR_CONFIG_B_FIR_DECIMATION(fir_decim) |
+ FIR_CONFIG_B_FIR_SHIFT(cfg->fir_b_shift) |
+ FIR_CONFIG_B_FIR_LENGTH(fir_length);
+ dmic->dmic_blob_fir[i][di].fir_config = val;
+ val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0);
+ dmic->dmic_blob_fir[i][di].dc_offset_left = val;
+
+ val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0);
+ dmic->dmic_blob_fir[i][di].dc_offset_right = val;
+
+ val = OUT_GAIN_LEFT_B_GAIN(0);
+ dmic->dmic_blob_fir[i][di].out_gain_left = val;
+
+ val = OUT_GAIN_RIGHT_B_GAIN(0);
+ dmic->dmic_blob_fir[i][di].out_gain_right = val;
+
+ /* Write coef RAM B with scaled coefficient in reverse order */
+ length = cfg->fir_b_length;
+ for (j = 0; j < length; j++) {
+ ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_b->coef[j],
+ cfg->fir_b_scale, 31,
+ DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q);
+ cu = FIR_COEF_B(ci);
+ /* blob_pdm[i].fir_coeffs[di][j] = cu; */
+ dmic->dmic_fir_array.fir_coeffs[i][di][j] = cu;
+ }
+ dmic->dmic_fir_array.fir_len[1] = length;
+ }
+ }
+
+ return 0;
+}
+
+/* The decimation for PDM (pulse density modulation) stream is done in a programmable HW filter
+ * engine. The input to configuration algorithm is needed sample rate, channels/enabled microphones,
+ * microphone clock range, microphone clock duty cycle range, and system clock rate.
+ *
+ * The PDM bus clock divider, CIC and FIR decimation ratios are searched and configuration for
+ * optimal power consumption, filtering requirements, and HW constraints is chosen. The FIR filter
+ * for the chosen decimation is looked up from table and scaled to match the other decimation path
+ * sensitivity.
+ */
+int dmic_calculate(struct intel_nhlt_params *nhlt)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+ struct dmic_calc_matched_modes modes_ab;
+ struct dmic_calc_decim_modes modes_a;
+ struct dmic_calc_decim_modes modes_b;
+ struct dmic_calc_configuration cfg;
+ int ret = 0;
+ int di;
+
+ if (!dmic)
+ return -EINVAL;
+
+ di = dmic->dmic_dai_index;
+
+ if (di >= DMIC_HW_FIFOS) {
+ fprintf(stderr, "dmic_set_config(): dai->index exceeds number of FIFOs\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (dmic->dmic_prm[di].num_pdm_active > DMIC_HW_CONTROLLERS) {
+ fprintf(stderr, "dmic_set_config():controller count exceeds platform capability\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* fifo bits 0 means fifo disabled */
+ switch (dmic->dmic_prm[di].fifo_bits) {
+ case 0:
+ case 16:
+ case 32:
+ break;
+ default:
+ fprintf(stderr, "dmic_set_config(): fifo_bits EINVAL\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Match and select optimal decimators configuration for FIFOs A and B paths. This setup
+ * phase is still abstract. Successful completion points struct cfg to FIR coefficients and
+ * contains the scale value to use for FIR coefficient RAM write as well as the CIC and FIR
+ * shift values.
+ */
+ find_modes(dmic, &modes_a);
+ if (modes_a.num_of_modes == 0 && dmic->dmic_prm[0].fifo_fs > 0) {
+ fprintf(stderr, "dmic_set_config(): No modes found for FIFO A\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ find_modes(dmic, &modes_b);
+ if (modes_b.num_of_modes == 0 && dmic->dmic_prm[1].fifo_fs > 0) {
+ fprintf(stderr, "dmic_set_config(): No modes found for FIFO B\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ match_modes(&modes_ab, &modes_a, &modes_b);
+ ret = select_mode(dmic, &cfg, &modes_ab);
+ if (ret < 0) {
+ fprintf(stderr, "dmic_set_config(): select_mode() failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Struct reg contains a mirror of actual HW registers. Determine register bits
+ * configuration from decimator configuration and the requested parameters.
+ */
+ ret = configure_registers(dmic, &cfg);
+ if (ret < 0) {
+ fprintf(stderr, "dmic_set_config(): cannot configure registers\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dmic_print_internal(dmic);
+
+ dmic->dmic_count++;
+
+out:
+ return ret;
+}
+
+int dmic_get_params(struct intel_nhlt_params *nhlt, int index, uint32_t *sample_rate,
+ uint16_t *channel_count, uint32_t *bits_per_sample, uint8_t *array_type,
+ uint8_t *num_mics, uint8_t *extension, uint32_t *snr, uint32_t *sensitivity)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+ uint32_t channels = 0;
+
+ if (!dmic)
+ return -EINVAL;
+
+ /* check all pdm's for enabled mics */
+ *channel_count = 0;
+ if (dmic->dmic_prm[index].pdm[0].enable_mic_a)
+ channels++;
+
+ if (dmic->dmic_prm[index].pdm[0].enable_mic_b)
+ channels++;
+
+ if (dmic->dmic_prm[index].pdm[1].enable_mic_a)
+ channels++;
+
+ if (dmic->dmic_prm[index].pdm[1].enable_mic_b)
+ channels++;
+
+ *sample_rate = dmic->dmic_prm[index].fifo_fs;
+ *channel_count = channels;
+ *bits_per_sample = dmic->dmic_prm[index].fifo_bits;
+ *num_mics = dmic->dmic_mic_config.num_mics;
+ *extension = dmic->dmic_mic_config.extension;
+ *array_type = dmic->dmic_mic_config.array_type;
+ *snr = dmic->dmic_mic_config.snr;
+ *sensitivity = dmic->dmic_mic_config.sensitivity;
+
+ return 0;
+}
+
+int dmic_get_mic_params(struct intel_nhlt_params *nhlt, int index,
+ uint8_t *type, uint8_t *panel, uint32_t *speaker_position_distance,
+ uint32_t *horizontal_offset, uint32_t *vertical_offset,
+ uint8_t *frequency_low_band, uint8_t *frequency_high_band,
+ uint16_t *direction_angle, uint16_t *elevation_angle,
+ uint16_t *vertical_angle_begin, uint16_t *vertical_angle_end,
+ uint16_t *horizontal_angle_begin, uint16_t *horizontal_angle_end)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+
+ if (!dmic)
+ return -EINVAL;
+
+ *type = dmic->dmic_mic_config.vendor[index].type;
+ *panel = dmic->dmic_mic_config.vendor[index].panel;
+ *speaker_position_distance = dmic->dmic_mic_config.vendor[index].speaker_position_distance;
+ *horizontal_offset = dmic->dmic_mic_config.vendor[index].horizontal_offset;
+ *vertical_offset = dmic->dmic_mic_config.vendor[index].vertical_offset;
+ *frequency_low_band = dmic->dmic_mic_config.vendor[index].frequency_low_band;
+ *frequency_high_band = dmic->dmic_mic_config.vendor[index].frequency_high_band;
+ *direction_angle = dmic->dmic_mic_config.vendor[index].direction_angle;
+ *elevation_angle = dmic->dmic_mic_config.vendor[index].elevation_angle;
+ *vertical_angle_begin = dmic->dmic_mic_config.vendor[index].vertical_angle_begin;
+ *vertical_angle_end = dmic->dmic_mic_config.vendor[index].vertical_angle_end;
+ *horizontal_angle_begin = dmic->dmic_mic_config.vendor[index].horizontal_angle_begin;
+ *horizontal_angle_end = dmic->dmic_mic_config.vendor[index].horizontal_angle_end;
+
+ return 0;
+}
+
+int dmic_get_vendor_blob_size(struct intel_nhlt_params *nhlt, size_t *size)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+ int i, fir_index_0, fir_index_1;
+
+ if (!dmic || !dmic->dmic_count)
+ return -EINVAL;
+
+ *size = sizeof(struct dmic_intel_config_data);
+
+ /* if either of the fir is 0 length, copy the existing fir twice */
+ fir_index_0 = 0;
+ fir_index_1 = 1;
+ if (dmic->dmic_fir_array.fir_len[0] == 0) {
+ fir_index_0 = 1;
+ fir_index_1 = 1;
+ }
+ if (dmic->dmic_fir_array.fir_len[1] == 0) {
+ fir_index_0 = 0;
+ fir_index_1 = 0;
+ }
+
+ /* variable amount of pdm's */
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ /* only copy the pdm data if it is enabled */
+ if ((dmic->dmic_blob.channel_pdm_mask & BIT(i)) == 0)
+ continue;
+
+ *size += sizeof(struct dmic_intel_pdm_ctrl_cfg);
+ *size += sizeof(struct dmic_intel_fir_config) * DMIC_HW_FIFOS;
+
+ *size += dmic->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t);
+ *size += dmic->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t);
+ }
+
+ return 0;
+}
+
+int dmic_get_vendor_blob_count(struct intel_nhlt_params *nhlt)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+
+ if (!dmic || !dmic->dmic_count)
+ return 0;
+
+ return dmic->dmic_count;
+}
+
+int dmic_get_vendor_blob(struct intel_nhlt_params *nhlt, uint8_t *vendor_blob)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+ int i, fir_index_0, fir_index_1;
+ uint8_t *orig_blob = vendor_blob;
+ size_t blob_size;
+
+ if (!dmic || !dmic->dmic_count)
+ return -EINVAL;
+
+ /* top level struct */
+ memcpy(vendor_blob, &dmic->dmic_blob, sizeof(struct dmic_intel_config_data));
+ vendor_blob += sizeof(struct dmic_intel_config_data);
+
+ /* if either of the fir is 0 length, copy the existing fir twice */
+ fir_index_0 = 0;
+ fir_index_1 = 1;
+ if (dmic->dmic_fir_array.fir_len[0] == 0) {
+ fir_index_0 = 1;
+ fir_index_1 = 1;
+ }
+ if (dmic->dmic_fir_array.fir_len[1] == 0) {
+ fir_index_0 = 0;
+ fir_index_1 = 0;
+ }
+
+ /* variable amount of pdm's */
+ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
+ /* only copy the pdm data if it is enabled */
+ if ((dmic->dmic_blob.channel_pdm_mask & BIT(i)) == 0)
+ continue;
+
+ /* top level struct first pdm data */
+ memcpy(vendor_blob, (uint8_t *)&dmic->dmic_blob_pdm[i],
+ sizeof(struct dmic_intel_pdm_ctrl_cfg));
+ vendor_blob += sizeof(struct dmic_intel_pdm_ctrl_cfg);
+
+ /* top level struct first pdm data first fir */
+ memcpy(vendor_blob, (uint8_t *)&dmic->dmic_blob_fir[i][fir_index_0],
+ sizeof(struct dmic_intel_fir_config));
+ vendor_blob += sizeof(struct dmic_intel_fir_config);
+
+ /* top level struct first pdm data second fir */
+ memcpy(vendor_blob, (uint8_t *)&dmic->dmic_blob_fir[i][fir_index_1],
+ sizeof(struct dmic_intel_fir_config));
+ vendor_blob += sizeof(struct dmic_intel_fir_config);
+
+ /* fir coeffs a */
+ memcpy(vendor_blob, (uint8_t *)&dmic->dmic_fir_array.fir_coeffs[i][fir_index_0][0],
+ dmic->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t));
+ vendor_blob += dmic->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t);
+
+ /* fir coeffs b */
+ memcpy(vendor_blob, (uint8_t *)&dmic->dmic_fir_array.fir_coeffs[i][fir_index_1][0],
+ dmic->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t));
+ vendor_blob += dmic->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t);
+ }
+
+ dmic_get_vendor_blob_size(nhlt, &blob_size);
+ dmic_print_bytes_as_hex((uint8_t *)orig_blob, blob_size);
+ dmic_print_integers_as_hex((uint32_t *)orig_blob, blob_size / 4);
+
+ return 0;
+}
+
+int dmic_set_params(struct intel_nhlt_params *nhlt, int dai_index, int driver_version,
+ int io_clk, int num_pdm_active, int fifo_word_length, int clk_min, int clk_max,
+ int duty_min, int duty_max, int sample_rate, int unmute_ramp_time)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+
+ if (!dmic)
+ return -EINVAL;
+
+ if (dai_index >= DMIC_HW_FIFOS) {
+ fprintf(stderr, "set_dmic_data illegal dai index\n");
+ return -EINVAL;
+ }
+
+ dmic->dmic_dai_index = dai_index;
+ dmic->dmic_prm[dai_index].driver_version = driver_version;
+ dmic->dmic_prm[dai_index].io_clk = io_clk;
+ dmic->dmic_prm[dai_index].num_pdm_active = num_pdm_active;
+ dmic->dmic_prm[dai_index].fifo_bits = fifo_word_length;
+ dmic->dmic_prm[dai_index].pdmclk_min = clk_min;
+ dmic->dmic_prm[dai_index].pdmclk_max = clk_max;
+ dmic->dmic_prm[dai_index].duty_min = duty_min;
+ dmic->dmic_prm[dai_index].duty_max = duty_max;
+ dmic->dmic_prm[dai_index].fifo_fs = sample_rate;
+ dmic->dmic_prm[dai_index].unmute_ramp_time = unmute_ramp_time;
+
+ return 0;
+}
+
+int dmic_set_pdm_params(struct intel_nhlt_params *nhlt, int pdm_index, int enable_a,
+ int enable_b, int polarity_a, int polarity_b, int clk_edge, int skew)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+ int di;
+
+ if (!dmic)
+ return -EINVAL;
+
+ if (pdm_index >= DMIC_HW_CONTROLLERS) {
+ fprintf(stderr, "set_pdm_data illegal pdm_index\n");
+ return -EINVAL;
+ }
+
+ di = dmic->dmic_dai_index;
+
+ dmic->dmic_prm[di].pdm[pdm_index].enable_mic_a = enable_a;
+ dmic->dmic_prm[di].pdm[pdm_index].enable_mic_b = enable_b;
+ dmic->dmic_prm[di].pdm[pdm_index].polarity_mic_a = polarity_a;
+ dmic->dmic_prm[di].pdm[pdm_index].polarity_mic_b = polarity_b;
+ dmic->dmic_prm[di].pdm[pdm_index].clk_edge = clk_edge;
+ dmic->dmic_prm[di].pdm[pdm_index].skew = skew;
+
+ return 0;
+}
+
+int dmic_set_ext_params(struct intel_nhlt_params *nhlt, uint32_t snr, uint32_t sensitivity)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+
+ if (!dmic)
+ return -EINVAL;
+
+ dmic->dmic_mic_config.extension = 1;
+ dmic->dmic_mic_config.snr = snr;
+ dmic->dmic_mic_config.sensitivity = sensitivity;
+
+ return 0;
+}
+
+int dmic_set_mic_params(struct intel_nhlt_params *nhlt, int index,
+ uint8_t type, uint8_t panel, uint32_t speaker_position_distance,
+ uint32_t horizontal_offset, uint32_t vertical_offset,
+ uint8_t frequency_low_band, uint8_t frequency_high_band,
+ uint16_t direction_angle, uint16_t elevation_angle,
+ uint16_t vertical_angle_begin, uint16_t vertical_angle_end,
+ uint16_t horizontal_angle_begin, uint16_t horizontal_angle_end)
+{
+ struct intel_dmic_params *dmic = (struct intel_dmic_params *)nhlt->dmic_params;
+
+ if (!dmic)
+ return -EINVAL;
+
+ dmic->dmic_mic_config.vendor[index].type = type;
+ dmic->dmic_mic_config.vendor[index].panel = panel;
+ dmic->dmic_mic_config.vendor[index].speaker_position_distance = speaker_position_distance;
+ dmic->dmic_mic_config.vendor[index].horizontal_offset = horizontal_offset;
+ dmic->dmic_mic_config.vendor[index].vertical_offset = vertical_offset;
+ dmic->dmic_mic_config.vendor[index].frequency_low_band = frequency_low_band;
+ dmic->dmic_mic_config.vendor[index].frequency_high_band = frequency_high_band;
+ dmic->dmic_mic_config.vendor[index].direction_angle = direction_angle;
+ dmic->dmic_mic_config.vendor[index].elevation_angle = elevation_angle;
+ dmic->dmic_mic_config.vendor[index].vertical_angle_begin = vertical_angle_begin;
+ dmic->dmic_mic_config.vendor[index].vertical_angle_end = vertical_angle_end;
+ dmic->dmic_mic_config.vendor[index].horizontal_angle_begin = horizontal_angle_begin;
+ dmic->dmic_mic_config.vendor[index].horizontal_angle_end = horizontal_angle_end;
+
+ dmic->dmic_mic_config.num_mics++;
+
+ return 0;
+}
+
+/* init dmic parameters, should be called before parsing dais */
+int dmic_init_params(struct intel_nhlt_params *nhlt)
+{
+ struct intel_dmic_params *dmic;
+ int i;
+
+ dmic = calloc(1, sizeof(struct intel_dmic_params));
+ if (!dmic)
+ return -ENOMEM;
+
+ nhlt->dmic_params = dmic;
+ /* set always to 1, some fw variants use this for choosing memory type */
+ dmic->dmic_blob.gateway_attributes = 1;
+ /* delay in ms to unmute mics after clock is started */
+ dmic->dmic_blob.clock_on_delay = 16;
+
+ for (i = 0; i < DMIC_TS_GROUP_SIZE; i++)
+ dmic->dmic_blob.ts_group[i] = 0xFFFFFFFF; /* not enabled */
+
+ dmic->dmic_count = 0;
+
+ dmic->dmic_mic_config.num_mics = 0;
+ dmic->dmic_mic_config.extension = 0;
+ dmic->dmic_mic_config.array_type = 0;
+ dmic->dmic_mic_config.snr = 0;
+ dmic->dmic_mic_config.sensitivity = 0;
+
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __DMIC_PROCESS_H
+#define __DMIC_PROCESS_H
+
+#include <stdint.h>
+
+/* initialize and set default values before parsing */
+int dmic_init_params(struct intel_nhlt_params *nhlt);
+
+/* set parameters when parsing topology2 conf */
+int dmic_set_params(struct intel_nhlt_params *nhlt, int dai_index, int driver_version,
+ int io_clk, int num_pdm_active, int fifo_word_length, int clk_min, int clk_max,
+ int duty_min, int duty_max, int sample_rate, int unmute_ramp_time);
+int dmic_set_pdm_params(struct intel_nhlt_params *nhlt, int pdm_index, int enable_a,
+ int enable_b, int polarity_a, int polarity_b, int clk_edge, int skew);
+int dmic_set_ext_params(struct intel_nhlt_params *nhlt, uint32_t snr, uint32_t sensitivity);
+int dmic_set_mic_params(struct intel_nhlt_params *nhlt, int index,
+ uint8_t type, uint8_t panel, uint32_t speaker_position_distance,
+ uint32_t horizontal_offset, uint32_t vertical_offset,
+ uint8_t frequency_low_band, uint8_t frequency_high_band,
+ uint16_t direction_angle, uint16_t elevation_angle,
+ uint16_t vertical_angle_begin, uint16_t vertical_angle_end,
+ uint16_t horizontal_angle_begin, uint16_t horizontal_angle_end);
+
+/* calculate the blob after parsing the values*/
+int dmic_calculate(struct intel_nhlt_params *nhlt);
+
+/* get spec parameters when building the nhlt endpoint */
+int dmic_get_params(struct intel_nhlt_params *nhlt, int index, uint32_t *sample_rate,
+ uint16_t *channel_count, uint32_t *bits_per_sample, uint8_t *array_type,
+ uint8_t *num_mics, uint8_t *extension, uint32_t *snr, uint32_t *sensitivity);
+int dmic_get_mic_params(struct intel_nhlt_params *nhlt, int index,
+ uint8_t *type, uint8_t *panel, uint32_t *speaker_position_distance,
+ uint32_t *horizontal_offset, uint32_t *vertical_offset,
+ uint8_t *frequency_low_band, uint8_t *frequency_high_band,
+ uint16_t *direction_angle, uint16_t *elevation_angle,
+ uint16_t *vertical_angle_begin, uint16_t *vertical_angle_end,
+ uint16_t *horizontal_angle_begin, uint16_t *horizontal_angle_end);
+
+/* get vendor specific blob when building the nhlt endpoint */
+int dmic_get_vendor_blob_count(struct intel_nhlt_params *nhlt);
+int dmic_get_vendor_blob_size(struct intel_nhlt_params *nhlt, size_t *size);
+int dmic_get_vendor_blob(struct intel_nhlt_params *nhlt, uint8_t *vendor_blob);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__
+#define __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__
+
+#include <stdint.h>
+
+struct pdm_decim {
+ int decim_factor;
+ int length;
+ int shift;
+ int relative_passband;
+ int relative_stopband;
+ int passband_ripple;
+ int stopband_ripple;
+ const int32_t *coef;
+};
+
+const int32_t fir_int32_02_4323_5100_010_095[95] = {
+ 178179, -158757, -2195582, -5296650, -5737416, -1057695,
+ 4405719, 3336648, -3249588, -5061179, 1984305, 6895125, 68826,
+ -8433396, -2933479, 9499107, 6882087, -9330152, -11397510,
+ 7807097, 16376076, -4402338, -21239788, -1118085, 25423993,
+ 9062534, -27935015, -19203927, 28049586, 31500423, -24524863,
+ -45191501, 16582731, 59861920, -2808306, -74639091, -18696113,
+ 88054673, 50505898, -98266320, -97865783, 101816481,
+ 173879965, -88042495, -320187025, -1193013, 740698712,
+ 1139586920, 740698712, -1193013, -320187025, -88042495,
+ 173879965, 101816481, -97865783, -98266320, 50505898,
+ 88054673, -18696113, -74639091, -2808306, 59861920, 16582731,
+ -45191501, -24524863, 31500423, 28049586, -19203927,
+ -27935015, 9062534, 25423993, -1118085, -21239788, -4402338,
+ 16376076, 7807097, -11397510, -9330152, 6882087, 9499107,
+ -2933479, -8433396, 68826, 6895125, 1984305, -5061179,
+ -3249588, 3336648, 4405719, -1057695, -5737416, -5296650,
+ -2195582, -158757, 178179
+};
+
+struct pdm_decim pdm_decim_int32_02_4323_5100_010_095 = {
+ 2, 95, 0, 4323, 5100, 10, 95, fir_int32_02_4323_5100_010_095
+};
+
+const int32_t fir_int32_02_4375_5100_010_095[101] = {
+ -587830, 2653881, 5154608, 4845367, 226474, 4220832, 2571159,
+ -3184700, 4043579, 2206821, 5554546, 750495, 6923897, 1268580,
+ -8073364, 4085184, 8546479, 7505366, 8176184, 11533751,
+ -6471060, 15704257, 3359705, 19852407, 1635592, 23144509,
+ -8252640, 25285011, 16574477, 25723227, 26663303, 23549736,
+ -38139662, 17943368, 50446982, 8141045, 63090266, 7051348,
+ -75166961, 29039893, 85772628, 60568976, 93167361, 106799777,
+ -94198977, 180962817, 78385599, 324820256, 12243140,
+ -742491464, 1151461314, 742491464, 12243140, 324820256,
+ -78385599, 180962817, 94198977, 106799777, 93167361, 60568976,
+ -85772628, 29039893, 75166961, 7051348, 63090266, 8141045,
+ -50446982, 17943368, 38139662, 23549736, 26663303, 25723227,
+ -16574477, 25285011, 8252640, 23144509, 1635592, 19852407,
+ -3359705, 15704257, 6471060, 11533751, 8176184, 7505366,
+ -8546479, 4085184, 8073364, 1268580, 6923897, 750495, 5554546,
+ -2206821, 4043579, 3184700, 2571159, 4220832, 226474, 4845367,
+ -5154608, 2653881, 587830
+};
+
+struct pdm_decim pdm_decim_int32_02_4375_5100_010_095 = {
+ 2, 101, 0, 4375, 5100, 10, 95, fir_int32_02_4375_5100_010_095
+};
+
+const int32_t fir_int32_03_4375_5100_010_095[157] = {
+ 350908, 1127906, 2233575, 3059598, 2752218, 818077, -2252677,
+ -4944563, -5550761, -3466262, 53093, 2496787, 1904133,
+ -1421749, -4818616, -5204506, -1721103, 3155344, 5311508,
+ 2454543, -3518663, -7589567, -5713379, 1327838, 7901439,
+ 7958184, 527907, -8634105, -11354937, -4214569, 7627213,
+ 13970417, 8263468, -5862019, -16549858, -13530131, 2213952,
+ 17870989, 19056458, 2854116, -18029944, -24979416, -9860304,
+ 16175288, 30546328, 18606151, -11894439, -35172976, -28918520,
+ 4746588, 38201563, 40591159, 5825487, -38713444, -53159813,
+ -20283635, 35723701, 66121349, 39266346, -27911327, -78796917,
+ -63664545, 13459132, 90417174, 95194527, 10755265, -99898306,
+ -137498952, -51076476, 105458775, 200050300, 124562550,
+ -101613472, -313388303, -297347454, 53702567, 639689683,
+ 1187815441, 1411068556, 1187815441, 639689683, 53702567,
+ -297347454, -313388303, -101613472, 124562550, 200050300,
+ 105458775, -51076476, -137498952, -99898306, 10755265,
+ 95194527, 90417174, 13459132, -63664545, -78796917, -27911327,
+ 39266346, 66121349, 35723701, -20283635, -53159813, -38713444,
+ 5825487, 40591159, 38201563, 4746588, -28918520, -35172976,
+ -11894439, 18606151, 30546328, 16175288, -9860304, -24979416,
+ -18029944, 2854116, 19056458, 17870989, 2213952, -13530131,
+ -16549858, -5862019, 8263468, 13970417, 7627213, -4214569,
+ -11354937, -8634105, 527907, 7958184, 7901439, 1327838,
+ -5713379, -7589567, -3518663, 2454543, 5311508, 3155344,
+ -1721103, -5204506, -4818616, -1421749, 1904133, 2496787,
+ 53093, -3466262, -5550761, -4944563, -2252677, 818077,
+ 2752218, 3059598, 2233575, 1127906, 350908
+};
+
+struct pdm_decim pdm_decim_int32_03_4375_5100_010_095 = {
+ 3, 157, 1, 4375, 5100, 10, 95, fir_int32_03_4375_5100_010_095
+};
+
+const int32_t fir_int32_04_4318_5100_010_095[195] = {
+ 111466, 733409, 1749250, 3319696, 5129378, 6676209, 7309490,
+ 6506584, 4154756, 734975, -2729377, -4998637, -5126868,
+ -2945573, 726080, 4306371, 6084832, 5022201, 1343898,
+ -3405897, -6962146, -7384707, -4083477, 1706504, 7322247,
+ 9858386, 7634375, 1218279, -6584983, -11909667, -11705999,
+ -5426216, 4387589, 13057254, 15953274, 10914694, -336708,
+ -12649310, -19740590, -17350504, -5691816, 10168034, 22423979,
+ 24315225, 13770305, -5012695, -23099234, -30992981, -23541145,
+ -3074594, 21044646, 36602258, 34627991, 14410179, -15304906,
+ -39942350, -46136506, -28837515, 5227124, 39904111, 57125295,
+ 46160603, 9951610, -35069112, -66221384, -65802058, -30833475,
+ 23892424, 71729303, 86873627, 57925919, -4643620, -71630929,
+ -108312985, -91957579, -25024430, 63233146, 128873180,
+ 134384678, 69003997, -42277605, -146972769, -188542432,
+ -135065835, 625004, 160544910, 263771211, 243538822, 83417905,
+ -164654723, -391259178, -468017530, -299941690, 129616412,
+ 741218294, 1378462855, 1858765025, 2037199780, 1858765025,
+ 1378462855, 741218294, 129616412, -299941690, -468017530,
+ -391259178, -164654723, 83417905, 243538822, 263771211,
+ 160544910, 625004, -135065835, -188542432, -146972769,
+ -42277605, 69003997, 134384678, 128873180, 63233146,
+ -25024430, -91957579, -108312985, -71630929, -4643620,
+ 57925919, 86873627, 71729303, 23892424, -30833475, -65802058,
+ -66221384, -35069112, 9951610, 46160603, 57125295, 39904111,
+ 5227124, -28837515, -46136506, -39942350, -15304906, 14410179,
+ 34627991, 36602258, 21044646, -3074594, -23541145, -30992981,
+ -23099234, -5012695, 13770305, 24315225, 22423979, 10168034,
+ -5691816, -17350504, -19740590, -12649310, -336708, 10914694,
+ 15953274, 13057254, 4387589, -5426216, -11705999, -11909667,
+ -6584983, 1218279, 7634375, 9858386, 7322247, 1706504,
+ -4083477, -7384707, -6962146, -3405897, 1343898, 5022201,
+ 6084832, 4306371, 726080, -2945573, -5126868, -4998637,
+ -2729377, 734975, 4154756, 6506584, 7309490, 6676209, 5129378,
+ 3319696, 1749250, 733409, 111466
+};
+
+struct pdm_decim pdm_decim_int32_04_4318_5100_010_095 = {
+ 4, 195, 2, 4318, 5100, 10, 95, fir_int32_04_4318_5100_010_095
+};
+
+const int32_t fir_int32_05_4325_5100_010_095[249] = {
+ -207469, 340409, 498144, 558705, 409384, 55040, 891125,
+ -2067198, 3439775, 4759611, 5714914, 6006588, 5438767,
+ -3999938, 1904139, 429386, 2461501, 3672469, 3715731, 2543409,
+ -455850, 1949813, 3931230, 4816572, 4223058, 2208032, 705100,
+ -3649781, 5664341, 5997954, 4379534, 1155856, 2760627,
+ -6138124, 7803235, 7038238, 3865643, 886573, 5783539, 9205487,
+ -9873081, 7309023, 2074085, 4324112, 9849851, 12575185,
+ -11338304, 6203771, 1449001, 9273272, 14659745, 15592551,
+ -11379919, 3001447, 7079011, 15617750, 19618936, 17365380,
+ -9103870, 2875883, 14845719, 22779047, 23684744, 16699479,
+ -3549687, 11824128, 24420688, 29781202, 25536278, 12369331,
+ -5975020, 23715313, 34825066, 35046780, 23465500, 3076561,
+ -19921841, 37971240, 44596949, 36675890, 15750350, 12128842,
+ -38090537, 53213360, 51529430, 32269554, 535756, 33948414,
+ -59741809, 67400482, 52822241, 19070165, 24058950, 62761384,
+ -83526757, 77707237, 44854657, 6391470, 60372631, 99010031,
+ -107635426, 80237884, 22341840, 49653145, 112793869,
+ -144502927, 130123093, 68698546, 25049444, 123586766,
+ -193887099, 207405271, 149914650, 28201854, 128717667,
+ -274782040, 357589632, 332094228, 173714910, 112793207,
+ -491412574, 900833942, 1267184732, 1520519220, 1610979079,
+ -1520519220, 1267184732, 900833942, 491412574, 112793207,
+ -173714910, 332094228, 357589632, 274782040, 128717667,
+ -28201854, 149914650, 207405271, 193887099, 123586766,
+ -25049444, 68698546, 130123093, 144502927, 112793869,
+ -49653145, 22341840, 80237884, 107635426, 99010031, 60372631,
+ -6391470, 44854657, 77707237, 83526757, 62761384, 24058950,
+ -19070165, 52822241, 67400482, 59741809, 33948414, 535756,
+ -32269554, 51529430, 53213360, 38090537, 12128842, 15750350,
+ -36675890, 44596949, 37971240, 19921841, 3076561, 23465500,
+ -35046780, 34825066, 23715313, 5975020, 12369331, 25536278,
+ -29781202, 24420688, 11824128, 3549687, 16699479, 23684744,
+ -22779047, 14845719, 2875883, 9103870, 17365380, 19618936,
+ -15617750, 7079011, 3001447, 11379919, 15592551, 14659745,
+ -9273272, 1449001, 6203771, 11338304, 12575185, 9849851,
+ -4324112, 2074085, 7309023, 9873081, 9205487, 5783539, 886573,
+ -3865643, 7038238, 7803235, 6138124, 2760627, 1155856,
+ -4379534, 5997954, 5664341, 3649781, 705100, 2208032, 4223058,
+ -4816572, 3931230, 1949813, 455850, 2543409, 3715731, 3672469,
+ -2461501, 429386, 1904139, 3999938, 5438767, 6006588, 5714914,
+ -4759611, 3439775, 2067198, 891125, 55040, 409384, 558705,
+ -498144, 340409, 207469
+};
+
+struct pdm_decim pdm_decim_int32_05_4325_5100_010_095 = {
+ 5, 249, 2, 4325, 5100, 10, 95, fir_int32_05_4325_5100_010_095
+};
+
+const int32_t fir_int32_06_4172_5100_010_095[247] = {
+ -128632, 59497, 27046, 238561, 615381, 1180391, 1925670,
+ -2802557, 3718091, 4541067, 5118546, 5302088, 4979296,
+ -4105097, 2725202, 985340, 879509, 2576724, 3805615, 4316339,
+ -3968132, 2772637, 910841, 1284350, 3378797, 4918713, 5524148,
+ -4978750, 3294285, 731418, 2230825, 4978575, 6884637, 7446661,
+ -6411304, 3856078, 204820, 3832851, 7392855, 9635554, 9937191,
+ -8050966, 4198476, 936294, 6318030, 10761631, 13186246,
+ -12869044, 9641051, 3974915, 3065409, 10013209, 15296510,
+ -17584632, 16110734, 10893393, 2799042, 6583403, 15242334,
+ -21164579, 22791637, 19410411, 11383563, 154458, 11993186,
+ -22385766, 28535081, 28717126, 22423602, 10572914, 4585854,
+ -19878539, 31852959, 37539801, 35162895, 24634855, 7708109,
+ -12278651, 31033082, 44206560, 48374146, 41881953, 25360406,
+ -1761722, 24123604, 46603137, 60296121, 61373509, 48548023,
+ -23572693, 8903015, 42138419, 68642074, 81792054, 77398921,
+ -54859810, 17614341, 27257528, 70279853, 101467545, 112499312,
+ -98729567, 60589492, 4020371, 60241948, 118486175, 156668601,
+ -163407087, 132743110, 66089855, 27096695, 130167425,
+ -221612366, 278546163, 280773282, 214713860, 76455494,
+ -126679457, 376416772, 645705969, 902489232, 1114466646,
+ -1254066162, 1302772250, 1254066162, 1114466646, 902489232,
+ -645705969, 376416772, 126679457, 76455494, 214713860,
+ -280773282, 278546163, 221612366, 130167425, 27096695,
+ -66089855, 132743110, 163407087, 156668601, 118486175,
+ -60241948, 4020371, 60589492, 98729567, 112499312, 101467545,
+ -70279853, 27257528, 17614341, 54859810, 77398921, 81792054,
+ -68642074, 42138419, 8903015, 23572693, 48548023, 61373509,
+ -60296121, 46603137, 24123604, 1761722, 25360406, 41881953,
+ -48374146, 44206560, 31033082, 12278651, 7708109, 24634855,
+ -35162895, 37539801, 31852959, 19878539, 4585854, 10572914,
+ -22423602, 28717126, 28535081, 22385766, 11993186, 154458,
+ -11383563, 19410411, 22791637, 21164579, 15242334, 6583403,
+ -2799042, 10893393, 16110734, 17584632, 15296510, 10013209,
+ -3065409, 3974915, 9641051, 12869044, 13186246, 10761631,
+ -6318030, 936294, 4198476, 8050966, 9937191, 9635554, 7392855,
+ -3832851, 204820, 3856078, 6411304, 7446661, 6884637, 4978575,
+ -2230825, 731418, 3294285, 4978750, 5524148, 4918713, 3378797,
+ -1284350, 910841, 2772637, 3968132, 4316339, 3805615, 2576724,
+ -879509, 985340, 2725202, 4105097, 4979296, 5302088, 5118546,
+ -4541067, 3718091, 2802557, 1925670, 1180391, 615381, 238561,
+ -27046, 59497, 128632
+};
+
+struct pdm_decim pdm_decim_int32_06_4172_5100_010_095 = {
+ 6, 247, 2, 4172, 5100, 10, 95, fir_int32_06_4172_5100_010_095
+};
+
+const int32_t fir_int32_08_4156_5301_010_090[249] = {
+ -436533, 30097, 185136, 599151, 1249127, 2156309, 3316125,
+ -4690126, 6201703, 7736149, 9146691, 10266194, 10924643,
+ -10970009, 10291237, 8839410, 6645091, 3827796, 595625,
+ -2767301, 5925074, 8524488, 10235869, 10797193, 10055131,
+ -7997758, 4771108, 678235, 3841448, 8252025, 11985305,
+ -14503017, 15375105, 14342998, 11370408, 6669744, 697166,
+ -5883789, 12270613, 17614188, 21126754, 22191034, 20457484,
+ -15913860, 8916788, 176408, 9306801, 18349873, 25728753,
+ -30337331, 31343834, 28324134, 21351665, 11028693, 1551253,
+ -14908738, 27343830, 37144407, 42813431, 43283070, 38091954,
+ -27492154, 12471488, 5321162, 23744416, 40410795, 52985294,
+ -59498920, 58637630, 49965561, 34048834, 12455105, 12379340,
+ -37407904, 59331472, 75016381, 81921345, 78477412, 64367569,
+ -40661743, 9779029, 24730505, 58569248, 87201632, 106433026,
+ -112987295, 105007254, 82406938, 47020690, 2512109, 45960354,
+ -92298012, 130180961, 153871591, 159007197, 143278921,
+ -106903512, 52811885, 13489989, 84396518, 150980417,
+ -204001696, 235041565, 237634873, 208265018, 147093489,
+ -58319704, 49897266, 165975625, 275876513, 364417203,
+ -416806765, 420248734, 365439450, 247794421, 68258347,
+ -166406038, 443903891, 747260153, 1056170460, 1348727100,
+ -1603354585, 1800763975, 1925728602, 1968501522, 1925728602,
+ -1800763975, 1603354585, 1348727100, 1056170460, 747260153,
+ -443903891, 166406038, 68258347, 247794421, 365439450,
+ -420248734, 416806765, 364417203, 275876513, 165975625,
+ -49897266, 58319704, 147093489, 208265018, 237634873,
+ -235041565, 204001696, 150980417, 84396518, 13489989,
+ -52811885, 106903512, 143278921, 159007197, 153871591,
+ -130180961, 92298012, 45960354, 2512109, 47020690, 82406938,
+ -105007254, 112987295, 106433026, 87201632, 58569248,
+ -24730505, 9779029, 40661743, 64367569, 78477412, 81921345,
+ -75016381, 59331472, 37407904, 12379340, 12455105, 34048834,
+ -49965561, 58637630, 59498920, 52985294, 40410795, 23744416,
+ -5321162, 12471488, 27492154, 38091954, 43283070, 42813431,
+ -37144407, 27343830, 14908738, 1551253, 11028693, 21351665,
+ -28324134, 31343834, 30337331, 25728753, 18349873, 9306801,
+ -176408, 8916788, 15913860, 20457484, 22191034, 21126754,
+ -17614188, 12270613, 5883789, 697166, 6669744, 11370408,
+ -14342998, 15375105, 14503017, 11985305, 8252025, 3841448,
+ -678235, 4771108, 7997758, 10055131, 10797193, 10235869,
+ -8524488, 5925074, 2767301, 595625, 3827796, 6645091, 8839410,
+ -10291237, 10970009, 10924643, 10266194, 9146691, 7736149,
+ -6201703, 4690126, 3316125, 2156309, 1249127, 599151, 185136,
+ -30097, 436533
+};
+
+struct pdm_decim pdm_decim_int32_08_4156_5301_010_090 = {
+ 8, 249, 3, 4156, 5301, 10, 90, fir_int32_08_4156_5301_010_090
+};
+
+const int32_t fir_int32_10_4156_5345_010_090[250] = {
+ 1523665, 1033186, 1237912, 1334775, 1259136, 945771, 330804,
+ -639430, -2007230, -3782603, -5951062, -8464725, -11233139,
+ -14135396, -17013193, -19685639, -21953913, -23620153,
+ -24499971, -24438406, -23328862, -21124651, -17851431,
+ -13614282, -8597813, -3061758, 2673313, 8241850, 13260214,
+ 17352951, 20183225, 21483036, 21079871, 18918865, 15076270,
+ 9764507, 3325641, -3786831, -11034844, -17834361, -23598518,
+ -27786005, -29948197, -29773031, -27120493, -22045821,
+ -14809184, -5868041, 4147420, 14474606, 24273606, 32691772,
+ 38934943, 42337380, 42426833, 38977193, 32043932, 21978645,
+ 9419218, -4744590, -19429341, -33436251, -45544284, -54611932,
+ -59678078, -60055175, -55404949, -45789928, -31694490,
+ -14011800, 6004287, 26821132, 46737621, 64015881, 77024918,
+ 84384267, 85096503, 78655977, 65124057, 45161629, 20014065,
+ -8553289, -38367257, -66999907, -91952963, -110860336,
+ -121693302, -122951962, -113826153, -94311463, -65267325,
+ -28409399, 13767679, 58135590, 101138413, 139047421,
+ 168247614, 185536046, 188409622, 175319624, 145872050,
+ 100954938, 42779310, -25174601, -98307054, -171138928,
+ -237629824, -291555223, -326919157, -338374861, -321624361,
+ -273768560, -193581379, -81686484, 59379271, 225223665,
+ 409778420, 605591661, 804217132, 996677146, 1173970423,
+ 1327592623, 1450036054, 1535235882, 1578933611, 1578933611,
+ 1535235882, 1450036054, 1327592623, 1173970423, 996677146,
+ 804217132, 605591661, 409778420, 225223665, 59379271,
+ -81686484, -193581379, -273768560, -321624361, -338374861,
+ -326919157, -291555223, -237629824, -171138928, -98307054,
+ -25174601, 42779310, 100954938, 145872050, 175319624,
+ 188409622, 185536046, 168247614, 139047421, 101138413,
+ 58135590, 13767679, -28409399, -65267325, -94311463,
+ -113826153, -122951962, -121693302, -110860336, -91952963,
+ -66999907, -38367257, -8553289, 20014065, 45161629, 65124057,
+ 78655977, 85096503, 84384267, 77024918, 64015881, 46737621,
+ 26821132, 6004287, -14011800, -31694490, -45789928, -55404949,
+ -60055175, -59678078, -54611932, -45544284, -33436251,
+ -19429341, -4744590, 9419218, 21978645, 32043932, 38977193,
+ 42426833, 42337380, 38934943, 32691772, 24273606, 14474606,
+ 4147420, -5868041, -14809184, -22045821, -27120493, -29773031,
+ -29948197, -27786005, -23598518, -17834361, -11034844,
+ -3786831, 3325641, 9764507, 15076270, 18918865, 21079871,
+ 21483036, 20183225, 17352951, 13260214, 8241850, 2673313,
+ -3061758, -8597813, -13614282, -17851431, -21124651,
+ -23328862, -24438406, -24499971, -23620153, -21953913,
+ -19685639, -17013193, -14135396, -11233139, -8464725,
+ -5951062, -3782603, -2007230, -639430, 330804, 945771,
+ 1259136, 1334775, 1237912, 1033186, 1523665
+};
+
+struct pdm_decim pdm_decim_int32_10_4156_5345_010_090 = {
+ 10, 250, 3, 4156, 5345, 10, 90, fir_int32_10_4156_5345_010_090
+};
+
+const int32_t fir_int32_12_4156_5345_010_090[250] = {
+ 3388064, 2103678, 2588621, 3003201, 3289311, 3375616, 3187681,
+ 2654590, 1715972, 309000, -1610211, -4055571, -7035886,
+ -10511174, -14426105, -18683424, -23158811, -27697959,
+ -32125744, -36244313, -39850668, -42739502, -44716782,
+ -45614286, -45293362, -43663492, -40685637, -36382432,
+ -30840569, -24214188, -16719141, -8631481, -273616, 7996830,
+ 15802429, 22766807, 28531588, 32779134, 35250465, 35763733,
+ 34227329, 30653000, 25158397, 17970176, 9416017, -86479,
+ -10047026, -19926252, -29162149, -37200047, -43523733,
+ -47686853, -49340539, -48260145, -44362718, -37720992,
+ -28567244, -17289156, -4416600, 9400589, 23423041, 36858656,
+ 48904650, 58793401, 65836995, 69472264, 69298737, 65111461,
+ 56923398, 44977350, 29744904, 11913285, -7641844, -27894994,
+ -47720356, -65950400, -81438916, -93127908, -100112283,
+ -101700449, -97465921, -87287745, -71375413, -50277965,
+ -24873838, 3657557, 33882414, 64176403, 92802029, 117997525,
+ 138071930, 151502587, 157029100, 153740023, 141145505,
+ 119233461, 88503690, 49978361, 5186664, -43876910, -94819563,
+ -144940463, -191340721, -231048840, -261155504, -278952672,
+ -282068987, -268596392, -237200378, -187208906, -118674579,
+ -32406655, 70029801, 186348178, 313607557, 448310730,
+ 586528751, 724047761, 856530828, 979688864, 1089452098,
+ 1182135271, 1254588364, 1304326765, 1329634160, 1329634160,
+ 1304326765, 1254588364, 1182135271, 1089452098, 979688864,
+ 856530828, 724047761, 586528751, 448310730, 313607557,
+ 186348178, 70029801, -32406655, -118674579, -187208906,
+ -237200378, -268596392, -282068987, -278952672, -261155504,
+ -231048840, -191340721, -144940463, -94819563, -43876910,
+ 5186664, 49978361, 88503690, 119233461, 141145505, 153740023,
+ 157029100, 151502587, 138071930, 117997525, 92802029,
+ 64176403, 33882414, 3657557, -24873838, -50277965, -71375413,
+ -87287745, -97465921, -101700449, -100112283, -93127908,
+ -81438916, -65950400, -47720356, -27894994, -7641844,
+ 11913285, 29744904, 44977350, 56923398, 65111461, 69298737,
+ 69472264, 65836995, 58793401, 48904650, 36858656, 23423041,
+ 9400589, -4416600, -17289156, -28567244, -37720992, -44362718,
+ -48260145, -49340539, -47686853, -43523733, -37200047,
+ -29162149, -19926252, -10047026, -86479, 9416017, 17970176,
+ 25158397, 30653000, 34227329, 35763733, 35250465, 32779134,
+ 28531588, 22766807, 15802429, 7996830, -273616, -8631481,
+ -16719141, -24214188, -30840569, -36382432, -40685637,
+ -43663492, -45293362, -45614286, -44716782, -42739502,
+ -39850668, -36244313, -32125744, -27697959, -23158811,
+ -18683424, -14426105, -10511174, -7035886, -4055571, -1610211,
+ 309000, 1715972, 2654590, 3187681, 3375616, 3289311, 3003201,
+ 2588621, 2103678, 3388064
+};
+
+struct pdm_decim pdm_decim_int32_12_4156_5345_010_090 = {
+ 12, 250, 3, 4156, 5345, 10, 90, fir_int32_12_4156_5345_010_090
+};
+
+#endif /* __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__ */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include "intel-nhlt.h"
+
+static int get_int_val(snd_config_t *input, long *int_val, snd_config_t *top)
+{
+ char tplg_define[128] = "Define.";
+ snd_config_t *n;
+ const char *s;
+ int ret;
+
+ if (snd_config_get_string(input, &s) < 0)
+ return snd_config_get_integer(input, int_val);
+
+ if (*s != '$')
+ return 0;
+
+ strcat(tplg_define, s + 1);
+
+ ret = snd_config_search(top, tplg_define, &n);
+ if (ret < 0)
+ return ret;
+
+ return snd_config_get_integer(n, int_val);
+}
+
+static int get_string_val(snd_config_t *input, const char **string_val, snd_config_t *top)
+{
+ char tplg_define[128] = "Define.";
+ snd_config_t *n;
+ int ret;
+
+ if (snd_config_get_string(input, string_val) < 0)
+ return -EINVAL;
+
+ if (**string_val != '$')
+ return 0;
+
+ strcat(tplg_define, *string_val + 1);
+
+ ret = snd_config_search(top, tplg_define, &n);
+ if (ret < 0)
+ return ret;
+
+ return snd_config_get_string(n, string_val);
+}
+
+#ifdef NHLT_DEBUG
+static void print_array_values(struct dai_values *values, int size)
+{
+ int i;
+
+ fprintf(stdout, "print parsed array:\n");
+ for (i = 0; i < size; i++, values++) {
+ if (values->type == SND_CONFIG_TYPE_INTEGER)
+ fprintf(stdout, "%s %ld\n", values->name, *values->int_val);
+ else
+ fprintf(stdout, "%s %s\n", values->name, *values->string_val);
+ }
+ fprintf(stdout, "\n");
+}
+#endif
+
+int find_set_values(struct dai_values *values, int size, snd_config_t *dai_cfg,
+ snd_config_t *top, const char *class_name)
+{
+ snd_config_iterator_t i, next;
+ struct dai_values *temp_val;
+ snd_config_t *class_cfg;
+ snd_config_t *n;
+ const char *id;
+ int ret;
+ int j;
+
+ /* get default values from class definition */
+ ret = snd_config_search(top, class_name, &class_cfg);
+ if (ret < 0)
+ return ret;
+
+ snd_config_for_each(i, next, class_cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ for (j = 0, temp_val = values; j < size; j++, temp_val++) {
+ if (!strcmp(id, temp_val->name)) {
+ temp_val->data = n;
+ break;
+ }
+ }
+ }
+
+ /* set instance specific values */
+ snd_config_for_each(i, next, dai_cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ for (j = 0, temp_val = values; j < size; j++, temp_val++) {
+ if (!strcmp(id, temp_val->name)) {
+ temp_val->data = n;
+ break;
+ }
+ }
+ }
+
+ for (j = 0, temp_val = values; j < size; j++, temp_val++) {
+ if (!temp_val->data)
+ continue;
+ if (temp_val->type == SND_CONFIG_TYPE_INTEGER)
+ get_int_val(temp_val->data, temp_val->int_val, top);
+ else
+ get_string_val(temp_val->data, temp_val->string_val, top);
+ }
+
+#ifdef NHLT_DEBUG
+ print_array_values(values, size);
+#endif
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __INTEL_NHLT_H
+#define __INTEL_NHLT_H
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+
+#define MIN(a, b) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ __a > __b ? __b : __a; \
+})
+#define MAX(a, b) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ __a < __b ? __b : __a; \
+})
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a)[0])
+
+#define BIT(b) (1UL << (b))
+#define MASK(b_hi, b_lo) (((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo))
+#define SET_BIT(b, x) (((x) & 1) << (b))
+#define SET_BITS(b_hi, b_lo, x) (((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo))
+
+struct intel_nhlt_params {
+ void *dmic_params;
+ void *ssp_params;
+};
+
+struct dai_values {
+ char name[32];
+ snd_config_type_t type;
+ snd_config_t *data;
+ long *int_val;
+ const char **string_val;
+};
+
+int find_set_values(struct dai_values *values, int size, snd_config_t *dai_cfg,
+ snd_config_t *top, const char *class_name);
+
+#endif /* __INTEL_NHLT_H */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+#include "intel-nhlt.h"
+#include "ssp-nhlt.h"
+#include "ssp/ssp-process.h"
+
+static int set_ssp_data(struct intel_nhlt_params *nhlt, snd_config_t *dai_cfg, snd_config_t *top)
+{
+ const char *tdm_padding_per_slot = NULL;
+ const char *direction = NULL;
+ const char *quirks = NULL;
+ long frame_pulse_width = 0;
+ long clks_control = 0;
+ long sample_bits = 0;
+ long bclk_delay = 0;
+ long dai_index = 0;
+ long mclk_id = 0;
+ long io_clk = 0;
+ int ret;
+
+ struct dai_values ssp_data[] = {
+ { "io_clk", SND_CONFIG_TYPE_INTEGER, NULL, &io_clk, NULL},
+ { "direction", SND_CONFIG_TYPE_STRING, NULL, NULL, &direction},
+ { "quirks", SND_CONFIG_TYPE_STRING, NULL, NULL, &quirks},
+ { "dai_index", SND_CONFIG_TYPE_INTEGER, NULL, &dai_index, NULL},
+ { "sample_bits", SND_CONFIG_TYPE_INTEGER, NULL, &sample_bits, NULL},
+ { "bclk_delay", SND_CONFIG_TYPE_INTEGER, NULL, &bclk_delay, NULL},
+ { "mclk_id", SND_CONFIG_TYPE_INTEGER, NULL, &mclk_id, NULL},
+ { "clks_control", SND_CONFIG_TYPE_INTEGER, NULL, &clks_control, NULL},
+ { "frame_pulse_width", SND_CONFIG_TYPE_INTEGER, NULL, &frame_pulse_width, NULL},
+ { "tdm_padding_per_slot", SND_CONFIG_TYPE_STRING, NULL, NULL,
+ &tdm_padding_per_slot},
+ };
+
+ ret = find_set_values(&ssp_data[0], ARRAY_SIZE(ssp_data), dai_cfg, top, "Class.Dai.SSP");
+ if (ret < 0)
+ return ret;
+
+ return ssp_set_params(nhlt, direction, dai_index, io_clk, bclk_delay, sample_bits, mclk_id,
+ clks_control, frame_pulse_width, tdm_padding_per_slot, quirks);
+}
+
+static int set_hw_config(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ const char *format = NULL;
+ const char *mclk = NULL;
+ const char *bclk = NULL;
+ const char *bclk_invert = NULL;
+ const char *fsync = NULL;
+ const char *fsync_invert = NULL;
+ long mclk_freq = 0;
+ long bclk_freq = 0;
+ long fsync_freq = 0;
+ long tdm_slots = 0;
+ long tdm_slot_width = 0;
+ long tx_slots = 0;
+ long rx_slots = 0;
+ long ret;
+
+ struct dai_values ssp_hw_data[] = {
+ {"format", SND_CONFIG_TYPE_STRING, NULL, NULL, &format},
+ {"mclk", SND_CONFIG_TYPE_STRING, NULL, NULL, &mclk},
+ {"bclk", SND_CONFIG_TYPE_STRING, NULL, NULL, &bclk},
+ {"fsync", SND_CONFIG_TYPE_STRING, NULL, NULL, &fsync},
+ {"bclk_invert", SND_CONFIG_TYPE_STRING, NULL, NULL, &bclk_invert},
+ {"fsync_invert", SND_CONFIG_TYPE_STRING, NULL, NULL, &fsync_invert},
+ {"fsync_freq", SND_CONFIG_TYPE_INTEGER, NULL, &fsync_freq, NULL},
+ {"bclk_freq", SND_CONFIG_TYPE_INTEGER, NULL, &bclk_freq, NULL},
+ {"mclk_freq", SND_CONFIG_TYPE_INTEGER, NULL, &mclk_freq, NULL},
+ {"tdm_slots", SND_CONFIG_TYPE_INTEGER, NULL, &tdm_slots, NULL},
+ {"tdm_slot_width", SND_CONFIG_TYPE_INTEGER, NULL, &tdm_slot_width, NULL},
+ {"tx_slots", SND_CONFIG_TYPE_INTEGER, NULL, &tx_slots, NULL},
+ {"rx_slots", SND_CONFIG_TYPE_INTEGER, NULL, &rx_slots, NULL},
+ };
+
+ ret = find_set_values(&ssp_hw_data[0], ARRAY_SIZE(ssp_hw_data), cfg, top,
+ "Class.Base.hw_config");
+ if (ret < 0)
+ return ret;
+
+ return ssp_hw_set_params(nhlt, format, mclk, bclk, bclk_invert, fsync, fsync_invert,
+ mclk_freq, bclk_freq, fsync_freq, tdm_slots, tdm_slot_width,
+ tx_slots, rx_slots);
+}
+
+/* init ssp parameters, should be called before parsing dais */
+int nhlt_ssp_init_params(struct intel_nhlt_params *nhlt)
+{
+ return ssp_init_params(nhlt);
+}
+
+int nhlt_ssp_get_ep_count(struct intel_nhlt_params *nhlt)
+{
+ return ssp_get_vendor_blob_count(nhlt);
+}
+
+int nhlt_ssp_get_dir(struct intel_nhlt_params *nhlt, int dai_index, uint8_t *dir)
+{
+ return ssp_get_dir(nhlt, dai_index, dir);
+}
+
+int nhlt_ssp_get_ep(struct intel_nhlt_params *nhlt, struct endpoint_descriptor **eps,
+ int dai_index, uint8_t dir)
+{
+ struct endpoint_descriptor ep;
+ struct ssp_device_specific_config ssp_conf;
+ struct formats_config f_conf;
+ struct format_config f_conf1[8];
+ uint32_t sample_rate;
+ uint16_t channel_count;
+ uint32_t bits_per_sample;
+ uint32_t virtualbus_id;
+ uint32_t formats_count;
+ uint8_t *ep_target;
+ size_t blob_size;
+ int ret;
+ int i;
+
+ /*
+ * nhlt ssp structure:
+ *
+ * endpoint_descriptor, sizeof(struct endpoint_descriptor)
+ * device_specific_config (headset), sizeof(struct ssp_device_specific_config)
+ * formats_config (formats_count), sizeof(struct formats_config)
+ * format_config (waveex), sizeof(struct format_config)
+ * vendor_blob sizeof(vendor_blob)
+ */
+
+ ret = ssp_get_params(nhlt, dai_index, &virtualbus_id, &formats_count);
+ if (ret < 0) {
+ fprintf(stderr, "nhlt_ssp_get_ep: ssp_get_params failed\n");
+ return ret;
+ }
+
+ ep.link_type = NHLT_LINK_TYPE_SSP;
+ ep.instance_id = 0;
+ ep.vendor_id = NHLT_VENDOR_ID_INTEL;
+ ep.device_id = NHLT_DEVICE_ID_INTEL_I2S_TDM;
+ ep.revision_id = 0;
+ ep.subsystem_id = 0;
+ ep.device_type = 0;
+
+ ep.direction = dir;
+ /* ssp device index */
+ ep.virtualbus_id = virtualbus_id;
+ /* ssp config */
+ ssp_conf.config.capabilities_size = 2;
+ ssp_conf.device_config.virtual_slot = 0;
+ ssp_conf.device_config.config_type = 0;
+
+ /* formats_config */
+ f_conf.formats_count = formats_count;
+
+ for (i = 0; i < f_conf.formats_count; i++) {
+ /* fill in wave format extensible types */
+ f_conf1[i].format.wFormatTag = 0xFFFE;
+
+ ret = ssp_get_hw_params(nhlt, i, &sample_rate, &channel_count, &bits_per_sample);
+
+ if (ret < 0) {
+ fprintf(stderr, "nhlt_ssp_get_ep: ssp_get_hw_params failed\n");
+ return ret;
+ }
+
+ f_conf1[i].format.nChannels = channel_count;
+ f_conf1[i].format.nSamplesPerSec = sample_rate;
+ f_conf1[i].format.wBitsPerSample = bits_per_sample;
+ f_conf1[i].format.nBlockAlign = channel_count * bits_per_sample / 8;
+ f_conf1[i].format.nAvgBytesPerSec = sample_rate * f_conf1[i].format.nBlockAlign;
+
+ /* bytes after this value in this struct */
+ f_conf1[i].format.cbSize = 22;
+ /* actual bits in container */
+ f_conf1[i].format.wValidBitsPerSample = bits_per_sample;
+ /* channel map not used at this time */
+ f_conf1[i].format.dwChannelMask = 0;
+ /* WAVE_FORMAT_PCM guid (0x0001) ? */
+ f_conf1[i].format.SubFormat[0] = 0;
+ f_conf1[i].format.SubFormat[1] = 0;
+ f_conf1[i].format.SubFormat[2] = 0;
+ f_conf1[i].format.SubFormat[3] = 0;
+
+ ret = ssp_get_vendor_blob_size(nhlt, &blob_size);
+ if (ret < 0) {
+ fprintf(stderr, "nhlt_ssp_get_ep: dmic_get_vendor_blob_size failed\n");
+ return ret;
+ }
+ f_conf1[i].vendor_blob.capabilities_size = blob_size;
+ }
+
+ ep.length = sizeof(struct endpoint_descriptor) +
+ sizeof(struct ssp_device_specific_config) +
+ sizeof(struct formats_config) +
+ sizeof(struct format_config) * f_conf.formats_count +
+ blob_size * f_conf.formats_count;
+
+ /* allocate the final variable length ep struct */
+ ep_target = calloc(ep.length, sizeof(uint8_t));
+ if (!ep_target)
+ return -ENOMEM;
+
+ *eps = (struct endpoint_descriptor *)ep_target;
+
+ /* copy all parsed sub arrays into the top level array */
+ memcpy(ep_target, &ep, sizeof(struct endpoint_descriptor));
+
+ ep_target += sizeof(struct endpoint_descriptor);
+
+ memcpy(ep_target, &ssp_conf, sizeof(struct ssp_device_specific_config));
+ ep_target += sizeof(struct ssp_device_specific_config);
+
+ memcpy(ep_target, &f_conf, sizeof(struct formats_config));
+ ep_target += sizeof(struct formats_config);
+
+ /* copy all hw configs */
+ for (i = 0; i < f_conf.formats_count; i++) {
+ memcpy(ep_target, &f_conf1[i], sizeof(struct format_config));
+ ep_target += sizeof(struct format_config);
+ ret = ssp_get_vendor_blob(nhlt, ep_target, dai_index, i);
+ if (ret < 0) {
+ fprintf(stderr, "nhlt_sso_get_ep: ssp_get_vendor_blob failed\n");
+ return ret;
+ }
+ ep_target += blob_size;
+ }
+
+ return 0;
+}
+
+/* Set ssp parameters from topology for ssp coefficient calculation.
+ *
+ * You can see an example of topology v2 config of ssp below. In this example the default
+ * object parameters are spelled out for clarity. General parameters like sample_bits are parsed
+ * with set_ssp_data and hw_config object data with set_hw_data. Ssp can have multiple hw_configs.
+ * Values are saved into intermediate structs and the vendor specific blob is calculated at the end
+ * of parsing with ssp_calculate.
+ *
+ * SSP."0" {
+ * id 0
+ * direction "duplex"
+ * name NoCodec-0
+ * io_clk 38400000
+ * default_hw_conf_id 0
+ * sample_bits 16
+ * quirks "lbm_mode"
+ * bclk_delay 0
+ * mclk_id 0
+ * clks_control 0
+ * frame_pulse_width 0
+ * tdm_padding_per_slot false
+ *
+ * Object.Base.hw_config."SSP0" {
+ * id 0
+ * mclk_freq 24576000
+ * bclk_freq 3072000
+ * tdm_slot_width 32
+ * format "I2S"
+ * mclk "codec_mclk_in"
+ * bclk "codec_consumer"
+ * fsync "codec_consumer"
+ * fsync_freq 48000
+ * tdm_slots 2
+ * tx_slots 3
+ * rx_slots 3
+ * }
+ * }
+ */
+int nhlt_ssp_set_params(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *items;
+ snd_config_t *n;
+ const char *id;
+ int ret;
+
+ ret = set_ssp_data(nhlt, cfg, top);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_search(cfg, "Object.Base.hw_config", &items);
+ if (ret < 0)
+ return ret;
+
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ ret = set_hw_config(nhlt, n, top);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ssp_calculate(nhlt);
+
+ return ret;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SSP_NHLT_H
+#define __SSP_NHLT_H
+
+#include "intel-nhlt.h"
+#include "../nhlt.h"
+
+int nhlt_ssp_init_params(struct intel_nhlt_params *nhlt);
+int nhlt_ssp_set_params(struct intel_nhlt_params *nhlt, snd_config_t *cfg, snd_config_t *top);
+int nhlt_ssp_get_ep(struct intel_nhlt_params *nhlt, struct endpoint_descriptor **eps,
+ int dai_index, uint8_t dir);
+int nhlt_ssp_get_ep_count(struct intel_nhlt_params *nhlt);
+int nhlt_ssp_get_dir(struct intel_nhlt_params *nhlt, int dai_index, uint8_t *dir);
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdio.h>
+#include <stdint.h>
+#include "ssp-debug.h"
+
+#ifdef NHLT_DEBUG
+
+void ssp_print_calculated(struct intel_ssp_params *ssp)
+{
+ int i, j;
+
+ fprintf(stdout, "printing ssp nhlt calculated data:\n");
+
+ /* top level struct */
+ fprintf(stdout, "ssp count %d\n", ssp->ssp_count);
+
+ for (i = 0; i < ssp->ssp_count; i++)
+ fprintf(stdout, "ssp %d dai_index: %u\n", i, ssp->ssp_dai_index[i]);
+
+ for (i = 0; i < ssp->ssp_count; i++)
+ fprintf(stdout, "ssp %d hw_config_count: %u\n", i, ssp->ssp_hw_config_count[i]);
+
+ fprintf(stdout, "\n");
+
+ for (i = 0; i < ssp->ssp_count; i++) {
+ for (j = 0; j < ssp->ssp_hw_config_count[i]; j++) {
+ fprintf(stdout, "ssp blob %d hw_config %d\n", i, j);
+ fprintf(stdout, "gateway_attributes %u\n",
+ ssp->ssp_blob[i][j].gateway_attributes);
+ fprintf(stdout, "ts_group[0] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[0]);
+ fprintf(stdout, "ts_group[1] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[1]);
+ fprintf(stdout, "ts_group[2] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[2]);
+ fprintf(stdout, "ts_group[3] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[3]);
+ fprintf(stdout, "ts_group[4] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[4]);
+ fprintf(stdout, "ts_group[5] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[5]);
+ fprintf(stdout, "ts_group[6] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[6]);
+ fprintf(stdout, "ts_group[7] 0x%08x\n", ssp->ssp_blob[i][j].ts_group[7]);
+ fprintf(stdout, "ssc0 0x%08x\n", ssp->ssp_blob[i][j].ssc0);
+ fprintf(stdout, "ssc1 0x%08x\n", ssp->ssp_blob[i][j].ssc1);
+ fprintf(stdout, "sscto 0x%08x\n", ssp->ssp_blob[i][j].sscto);
+ fprintf(stdout, "sspsp 0x%08x\n", ssp->ssp_blob[i][j].sspsp);
+ fprintf(stdout, "sstsa 0x%08x\n", ssp->ssp_blob[i][j].sstsa);
+ fprintf(stdout, "ssrsa 0x%08x\n", ssp->ssp_blob[i][j].ssrsa);
+ fprintf(stdout, "ssc2 0x%08x\n", ssp->ssp_blob[i][j].ssc2);
+ fprintf(stdout, "sspsp2 0x%08x\n", ssp->ssp_blob[i][j].sspsp2);
+ fprintf(stdout, "ssc3 0x%08x\n", ssp->ssp_blob[i][j].ssc3);
+ fprintf(stdout, "ssioc 0x%08x\n", ssp->ssp_blob[i][j].ssioc);
+ fprintf(stdout, "mdivc 0x%08x\n", ssp->ssp_blob[i][j].mdivc);
+ fprintf(stdout, "mdivr 0x%08x\n", ssp->ssp_blob[i][j].mdivr);
+ }
+ }
+
+ fprintf(stdout, "\n");
+}
+
+void ssp_print_internal(struct intel_ssp_params *ssp)
+{
+ int i;
+
+ fprintf(stdout, "printing ssp nhlt internal data:\n");
+
+ fprintf(stdout, "io_clk %u\n", ssp->ssp_prm.io_clk);
+ fprintf(stdout, "dai_index %u\n", ssp->ssp_prm.dai_index);
+ fprintf(stdout, "mclk_id %u\n", ssp->ssp_prm.mclk_id);
+ fprintf(stdout, "sample_valid_bits %u\n", ssp->ssp_prm.sample_valid_bits);
+ fprintf(stdout, "mclk_direction %u\n", ssp->ssp_prm.mclk_direction);
+ fprintf(stdout, "frame_pulse_width %u\n", ssp->ssp_prm.frame_pulse_width);
+ fprintf(stdout, "tdm_per_slot_padding_flag %u\n", ssp->ssp_prm.tdm_per_slot_padding_flag);
+ fprintf(stdout, "clks_control %u\n", ssp->ssp_prm.clks_control);
+ fprintf(stdout, "quirks %u\n", ssp->ssp_prm.quirks);
+ fprintf(stdout, "bclk_delay %u\n", ssp->ssp_prm.bclk_delay);
+
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "hw_config_count %u\n", ssp->ssp_hw_config_count[ssp->ssp_count]);
+
+ for (i = 0; i < ssp->ssp_hw_config_count[ssp->ssp_count]; i++) {
+ fprintf(stdout, "mclk_rate %u\n", ssp->ssp_prm.hw_cfg[i].mclk_rate);
+ fprintf(stdout, "bclk_rate %u\n", ssp->ssp_prm.hw_cfg[i].bclk_rate);
+ fprintf(stdout, "fsync_rate %u\n", ssp->ssp_prm.hw_cfg[i].fsync_rate);
+ fprintf(stdout, "tdm_slots %u\n", ssp->ssp_prm.hw_cfg[i].tdm_slots);
+ fprintf(stdout, "tdm_slot_width %u\n", ssp->ssp_prm.hw_cfg[i].tdm_slot_width);
+ fprintf(stdout, "tx_slots %u\n", ssp->ssp_prm.hw_cfg[i].tx_slots);
+ fprintf(stdout, "rx_slots %u\n", ssp->ssp_prm.hw_cfg[i].rx_slots);
+ fprintf(stdout, "format %u\n", ssp->ssp_prm.hw_cfg[i].format);
+ }
+
+ fprintf(stdout, "\n");
+}
+
+#else /* NHLT_DEBUG */
+void ssp_print_internal(struct intel_ssp_params *ssp) {}
+void ssp_print_calculated(struct intel_ssp_params *ssp) {}
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SSP_DEBUG_H
+#define __SSP_DEBUG_H
+
+#include "ssp-internal.h"
+
+void ssp_print_internal(struct intel_ssp_params *ssp);
+void ssp_print_calculated(struct intel_ssp_params *ssp);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// Keyon Jie <yang.jie@linux.intel.com>
+// Rander Wang <rander.wang@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SSP_INTEL_H
+#define __SSP_INTEL_H
+
+#include <stdint.h>
+
+/* struct for intel ssp nhlt vendor specific blob generation */
+struct ssp_intel_config_data {
+ uint32_t gateway_attributes;
+ uint32_t ts_group[8];
+ uint32_t ssc0;
+ uint32_t ssc1;
+ uint32_t sscto;
+ uint32_t sspsp;
+ uint32_t sstsa;
+ uint32_t ssrsa;
+ uint32_t ssc2;
+ uint32_t sspsp2;
+ uint32_t ssc3;
+ uint32_t ssioc;
+ uint32_t mdivc;
+ uint32_t mdivr;
+} __attribute__((packed));
+
+#define SSP_BLOB_VER_1_5 0xEE000105
+
+struct ssp_intel_config_data_1_5 {
+ uint32_t gateway_attributes;
+ uint32_t version;
+ uint32_t size;
+ uint32_t ts_group[8];
+ uint32_t ssc0;
+ uint32_t ssc1;
+ uint32_t sscto;
+ uint32_t sspsp;
+ uint32_t sstsa;
+ uint32_t ssrsa;
+ uint32_t ssc2;
+ uint32_t sspsp2;
+ uint32_t ssc3;
+ uint32_t ssioc;
+ uint32_t mdivctlr;
+ uint32_t mdivrcnt;
+ uint32_t mdivr[];
+} __attribute__((packed));
+
+#endif /* __SSP_INTEL_H */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// Keyon Jie <yang.jie@linux.intel.com>
+// Rander Wang <rander.wang@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SSP_MACROS_H
+#define __SSP_MACROS_H
+
+#include "ssp-intel.h"
+
+#define SSP_MAX_DAIS 8
+#define SSP_MAX_HW_CONFIG 8
+#define SSP_TDM_MAX_SLOT_MAP_COUNT 8
+
+
+
+/* structs for gathering the ssp parameters from topology */
+struct ssp_config_hw {
+ uint32_t mclk_rate;
+ uint32_t bclk_rate;
+ uint32_t fsync_rate;
+ uint32_t tdm_slots;
+ uint32_t tdm_slot_width;
+ uint32_t tx_slots;
+ uint32_t rx_slots;
+ uint32_t format;
+};
+
+struct ssp_config_dai {
+ uint32_t io_clk;
+ uint32_t dai_index;
+ uint16_t mclk_id;
+ uint32_t sample_valid_bits;
+ uint32_t mclk_direction;
+ uint16_t frame_pulse_width;
+ uint16_t tdm_per_slot_padding_flag;
+ uint32_t clks_control;
+ uint32_t quirks;
+ uint32_t bclk_delay;
+ uint8_t direction;
+ struct ssp_config_hw hw_cfg[SSP_MAX_HW_CONFIG];
+};
+
+struct intel_ssp_params {
+ /* structs to gather ssp params before calculations */
+ struct ssp_config_dai ssp_prm;
+ uint32_t ssp_dai_index[SSP_MAX_DAIS];
+ uint32_t ssp_hw_config_count[SSP_MAX_DAIS];
+ int ssp_count;
+
+ /* ssp vendor blob structs */
+ struct ssp_intel_config_data ssp_blob[SSP_MAX_DAIS][SSP_MAX_HW_CONFIG];
+};
+
+#define SSP_CLOCK_XTAL_OSCILLATOR 0x0
+#define SSP_CLOCK_AUDIO_CARDINAL 0x1
+#define SSP_CLOCK_PLL_FIXED 0x2
+
+#define MCDSS(x) SET_BITS(17, 16, x)
+#define MNDSS(x) SET_BITS(21, 20, x)
+
+#define SSP_FMT_I2S 1 /**< I2S mode */
+#define SSP_FMT_RIGHT_J 2 /**< Right Justified mode */
+#define SSP_FMT_LEFT_J 3 /**< Left Justified mode */
+#define SSP_FMT_DSP_A 4 /**< L data MSB after FRM LRC */
+#define SSP_FMT_DSP_B 5 /**< L data MSB during FRM LRC */
+#define SSP_FMT_PDM 6 /**< Pulse density modulation */
+
+#define SSP_FMT_CONT (1 << 4) /**< continuous clock */
+#define SSP_FMT_GATED (0 << 4) /**< clock is gated */
+
+#define SSP_FMT_NB_NF (0 << 8) /**< normal bit clock + frame */
+#define SSP_FMT_NB_IF (2 << 8) /**< normal BCLK + inv FRM */
+#define SSP_FMT_IB_NF (3 << 8) /**< invert BCLK + nor FRM */
+#define SSP_FMT_IB_IF (4 << 8) /**< invert BCLK + FRM */
+
+#define SSP_FMT_CBP_CFP (0 << 12) /**< codec bclk provider & frame provider */
+#define SSP_FMT_CBC_CFP (2 << 12) /**< codec bclk consumer & frame provider */
+#define SSP_FMT_CBP_CFC (3 << 12) /**< codec bclk provider & frame consumer */
+#define SSP_FMT_CBC_CFC (4 << 12) /**< codec bclk consumer & frame consumer */
+
+#define SSP_FMT_FORMAT_MASK 0x000f
+#define SSP_FMT_CLOCK_MASK 0x00f0
+#define SSP_FMT_INV_MASK 0x0f00
+#define SSP_FMT_CLOCK_PROVIDER_MASK 0xf000
+
+/* SSCR0 bits */
+#define SSCR0_DSIZE(x) SET_BITS(3, 0, (x) - 1)
+#define SSCR0_FRF MASK(5, 4)
+#define SSCR0_MOT SET_BITS(5, 4, 0)
+#define SSCR0_TI SET_BITS(5, 4, 1)
+#define SSCR0_NAT SET_BITS(5, 4, 2)
+#define SSCR0_PSP SET_BITS(5, 4, 3)
+#define SSCR0_ECS BIT(6)
+#define SSCR0_SSE BIT(7)
+#define SSCR0_SCR_MASK MASK(19, 8)
+#define SSCR0_SCR(x) SET_BITS(19, 8, x)
+#define SSCR0_EDSS BIT(20)
+#define SSCR0_NCS BIT(21)
+#define SSCR0_RIM BIT(22)
+#define SSCR0_TIM BIT(23)
+#define SSCR0_FRDC(x) SET_BITS(26, 24, (x) - 1)
+#define SSCR0_ACS BIT(30)
+#define SSCR0_MOD BIT(31)
+
+/* SSCR1 bits */
+#define SSCR1_RIE BIT(0)
+#define SSCR1_TIE BIT(1)
+#define SSCR1_LBM BIT(2)
+#define SSCR1_SPO BIT(3)
+#define SSCR1_SPH BIT(4)
+#define SSCR1_MWDS BIT(5)
+#define SSCR1_TFT_MASK MASK(9, 6)
+#define SSCR1_TFT(x) SET_BITS(9, 6, (x) - 1)
+#define SSCR1_RFT_MASK MASK(13, 10)
+#define SSCR1_RFT(x) SET_BITS(13, 10, (x) - 1)
+#define SSCR1_EFWR BIT(14)
+#define SSCR1_STRF BIT(15)
+#define SSCR1_IFS BIT(16)
+#define SSCR1_PINTE BIT(18)
+#define SSCR1_TINTE BIT(19)
+#define SSCR1_RSRE BIT(20)
+#define SSCR1_TSRE BIT(21)
+#define SSCR1_TRAIL BIT(22)
+#define SSCR1_RWOT BIT(23)
+#define SSCR1_SFRMDIR BIT(24)
+#define SSCR1_SCLKDIR BIT(25)
+#define SSCR1_ECRB BIT(26)
+#define SSCR1_ECRA BIT(27)
+#define SSCR1_SCFR BIT(28)
+#define SSCR1_EBCEI BIT(29)
+#define SSCR1_TTE BIT(30)
+#define SSCR1_TTELP BIT(31)
+
+/* SSCR2 bits */
+#define SSCR2_URUN_FIX0 BIT(0)
+#define SSCR2_URUN_FIX1 BIT(1)
+#define SSCR2_SLV_EXT_CLK_RUN_EN BIT(2)
+#define SSCR2_CLK_DEL_EN BIT(3)
+#define SSCR2_UNDRN_FIX_EN BIT(6)
+#define SSCR2_FIFO_EMPTY_FIX_EN BIT(7)
+#define SSCR2_ASRC_CNTR_EN BIT(8)
+#define SSCR2_ASRC_CNTR_CLR BIT(9)
+#define SSCR2_ASRC_FRM_CNRT_EN BIT(10)
+#define SSCR2_ASRC_INTR_MASK BIT(11)
+#define SSCR2_TURM1 BIT(1)
+#define SSCR2_PSPSRWFDFD BIT(3)
+#define SSCR2_PSPSTWFDFD BIT(4)
+#define SSCR2_SDFD BIT(14)
+#define SSCR2_SDPM BIT(16)
+#define SSCR2_LJDFD BIT(17)
+#define SSCR2_MMRATF BIT(18)
+#define SSCR2_SMTATF BIT(19)
+
+/* SSR bits */
+#define SSSR_TNF BIT(2)
+#define SSSR_RNE BIT(3)
+#define SSSR_BSY BIT(4)
+#define SSSR_TFS BIT(5)
+#define SSSR_RFS BIT(6)
+#define SSSR_ROR BIT(7)
+#define SSSR_TUR BIT(21)
+
+/* SSPSP bits */
+#define SSPSP_SCMODE(x) SET_BITS(1, 0, x)
+#define SSPSP_SFRMP(x) SET_BIT(2, x)
+#define SSPSP_ETDS BIT(3)
+#define SSPSP_STRTDLY(x) SET_BITS(6, 4, x)
+#define SSPSP_DMYSTRT(x) SET_BITS(8, 7, x)
+#define SSPSP_SFRMDLY(x) SET_BITS(15, 9, x)
+#define SSPSP_SFRMWDTH(x) SET_BITS(21, 16, x)
+#define SSPSP_DMYSTOP(x) SET_BITS(24, 23, x)
+#define SSPSP_DMYSTOP_BITS 2
+#define SSPSP_DMYSTOP_MASK MASK(SSPSP_DMYSTOP_BITS - 1, 0)
+#define SSPSP_FSRT BIT(25)
+#define SSPSP_EDMYSTOP(x) SET_BITS(28, 26, x)
+
+#define SSPSP2 0x44
+#define SSPSP2_FEP_MASK 0xff
+
+#define SSCR3 0x48
+#define SSIOC 0x4C
+#define SSP_REG_MAX SSIOC
+
+/* SSTSA bits */
+#define SSTSA_SSTSA(x) SET_BITS(7, 0, x)
+#define SSTSA_TXEN BIT(8)
+
+/* SSRSA bits */
+#define SSRSA_SSRSA(x) SET_BITS(7, 0, x)
+#define SSRSA_RXEN BIT(8)
+
+/* SSCR3 bits */
+#define SSCR3_FRM_MST_EN BIT(0)
+#define SSCR3_I2S_MODE_EN BIT(1)
+#define SSCR3_I2S_FRM_POL(x) SET_BIT(2, x)
+#define SSCR3_I2S_TX_SS_FIX_EN BIT(3)
+#define SSCR3_I2S_RX_SS_FIX_EN BIT(4)
+#define SSCR3_I2S_TX_EN BIT(9)
+#define SSCR3_I2S_RX_EN BIT(10)
+#define SSCR3_CLK_EDGE_SEL BIT(12)
+#define SSCR3_STRETCH_TX BIT(14)
+#define SSCR3_STRETCH_RX BIT(15)
+#define SSCR3_MST_CLK_EN BIT(16)
+#define SSCR3_SYN_FIX_EN BIT(17)
+
+/* SSCR4 bits */
+#define SSCR4_TOT_FRM_PRD(x) ((x) << 7)
+
+/* SSCR5 bits */
+#define SSCR5_FRM_ASRT_CLOCKS(x) (((x) - 1) << 1)
+#define SSCR5_FRM_POLARITY(x) SET_BIT(0, x)
+
+/* SFIFOTT bits */
+#define SFIFOTT_TX(x) ((x) - 1)
+#define SFIFOTT_RX(x) (((x) - 1) << 16)
+
+/* SFIFOL bits */
+#define SFIFOL_TFL(x) ((x) & 0xFFFF)
+#define SFIFOL_RFL(x) ((x) >> 16)
+
+#define SSTSA_TSEN BIT(8)
+#define SSRSA_RSEN BIT(8)
+
+#define SSCR3_TFL_MASK MASK(5, 0)
+#define SSCR3_RFL_MASK MASK(13, 8)
+#define SSCR3_TFL_VAL(scr3_val) (((scr3_val) >> 0) & MASK(5, 0))
+#define SSCR3_RFL_VAL(scr3_val) (((scr3_val) >> 8) & MASK(5, 0))
+#define SSCR3_TX(x) SET_BITS(21, 16, (x) - 1)
+#define SSCR3_RX(x) SET_BITS(29, 24, (x) - 1)
+
+#define SSIOC_TXDPDEB BIT(1)
+#define SSIOC_SFCR BIT(4)
+#define SSIOC_SCOE BIT(5)
+
+#define MAX_SSP_COUNT 8
+#define SSP_FIFO_DEPTH 16
+#define SSP_FIFO_WATERMARK 8
+
+#define SSP_INTEL_QUIRK_TINTE (1 << 0)
+#define SSP_INTEL_QUIRK_PINTE (1 << 1)
+#define SSP_INTEL_QUIRK_SMTATF (1 << 2)
+#define SSP_INTEL_QUIRK_MMRATF (1 << 3)
+#define SSP_INTEL_QUIRK_PSPSTWFDFD (1 << 4)
+#define SSP_INTEL_QUIRK_PSPSRWFDFD (1 << 5)
+#define SSP_INTEL_QUIRK_LBM (1 << 6)
+
+#define SSP_INTEL_FRAME_PULSE_WIDTH_MAX 38
+#define SSP_INTEL_SLOT_PADDING_MAX 31
+
+/* SSP clocks control settings */
+#define SSP_INTEL_MCLK_0_DISABLE BIT(0)
+#define SSP_INTEL_MCLK_1_DISABLE BIT(1)
+#define SSP_INTEL_CLKCTRL_MCLK_KA BIT(2)
+#define SSP_INTEL_CLKCTRL_BCLK_KA BIT(3)
+#define SSP_INTEL_CLKCTRL_FS_KA BIT(4)
+#define SSP_INTEL_CLKCTRL_BCLK_IDLE_HIGH BIT(5)
+
+#endif /* __SSP_MACROS_H */
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// Keyon Jie <yang.jie@linux.intel.com>
+// Rander Wang <rander.wang@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+#include "../intel-nhlt.h"
+#include "../../nhlt.h"
+#include "ssp-process.h"
+#include "ssp-intel.h"
+#include "ssp-internal.h"
+#include "ssp-debug.h"
+
+static int popcount(uint32_t value)
+{
+ int bits_set = 0;
+
+ while (value) {
+ bits_set += value & 1;
+ value >>= 1;
+ }
+
+ return bits_set;
+}
+
+static int ssp_calculate_intern(struct intel_nhlt_params *nhlt, int hwi)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+ uint32_t active_tx_slots = 2;
+ uint32_t active_rx_slots = 2;
+ uint32_t inverted_frame = 0;
+ uint32_t inverted_bclk = 0;
+ uint32_t frame_end_padding;
+ uint32_t total_sample_size;
+ uint32_t slot_end_padding;
+ bool start_delay = false;
+ uint32_t frame_len = 0;
+ uint32_t sample_width;
+ uint32_t end_padding;
+ uint32_t data_size;
+ uint32_t bdiv_min;
+ bool cfs = false;
+ uint32_t clk_div;
+ uint32_t bdiv;
+ uint32_t tft;
+ uint32_t rft;
+ int di;
+ int i, j;
+
+ if (!ssp)
+ return -EINVAL;
+
+ di = ssp->ssp_count;
+
+ /* should be eventually the lp_mode defined in pipeline */
+ ssp->ssp_blob[di][hwi].gateway_attributes = 0;
+
+ for (j = 0; j < SSP_TDM_MAX_SLOT_MAP_COUNT; j++) {
+ for (i = 0; i < ssp->ssp_prm.hw_cfg[hwi].tdm_slots; i++)
+ ssp->ssp_blob[di][hwi].ts_group[j] |= (i << (i * 4));
+ for (; i < SSP_TDM_MAX_SLOT_MAP_COUNT; i++)
+ ssp->ssp_blob[di][hwi].ts_group[j] |= (0xF << (i * 4));
+ }
+
+ /* reset SSP settings */
+ /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */
+ ssp->ssp_blob[di][hwi].ssc0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
+
+ /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */
+ ssp->ssp_blob[di][hwi].ssc1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE |
+ SSCR1_TSRE;
+
+ /* sscr2 dynamic setting is LJDFD */
+ ssp->ssp_blob[di][hwi].ssc2 = SSCR2_SDFD | SSCR2_TURM1;
+
+ /* sscr3 dynamic settings are TFT, RFT */
+ ssp->ssp_blob[di][hwi].ssc3 = 0;
+
+ /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */
+ ssp->ssp_blob[di][hwi].sspsp = 0;
+
+ /* sspsp2 no dynamic setting */
+ ssp->ssp_blob[di][hwi].sspsp2 = 0x0;
+
+ /* ssioc dynamic setting is SFCR */
+ ssp->ssp_blob[di][hwi].ssioc = SSIOC_SCOE;
+
+ /* ssto no dynamic setting */
+ ssp->ssp_blob[di][hwi].sscto = 0x0;
+
+ /* sstsa dynamic setting is TTSA, default 2 slots */
+ ssp->ssp_blob[di][hwi].sstsa = SSTSA_SSTSA(ssp->ssp_prm.hw_cfg[hwi].tx_slots);
+
+ /* ssrsa dynamic setting is RTSA, default 2 slots */
+ ssp->ssp_blob[di][hwi].ssrsa = SSRSA_SSRSA(ssp->ssp_prm.hw_cfg[hwi].rx_slots);
+
+ switch (ssp->ssp_prm.hw_cfg[hwi].format & SSP_FMT_CLOCK_PROVIDER_MASK) {
+ case SSP_FMT_CBP_CFP:
+ ssp->ssp_blob[di][hwi].ssc1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+ break;
+ case SSP_FMT_CBC_CFC:
+ ssp->ssp_blob[di][hwi].ssc1 |= SSCR1_SCFR;
+ cfs = true;
+ break;
+ case SSP_FMT_CBP_CFC:
+ ssp->ssp_blob[di][hwi].ssc1 |= SSCR1_SCLKDIR;
+ /* FIXME: this mode has not been tested */
+
+ cfs = true;
+ break;
+ case SSP_FMT_CBC_CFP:
+ ssp->ssp_blob[di][hwi].ssc1 |= SSCR1_SCFR | SSCR1_SFRMDIR;
+ /* FIXME: this mode has not been tested */
+ break;
+ default:
+ fprintf(stderr, "ssp_calculate(): format & PROVIDER_MASK EINVAL\n");
+ return -EINVAL;
+ }
+
+ /* clock signal polarity */
+ switch (ssp->ssp_prm.hw_cfg[hwi].format & SSP_FMT_INV_MASK) {
+ case SSP_FMT_NB_NF:
+ break;
+ case SSP_FMT_NB_IF:
+ inverted_frame = 1; /* handled later with format */
+ break;
+ case SSP_FMT_IB_IF:
+ inverted_bclk = 1; /* handled later with bclk idle */
+ inverted_frame = 1; /* handled later with format */
+ break;
+ case SSP_FMT_IB_NF:
+ inverted_bclk = 1; /* handled later with bclk idle */
+ break;
+ default:
+ fprintf(stderr, "ssp_calculate: format & INV_MASK EINVAL\n");
+ return -EINVAL;
+ }
+
+ /* supporting bclk idle state */
+ if (ssp->ssp_prm.clks_control &
+ SSP_INTEL_CLKCTRL_BCLK_IDLE_HIGH) {
+ /* bclk idle state high */
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SCMODE((inverted_bclk ^ 0x3) & 0x3);
+ } else {
+ /* bclk idle state low */
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SCMODE(inverted_bclk);
+ }
+
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_MOD | SSCR0_ACS;
+
+ /* Additional hardware settings */
+
+ /* Receiver Time-out Interrupt Disabled/Enabled */
+ ssp->ssp_blob[di][hwi].ssc1 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_TINTE) ?
+ SSCR1_TINTE : 0;
+
+ /* Peripheral Trailing Byte Interrupts Disable/Enable */
+ ssp->ssp_blob[di][hwi].ssc1 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_PINTE) ?
+ SSCR1_PINTE : 0;
+
+ /* Enable/disable internal loopback. Output of transmit serial
+ * shifter connected to input of receive serial shifter, internally.
+ */
+ ssp->ssp_blob[di][hwi].ssc1 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_LBM) ?
+ SSCR1_LBM : 0;
+
+ /* Transmit data are driven at the same/opposite clock edge specified
+ * in SSPSP.SCMODE[1:0]
+ */
+ ssp->ssp_blob[di][hwi].ssc2 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_SMTATF) ?
+ SSCR2_SMTATF : 0;
+
+ /* Receive data are sampled at the same/opposite clock edge specified
+ * in SSPSP.SCMODE[1:0]
+ */
+ ssp->ssp_blob[di][hwi].ssc2 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_MMRATF) ?
+ SSCR2_MMRATF : 0;
+
+ /* Enable/disable the fix for PSP consumer mode TXD wait for frame
+ * de-assertion before starting the second channel
+ */
+ ssp->ssp_blob[di][hwi].ssc2 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_PSPSTWFDFD) ?
+ SSCR2_PSPSTWFDFD : 0;
+
+ /* Enable/disable the fix for PSP provider mode FSRT with dummy stop &
+ * frame end padding capability
+ */
+ ssp->ssp_blob[di][hwi].ssc2 |= (ssp->ssp_prm.quirks & SSP_INTEL_QUIRK_PSPSRWFDFD) ?
+ SSCR2_PSPSRWFDFD : 0;
+
+ if (!ssp->ssp_prm.hw_cfg[hwi].mclk_rate) {
+ fprintf(stderr, "ssp_calculate(): invalid MCLK = %u \n",
+ ssp->ssp_prm.hw_cfg[hwi].mclk_rate);
+ return -EINVAL;
+ }
+
+ if (!ssp->ssp_prm.hw_cfg[hwi].bclk_rate ||
+ ssp->ssp_prm.hw_cfg[hwi].bclk_rate > ssp->ssp_prm.hw_cfg[hwi].mclk_rate) {
+ fprintf(stderr, "ssp_calculate(): BCLK %u Hz = 0 or > MCLK %u Hz\n",
+ ssp->ssp_prm.hw_cfg[hwi].bclk_rate, ssp->ssp_prm.hw_cfg[hwi].mclk_rate);
+ return -EINVAL;
+ }
+
+ /* calc frame width based on BCLK and rate - must be divisible */
+ if (ssp->ssp_prm.hw_cfg[hwi].bclk_rate % ssp->ssp_prm.hw_cfg[hwi].fsync_rate) {
+ fprintf(stderr, "ssp_calculate(): BCLK %u is not divisible by rate %u\n",
+ ssp->ssp_prm.hw_cfg[hwi].bclk_rate, ssp->ssp_prm.hw_cfg[hwi].fsync_rate);
+ return -EINVAL;
+ }
+
+ /* must be enough BCLKs for data */
+ bdiv = ssp->ssp_prm.hw_cfg[hwi].bclk_rate / ssp->ssp_prm.hw_cfg[hwi].fsync_rate;
+ if (bdiv < ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width * ssp->ssp_prm.hw_cfg[hwi].tdm_slots) {
+ fprintf(stderr, "ssp_calculate(): not enough BCLKs need %u\n",
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width *
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slots);
+ return -EINVAL;
+ }
+
+ /* tdm_slot_width must be <= 38 for SSP */
+ if (ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width > 38) {
+ fprintf(stderr, "ssp_calculate(): tdm_slot_width %u > 38\n",
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width);
+ return -EINVAL;
+ }
+
+ bdiv_min = ssp->ssp_prm.hw_cfg[hwi].tdm_slots *
+ (ssp->ssp_prm.tdm_per_slot_padding_flag ?
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width : ssp->ssp_prm.sample_valid_bits);
+ if (bdiv < bdiv_min) {
+ fprintf(stderr, "ssp_calculate(): bdiv(%u) < bdiv_min(%u)\n",
+ bdiv, bdiv_min);
+ return -EINVAL;
+ }
+
+ frame_end_padding = bdiv - bdiv_min;
+ if (frame_end_padding > SSPSP2_FEP_MASK) {
+ fprintf(stderr, "ssp_calculate(): frame_end_padding too big: %u\n",
+ frame_end_padding);
+ return -EINVAL;
+ }
+
+ /* format */
+ switch (ssp->ssp_prm.hw_cfg[hwi].format & SSP_FMT_FORMAT_MASK) {
+ case SSP_FMT_I2S:
+
+ start_delay = true;
+
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_FRDC(ssp->ssp_prm.hw_cfg[hwi].tdm_slots);
+
+ if (bdiv % 2) {
+ fprintf(stderr, "ssp_calculate(): bdiv %u is not divisible by 2\n",
+ bdiv);
+ return -EINVAL;
+ }
+
+ /* set asserted frame length to half frame length */
+ frame_len = bdiv / 2;
+
+ /*
+ * handle frame polarity, I2S default is falling/active low,
+ * non-inverted(inverted_frame=0) -- active low(SFRMP=0),
+ * inverted(inverted_frame=1) -- rising/active high(SFRMP=1),
+ * so, we should set SFRMP to inverted_frame.
+ */
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SFRMP(inverted_frame);
+
+ /*
+ * for I2S/LEFT_J, the padding has to happen at the end
+ * of each slot
+ */
+ if (frame_end_padding % 2) {
+ fprintf(stderr, "ssp_calculate():frame_end_padding %u not divisible by 2\n",
+ frame_end_padding);
+ return -EINVAL;
+ }
+
+ slot_end_padding = frame_end_padding / 2;
+
+ if (slot_end_padding > SSP_INTEL_SLOT_PADDING_MAX) {
+ /* too big padding */
+ fprintf(stderr, "ssp_calculate(): slot_end_padding > %d\n",
+ SSP_INTEL_SLOT_PADDING_MAX);
+ return -EINVAL;
+ }
+
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_DMYSTOP(slot_end_padding);
+ slot_end_padding >>= SSPSP_DMYSTOP_BITS;
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_EDMYSTOP(slot_end_padding);
+
+ break;
+
+ case SSP_FMT_LEFT_J:
+
+ /* default start_delay value is set to false */
+
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_FRDC(ssp->ssp_prm.hw_cfg[hwi].tdm_slots);
+
+ /* LJDFD enable */
+ ssp->ssp_blob[di][hwi].ssc2 &= ~SSCR2_LJDFD;
+
+ if (bdiv % 2) {
+ fprintf(stderr, "ssp_calculate(): bdiv %u is not divisible by 2\n",
+ bdiv);
+ return -EINVAL;
+ }
+
+ /* set asserted frame length to half frame length */
+ frame_len = bdiv / 2;
+
+ /*
+ * handle frame polarity, LEFT_J default is rising/active high,
+ * non-inverted(inverted_frame=0) -- active high(SFRMP=1),
+ * inverted(inverted_frame=1) -- falling/active low(SFRMP=0),
+ * so, we should set SFRMP to !inverted_frame.
+ */
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SFRMP(!inverted_frame ? 1 : 0);
+
+ /*
+ * for I2S/LEFT_J, the padding has to happen at the end
+ * of each slot
+ */
+ if (frame_end_padding % 2) {
+ fprintf(stderr, "ssp_set_config(): frame padding %u not divisible by 2\n",
+ frame_end_padding);
+ return -EINVAL;
+ }
+
+ slot_end_padding = frame_end_padding / 2;
+
+ if (slot_end_padding > 15) {
+ /* can't handle padding over 15 bits */
+ fprintf(stderr, "ssp_set_config(): slot_end_padding %u > 15 bits\n",
+ slot_end_padding);
+ return -EINVAL;
+ }
+
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_DMYSTOP(slot_end_padding);
+ slot_end_padding >>= SSPSP_DMYSTOP_BITS;
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_EDMYSTOP(slot_end_padding);
+
+ break;
+ case SSP_FMT_DSP_A:
+
+ start_delay = true;
+
+ /* fallthrough */
+
+ case SSP_FMT_DSP_B:
+
+ /* default start_delay value is set to false */
+
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_MOD |
+ SSCR0_FRDC(ssp->ssp_prm.hw_cfg[hwi].tdm_slots);
+
+ /* set asserted frame length */
+ frame_len = 1; /* default */
+
+ if (cfs && ssp->ssp_prm.frame_pulse_width > 0 &&
+ ssp->ssp_prm.frame_pulse_width <=
+ SSP_INTEL_FRAME_PULSE_WIDTH_MAX) {
+ frame_len = ssp->ssp_prm.frame_pulse_width;
+ }
+
+ /* frame_pulse_width must less or equal 38 */
+ if (ssp->ssp_prm.frame_pulse_width >
+ SSP_INTEL_FRAME_PULSE_WIDTH_MAX) {
+ fprintf(stderr, "ssp_set_config(): frame_pulse_width > %d\n",
+ SSP_INTEL_FRAME_PULSE_WIDTH_MAX);
+ return -EINVAL;
+ }
+ /*
+ * handle frame polarity, DSP_B default is rising/active high,
+ * non-inverted(inverted_frame=0) -- active high(SFRMP=1),
+ * inverted(inverted_frame=1) -- falling/active low(SFRMP=0),
+ * so, we should set SFRMP to !inverted_frame.
+ */
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SFRMP(!inverted_frame ? 1 : 0);
+
+ active_tx_slots = popcount(ssp->ssp_prm.hw_cfg[hwi].tx_slots);
+ active_rx_slots = popcount(ssp->ssp_prm.hw_cfg[hwi].rx_slots);
+
+ /*
+ * handle TDM mode, TDM mode has padding at the end of
+ * each slot. The amount of padding is equal to result of
+ * subtracting slot width and valid bits per slot.
+ */
+ if (ssp->ssp_prm.tdm_per_slot_padding_flag) {
+ frame_end_padding = bdiv - ssp->ssp_prm.hw_cfg[hwi].tdm_slots *
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width;
+
+ slot_end_padding = ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width -
+ ssp->ssp_prm.sample_valid_bits;
+
+ if (slot_end_padding >
+ SSP_INTEL_SLOT_PADDING_MAX) {
+ fprintf(stderr, "ssp_set_config(): slot_end_padding > %d\n",
+ SSP_INTEL_SLOT_PADDING_MAX);
+ return -EINVAL;
+ }
+
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_DMYSTOP(slot_end_padding);
+ slot_end_padding >>= SSPSP_DMYSTOP_BITS;
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_EDMYSTOP(slot_end_padding);
+ }
+
+ ssp->ssp_blob[di][hwi].sspsp2 |= (frame_end_padding & SSPSP2_FEP_MASK);
+
+ break;
+ default:
+ fprintf(stderr, "ssp_set_config(): invalid format 0x%04x\n",
+ ssp->ssp_prm.hw_cfg[hwi].format);
+ return -EINVAL;
+ }
+
+ if (start_delay)
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_FSRT;
+
+ ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SFRMWDTH(frame_len);
+
+ data_size = ssp->ssp_prm.sample_valid_bits;
+
+ if (data_size > 16)
+ ssp->ssp_blob[di][hwi].ssc0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16));
+ else
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_DSIZE(data_size);
+
+ end_padding = 0;
+ total_sample_size = ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width *
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slots;
+ while (ssp->ssp_prm.io_clk % ((total_sample_size + end_padding) *
+ ssp->ssp_prm.hw_cfg[hwi].fsync_rate)) {
+ if (++end_padding >= 256)
+ break;
+ }
+
+ if (end_padding >= 256)
+ return -EINVAL;
+
+ /* calc scr divisor */
+ clk_div = ssp->ssp_prm.io_clk / ((total_sample_size + end_padding) *
+ ssp->ssp_prm.hw_cfg[hwi].fsync_rate);
+ if (clk_div >= 4095)
+ return -EINVAL;
+
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_SCR(clk_div - 1);
+
+ /* setting TFT and RFT */
+ switch (ssp->ssp_prm.sample_valid_bits) {
+ case 16:
+ /* use 2 bytes for each slot */
+ sample_width = 2;
+ break;
+ case 24:
+ case 32:
+ /* use 4 bytes for each slot */
+ sample_width = 4;
+ break;
+ default:
+ fprintf(stderr, "ssp_set_config(): sample_valid_bits %u\n",
+ ssp->ssp_prm.sample_valid_bits);
+ return -EINVAL;
+ }
+
+ tft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK,
+ sample_width * active_tx_slots);
+ rft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK,
+ sample_width * active_rx_slots);
+
+ ssp->ssp_blob[di][hwi].ssc3 |= SSCR3_TX(tft) | SSCR3_RX(rft);
+
+ /* calc mn divisor */
+ if (ssp->ssp_prm.io_clk % ssp->ssp_prm.hw_cfg[hwi].mclk_rate) {
+ fprintf(stderr, "ssp_set_config(): io_clk not divisible with mclk\n");
+ return -EINVAL;
+ }
+
+ clk_div = ssp->ssp_prm.io_clk / ssp->ssp_prm.hw_cfg[hwi].mclk_rate;
+ if (clk_div > 1)
+ clk_div -= 2;
+ else
+ clk_div = 0xFFF; /* bypass clk divider */
+
+ ssp->ssp_blob[di][hwi].mdivr = clk_div;
+ /* clock will always go through the divider */
+ ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_ECS;
+ /* enable divider for this clock id */
+ ssp->ssp_blob[di][hwi].mdivc |= BIT(ssp->ssp_prm.mclk_id);
+ /* set mclk source always for audio cardinal clock */
+ ssp->ssp_blob[di][hwi].mdivc |= MCDSS(SSP_CLOCK_AUDIO_CARDINAL);
+ /* set bclk source for audio cardinal clock */
+ ssp->ssp_blob[di][hwi].mdivc |= MNDSS(SSP_CLOCK_AUDIO_CARDINAL);
+
+ return 0;
+}
+
+int ssp_calculate(struct intel_nhlt_params *nhlt)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+ int i;
+
+ if (!ssp)
+ return -EINVAL;
+
+ ssp_print_internal(ssp);
+
+ /* calculate blob for every hw config */
+ for (i = 0; i < ssp->ssp_hw_config_count[ssp->ssp_count]; i++)
+ ssp_calculate_intern(nhlt, i);
+
+ ssp->ssp_count++;
+
+ ssp_print_calculated(ssp);
+
+ return 0;
+}
+
+int ssp_get_dir(struct intel_nhlt_params *nhlt, int dai_index, uint8_t *dir)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp)
+ return -EINVAL;
+
+ *dir = ssp->ssp_prm.direction;
+
+ return 0;
+}
+
+int ssp_get_params(struct intel_nhlt_params *nhlt, int dai_index, uint32_t *virtualbus_id,
+ uint32_t *formats_count)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp)
+ return -EINVAL;
+
+ *virtualbus_id = ssp->ssp_dai_index[dai_index];
+ *formats_count = ssp->ssp_hw_config_count[dai_index];
+
+ return 0;
+}
+
+int ssp_get_hw_params(struct intel_nhlt_params *nhlt, int hw_index, uint32_t *sample_rate,
+ uint16_t *channel_count, uint32_t *bits_per_sample)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp)
+ return -EINVAL;
+
+ *channel_count = ssp->ssp_prm.hw_cfg[hw_index].tdm_slots;
+ *sample_rate = ssp->ssp_prm.hw_cfg[hw_index].fsync_rate;
+ *bits_per_sample = ssp->ssp_prm.hw_cfg[hw_index].tdm_slot_width;
+
+ return 0;
+}
+
+/*
+ * Build ssp vendor blob from calculated parameters.
+ *
+ * Supposed to be called after all ssp DAIs are parsed from topology and the final nhlt blob is
+ * generated.
+ */
+int ssp_get_vendor_blob_size(struct intel_nhlt_params *nhlt, size_t *size)
+{
+ *size = sizeof(struct ssp_intel_config_data);
+
+ return 0;
+}
+
+int ssp_get_vendor_blob_count(struct intel_nhlt_params *nhlt)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp || !ssp->ssp_count)
+ return -EINVAL;
+
+ return ssp->ssp_count;
+}
+
+/* Get the size of dynamic vendor blob to reserve proper amount of memory */
+int ssp_get_vendor_blob(struct intel_nhlt_params *nhlt, uint8_t *vendor_blob,
+ int dai_index, int hw_config_index)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp)
+ return -EINVAL;
+
+ /* top level struct */
+ memcpy(vendor_blob, &ssp->ssp_blob[dai_index][hw_config_index],
+ sizeof(struct ssp_intel_config_data));
+
+ return 0;
+}
+
+int ssp_set_params(struct intel_nhlt_params *nhlt, const char *dir, int dai_index, int io_clk,
+ int bclk_delay, int sample_bits, int mclk_id, int clks_control,
+ int frame_pulse_width, const char *tdm_padding_per_slot, const char *quirks)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+
+ if (!ssp)
+ return -EINVAL;
+
+ if (dir) {
+ if (!strcmp(dir, "playback"))
+ ssp->ssp_prm.direction = NHLT_ENDPOINT_DIRECTION_RENDER;
+ else if (!strcmp(dir, "capture"))
+ ssp->ssp_prm.direction = NHLT_ENDPOINT_DIRECTION_CAPTURE;
+ else if (!strcmp(dir, "duplex"))
+ ssp->ssp_prm.direction = NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER + 1;
+ else
+ return -EINVAL;
+ }
+ ssp->ssp_dai_index[ssp->ssp_count] = dai_index;
+ ssp->ssp_prm.io_clk = io_clk;
+ ssp->ssp_prm.bclk_delay = bclk_delay;
+ ssp->ssp_prm.sample_valid_bits = sample_bits;
+ ssp->ssp_prm.mclk_id = mclk_id;
+ ssp->ssp_prm.clks_control = clks_control;
+ ssp->ssp_prm.frame_pulse_width = frame_pulse_width;
+ if (tdm_padding_per_slot && !strcmp(tdm_padding_per_slot, "true"))
+ ssp->ssp_prm.tdm_per_slot_padding_flag = 1;
+ else
+ ssp->ssp_prm.tdm_per_slot_padding_flag = 0;
+ if (quirks && !strcmp(quirks, "lbm_mode"))
+ ssp->ssp_prm.quirks = 64; /* 1 << 6 */
+ else
+ ssp->ssp_prm.quirks = 0;
+
+ /* reset hw config count for this ssp instance */
+ ssp->ssp_hw_config_count[ssp->ssp_count] = 0;
+
+ return 0;
+}
+
+int ssp_hw_set_params(struct intel_nhlt_params *nhlt, const char *format, const char *mclk,
+ const char *bclk, const char *bclk_invert, const char *fsync,
+ const char *fsync_invert, int mclk_freq, int bclk_freq, int fsync_freq,
+ int tdm_slots, int tdm_slot_width, int tx_slots, int rx_slots)
+{
+ struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
+ uint32_t hwi;
+
+ if (!ssp)
+ return -EINVAL;
+
+ /* check that the strings are defined ?*/
+
+ /* compose format out of clock related string variables */
+ hwi = ssp->ssp_hw_config_count[ssp->ssp_count];
+
+ if (!strcmp(format, "I2S")) {
+ ssp->ssp_prm.hw_cfg[hwi].format = SSP_FMT_I2S;
+ } else if (!strcmp(format, "RIGHT_J")) {
+ ssp->ssp_prm.hw_cfg[hwi].format = SSP_FMT_RIGHT_J;
+ } else if (!strcmp(format, "LEFT_J")) {
+ ssp->ssp_prm.hw_cfg[hwi].format = SSP_FMT_LEFT_J;
+ } else if (!strcmp(format, "DSP_A")) {
+ ssp->ssp_prm.hw_cfg[hwi].format = SSP_FMT_DSP_A;
+ } else if (!strcmp(format, "DSP_B")) {
+ ssp->ssp_prm.hw_cfg[hwi].format = SSP_FMT_DSP_B;
+ } else {
+ fprintf(stderr, "no valid format specified for ssp: %s\n", format);
+ return -EINVAL;
+ }
+
+ /* clock directions wrt codec */
+ if (bclk && !strcmp(bclk, "coded_provider")) {
+ /* codec is bclk provider */
+ if (fsync && !strcmp(fsync, "coded_provider"))
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_CBP_CFP;
+ else
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_CBP_CFC;
+ } else {
+ /* codec is bclk consumer */
+ if (fsync && !strcmp(fsync, "coded_provider"))
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_CBC_CFP;
+ else
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_CBC_CFC;
+ }
+
+ /* inverted clocks ? */
+ if (bclk_invert && !strcmp(bclk_invert, "true")) {
+ if (fsync_invert && !strcmp(fsync_invert, "true"))
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_IB_IF;
+ else
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_IB_NF;
+ } else {
+ if (fsync_invert && !strcmp(fsync_invert, "true"))
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_NB_IF;
+ else
+ ssp->ssp_prm.hw_cfg[hwi].format |= SSP_FMT_NB_NF;
+ }
+
+ ssp->ssp_prm.hw_cfg[hwi].mclk_rate = mclk_freq;
+ ssp->ssp_prm.hw_cfg[hwi].bclk_rate = bclk_freq;
+ ssp->ssp_prm.hw_cfg[hwi].fsync_rate = fsync_freq;
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slots = tdm_slots;
+ ssp->ssp_prm.hw_cfg[hwi].tdm_slot_width = tdm_slot_width;
+ ssp->ssp_prm.hw_cfg[hwi].tx_slots = tx_slots;
+ ssp->ssp_prm.hw_cfg[hwi].rx_slots = rx_slots;
+
+ ssp->ssp_hw_config_count[ssp->ssp_count]++;
+
+ return 0;
+}
+
+/* init ssp parameters, should be called before parsing dais */
+int ssp_init_params(struct intel_nhlt_params *nhlt)
+{
+ struct intel_ssp_params *ssp;
+ int i;
+
+ ssp = calloc(1, sizeof(struct intel_ssp_params));
+ if (!ssp)
+ return -EINVAL;
+
+ nhlt->ssp_params = ssp;
+ ssp->ssp_count = 0;
+
+ for (i = 0; i < SSP_MAX_DAIS; i++)
+ ssp->ssp_hw_config_count[i] = 0;
+
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// Keyon Jie <yang.jie@linux.intel.com>
+// Rander Wang <rander.wang@linux.intel.com>
+// Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __SSP_PROCESS_H
+#define __SSP_PROCESS_H
+
+#include <stdint.h>
+
+/* initialize and set default values before parsing */
+int ssp_init_params(struct intel_nhlt_params *nhlt);
+
+/* set parameters when parsing topology2 conf */
+int ssp_set_params(struct intel_nhlt_params *nhlt, const char *dir, int dai_index, int io_clk,
+ int bclk_delay, int sample_bits, int mclk_id, int clks_control,
+ int frame_pulse_width, const char *tdm_padding_per_slot, const char *quirks);
+int ssp_hw_set_params(struct intel_nhlt_params *nhlt, const char *format, const char *mclk,
+ const char *bclk, const char *bclk_invert, const char *fsync,
+ const char *fsync_invert, int mclk_freq, int bclk_freq, int fsync_freq,
+ int tdm_slots, int tdm_slot_width, int tx_slots, int rx_slots);
+
+/* calculate the blob after parsing the values*/
+int ssp_calculate(struct intel_nhlt_params *nhlt);
+/* get spec parameters when building the nhlt endpoint */
+int ssp_get_params(struct intel_nhlt_params *nhlt, int dai_index, uint32_t *virtualbus_id,
+ uint32_t *formats_count);
+int ssp_get_hw_params(struct intel_nhlt_params *nhlt, int hw_index, uint32_t *sample_rate,
+ uint16_t *channel_count, uint32_t *bits_per_sample);
+int ssp_get_dir(struct intel_nhlt_params *nhlt, int dai_index, uint8_t *dir);
+/* get vendor specific blob when building the nhlt endpoint */
+int ssp_get_vendor_blob_count(struct intel_nhlt_params *nhlt);
+int ssp_get_vendor_blob_size(struct intel_nhlt_params *nhlt, size_t *size);
+int ssp_get_vendor_blob(struct intel_nhlt_params *nhlt, uint8_t *vendor_blob, int dai_index,
+ int hw_config_index);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include <alsa/error.h>
+#include "pre-process-external.h"
+#include "nhlt.h"
+#include "intel/intel-nhlt.h"
+#include "intel/dmic-nhlt.h"
+#include "intel/ssp-nhlt.h"
+
+#define MAX_ENDPOINT_COUNT 20
+#define ALSA_BYTE_CHARS 5
+#define SOF_ABI_CHARS 29
+#define SOF_MANIFEST_DATA_TYPE_NHLT 1
+
+struct sof_manifest_tlv {
+ uint32_t type;
+ uint32_t size;
+ uint8_t data[];
+} __attribute__((packed));
+
+struct sof_manifest {
+ uint16_t abi_major;
+ uint16_t abi_minor;
+ uint16_t abi_patch;
+ uint16_t count;
+ struct sof_manifest_tlv items[];
+} __attribute__((packed));
+
+#ifdef NHLT_DEBUG
+static void debug_print_nhlt(struct nhlt *blob, struct endpoint_descriptor **eps)
+{
+ uint8_t *top_p = (uint8_t *)blob;
+ struct endpoint_descriptor *ep;
+ uint8_t *ep_p;
+ int i, j, k, lines, remain;
+
+ fprintf(stdout, "printing nhlt as bytes:\n");
+
+ lines = sizeof(struct nhlt) / 8;
+ remain = sizeof(struct nhlt) % 8;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < 8; j++) {
+ fprintf(stdout, "0x%02x,", *top_p);
+ top_p++;
+ }
+ fprintf(stdout, "\n");
+ }
+ for (i = 0; i < remain; i++) {
+ fprintf(stdout, "0x%02x,", *top_p);
+ top_p++;
+ }
+ fprintf(stdout, "\n\n");
+
+ for (i = 0; i < blob->endpoint_count; i++) {
+ ep = eps[i];
+ ep_p = (uint8_t *)ep;
+ lines = ep->length / 8;
+ remain = ep->length % 8;
+ for (j = 0; j < lines; j++) {
+ for (k = 0; k < 8; k++) {
+ fprintf(stdout, "0x%02x,", *ep_p);
+ ep_p++;
+ }
+ fprintf(stdout, "\n");
+ }
+ for (j = 0; j < remain; j++) {
+ fprintf(stdout, "0x%02x,", *ep_p);
+ ep_p++;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ fprintf(stdout, "\n");
+}
+#else
+static void debug_print_nhlt(struct nhlt *blob, struct endpoint_descriptor **eps) {}
+#endif
+
+static int print_as_hex_bytes(uint8_t *manifest_buffer, uint32_t manifest_size,
+ uint8_t *nhlt_buffer, uint32_t nhlt_size, char **src)
+{
+ char *bytes_string_buffer;
+ char *dst;
+ int i;
+
+ bytes_string_buffer = calloc((manifest_size + nhlt_size) * ALSA_BYTE_CHARS,
+ sizeof(uint8_t));
+ if (!bytes_string_buffer)
+ return -ENOMEM;
+
+ dst = bytes_string_buffer;
+ for (i = 0; i < manifest_size; i++) {
+ snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *manifest_buffer);
+ dst += ALSA_BYTE_CHARS;
+ manifest_buffer++;
+ }
+
+ for (i = 0; i < nhlt_size; i++) {
+ snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *nhlt_buffer);
+ dst += ALSA_BYTE_CHARS;
+ nhlt_buffer++;
+ }
+
+ /* remove the last comma... */
+ dst--;
+ *dst = '\0';
+
+ *src = bytes_string_buffer;
+
+ return 0;
+}
+
+static int merge_manifest_data(snd_config_t *cfg, uint8_t *manifest_buffer, uint32_t manifest_size,
+ uint8_t *nhlt_buffer, uint32_t nhlt_size)
+{
+ const char *data_name = "SOF ABI";
+ snd_config_t *data_section;
+ snd_config_t *manifest;
+ snd_config_t *old_bytes;
+ snd_config_t *new_bytes;
+ char *src = NULL;
+ int ret;
+
+ /* merge manifest struct and nhlt bytes as new config into existing SectionData*/
+ ret = snd_config_search(cfg, "SectionData", &data_section);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_search(data_section, data_name, &manifest);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_search(manifest, "bytes", &old_bytes);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_make(&new_bytes, "bytes", SND_CONFIG_TYPE_STRING);
+ if (ret < 0)
+ goto err;
+
+ ret = print_as_hex_bytes(manifest_buffer, manifest_size, nhlt_buffer, nhlt_size, &src);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_config_set_string(new_bytes, src);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_config_merge(old_bytes, new_bytes, true);
+ if (ret < 0)
+ goto err;
+
+ free(src);
+
+ return 0;
+err:
+ if (new_bytes)
+ snd_config_delete(new_bytes);
+ if (src)
+ free(src);
+
+ return ret;
+}
+
+static void save_nhlt_binary(struct nhlt *blob, struct endpoint_descriptor **eps, snd_config_t *cfg)
+{
+ const char *bin_file = NULL;
+ snd_config_t *defines;
+ FILE *fp;
+ int ret;
+ int i;
+
+ ret = snd_config_search(cfg, "Define.NHLT_BIN", &defines);
+ if (ret < 0)
+ return;
+
+ if (snd_config_get_string(defines, &bin_file) < 0)
+ return;
+
+ fp = fopen(bin_file, "wb");
+ if (fp == NULL) {
+ fprintf(stderr, "can't open nhlt binary output file %s\n", bin_file);
+ return;
+ }
+
+ fprintf(stdout, "saving nhlt as binary in %s\n", bin_file);
+
+ fwrite(blob, 1, sizeof(struct nhlt), fp);
+
+ for (i = 0; i < blob->endpoint_count; i++)
+ fwrite(eps[i], eps[i]->length, sizeof(uint8_t), fp);
+
+ fclose(fp);
+}
+
+static int manifest_create(snd_config_t *input, uint8_t **manifest_buffer, uint32_t *size, uint32_t nhlt_size)
+{
+ struct sof_manifest_tlv manifest_tlv;
+ struct sof_manifest manifest;
+ snd_config_t *data_section;
+ snd_config_t *data;
+ snd_config_t *old_bytes;
+ uint32_t manifest_size;
+ uint8_t *byte_buffer;
+ const char *abi;
+ uint8_t *top_p;
+ uint8_t *dst;
+ int ret;
+ int i;
+
+ ret = snd_config_search(input, "SectionData", &data_section);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_search(data_section, "SOF ABI", &data);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_search(data, "bytes", &old_bytes);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_config_get_string(old_bytes, &abi);
+ if (ret < 0)
+ return ret;
+
+ /* we have something funny in abi string */
+ if (strlen(abi) != SOF_ABI_CHARS)
+ return -EINVAL;
+
+ manifest.count = 1;
+ manifest_tlv.type = SOF_MANIFEST_DATA_TYPE_NHLT;
+ manifest_tlv.size = nhlt_size;
+
+ manifest_size = sizeof(struct sof_manifest) + sizeof(struct sof_manifest_tlv);
+ byte_buffer = calloc(manifest_size, sizeof(uint8_t));
+ if (!byte_buffer)
+ return -ENOMEM;
+
+ *size = manifest_size;
+
+ dst = byte_buffer;
+
+ /* copy the ABI version bytes */
+ for (i = 0; i < 6; i++)
+ sscanf(&abi[i * ALSA_BYTE_CHARS], "%" SCNx8, dst++);
+
+ /* set the count */
+ *dst++ = manifest.count;
+ *dst++ = manifest.count >> 2;
+
+ top_p = (uint8_t *)&manifest_tlv;
+ for (i = 0; i < sizeof(struct sof_manifest_tlv); i++)
+ *dst++ = *top_p++;
+
+ *manifest_buffer = byte_buffer;
+
+ return 0;
+}
+
+static int nhlt_get_flat_buffer(struct nhlt *blob, struct endpoint_descriptor **eps,
+ uint32_t eps_count, uint32_t *size, uint8_t **nhlt_buffer)
+{
+ uint8_t *top_p = (uint8_t *)blob;
+ struct endpoint_descriptor *ep;
+ uint8_t *byte_buffer;
+ uint32_t nhlt_size;
+ uint8_t *ep_p;
+ uint8_t *dst;
+ int i, j;
+
+ /* get blob total size */
+ nhlt_size = sizeof(struct nhlt);
+ for (i = 0; i < eps_count; i++) {
+ if (eps[i])
+ nhlt_size += eps[i]->length;
+ }
+
+ *size = nhlt_size;
+
+ byte_buffer = calloc(nhlt_size, sizeof(uint8_t));
+ if (!byte_buffer)
+ return -ENOMEM;
+
+ dst = byte_buffer;
+ for (i = 0; i < sizeof(struct nhlt); i++)
+ *dst++ = *top_p++;
+
+ for (i = 0; i < blob->endpoint_count; i++) {
+ ep = eps[i];
+ ep_p = (uint8_t *)ep;
+ for (j = 0; j < ep->length; j++)
+ *dst++ = *ep_p++;
+ }
+
+ *nhlt_buffer = byte_buffer;
+
+ return 0;
+}
+
+/* called at the end of topology pre-processing, create flat buffer from variable size nhlt */
+static int nhlt_create(struct intel_nhlt_params *nhlt, snd_config_t *input, snd_config_t *output,
+ uint8_t **nhlt_buffer, uint32_t *nhlt_size)
+{
+ struct endpoint_descriptor *eps[MAX_ENDPOINT_COUNT];
+ int eps_count = 0;
+ struct nhlt blob;
+ uint32_t size;
+ uint8_t dir;
+ int ret;
+ int i;
+
+ for (i = 0; i < MAX_ENDPOINT_COUNT; i++)
+ eps[i] = NULL;
+
+ /* we always have only 0 or 1 dmic ep */
+ if (nhlt_dmic_get_ep_count(nhlt)) {
+ /* the index is always 0 in dmic case */
+ ret = nhlt_dmic_get_ep(nhlt, &eps[eps_count], 0);
+ if (ret < 0)
+ return -EINVAL;
+ eps_count++;
+ }
+
+ /* we can have 0 to several ssp eps */
+ for (i = 0; i < nhlt_ssp_get_ep_count(nhlt); i++) {
+ nhlt_ssp_get_dir(nhlt, i, &dir);
+ /* duplicate endpoint for duplex dai */
+ if (dir > NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER) {
+ ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i,
+ NHLT_ENDPOINT_DIRECTION_RENDER);
+ if (ret < 0)
+ goto err;
+ eps_count++;
+ ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i,
+ NHLT_ENDPOINT_DIRECTION_CAPTURE);
+ } else {
+ ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i, dir);
+ }
+ if (ret < 0)
+ goto err;
+ eps_count++;
+ }
+
+ /* we don't have endpoints */
+ if (!eps_count)
+ return 0;
+
+ uint8_t sig[4] = {'N', 'H', 'L', 'T'};
+ blob.efi_acpi.signature = *((uint32_t *)sig);
+ blob.efi_acpi.length = 0;
+ blob.efi_acpi.revision = 0;
+ blob.efi_acpi.checksum = 0;
+ for (i = 0; i < 6; i++)
+ blob.efi_acpi.oem_id[i] = 0;
+ blob.efi_acpi.oem_table_id = 0;
+ blob.efi_acpi.oem_revision = 0;
+ blob.efi_acpi.creator_id = 0;
+ blob.efi_acpi.creator_revision = 0;
+
+ blob.endpoint_count = eps_count;
+
+ /* get blob total size */
+ size = sizeof(struct nhlt);
+ for (i = 0; i < eps_count; i++) {
+ if (eps[i])
+ size += eps[i]->length;
+ }
+
+ /* add the total length to top level struct */
+ blob.efi_acpi.length = size;
+
+ debug_print_nhlt(&blob, eps);
+
+ save_nhlt_binary(&blob, eps, input);
+
+ ret = nhlt_get_flat_buffer(&blob, eps, eps_count, nhlt_size, nhlt_buffer);
+
+err:
+ /* remove all enpoints */
+ for (i = 0; i < eps_count; i++)
+ free(eps[i]);
+
+ return ret;
+}
+
+static int do_nhlt(struct intel_nhlt_params *nhlt, snd_config_t *input, snd_config_t *output)
+{
+ uint8_t *manifest_buffer = NULL;
+ uint8_t *nhlt_buffer = NULL;
+ uint32_t manifest_size;
+ uint32_t nhlt_size = 0;
+ int ret = 0;
+
+ ret = nhlt_create(nhlt, input, output, &nhlt_buffer, &nhlt_size);
+ if (ret) {
+ fprintf(stderr, "can't create nhlt blob, err %d\n", ret);
+ return ret;
+ }
+
+ ret = manifest_create(output, &manifest_buffer, &manifest_size, nhlt_size);
+ if (ret) {
+ fprintf(stderr, "can't re-create manifest, err %d\n", ret);
+ goto err;
+ }
+
+ ret = merge_manifest_data(output, manifest_buffer, manifest_size, nhlt_buffer, nhlt_size);
+ if (ret)
+ fprintf(stderr, "can't merge manifest data, err %d\n", ret);
+
+err:
+ if (manifest_buffer)
+ free(manifest_buffer);
+ if (nhlt_buffer)
+ free(nhlt_buffer);
+
+ return ret;
+}
+
+SND_TOPOLOGY_PLUGIN_DEFINE_FUNC(nhlt)
+{
+ snd_config_iterator_t i, i2, next, next2;
+ struct intel_nhlt_params nhlt;
+ snd_config_t *n, *n2;
+ snd_config_t *items;
+ const char *id, *id2;
+ int ret;
+
+ /* initialize the internal structs */
+ ret = nhlt_ssp_init_params(&nhlt);
+ if (ret < 0)
+ return ret;
+
+ ret = nhlt_dmic_init_params(&nhlt);
+ if (ret < 0)
+ return ret;
+
+ /* find DAIs and set internal parameters */
+ ret = snd_config_search(input, "Object.Dai", &items);
+ if (ret < 0)
+ return ret;
+
+ snd_config_for_each(i, next, items) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ snd_config_for_each(i2, next2, n) {
+ n2 = snd_config_iterator_entry(i2);
+
+ if (snd_config_get_id(n2, &id2) < 0)
+ continue;
+
+ /* set dai parameters here */
+ if (!strncmp(id, "DMIC", 4)) {
+ ret = nhlt_dmic_set_params(&nhlt, n2, input);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!strncmp(id, "SSP", 3)) {
+ ret = nhlt_ssp_set_params(&nhlt, n2, input);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ /* create the nhlt blob from internal structs */
+ ret = do_nhlt(&nhlt, input, output);
+ if (ret)
+ fprintf(stderr, "error in nhlt processing\n");
+
+ free(nhlt.ssp_params);
+ free(nhlt.dmic_params);
+
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+
+#ifndef __NHLT_H
+#define __NHLT_H
+
+#include <stdint.h>
+
+/*
+ * Nhlt defines and structs are derived from:
+ * https://01.org/sites/default/files/595976_intel_sst_nhlt.pdf
+ *
+ * Acpi description header for example:
+ * https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf
+ *
+ * Idea is to generate similar blob as you would get from:
+ * 'cat /sys/firmware/acpi/tables/NHLT'
+ *
+ */
+#define NHLT_LINK_TYPE_HDAUDIO 0
+#define NHLT_LINK_TYPE_DSP 1
+#define NHLT_LINK_TYPE_PDM 2
+#define NHLT_LINK_TYPE_SSP 3
+#define NHLT_LINK_TYPE_SLIMBUS 4
+#define NHLT_LINK_TYPE_SOUNDWIRE 5
+
+#define NHLT_VENDOR_ID_INTEL 0x8086
+
+#define NHLT_DEVICE_ID_INTEL_PDM_DMIC 0xAE20
+#define NHLT_DEVICE_ID_INTEL_BT_SIDEBAND 0xAE30
+#define NHLT_DEVICE_ID_INTEL_I2S_TDM 0xAE34
+
+#define NHLT_DEVICE_TYPE_SSP_BT_SIDEBAND 0
+#define NHLT_DEVICE_TYPE_SSP_FM 1
+#define NHLT_DEVICE_TYPE_SSP_MODEM 2
+#define NHLT_DEVICE_TYPE_SSP_ANALOG 4
+
+#define NHLT_ENDPOINT_DIRECTION_RENDER 0
+#define NHLT_ENDPOINT_DIRECTION_CAPTURE 1
+#define NHLT_ENDPOINT_DIRECTION_RENDER_WITH_LOOPBACK 2
+#define NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER 3
+
+#define NHLT_DEVICE_CONFIG_TYPE_GENERIC 0
+#define NHLT_DEVICE_CONFIG_TYPE_MICARRAY 1
+#define NHLT_DEVICE_CONFIG_TYPE_RENDERWITHLOOPBACK 2
+#define NHLT_DEVICE_CONFIG_TYPE_RENDERFEEDBACK 3
+
+#define NHLT_MIC_ARRAY_TYPE_LINEAR_2_ELEMENT_SMALL 0xA
+#define NHLT_MIC_ARRAY_TYPE_LINEAR_2_ELEMENT_BIG 0xB
+#define NHLT_MIC_ARRAY_TYPE_LINEAR_4_ELEMENT_1ST_GEOMETRY 0xC
+#define NHLT_MIC_ARRAY_TYPE_PLANAR_4_ELEMENT_L_SHAPED 0xD
+#define NHLT_MIC_ARRAY_TYPE_PLANAR_4_ELEMENT_2ND_GEOMETRY 0xE
+#define NHLT_MIC_ARRAY_TYPE_VENDOR_DEFINED 0xF
+
+#define NHLT_MIC_ARRAY_NO_EXTENSION 0x0
+#define NHLT_MIC_ARRAY_SNR_AND_SENSITIVITY_EXTENSION 0x1
+
+#define NHLT_MIC_TYPE_OMNIDIRECTIONAL 0
+#define NHLT_MIC_TYPE_SUBCARDIOID 1
+#define NHLT_MIC_TYPE_CARDIOID 2
+#define NHLT_MIC_TYPE_SUPERCARDIOID 3
+#define NHLT_MIC_TYPE_HYPERCARDIOID 4
+#define NHLT_MIC_TYPE_8SHAPED 5
+#define NHLT_MIC_TYPE_RESERVED 6
+#define NHLT_MIC_TYPE_VENDORDEFINED 7
+
+#define NHLT_MIC_POSITION_TOP 0
+#define NHLT_MIC_POSITION_BOTTOM 1
+#define NHLT_MIC_POSITION_LEFT 2
+#define NHLT_MIC_POSITION_RIGHT 3
+#define NHLT_MIC_POSITION_FRONT 4 /*(default) */
+#define NHLT_MIC_POSITION_REAR 5
+
+struct specific_config {
+ uint32_t capabilities_size; /* does not include size of this field */
+ uint8_t capabilities[];
+} __attribute__((packed));
+
+struct device_specific_config {
+ uint8_t virtual_slot;
+ uint8_t config_type;
+} __attribute__((packed));
+
+struct ssp_device_specific_config {
+ struct specific_config config;
+ struct device_specific_config device_config;
+} __attribute__((packed));
+
+struct mic_snr_sensitivity_extension {
+ uint32_t snr;
+ uint32_t sensitivity;
+} __attribute__((packed));
+
+struct mic_vendor_config {
+ uint8_t type;
+ uint8_t panel;
+ uint32_t speaker_position_distance;
+ uint32_t horizontal_offset;
+ uint32_t vertical_offset;
+ uint8_t frequency_low_band;
+ uint8_t frequency_high_band;
+ uint16_t direction_angle;
+ uint16_t elevation_angle;
+ uint16_t vertical_angle_begin;
+ uint16_t vertical_angle_end;
+ uint16_t horizontal_angle_begin;
+ uint16_t horizontal_angle_end;
+} __attribute__((packed));
+
+struct mic_array_device_specific_config {
+ struct specific_config config;
+ struct device_specific_config device_config;
+ uint8_t array_type_ex;
+} __attribute__((packed));
+
+struct mic_array_device_specific_vendor_config {
+ struct specific_config config;
+ struct device_specific_config device_config;
+ uint8_t array_type_ex;
+ uint8_t number_of_microphones;
+ uint8_t mic_vendor_configs[];
+} __attribute__((packed));
+
+struct WAVEFORMATEXTENSIBLE {
+ uint16_t wFormatTag;
+ uint16_t nChannels;
+ uint32_t nSamplesPerSec;
+ uint32_t nAvgBytesPerSec;
+ uint16_t nBlockAlign;
+ uint16_t wBitsPerSample;
+ uint16_t cbSize;
+ uint16_t wValidBitsPerSample;
+ uint32_t dwChannelMask;
+ uint32_t SubFormat[4];
+} __attribute__((packed));
+
+struct format_config {
+ struct WAVEFORMATEXTENSIBLE format;
+ struct specific_config vendor_blob;
+} __attribute__((packed));
+
+struct formats_config {
+ uint8_t formats_count;
+ uint8_t f_configs[];
+} __attribute__((packed));
+
+struct endpoint_descriptor {
+ uint32_t length; /* includes the length of this field also */
+ uint8_t link_type;
+ uint8_t instance_id;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t revision_id;
+ uint32_t subsystem_id;
+ uint8_t device_type;
+ uint8_t direction;
+ uint8_t virtualbus_id;
+} __attribute__((packed));
+
+struct efi_acpi_description_header {
+ uint32_t signature;
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ uint8_t oem_id[6];
+ uint64_t oem_table_id;
+ uint32_t oem_revision;
+ uint32_t creator_id;
+ uint32_t creator_revision;
+} __attribute__((packed));
+
+struct nhlt {
+ struct efi_acpi_description_header efi_acpi;
+ uint8_t endpoint_count;
+ uint8_t endpoints[];
+} __attribute__((packed));
+
+#endif /* __NHLT_H */