Звукова підсистема на одноплатних комп’ютерах
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.
Деякий час тому мене зацікавило, як влаштована звукова підсистема на одноплатних комп’ютерах (SBC). Ця стаття — короткий підсумок аналізу декількох моделей SBC, що базуються на SoC Rockchip RK3328, Rockchip RK3399 та Allwinner A64.
Загальний огляд
Аудіотракт можна розглядати як абстрактну чорну скриньку з входом та виходом. На вхід подаємо семпли, на виході отримуємо звукові коливання. Якщо заглянути всередину цієї скриньки, то там буде ще кілька «скриньок» сполучених між собою, приблизно як на цій діаграмі:
Компоненти, які використовуються для цих функціональних блоків, у кожному SBC різні, але загальний принцип однаковий: два основні блоки, котроллер I2S та CODEC, сполучені за допомогою I2S шини.
CODEC
Для початку розглянемо CODEC (COder/DECoder). Цей компонент, головна функція якого конвертування цифрових даних (байтів) в аналоговий сигнал, та навпаки. Два основні його блоки: DAC (digital-to-analog converter) та ADC (analog-to-digital converter). CODEC може мати кілька цифрових входів-виходів і кілька аналогових: мікрофон, лінійний вхід, динамік, навушники, лінійний вихід, тощо. Окрім конвертерів, CODEC зазвичай має й інші функціональні частини: внутрішні підсилювачі та послаблювачі, мікшери, реалізації різних DSP алгоритмів. Все це разом дещо нагадує пульт в студії звукозапису.CODEC може бути реалізований як внутрішній IP блок SoC або як фізично окрема мікросхема. Від того як це зроблено залежить спосіб, яким операційна система контролює його налаштування. У випадку інтеграції на SoC, процесор може отримати прямий доступ до регістрів CODEC-у через «вікно» в фізичній пам’яті. Якщо ж це окрема мікросхема, то всі операції з регістрами виконуються за допомогою послідовної шини даних, яка зв’язує CODEC та SoC. Найчастіше для цього використовується I2C.
Якщо CODEC не має внутрішнього підсилювача, або він недостатньо потужний, то між аналоговим виходом та динаміком ставиться зовнішній підсилювач, зазвичай з можливістю керування ним операційною системою, яка може вмикати його при програванні звуку та вимикати коли він не потрібен, щоб зменшити споживання енергії.
Контроллер I2S
CODEC отримує аудіодані від контроллера I2S. Теоретично, якщо CODEC є частиною SoC, він може мати прямий доступ до пам’яті через DMA запити або в PIO режимі. На практиці ж навіть у цьому випадку цим займається контроллер, а CODEC отримує дані через внутрішню (щодо SoC) I2S шину.
Основна задача контроллера, у випадку програвання звуку: отримати байти від процесора/пам’яті і передати їх далі, CODEC-у. Для запису напрямок даних зворотній: отримати байти від CODEC-у та записати їх в пам’ять або передати процесору.
Дані, які мають бути надіслані, або тільки що отримані контроллер зберігає в одному з внутрішніх FIFO буферів: TXFIFO або RXFIFO. Перед тим як почати транспортування даних драйвер контроллера повинен налаштувати параметри
аудіопотоку: частоту дискретизації, кількість каналів, розмір семпла. Після цього драйвер заповнює (при передачі) внутрішній буфер контроллера за допомогую DMA операції, або послідовно записуючи семпли в регістр TXDATA, та віддає команду почати надсилати. Кожен надісланий семпл видаляється з буфера, і як тільки заповнення TXFIFO падає нижче визначегого рівня контроллер повідомляє про це операційну систему через переривання і драйвер надає нову порцію даних.
При запису аудіо все відбувається в зворотному випадку: контроллер додає отримані семпли в RXFIFO і як тільки їх стає більше певного рівня, контроллер повідомляє ОС що потрібно їх звідти забрати.
Шина I2S
CODEC та контроллер поєднані за допомогою I2S шини. Це послідовний інтерфейс розроблений компанією Philips в 1986 році спеціально для поєднання аудіоприладів. Найпростіший варінт шини має три сигнали: SCK (serial clock, bit clock, BCLK), WS (word select, left/right clock, LRCLK, frame-sync, FS), SD (serial data, SDIN, SDOUT, DACDAT, ADCDAT). SCK надає частоту тактування, SD передає семпли, а WS визначає для в який канал має піти семпл — правий чи лівий. Шина може мати більше одного сигнала SD, для одночасних операцій програвання та запису, або для операцій з масивами мікрофонів чи динаміків. Також замість одного сигналу LRCLK може бути два — окремо для програвання (TXLRCLK) та запису (RXLRCLK).
I2S timing diagram (source)
Якщо один з компонентів має внутрішню цифрову логіку, наприклад DSP алгоритми в CODEC, ця логіка потребує джерело тактової частоти для функціонування. Ним може бути ще один, необов’язковий, сигнал шини — MCLK (master clock). Його частота, зазвичай, це частота дискретизації помножена на степінь двійки, найчастіше 256, але іноді 128 або 512.
Альтернативно, на вхід MCLK CODEC-у може подаватись сигнал не з контроллера, а з зовнішього фіксованого генератора або з одного з генераторів SoC.
Стандарт I2S не визначає яка з сторін має генерувати BCLK та WS. Тому перш ніж почати комунікацію контроллер та CODEC мають погодити, хто є генератором, а хто споживачем синхросигналів (про це трохи нижче).
Існує декілька форматів (або ж варіантів) I2S: стандартний, left-justified, right-justified, DSP, PCM. Вони функціонують за більш-менш однаковим принципом і відрізняються тільки деталями: де початок семпла, як FS сигналізує зміну канала. Сучасні компоненти з підтримкою I2S можуть використовувати принаймні найбільш популярні з цих форматів.
Device Tree та драйвера
Інформація про всю цю структуру (контроллер, CODEC, підсилювачі, шина) зберігається в .dtb файлі, який містить опис «дерева пристроїв» і передається операційній системі завантажувачем ОС або вбудовується в саме ядро під час компіляції.
Для прикладу розглянемо релевантні частини з опису Pinebook Pro, дещо змінені для більшої наглядності.
rk3399-pinebook-pro.dts
/ { /* Audio components */ es8316-sound { compatible = "simple-audio-card"; pinctrl-names = "default"; pinctrl-0 = <&hp_det_gpio>; simple-audio-card,name = "rockchip,es8316-codec"; simple-audio-card,format = "i2s"; simple-audio-card,mclk-fs = <256>; simple-audio-card,aux-devs = <&speaker_amp>; simple-audio-card,bitclock-master = <&i2s1>; simple-audio-card,frame-master = <&i2s1>; simple-audio-card,cpu { sound-dai = <&i2s1>; }; simple-audio-card,codec { sound-dai = <&es8316>; }; }; speaker_amp: speaker-amplifier { compatible = "simple-audio-amplifier"; enable-gpios = <&gpio4 RK_PD3 GPIO_ACTIVE_HIGH>; sound-name-prefix = "Speaker Amplifier"; VCC-supply = <&pa_5v>; }; i2s1: [email protected] { compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s"; reg = <0x0 0xff890000 0x0 0x1000>; interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH 0>; dmas = <&dmac_bus 2>, <&dmac_bus 3>; dma-names = "tx", "rx"; clock-names = "i2s_clk", "i2s_hclk"; clocks = <&cru SCLK_I2S1_8CH>, <&cru HCLK_I2S1_8CH>; pinctrl-names = "default"; pinctrl-0 = <&i2s_8ch_mclk_gpio>, <&i2s1_2ch_bus>; power-domains = <&power RK3399_PD_SDIOAUDIO>; #sound-dai-cells = <0>; rockchip,capture-channels = <8>; rockchip,playback-channels = <8>; status = "okay"; }; i2c1 { es8316: [email protected] { compatible = "everest,es8316"; reg = <0x11>; clocks = <&cru SCLK_I2S_8CH_OUT>; clock-names = "mclk"; #sound-dai-cells = <0>; }; }; };
Вузол es8316 описує ES8316 CODEC. Це окрема мікросхема, вона під’єднана до I2C шини номер 1 (i2c1), має I2C адресу 0×11. MCLK подається напряму від RK3399 SoC, з генератора SCLK_I2S_8CH_OUT.
Вузол i2s1 — це один з трьох контроллерів I2C RK3399. Поля цього вузла описують вікно в фізичній пам’яті через яке процессор має доступ до внутрішніх регістрів контроллера, номер переривання, DMA канали для операцій з пам’яттю та генератори, які подаються на вхід IP блоку контроллера.
ES8316 не має внутрішніх підсилювачів, тільки послаблювачі, тож для дого щоб подати звук на динамік використовується зовнішній підсилювач описаний вузлом speaker_amp. Драйвер підсилювача може контролювати напругу подану на нього через регулятор живлення або ввімкнути чи вимкнути проходження звуку змінюючи значення GPIO сигналу.
Поєднує всі три компонента віртульна звукова карта es8316-sound. Поле simple-audio-card,cpu містить посилання на CPU-частину аудіотракту, яка обмінюється даними з пам’яттю та процесором. У даному випадку це i2s1. simple-audio-card,codec посилається на CODEC-частину (es8316). А поле simple-audio-card,aux-devs є списком всіх допоміжних компонентів, які не є частиною основної архітектури «контроллер/CODEC». В Pinebook Pro це лише один підсилювач.
Решта полів використовується для узгодження параметрів шини I2S між сторонами тракту: який варіант I2S обрати, яка константа використовується для обчислення частоти MCLK, яка зі сторін, CODEC чи контроллер, є генератором синхросигналів, а яка споживачем.
Для того щоб відтворити звук додаток відкриває канал зв’язку з драйвером віртуальної карти simple-audio-card. В UNIX-подібних системах, це зазвичай файл пристрою в директорії /dev/. Використовуючи API ядра додаток спочатку встановлює параметри аудіопотоку: частоту дискретизації, формат семплів, кількість каналів. Поєднуючи ці дані з інформацією з вузла device tree драйвер обчислює частоти BCLK, WS, MCLK, конфігурує CPU-частину та CODEC-частину аудіотракту і запускає операцію передачі данних.
23 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів