Про audio кодеки Everest es83x6

Материал из ALT Linux Wiki

На текущий момент проблем с совместимостью в ветке апстрима (ядра 6.6 и выше) нет, за исключением ситуаций, когда для конкретной модели ноутбука требуется явно указать драйверу об особенности печатной платы.

Делается это при помощи специального параметра модуля - quirk, дословно - причуда, то есть опция, придающая определенное свойство.

Рассмотрим подробнее, как это использовать.

Для начала вводные данные:

es83x6 - это названия аудиокодеков, которые часто встречаются на платформах intel и поддерживаются sof драйвером верхнего уровня snd_soc_sof_es8336 sound/soc/intel/boards/sof_es8336.c, который в свою очередь работает с драйверами низкого уровня sound/soc/codecs/es83*.

Помимо поддержки драйвером ядра, требуется наличие в системе дополнительных sof файлов - прошивки (.fw) DSP и файлы топологий (.tplg), которые поставляются пакетом firmware-alsa-sof (upstream), если каких-то файлов не хватает - в логах ядра появится сообщение.

Рассмотрим типовое применение quirk

В исх коде драйвера определены биты для след опций:

#define SOF_ES8336_ENABLE_DMIC      BIT(5)
#define SOF_ES8336_JD_INVERTED      BIT(6)
#define SOF_ES8336_HEADPHONE_GPIO    BIT(7)
#define SOC_ES8336_HEADSET_MIC1      BIT(8)

Например, для включения опций SOF_ES8336_ENABLE_DMIC и SOF_ES8336_JD_INVERTED нужно вычислить маску и передать параметр quirk драйверу:

su -
echo  "options snd_soc_sof_es8336 quirk=0x60" > /etc/modprobe.d/sof_es8336.conf
reboot

Если проблема ушла, можно добавить в исходный код драйвера применение опции для тестируемой модели в таблицу sof_es8336_quirk_table, например как здесь.

При этом важно правильно указать имена DMI_SYS_VENDOR и DMI_BOARD_NAME, их можно узнать из dmidecode или собрав информацию скриптом alsa-info.sh (пакет alsa-utils):

su -
alsa-info.sh --no-upload --output alsa-info.txt

Например, для модели Аквариус:

Manufacturer:      Aquarius
Product Name:      Cmp NS483
Board Vendor:      Aquarius
Board Name:        NS483IS2

DMI_SYS_VENDOR - "Aquarius"

DMI_BOARD_NAME - "NS483IS2"

Во время инициализации драйвера поиск происходит по первому вхождению строки, то есть результат будет такой же, если сократить до: DMI_BOARD_NAME - "NS483"

Примеры патчей ( 1 , 2 , 3 ). Патч-сет для поддержки es8326 на ядре 6.1 .

При отладке звука следует разграничивать проблемы hard и soft :

Use the following commands to check if the SOF driver is functional at the hardware device level:

speaker-test -Dhw:0,0 -c2 -r48000 -f S16_LE
arecord -Dhw:0,0 -c2 -r48000 -f S16_LE -d 10 test.wav

Если звук выводится, микрофон пишет, а, например, в браузере не работает - причину нужно искать в звуковом сервере, библиотеке alsa-lib или в конфигурационных файлах alsa-ucm-conf.

Если после выполнения команд

su -
rm -f /var/lib/alsa/*
alsactl restore

других сообщений, кроме

alsactl: load_state:1689: Cannot open /var/lib/alsa/asound.state for reading: No such file or directory

не выводится, то для кодека конфиги успешно обнаружены и распарсены.

На некоторых системах, если home примонтирован с параметром noexec, могут возникнуть проблемы с звуковым сервером (pulseaudio, pipewire), так как для корректной работы ему необходимо выполнение исполняемых файлов в каталоге ~/.config/pulse .

Также стоит помнить, что на некоторых моделях ноутбуков микрофон может быть выключен аппаратно переключателем (часто вместе с блокировкой встр. камеры) - в таком случае чувствительность устройства ввода в gui приложения pavucontrol будет нулевая.

В остальных случаях может потребоваться отладка:

1) собрать ядро с конфигом

CONFIG_DYNAMIC_DEBUG=y

2) добавить опции для интересующих модулей:

su-
echo "options snd_soc_sof_es8336 dyndbg=+pmf" >> /etc/modprobe.d/sof-dyndbg.conf
echo "options snd_soc_es8326 dyndbg=+pmf" >> /etc/modprobe.d/sof-dyndbg.conf
echo "options snd_soc_es8316 dyndbg=+pmf" >> /etc/modprobe.d/sof-dyndbg.conf

3) увеличить буфер журнала для учета дополнительных логов

грузиться с параметром ядра log_buf_len=4M

Процесс инициализации звукового кодека на примере Everest es8326, а также взаимодействие с HDMI-кодеком.

Этот процесс включает множество шагов, которые охватывают несколько уровней звуковых драйверов и системных компонентов. Ниже приведён сокращенно такой процесc, чтобы понять, как они взаимодействуют в звуковой подсистеме Linux.

Рассмотрим на примере ядра kernel-image-un-def-6.1.105-alt0.c10f.2 , в котором имеются патчи для полной поддержки указанного кодека. Чтобы понимать, какие драйверы загружены, воспользуемся выводом lsmod | grep snd, весь вывод сейчас не требуется, достаточно этого:

snd_soc_sof_es8336     24576  5
snd_soc_es8326         40960  1
snd_soc_core          339968  8 soundwire_intel,snd_sof,snd_sof_intel_hda_common,snd_soc_hdac_hda,snd_soc_sof_es8336,snd_soc_es8326,snd_sof_probes,snd_soc_dmic
snd                   118784  23 snd_seq,snd_seq_device,snd_hda_codec_hdmi,snd_hwdep,snd_hda_intel,snd_hda_codec,snd_sof,snd_timer,snd_compress,snd_soc_core,snd_soc_sof_es8336,snd_pcm,snd_rawmidi
soundcore              16384  1 snd

Инициализация ядра ALSA: soundcore, snd и связанные компоненты

Первым загружается модуль soundcore (CONFIG_SOUND=m), поскольку он предоставляет базовую инфраструктуру для работы с аудио в ядре Linux. Он является общим модулем для звуковой подсистемы и необходим для работы ALSA.

Размер модуля невелик (обычно около 16 KB), так как он содержит только базовые функции для работы с аудио.

После этого загружается модуль snd (CONFIG_SND=m), который расширяет функциональность и предоставляет API для других модулей ALSA.

snd — это главный модуль ALSA, который служит основой для всех остальных модулей звуковой подсистемы. Этот модуль загружается перед другими модулями ALSA, такими как snd_hda_intel, и предоставляет основную функциональность для работы со звуком.

Обнаружение кодека с ACPI именем ESSX8326 и инициализация SOF-платформы

При загрузке модуля snd, ядро создает основные структуры данных ALSA и регистрирует устройства, обнаруженные на шинах (ACPI, PCI, USB и т.д.). Для рассматриваемого случая PCI chip-ID аудиокарты a0c8:

inxi -Axxx
Audio:
  Device-1: Intel Tiger Lake-LP Smart Sound Audio vendor: Emdoor Digital
    driver: sof-audio-pci-intel-tgl bus-ID: 00:1f.3 chip-ID: 8086:a0c8
    class-ID: 0401
  API: ALSA v: k6.1.105-un-def-alt0.c10f.2 status: kernel-api
  Server-1: PulseAudio v: 17.0 status: active

ACPI ID кодека ESSX8326:

sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
iasl dsdt.dat
grep -A 30 ESSX dsdt.dsl 
                Device (ESSX)
                {
                    Name (_ADR, Zero)  // _ADR: Address
                    Name (_CID, "ESSX8326")  // _CID: Compatible ID
                    Name (_HID, "ESSX8326")  // _HID: Hardware ID
                    Name (_DDN, "ES8326")  // _DDN: DOS Device Name
                    Name (_UID, One)  // _UID: Unique ID
                    Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                    {
                        Name (BBUF, ResourceTemplate ()
                        {
                            I2cSerialBusV2 (0x0019, ControllerInitiated, 0x00061A80,
                                AddressingMode7Bit, "\\_SB.PC00.I2C3",
                                0x00, ResourceConsumer, , Exclusive,
                                )
                        })
                        Name (UBUF, ResourceTemplate ()
                        {
                            GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                                "\\_SB.GPI0", 0x00, ResourceConsumer, ,
                                )
                                {   // Pin list
                                    0x0000
                                }
                        })
                        Name (GBUF, ResourceTemplate ()
                        {
                            GpioInt (Edge, ActiveBoth, Exclusive, PullNone, 0x0000,
                                "\\_SB.GPI0", 0x00, ResourceConsumer, ,
                                )
                                {   // Pin list
                                    0x0000
                                }
                        })

Здесь же в DSDT указана шина I2C, по которой происходит управление кодеком (wr/rd регистров), а также GPIO, которыми управляет DSP обвязкой кодека на плате (например, включение динамиков). ACPI информация является частью прошивки BIOS, и может быть некорректной, например, ошибка в имени. В таком случае можно внести изменения в текстовый код dsdt.dsl, инкрементировать версию в секции DefinitionBlock, перекомпилировать и подложить подсистеме acpi:

iasl -tc dsdt.dsl
cp dsdt.aml > /boot/dsdt.new

затем в grub перед загрузкой ядра добавить строчку:

acpi /boot/dsdt.new

Вернемся к обнаружению кодека.

В файле sound/hda/intel-dsp-config.c (модуля snd_intel_dspcfg ):

int snd_intel_dsp_driver_probe(struct pci_dev *pci)
{
  . . .
  cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table))
  . . .

идет поиск соответствия PCI ID карты с данными в таблице config_table[]:

static const struct config_entry config_table[] = {
. . .
/* Tigerlake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
        {
                .flags = FLAG_SOF,
                .device = 0xa0c8,
                .codec_hid =  &essx_83x6,
        },
. . .

где &essx_83x6 структура с ACPI именами кодеков Everest:

static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
        .num_codecs = 3,
        .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
};

Когда сопоставление успешно, для этой связки будет использоваться драйвер SOF ( в таблице .flags = FLAG_SOF). Затем, для выбранной платформы (tgl) указывается имя драйвера верхнего уровня и файл топологии в другой файле модуля (snd_soc_acpi_intel_match) sound/soc/intel/common/soc-acpi-intel-tgl-match.c:

struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
   . . .
        {
                .comp_ids = &essx_83x6,
                .drv_name = "sof-essx8336",
                .sof_tplg_filename = "sof-tgl-es8336", /* the tplg suffix is added at run time */
                .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
                                        SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
                                        SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
        },
   . . .

А собственно ссылка на верхнюю таблицу находится в структуре-дескрипторе tgl платформы, там же пути и имя прошивки DSP в файле sound/soc/sof/intel/pci-tgl.c:

static const struct sof_dev_desc tgl_desc = {
        .machines               = snd_soc_acpi_intel_tgl_machines,
    . . .
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
        },
        .default_tplg_path = {
                [SOF_IPC] = "intel/sof-tplg",
        },
          .default_fw_filename = {
                [SOF_IPC] = "sof-tgl.ri",
    . . .

Прошивка, которая управляет низкоуровневыми операциями DSP, загружается из пользовательского пространства в память устройства через интерфейс драйвера SOF. Обычно прошивка представлена файлом с расширением .ri, который загружается с использованием функции request_firmware().

Топология определяет, как аудиокомпоненты связаны друг с другом: как маршрутизируются аудиопотоки, какие кодеки и виджеты используются, и как они взаимодействуют. Топология загружается из файла .tplg, который парсится драйвером SOF (sound/soc/sof/topology.c). Этот файл определяет, какие маршруты и компоненты аудиосистемы будут задействованы.

Подробнее описано на официальном сайте SOF Project , например про архитектуру linux драйвера .

Параллельно с предыдущим процессом инициализируется драйвер кодека низкого уровня ( интерфейс регистров i2c ) по соспоставлению с ACPI ID (CONFIG_ACPI=y) в файле sound/soc/codecs/es8326.c (модуля snd_soc_es8326):

#ifdef CONFIG_ACPI
static const struct acpi_device_id es8326_acpi_match[] = {
        {"ESSX8326", 0},
        {},
};
    . . .
static struct i2c_driver es8326_i2c_driver = {
        .driver = {
                .name = "es8326",
                .acpi_match_table = ACPI_PTR(es8326_acpi_match),
    . . .

Если видеокарта поддерживается видеодрайвером (в этом примере i915) текущей версии ядра, то одновременно система может обнаружить HDMI-кодеки, подключенные через интерфейс HDA (High Definition Audio). Драйвер snd_hda_intel загружается и инициализирует HDA кодеки, включая обнаружение и настройку всех доступных HDMI-портов. Обычно это три HDMI порта, которые будут использоваться для передачи аудио через HDMI-интерфейс. Для каждого HDMI порта создаются свои собственные аудиокодеки, и они регистрируются как отдельные PCM устройства в ALSA.

Драйвер видеокарты, такой как i915 для Intel, отвечает за управление графическим процессором (GPU), включая отображение видео и управление HDMI портами. Когда драйвер snd_hda_intel инициализирует HDMI-кодек, он взаимодействует с драйвером i915 для установления связи между аудиопотоками и видеовыходом. Это обеспечивает синхронизацию аудио и видео при передаче сигнала через HDMI. Драйверы snd_hda_intel и i915 используют механизм linking для координации передачи аудио через те же порты, которые используются для видео, что особенно важно для HDMI. Это гарантирует, что аудиопоток правильно направляется на HDMI-интерфейсы и синхронизирован с видеопотоком.

В дальнейшем после создания символьных устройств в директории /dev/snd/ с обнаруженными кодеками будут установлены связи в драйвере верхнего уровня snd_soc_sof_es8336 в файле sound/soc/intel/boards/sof_es8336.c:

static int sof_es8336_probe(struct platform_device *pdev)
{
    struct snd_soc_dai_link *dai_links;
    . . .
        dai_links = sof_card_dai_links_create(dev,
                                              SOF_ES8336_SSP_CODEC(quirk),
                                              dmic_be_num, hdmi_num);
    . . .
        /* fixup codec name based on HID */
        adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
        if (adev) {
                snprintf(codec_name, sizeof(codec_name),
                         "i2c-%s", acpi_dev_name(adev));
                dai_links[0].codecs->name = codec_name;

                /* also fixup codec dai name if relevant */
                if (!strncmp(mach->id, "ESSX8326", SND_ACPI_I2C_ID_LEN)) {
                        dai_links[0].codecs->dai_name = "ES8326 HiFi";
                        card->name = "essx8326";
                }
    . . .
}

где в этом же драйвере вызывается функция

static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
                                                          int ssp_codec,
                                                          int dmic_be_num,
                                                          int hdmi_num)

в которой создаются и настраиваются DAI (Digital Audio Interface) ссылки, необходимых для связи между различными компонентами аудиокарты, такими как кодеки, цифровые микрофоны (DMIC), и HDMI.

Создание интерфейсов ALSA для PCM устройств

После регистрации звуковых карт драйверами SOF и HDA, ALSA создает соответствующие интерфейсы для взаимодействия с пользователем. Это включает устройства /dev/snd/pcmC0D0p для PCM воспроизведения и другие устройства для записи и работы с микшером:

tree /dev/snd
/dev/snd
├── by-path
│   └── pci-0000:00:1f.3-platform-sof-essx8336 -> ../controlC0
├── controlC0
├── hwC0D2
├── pcmC0D0c
├── pcmC0D0p
├── pcmC0D5p
├── pcmC0D6p
├── pcmC0D7p
├── seq
└── timer

где:

controlC0 — основной контроллер звуковой карты

hwC0D2 — устройство аппаратного уровня

pcmC0D0c, pcmC0D0p и т.д. — каналы для воспроизведения (p) и записи (c) звука (PCM - Pulse Code Modulation).

seq — интерфейс для работы с MIDI-секвенсорами

timer — таймер для синхронизации операций

pci-0000:00:1f.3-platform-sof-essx8336 — ссылка на на контроллер карты controlC0

Каждый HDMI порт также регистрируется как отдельное PCM устройство, предоставляющее интерфейсы для передачи аудио через HDMI.

При создании этих файлов и ссылки, содержащей ключевое слово platform-sof-essx8336 система управления устройствами udev загружает соответствующий модуль верхнего уровня snd_soc_sof_es8336:

$ sudo modinfo snd-soc-sof_es8336
    . . .
description:    ASoC Intel(R) SOF + ES8336 Machine driver
alias:          platform:sof-essx8336
    . . .

Утилиты ALSA, такие как aplay или arecord, открывают PCM устройства для воспроизведения или записи аудио. Они взаимодействуют с аудиодрайверами через интерфейсы ALSA, передавая данные между пользовательскими приложениями и аудиокодеками.

ALSA UCM (Use Case Manager) конфиг. файлы Это механизм, используемый в аудиоподсистеме для управления специфичными для устройства аудиоконфигурациями. Он предоставляет возможности настройки и управления различными сценариями использования аудиоустройств, такими как воспроизведение, запись, работа с микрофоном и другие. UCM-конфигурации представляют собой набор файлов на языке INI, которые описывают различные сценарии использования для конкретных аудиоустройств. Эти файлы обычно хранятся в каталоге /usr/share/alsa/ucm2/ и содержат информацию о профилях, контроллерах, и используемых настройках для каждого из сценариев. Например, для этого кодека:

$ aplay -l 
**** List of PLAYBACK Hardware Devices ****
card 0: sofessx8326 [sof-essx8326], device 0: ES8336 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: sofessx8326 [sof-essx8326], device 5: HDMI 1 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: sofessx8326 [sof-essx8326], device 6: HDMI 2 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: sofessx8326 [sof-essx8326], device 7: HDMI 3 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

В квадратных скобках имя (sof-essx8326), которое состоит из приставки "sof-" и имени (см. ранее: card->name = "essx8326"). Это имя используется для поиска главного конф. файла:

$ tree /usr/share/alsa/ucm2/conf.d/sof-essx8326/
/usr/share/alsa/ucm2/conf.d/sof-essx8326/
└── sof-essx8326.conf -> ../../Intel/sof-essx8326/sof-essx8326.conf

В нем же ссылка на каталог с конфигами:

$ tree /usr/share/alsa/ucm2/Intel/sof-essx8326/
/usr/share/alsa/ucm2/Intel/sof-essx8326/
├── Hdmi.conf
├── HiFi.conf
└── sof-essx8326.conf

Эти файлы поставляются пакетом alsa-ucm-conf. Определение полного имени файла (для демонстрации файлы отсутствуют):

su-
strace -o ucm.txt alsactl restore

при анализе лога ucm.txt видим отсутствие файла:

access("/usr/share/alsa/ucm2/conf.d/sof-essx8326/sof-essx8326.conf", R_OK) = -1 ENOENT (Нет такого файла или каталога)

Интеграция с PulseAudio/PipeWire. После того как аудиоустройства и кодеки инициализированы и доступны в системе, следующим важным этапом является интеграция аудиоподсистемы с пользовательскими звуковыми серверами, такими как PulseAudio или PipeWire. Эти звуковые серверы выполняют важную роль в управлении аудиопотоками, микшировании, маршрутизации и предоставлении аудио в приложениях.