topology: plugins - add Intel nhlt encoder plugin

Add Intel nhlt acpi table encoder plugin into topology2.0 processing.
Nhlt internal structure is defined in:
https://01.org/sites/default/files/595976_intel_sst_nhlt.pdf
Nhlt acpi table contain vendor specific binary data blobs that are used
in some Intel dsp platforms for configuring the dmic and ssp hardware.

The function of this code is mainly to generate the vendor specific
binary blobs, but as there is existing nhlt parser code and header in
kernel there's no point of re-inventing the container: just use the
existing nhlt acpi table format. Basically this code is creating similar
nhlt acpi table that you would get from: cat
/sys/firmware/acpi/tables/NHLT

This code will have implementation for dmic and ssp endpoints. Thus the
code will translate the topology dai tokens into vendor specific binary
blobs and pack them into nhlt acpi table. Ssp and dmic code is lifted
from Sound Open Firmware (sof) code base, thus it will have BSD-3
license.

This plugin can be enabled from command line with:

alsatplg -DPREPROCESS_PLUGINS="nhlt" -c foo.conf -p -o bar.tplg

You can also dump the nhlt binary into a file with additional define:

-DNHLT_BIN="nhlt.bin"

Link: https://github.com/alsa-project/alsa-utils/pull/129
Signed-off-by: Jaska Uimonen <jaska.uimonen@linux.intel.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaska Uimonen 2021-12-14 17:20:50 +02:00 committed by Jaroslav Kysela
parent 44d3e8aa44
commit f9e6010d5e
26 changed files with 5369 additions and 2 deletions

5
.gitignore vendored
View file

@ -11,6 +11,9 @@ confdefs.h
aclocal.m4 aclocal.m4
Makefile Makefile
Makefile.in Makefile.in
libtool
ltmain.sh
ltconfig
version version
autom4te.cache autom4te.cache
compile compile
@ -22,6 +25,8 @@ test-driver
ABOUT-NLS ABOUT-NLS
*.ok *.ok
*.gmo *.gmo
*.la
*.lo
*.o *.o
*~ *~
.deps .deps

View file

@ -18,6 +18,7 @@ AC_PROG_INSTALL
AC_PROG_MKDIR_P AC_PROG_MKDIR_P
AC_PROG_LN_S AC_PROG_LN_S
AC_PROG_SED AC_PROG_SED
AM_PROG_LIBTOOL
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
AM_PATH_ALSA(1.2.5) AM_PATH_ALSA(1.2.5)
if test "x$enable_alsatest" = "xyes"; then if test "x$enable_alsatest" = "xyes"; then
@ -363,6 +364,7 @@ AC_SUBST(CURSESLIB)
AC_SUBST(CURSES_CFLAGS) AC_SUBST(CURSES_CFLAGS)
test "x$prefix" = xNONE && prefix=$ac_default_prefix test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix=$prefix
eval dir="$datadir" eval dir="$datadir"
case "$dir" in case "$dir" in
@ -437,12 +439,32 @@ AC_ARG_WITH([alsactl-daemonswitch],
[ALSACTL_DAEMONSWITCH="/etc/alsa/state-daemon.conf"]) [ALSACTL_DAEMONSWITCH="/etc/alsa/state-daemon.conf"])
AC_SUBST(ALSACTL_DAEMONSWITCH) 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 \ AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
alsamixer/Makefile amidi/Makefile amixer/Makefile \ alsamixer/Makefile amidi/Makefile amixer/Makefile \
m4/Makefile po/Makefile.in \ m4/Makefile po/Makefile.in \
alsaconf/alsaconf alsaconf/Makefile \ alsaconf/alsaconf alsaconf/Makefile \
alsaconf/po/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 \ bat/Makefile bat/tests/Makefile bat/tests/asound_state/Makefile \
aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \ aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \
utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \ utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \

View file

@ -11,6 +11,9 @@ gettextize -c -f --no-changelog
echo "EXTRA_DIST = gettext.m4" > m4/Makefile.am echo "EXTRA_DIST = gettext.m4" > m4/Makefile.am
cp Makefile.am.ok Makefile.am cp Makefile.am.ok Makefile.am
cp configure.ac.ok configure.ac cp configure.ac.ok configure.ac
touch ltconfig
libtoolize --force --copy --automake
aclocal $ACLOCAL_FLAGS
autoheader autoheader
automake --foreign --copy --add-missing automake --foreign --copy --add-missing
touch depcomp # for older automake touch depcomp # for older automake

View file

@ -1,3 +1,5 @@
SUBDIRS = nhlt
bin_PROGRAMS = \ bin_PROGRAMS = \
alsatplg alsatplg
@ -14,7 +16,7 @@ alsatplg_SOURCES = topology.c pre-processor.c pre-process-class.c pre-process-ob
noinst_HEADERS = topology.h pre-processor.h noinst_HEADERS = topology.h pre-processor.h
AM_CPPFLAGS = \ 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) alsatplg_LDADD = $(ALSA_TOPOLOGY_LIBS)

16
topology/nhlt/Makefile.am Normal file
View file

@ -0,0 +1,16 @@
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

View file

@ -0,0 +1,521 @@
// 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;
}

View file

@ -0,0 +1,19 @@
// 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

View file

@ -0,0 +1,186 @@
// 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

View file

@ -0,0 +1,17 @@
// 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

View file

@ -0,0 +1,48 @@
// 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 */

View file

@ -0,0 +1,335 @@
// 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 */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
// 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

View file

@ -0,0 +1,401 @@
// 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__ */

View file

@ -0,0 +1,126 @@
// 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;
}

View file

@ -0,0 +1,54 @@
// 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 */

View file

@ -0,0 +1,311 @@
// 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;
}

View file

@ -0,0 +1,19 @@
// 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

View file

@ -0,0 +1,99 @@
// 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

View file

@ -0,0 +1,15 @@
// 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

View file

@ -0,0 +1,55 @@
// 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 */

View file

@ -0,0 +1,263 @@
// 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 */

View file

@ -0,0 +1,741 @@
// 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;
}

View file

@ -0,0 +1,41 @@
// 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

View file

@ -0,0 +1,492 @@
// 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;
}

181
topology/nhlt/nhlt.h Normal file
View file

@ -0,0 +1,181 @@
// 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 */