├── LICENSE ├── Makefile ├── README.md ├── ac101.c ├── ac101_regs.h ├── ac108.c ├── ac108.h ├── ac108_6mic.state ├── ac108_asound.state ├── ac108_plugin ├── Makefile ├── README.md ├── ac108_help.c ├── ac108_help.h ├── libasound_module_pcm_ac108.so └── pcm_ac108.c ├── ac10x.h ├── asound_2mic.conf ├── asound_4mic.conf ├── asound_6mic.conf ├── builddtbo.sh ├── default.pa ├── dkms.conf ├── install.sh ├── patches ├── back-to-v4.19.diff ├── back-to-v5.4.diff └── back-to-v5.8.diff ├── pulseaudio ├── 91-seeedvoicecard.rules ├── README.md ├── pulse_config_4mic │ ├── daemon.conf │ ├── default.pa │ └── seeed-voicecard.conf ├── pulse_config_6mic │ ├── daemon.conf │ ├── default.pa │ └── seeed-voicecard.conf ├── udev_rules_4mic.png └── udev_rules_6mic.png ├── seeed-2mic-voicecard-overlay.dts ├── seeed-2mic-voicecard.dtbo ├── seeed-4mic-voicecard-overlay.dts ├── seeed-4mic-voicecard.dtbo ├── seeed-8mic-voicecard-overlay.dts ├── seeed-8mic-voicecard.dtbo ├── seeed-voicecard ├── seeed-voicecard.c ├── seeed-voicecard.service ├── sound-compatible-4.18.h ├── tools ├── coherence.py └── phase_test.py ├── ubuntu-prerequisite.sh ├── uninstall.sh ├── wm8960.c ├── wm8960.h └── wm8960_asound.state /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Peter Yang 3 | # Copyright (c) 2019 Seeed Studio 4 | # 5 | # MIT License 6 | # 7 | 8 | uname_r=$(shell uname -r) 9 | 10 | # If KERNELRELEASE is defined, we've been invoked from the 11 | # kernel build system and can use its language 12 | ifneq ($(KERNELRELEASE),) 13 | # $(warning KERNELVERSION=$(KERNELVERSION)) 14 | 15 | snd-soc-wm8960-objs := wm8960.o 16 | snd-soc-ac108-objs := ac108.o ac101.o 17 | snd-soc-seeed-voicecard-objs := seeed-voicecard.o 18 | 19 | 20 | obj-m += snd-soc-wm8960.o 21 | obj-m += snd-soc-ac108.o 22 | obj-m += snd-soc-seeed-voicecard.o 23 | 24 | ifdef DEBUG 25 | ifneq ($(DEBUG),0) 26 | ccflags-y += -DDEBUG -DAC101_DEBG 27 | endif 28 | endif 29 | 30 | 31 | 32 | else 33 | 34 | DEST := /lib/modules/$(uname_r)/kernel 35 | 36 | all: 37 | make -C /lib/modules/$(uname_r)/build M=$(PWD) modules 38 | 39 | clean: 40 | make -C /lib/modules/$(uname_r)/build M=$(PWD) clean 41 | 42 | install: 43 | sudo cp snd-soc-ac108.ko ${DEST}/sound/soc/codecs/ 44 | sudo cp snd-soc-wm8960.ko ${DEST}/sound/soc/codecs/ 45 | sudo cp snd-soc-seeed-voicecard.ko ${DEST}/sound/soc/bcm/ 46 | sudo depmod -a 47 | 48 | 49 | .PHONY: all clean install 50 | 51 | endif 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # seeed-voicecard 2 | 3 | The drivers for [ReSpeaker Mic Hat](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html), [ReSpeaker 4 Mic Array](https://www.seeedstudio.com/ReSpeaker-4-Mic-Array-for-Raspberry-Pi-p-2941.html), [6-Mics Circular Array Kit](), and [4-Mics Linear Array Kit]() for Raspberry Pi. 4 | 5 | ### Install seeed-voicecard 6 | Get the seeed voice card source code and install all linux kernel drivers 7 | ```bash 8 | git clone https://github.com/HinTak/seeed-voicecard 9 | cd seeed-voicecard 10 | sudo ./install.sh 11 | sudo reboot 12 | ``` 13 | ## ReSpeaker Documentation 14 | 15 | Up to date documentation for reSpeaker products can be found in [Seeed Studio Wiki](https://wiki.seeedstudio.com/ReSpeaker/)! 16 | ![](https://files.seeedstudio.com/wiki/ReSpeakerProductGuide/img/Raspberry_Pi_Mic_Array_Solutions.png) 17 | 18 | 19 | ### Coherence 20 | 21 | Estimate the magnitude squared coherence using Welch’s method. 22 | ![4-mics-linear-array-kit coherence](https://user-images.githubusercontent.com/3901856/37277486-beb1dd96-261f-11e8-898b-84405bfc7cea.png) 23 | Note: 'CO 1-2' means the coherence between channel 1 and channel 2. 24 | 25 | ```bash 26 | # How to get the coherence of the captured audio(a.wav for example). 27 | sudo apt install python-numpy python-scipy python-matplotlib 28 | python tools/coherence.py a.wav 29 | 30 | # Requirement of the input audio file: 31 | - format: WAV(Microsoft) signed 16-bit PCM 32 | - channels: >=2 33 | ``` 34 | 35 | ### uninstall seeed-voicecard 36 | If you want to upgrade the driver , you need uninstall the driver first. 37 | 38 | ``` 39 | pi@raspberrypi:~/seeed-voicecard $ sudo ./uninstall.sh 40 | ... 41 | ------------------------------------------------------ 42 | Please reboot your raspberry pi to apply all settings 43 | Thank you! 44 | ------------------------------------------------------ 45 | ``` 46 | 47 | Enjoy ! 48 | 49 | ### Technical support 50 | 51 | For hardware testing purposes we made a Rasperry Pi OS 5.10.17-v7l+ 32-bit image with reSpeaker drivers pre-installed, which you can download by clicking on [this link](https://files.seeedstudio.com/linux/Raspberry%20Pi%204%20reSpeaker/2021-05-07-raspios-buster-armhf-lite-respeaker.img.xz). 52 | 53 | We provide official support for using reSpeaker with the following OS: 54 | - 32-bit Raspberry Pi OS 55 | - 64-bit Raspberry Pi OS 56 | 57 | And following hardware platforms: 58 | - Raspberry Pi 3 (all models), Raspberry Pi 4 (all models) 59 | 60 | Anything beyond the scope of official support is considered to be community supported. Support for other OS/hardware platforms can be added, provided MOQ requirements can be met. 61 | 62 | If you have a technical problem when using reSpeaker with one of the officially supported platforms/OS, feel free to create an issue on Github. For general questions or suggestions, please use [Seeed forum](https://forum.seeedstudio.com/c/products/respeaker/15). 63 | 64 | 65 | -------------------------------------------------------------------------------- /ac101_regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ac101_regs.h 3 | * 4 | * (C) Copyright 2017-2018 5 | * Seeed Technology Co., Ltd. 6 | * 7 | * PeterYang 8 | * 9 | * (C) Copyright 2010-2017 10 | * Reuuimlla Technology Co., Ltd. 11 | * huangxin 12 | * 13 | * some simple description for this code 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License as 17 | * published by the Free Software Foundation; either version 2 of 18 | * the License, or (at your option) any later version. 19 | * 20 | */ 21 | #ifndef __AC101_REGS_H__ 22 | #define __AC101_REGS_H__ 23 | 24 | /*pll source*/ 25 | #define AC101_MCLK1 1 26 | #define AC101_MCLK2 2 27 | #define AC101_BCLK1 3 28 | #define AC101_BCLK2 4 29 | 30 | #define AIF1_CLK 1 31 | #define AIF2_CLK 2 32 | 33 | #define CHIP_AUDIO_RST 0x0 34 | #define PLL_CTRL1 0x1 35 | #define PLL_CTRL2 0x2 36 | #define SYSCLK_CTRL 0x3 37 | #define MOD_CLK_ENA 0x4 38 | #define MOD_RST_CTRL 0x5 39 | #define AIF_SR_CTRL 0x6 40 | 41 | #define AIF1_CLK_CTRL 0x10 42 | #define AIF1_ADCDAT_CTRL 0x11 43 | #define AIF1_DACDAT_CTRL 0x12 44 | #define AIF1_MXR_SRC 0x13 45 | #define AIF1_VOL_CTRL1 0x14 46 | #define AIF1_VOL_CTRL2 0x15 47 | #define AIF1_VOL_CTRL3 0x16 48 | #define AIF1_VOL_CTRL4 0x17 49 | #define AIF1_MXR_GAIN 0x18 50 | #define AIF1_RXD_CTRL 0x19 51 | #define ADC_DIG_CTRL 0x40 52 | #define ADC_VOL_CTRL 0x41 53 | #define ADC_DBG_CTRL 0x42 54 | 55 | #define HMIC_CTRL1 0x44 56 | #define HMIC_CTRL2 0x45 57 | #define HMIC_STS 0x46 58 | 59 | #define DAC_DIG_CTRL 0x48 60 | #define DAC_VOL_CTRL 0x49 61 | #define DAC_DBG_CTRL 0x4a 62 | #define DAC_MXR_SRC 0x4c 63 | #define DAC_MXR_GAIN 0x4d 64 | 65 | #define ADC_APC_CTRL 0x50 66 | #define ADC_SRC 0x51 67 | #define ADC_SRCBST_CTRL 0x52 68 | #define OMIXER_DACA_CTRL 0x53 69 | #define OMIXER_SR 0x54 70 | #define OMIXER_BST1_CTRL 0x55 71 | #define HPOUT_CTRL 0x56 72 | #define ESPKOUT_CTRL 0x57 73 | #define SPKOUT_CTRL 0x58 74 | #define LOUT_CTRL 0x59 75 | #define ADDA_TUNE1 0x5a 76 | #define ADDA_TUNE2 0x5b 77 | #define ADDA_TUNE3 0x5c 78 | #define HPOUT_STR 0x5d 79 | 80 | /*CHIP_AUDIO_RST*/ 81 | #define AC101_CHIP_ID 0x0101 82 | 83 | /*PLL_CTRL1*/ 84 | #define DPLL_DAC_BIAS 14 85 | #define PLL_POSTDIV_M 8 86 | #define CLOSE_LOOP 6 87 | #define INT 0 88 | 89 | /*PLL_CTRL2*/ 90 | #define PLL_EN 15 91 | #define PLL_LOCK_STATUS 14 92 | #define PLL_PREDIV_NI 4 93 | #define PLL_POSTDIV_NF 0 94 | 95 | /*SYSCLK_CTRL*/ 96 | #define PLLCLK_ENA 15 97 | #define PLLCLK_SRC 12 98 | #define AIF1CLK_ENA 11 99 | #define AIF1CLK_SRC 8 100 | #define AIF2CLK_ENA 7 101 | #define AIF2CLK_SRC 4 102 | #define SYSCLK_ENA 3 103 | #define SYSCLK_SRC 0 104 | 105 | /*MOD_CLK_ENA*/ 106 | #define MOD_CLK_AIF1 15 107 | #define MOD_CLK_AIF2 14 108 | #define MOD_CLK_AIF3 13 109 | #define MOD_CLK_SRC1 11 110 | #define MOD_CLK_SRC2 10 111 | #define MOD_CLK_HPF_AGC 7 112 | #define MOD_CLK_HPF_DRC 6 113 | #define MOD_CLK_ADC_DIG 3 114 | #define MOD_CLK_DAC_DIG 2 115 | 116 | /*MOD_RST_CTRL*/ 117 | #define MOD_RESET_CTL 0 118 | #define MOD_RESET_AIF1 15 119 | #define MOD_RESET_AIF2 14 120 | #define MOD_RESET_AIF3 13 121 | #define MOD_RESET_SRC1 11 122 | #define MOD_RESET_SRC2 10 123 | #define MOD_RESET_HPF_AGC 7 124 | #define MOD_RESET_HPF_DRC 6 125 | #define MOD_RESET_ADC_DIG 3 126 | #define MOD_RESET_DAC_DIG 2 127 | 128 | /*AIF_SR_CTRL*/ 129 | #define AIF1_FS 12 //AIF1 Sample Rate 130 | #define AIF2_FS 8 //AIF2 Sample Rate 131 | #define SRC1_ENA 3 132 | #define SRC1_SRC 2 133 | #define SRC2_ENA 1 134 | #define SRC2_SRC 0 135 | 136 | /*AIF1LCK_CTRL*/ 137 | #define AIF1_MSTR_MOD 15 138 | #define AIF1_BCLK_INV 14 139 | #define AIF1_LRCK_INV 13 140 | #define AIF1_BCLK_DIV 9 141 | #define AIF1_LRCK_DIV 6 142 | #define AIF1_WORK_SIZ 4 143 | #define AIF1_DATA_FMT 2 144 | #define DSP_MONO_PCM 1 145 | #define AIF1_TDMM_ENA 0 146 | 147 | /*AIF1_ADCDAT_CTRL*/ 148 | #define AIF1_AD0L_ENA 15 149 | #define AIF1_AD0R_ENA 14 150 | #define AIF1_AD1L_ENA 13 151 | #define AIF1_AD1R_ENA 12 152 | #define AIF1_AD0L_SRC 10 153 | #define AIF1_AD0R_SRC 8 154 | #define AIF1_AD1L_SRC 6 155 | #define AIF1_AD1R_SRC 4 156 | #define AIF1_ADCP_ENA 3 157 | #define AIF1_ADUL_ENA 2 158 | #define AIF1_SLOT_SIZ 0 159 | 160 | /*AIF1_DACDAT_CTRL*/ 161 | #define AIF1_DA0L_ENA 15 162 | #define AIF1_DA0R_ENA 14 163 | #define AIF1_DA1L_ENA 13 164 | #define AIF1_DA1R_ENA 12 165 | #define AIF1_DA0L_SRC 10 166 | #define AIF1_DA0R_SRC 8 167 | #define AIF1_DA1L_SRC 6 168 | #define AIF1_DA1R_SRC 4 169 | #define AIF1_DACP_ENA 3 170 | #define AIF1_DAUL_ENA 2 171 | #define AIF1_SLOT_SIZ 0 172 | 173 | /*AIF1_MXR_SRC*/ 174 | #define AIF1_AD0L_AIF1_DA0L_MXR 15 175 | #define AIF1_AD0L_AIF2_DACL_MXR 14 176 | #define AIF1_AD0L_ADCL_MXR 13 177 | #define AIF1_AD0L_AIF2_DACR_MXR 12 178 | #define AIF1_AD0R_AIF1_DA0R_MXR 11 179 | #define AIF1_AD0R_AIF2_DACR_MXR 10 180 | #define AIF1_AD0R_ADCR_MXR 9 181 | #define AIF1_AD0R_AIF2_DACL_MXR 8 182 | #define AIF1_AD1L_AIF2_DACL_MXR 7 183 | #define AIF1_AD1L_ADCL_MXR 6 184 | #define AIF1_AD1L_MXR_SRC 6 185 | #define AIF1_AD1R_AIF2_DACR_MXR 3 186 | #define AIF1_AD1R_ADCR_MXR 2 187 | #define AIF1_AD1R_MXR_SRC 2 188 | 189 | /*AIF1_VOL_CTRL1*/ 190 | #define AIF1_AD0L_VOL 8 191 | #define AIF1_AD0R_VOL 0 192 | 193 | /*AIF1_VOL_CTRL2*/ 194 | #define AIF1_AD1L_VOL 8 195 | #define AIF1_AD1R_VOL 0 196 | 197 | /*AIF1_VOL_CTRL3*/ 198 | #define AIF1_DA0L_VOL 8 199 | #define AIF1_DA0R_VOL 0 200 | 201 | /*AIF1_VOL_CTRL4*/ 202 | #define AIF1_DA1L_VOL 8 203 | #define AIF1_DA1R_VOL 0 204 | 205 | /*AIF1_MXR_GAIN*/ 206 | #define AIF1_AD0L_MXR_GAIN 12 207 | #define AIF1_AD0R_MXR_GAIN 8 208 | #define AIF1_AD1L_MXR_GAIN 6 209 | #define AIF1_AD1R_MXR_GAIN 2 210 | 211 | /*AIF1_RXD_CTRL*/ 212 | #define AIF1_N_DATA_DISCARD 8 213 | 214 | /*ADC_DIG_CTRL*/ 215 | #define ENAD 15 216 | #define ENDM 14 217 | #define ADFIR32 13 218 | #define ADOUT_DTS 2 219 | #define ADOUT_DLY 1 220 | 221 | /*ADC_VOL_CTRL*/ 222 | #define ADC_VOL_L 8 223 | #define ADC_VOL_R 0 224 | 225 | /*ADC_DBG_CTRL*/ 226 | #define ADSW 15 227 | #define DMIC_CLK_PIN_CTRL 12 228 | 229 | /*HMIC_CTRL1*/ 230 | #define HMIC_M 12 231 | #define HMIC_N 8 232 | #define HMIC_DATA_IRQ_MODE 7 233 | #define HMIC_TH1_HYSTERESIS 5 234 | #define HMIC_PULLOUT_IRQ 4 235 | #define HMIC_PLUGIN_IRQ 3 236 | #define HMIC_KEYUP_IRQ 2 237 | #define HMIC_KEYDOWN_IRQ 1 238 | #define HMIC_DATA_IRQ_EN 0 239 | 240 | /*HMIC_CTRL2*/ 241 | #define HMIC_SAMPLE_SELECT 14 242 | #define HMIC_TH2_HYSTERESIS 13 243 | #define HMIC_TH2 8 244 | #define HMIC_SF 6 245 | #define KEYUP_CLEAR 5 246 | #define HMIC_TH1 0 247 | 248 | /*HMIC_STS*/ 249 | #define HMIC_DATA 8 250 | #define GET_HMIC_DATA(r) (((r) >> HMIC_DATA) & 0x1F) 251 | #define HMIC_PULLOUT_PEND 4 252 | #define HMIC_PLUGIN_PEND 3 253 | #define HMIC_KEYUP_PEND 2 254 | #define HMKC_KEYDOWN_PEND 1 255 | #define HMIC_DATA_PEND 0 256 | #define HMIC_PEND_ALL (0x1F) 257 | 258 | /*DAC_DIG_CTRL*/ 259 | #define ENDA 15 260 | #define ENHPF 14 261 | #define DAFIR32 13 262 | #define MODQU 8 263 | 264 | /*DAC_VOL_CTRL*/ 265 | #define DAC_VOL_L 8 266 | #define DAC_VOL_R 0 267 | 268 | /*DAC_DBG_CTRL*/ 269 | #define DASW 15 270 | #define ENDWA_N 14 271 | #define DAC_MOD_DBG 13 272 | #define DAC_PTN_SEL 6 273 | #define DVC 0 274 | 275 | /*DAC_MXR_SRC*/ 276 | #define DACL_MXR_AIF1_DA0L 15 277 | #define DACL_MXR_AIF1_DA1L 14 278 | #define DACL_MXR_AIF2_DACL 13 279 | #define DACL_MXR_ADCL 12 280 | #define DACL_MXR_SRC 12 281 | #define DACR_MXR_AIF1_DA0R 11 282 | #define DACR_MXR_AIF1_DA1R 10 283 | #define DACR_MXR_AIF2_DACR 9 284 | #define DACR_MXR_ADCR 8 285 | #define DACR_MXR_SRC 8 286 | 287 | /*DAC_MXR_GAIN*/ 288 | #define DACL_MXR_GAIN 12 289 | #define DACR_MXR_GAIN 8 290 | 291 | /*ADC_APC_CTRL*/ 292 | #define ADCREN 15 293 | #define ADCRG 12 294 | #define ADCLEN 11 295 | #define ADCLG 8 296 | #define MBIASEN 7 297 | #define MMIC_BIAS_CHOP_EN 6 298 | #define MMIC_BIAS_CHOP_CKS 4 299 | #define HBIASMOD 2 300 | #define HBIASEN 1 301 | #define HBIASADCEN 0 302 | 303 | /*ADC_SRC*/ 304 | #define RADCMIXMUTEMIC1BOOST (13) 305 | #define RADCMIXMUTEMIC2BOOST (12) 306 | #define RADCMIXMUTELINEINLR (11) 307 | #define RADCMIXMUTELINEINR (10) 308 | #define RADCMIXMUTEAUXINR (9) 309 | #define RADCMIXMUTEROUTPUT (8) 310 | #define RADCMIXMUTELOUTPUT (7) 311 | #define LADCMIXMUTEMIC1BOOST (6) 312 | #define LADCMIXMUTEMIC2BOOST (5) 313 | #define LADCMIXMUTELINEINLR (4) 314 | #define LADCMIXMUTELINEINL (3) 315 | #define LADCMIXMUTEAUXINL (2) 316 | #define LADCMIXMUTELOUTPUT (1) 317 | #define LADCMIXMUTEROUTPUT (0) 318 | 319 | 320 | /*ADC_SRCBST_CTRL*/ 321 | #define MIC1AMPEN 15 322 | #define ADC_MIC1G 12 323 | #define MIC2AMPEN 11 324 | #define ADC_MIC2G 8 325 | #define MIC2SLT 7 326 | #define LINEIN_PREG 4 327 | #define AUXI_PREG 0 328 | 329 | /*OMIXER_DACA_CTRL*/ 330 | #define DACAREN 15 331 | #define DACALEN 14 332 | #define RMIXEN 13 333 | #define LMIXEN 12 334 | #define HPOUTPUTENABLE 8 335 | 336 | /*OMIXER_SR*/ 337 | #define RMIXMUTEMIC1BOOST (13) 338 | #define RMIXMUTEMIC2BOOST (12) 339 | #define RMIXMUTELINEINLR (11) 340 | #define RMIXMUTELINEINR (10) 341 | #define RMIXMUTEAUXINR (9) 342 | #define RMIXMUTEDACR (8) 343 | #define RMIXMUTEDACL (7) 344 | #define LMIXMUTEMIC1BOOST (6) 345 | #define LMIXMUTEMIC2BOOST (5) 346 | #define LMIXMUTELINEINLR (4) 347 | #define LMIXMUTELINEINL (3) 348 | #define LMIXMUTEAUXINL (2) 349 | #define LMIXMUTEDACL (1) 350 | #define LMIXMUTEDACR (0) 351 | 352 | /*OMIXER_BST1_CTRL*/ 353 | #define BIASVOLTAGE 12 354 | #define AXG 9 355 | #define OMIXER_MIC1G 6 356 | #define OMIXER_MIC2G 3 357 | #define LINEING 0 358 | 359 | /*HPOUT_CTRL*/ 360 | #define RHPS 15 361 | #define LHPS 14 362 | #define RHPPA_MUTE 13 363 | #define LHPPA_MUTE 12 364 | #define HPPA_EN 11 365 | #define HP_VOL 4 366 | #define HPPA_DEL 2 367 | #define HPPA_IS 0 368 | 369 | /*ESPKOUT_CTRL*/ 370 | #define EAR_RAMP_TIME 11 371 | #define ESPA_OUT_CURRENT 9 372 | #define ESPSR 7 373 | #define ESPPA_MUTE 6 374 | #define ESPPA_EN 5 375 | #define ESP_VOL 0 376 | 377 | /*SPKOUT_CTRL*/ 378 | #define HPCALICKS 13 379 | #define RSPKS 12 380 | #define RSPKINVEN 11 381 | #define RSPK_EN 9 382 | #define LSPKS 8 383 | #define LSPKINVEN 7 384 | #define LSPK_EN 5 385 | #define SPK_VOL 0 386 | 387 | /*LOUT_CTRL*/ 388 | #define LINEOUTG 5 389 | #define LINEOUTEN 4 390 | #define LINEOUTS0 3 391 | #define LINEOUTS1 2 392 | #define LINEOUTS2 1 393 | #define LINEOUTS3 0 394 | 395 | /*ADDA_TUNE1*/ 396 | #define CURRENT_TEST_SELECT 14 397 | #define BIHE_CTRL 12 398 | #define DITHER 11 399 | #define DITHER_CLK 9 400 | #define ZERO_CROSSOVER_EN 8 401 | #define ZERO_CROSSOVER_TIME 7 402 | #define EAR_SPEED_SELECT 6 403 | #define REF_CHOPPEN_CKS 4 404 | #define OPMIC_BIAS_CUR 0 405 | 406 | /*ADDA_TUNE2*/ 407 | #define OPDAC_BIAS_CUR 14 408 | #define OPDRV_BIAS_CUR 12 409 | #define OPMIX_BIAS_CUR 10 410 | #define OPEAR_BIAS_CUR 8 411 | #define OPVR_BIAS_CUR 6 412 | #define OPAAF_BIAS_CUR 4 413 | #define OPADC1_BIAS_CUR 2 414 | #define OPADC2_BIAS_CUR 0 415 | 416 | /*ADDA_TUNE3*/ 417 | #define LDOEN 15 418 | #define LDO_SEL 12 419 | #define BIASCALIVERIFY 11 420 | #define BIASMODE 10 421 | #define BIASCALIDATA 9 422 | #define OSCS 1 423 | #define OSCEN 0 424 | 425 | /*HPOUT_STR*/ 426 | #define HPVL_SOFT_MOD 14 427 | #define HPVL_STEP_CTRL 8 428 | #define DACA_CHND_ENA 7 429 | #define HPPA_MXRD_ENA 6 430 | #define HPVL_CTRL_OUT 0 431 | 432 | #endif//__AC101_REGS_H__ 433 | -------------------------------------------------------------------------------- /ac108.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ac108.h -- ac108 ALSA Soc Audio driver 3 | * 4 | * Author: panjunwen 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | * 10 | */ 11 | 12 | #ifndef _AC108_H 13 | #define _AC108_H 14 | 15 | 16 | /*** AC108 Codec Register Define***/ 17 | 18 | //Chip Reset 19 | #define CHIP_RST 0x00 20 | #define CHIP_RST_VAL 0x12 21 | 22 | //Power Control 23 | #define PWR_CTRL1 0x01 24 | #define PWR_CTRL2 0x02 25 | #define PWR_CTRL3 0x03 26 | #define PWR_CTRL4 0x04 27 | #define PWR_CTRL5 0x05 28 | #define PWR_CTRL6 0x06 29 | #define PWR_CTRL7 0x07 30 | #define PWR_CTRL8 0x08 31 | #define PWR_CTRL9 0x09 32 | 33 | //PLL Configure Control 34 | #define PLL_CTRL1 0x10 35 | #define PLL_CTRL2 0x11 36 | #define PLL_CTRL3 0x12 37 | #define PLL_CTRL4 0x13 38 | #define PLL_CTRL5 0x14 39 | #define PLL_CTRL6 0x16 40 | #define PLL_CTRL7 0x17 41 | #define PLL_LOCK_CTRL 0x18 42 | 43 | //System Clock Control 44 | #define SYSCLK_CTRL 0x20 45 | #define MOD_CLK_EN 0x21 46 | #define MOD_RST_CTRL 0x22 47 | #define DSM_CLK_CTRL 0x25 48 | 49 | //I2S Common Control 50 | #define I2S_CTRL 0x30 51 | #define I2S_BCLK_CTRL 0x31 52 | #define I2S_LRCK_CTRL1 0x32 53 | #define I2S_LRCK_CTRL2 0x33 54 | #define I2S_FMT_CTRL1 0x34 55 | #define I2S_FMT_CTRL2 0x35 56 | #define I2S_FMT_CTRL3 0x36 57 | 58 | //I2S TX1 Control 59 | #define I2S_TX1_CTRL1 0x38 60 | #define I2S_TX1_CTRL2 0x39 61 | #define I2S_TX1_CTRL3 0x3A 62 | #define I2S_TX1_CHMP_CTRL1 0x3C 63 | #define I2S_TX1_CHMP_CTRL2 0x3D 64 | #define I2S_TX1_CHMP_CTRL3 0x3E 65 | #define I2S_TX1_CHMP_CTRL4 0x3F 66 | 67 | //I2S TX2 Control 68 | #define I2S_TX2_CTRL1 0x40 69 | #define I2S_TX2_CTRL2 0x41 70 | #define I2S_TX2_CTRL3 0x42 71 | #define I2S_TX2_CHMP_CTRL1 0x44 72 | #define I2S_TX2_CHMP_CTRL2 0x45 73 | #define I2S_TX2_CHMP_CTRL3 0x46 74 | #define I2S_TX2_CHMP_CTRL4 0x47 75 | 76 | //I2S RX1 Control 77 | #define I2S_RX1_CTRL1 0x50 78 | #define I2S_RX1_CHMP_CTRL1 0x54 79 | #define I2S_RX1_CHMP_CTRL2 0x55 80 | #define I2S_RX1_CHMP_CTRL3 0x56 81 | #define I2S_RX1_CHMP_CTRL4 0x57 82 | 83 | //I2S Loopback Debug 84 | #define I2S_LPB_DEBUG 0x58 85 | 86 | //ADC Common Control 87 | #define ADC_SPRC 0x60 88 | #define ADC_DIG_EN 0x61 89 | #define DMIC_EN 0x62 90 | #define ADC_DSR 0x63 91 | #define ADC_FIR 0x64 92 | #define ADC_DDT_CTRL 0x65 93 | 94 | //HPF Control 95 | #define HPF_EN 0x66 96 | #define HPF_COEF_REGH1 0x67 97 | #define HPF_COEF_REGH2 0x68 98 | #define HPF_COEF_REGL1 0x69 99 | #define HPF_COEF_REGL2 0x6A 100 | #define HPF_GAIN_REGH1 0x6B 101 | #define HPF_GAIN_REGH2 0x6C 102 | #define HPF_GAIN_REGL1 0x6D 103 | #define HPF_GAIN_REGL2 0x6E 104 | 105 | //ADC Digital Channel Volume Control 106 | #define ADC1_DVOL_CTRL 0x70 107 | #define ADC2_DVOL_CTRL 0x71 108 | #define ADC3_DVOL_CTRL 0x72 109 | #define ADC4_DVOL_CTRL 0x73 110 | 111 | //ADC Digital Mixer Source and Gain Control 112 | #define ADC1_DMIX_SRC 0x76 113 | #define ADC2_DMIX_SRC 0x77 114 | #define ADC3_DMIX_SRC 0x78 115 | #define ADC4_DMIX_SRC 0x79 116 | 117 | //ADC Digital Debug Control 118 | #define ADC_DIG_DEBUG 0x7F 119 | 120 | //I2S Pad Drive Control 121 | #define I2S_DAT_PADDRV_CTRL 0x80 122 | #define I2S_CLK_PADDRV_CTRL 0x81 123 | 124 | //Analog PGA Control 125 | #define ANA_PGA1_CTRL 0x90 126 | #define ANA_PGA2_CTRL 0x91 127 | #define ANA_PGA3_CTRL 0x92 128 | #define ANA_PGA4_CTRL 0x93 129 | 130 | //MIC Offset Control 131 | #define MIC_OFFSET_CTRL1 0x96 132 | #define MIC_OFFSET_CTRL2 0x97 133 | #define MIC1_OFFSET_STATU1 0x98 134 | #define MIC1_OFFSET_STATU2 0x99 135 | #define MIC2_OFFSET_STATU1 0x9A 136 | #define MIC2_OFFSET_STATU2 0x9B 137 | #define MIC3_OFFSET_STATU1 0x9C 138 | #define MIC3_OFFSET_STATU2 0x9D 139 | #define MIC4_OFFSET_STATU1 0x9E 140 | #define MIC4_OFFSET_STATU2 0x9F 141 | 142 | //ADC1 Analog Control 143 | #define ANA_ADC1_CTRL1 0xA0 144 | #define ANA_ADC1_CTRL2 0xA1 145 | #define ANA_ADC1_CTRL3 0xA2 146 | #define ANA_ADC1_CTRL4 0xA3 147 | #define ANA_ADC1_CTRL5 0xA4 148 | #define ANA_ADC1_CTRL6 0xA5 149 | #define ANA_ADC1_CTRL7 0xA6 150 | 151 | //ADC2 Analog Control 152 | #define ANA_ADC2_CTRL1 0xA7 153 | #define ANA_ADC2_CTRL2 0xA8 154 | #define ANA_ADC2_CTRL3 0xA9 155 | #define ANA_ADC2_CTRL4 0xAA 156 | #define ANA_ADC2_CTRL5 0xAB 157 | #define ANA_ADC2_CTRL6 0xAC 158 | #define ANA_ADC2_CTRL7 0xAD 159 | 160 | //ADC3 Analog Control 161 | #define ANA_ADC3_CTRL1 0xAE 162 | #define ANA_ADC3_CTRL2 0xAF 163 | #define ANA_ADC3_CTRL3 0xB0 164 | #define ANA_ADC3_CTRL4 0xB1 165 | #define ANA_ADC3_CTRL5 0xB2 166 | #define ANA_ADC3_CTRL6 0xB3 167 | #define ANA_ADC3_CTRL7 0xB4 168 | 169 | //ADC4 Analog Control 170 | #define ANA_ADC4_CTRL1 0xB5 171 | #define ANA_ADC4_CTRL2 0xB6 172 | #define ANA_ADC4_CTRL3 0xB7 173 | #define ANA_ADC4_CTRL4 0xB8 174 | #define ANA_ADC4_CTRL5 0xB9 175 | #define ANA_ADC4_CTRL6 0xBA 176 | #define ANA_ADC4_CTRL7 0xBB 177 | 178 | //GPIO Configure 179 | #define GPIO_CFG1 0xC0 180 | #define GPIO_CFG2 0xC1 181 | #define GPIO_DAT 0xC2 182 | #define GPIO_DRV 0xC3 183 | #define GPIO_PULL 0xC4 184 | #define GPIO_INT_CFG 0xC5 185 | #define GPIO_INT_EN 0xC6 186 | #define GPIO_INT_STATUS 0xC7 187 | 188 | //Misc 189 | #define BGTC_DAT 0xD1 190 | #define BGVC_DAT 0xD2 191 | #define PRNG_CLK_CTRL 0xDF 192 | 193 | 194 | 195 | /*** AC108 Codec Register Bit Define***/ 196 | 197 | /*PWR_CTRL1*/ 198 | #define CP12_CTRL 4 199 | #define CP12_SENSE_SELECT 3 200 | 201 | /*PWR_CTRL2*/ 202 | #define CP12_SENSE_FILT 6 203 | #define CP12_COMP_FF_EN 3 204 | #define CP12_FORCE_ENABLE 2 205 | #define CP12_FORCE_RSTB 1 206 | 207 | /*PWR_CTRL3*/ 208 | #define LDO33DIG_CTRL 0 209 | 210 | /*PWR_CTRL6*/ 211 | #define LDO33ANA_2XHDRM 2 212 | #define LDO33ANA_ENABLE 0 213 | 214 | /*PWR_CTRL7*/ 215 | #define VREF_SEL 3 216 | #define VREF_FASTSTART_ENABLE 1 217 | #define VREF_ENABLE 0 218 | 219 | /*PWR_CTRL9*/ 220 | #define VREFP_FASTSTART_ENABLE 7 221 | #define VREFP_RESCTRL 5 222 | #define VREFP_LPMODE 4 223 | #define IGEN_TRIM 1 224 | #define VREFP_ENABLE 0 225 | 226 | 227 | /*PLL_CTRL1*/ 228 | #define PLL_IBIAS 4 229 | #define PLL_NDET 3 230 | #define PLL_LOCKED_STATUS 2 231 | #define PLL_COM_EN 1 232 | #define PLL_EN 0 233 | 234 | /*PLL_CTRL2*/ 235 | #define PLL_PREDIV2 5 236 | #define PLL_PREDIV1 0 237 | 238 | /*PLL_CTRL3*/ 239 | #define PLL_LOOPDIV_MSB 0 240 | 241 | /*PLL_CTRL4*/ 242 | #define PLL_LOOPDIV_LSB 0 243 | 244 | /*PLL_CTRL5*/ 245 | #define PLL_POSTDIV2 5 246 | #define PLL_POSTDIV1 0 247 | 248 | /*PLL_CTRL6*/ 249 | #define PLL_LDO 6 250 | #define PLL_CP 0 251 | 252 | /*PLL_CTRL7*/ 253 | #define PLL_CAP 6 254 | #define PLL_RES 4 255 | #define PLL_TEST_EN 0 256 | 257 | /*PLL_LOCK_CTRL*/ 258 | #define LOCK_LEVEL1 2 259 | #define LOCK_LEVEL2 1 260 | #define PLL_LOCK_EN 0 261 | 262 | 263 | /*SYSCLK_CTRL*/ 264 | #define PLLCLK_EN 7 265 | #define PLLCLK_SRC 4 266 | #define SYSCLK_SRC 3 267 | #define SYSCLK_EN 0 268 | 269 | /*MOD_CLK_EN & MOD_RST_CTRL*/ 270 | #define I2S 7 271 | #define ADC_DIGITAL 4 272 | #define MIC_OFFSET_CALIBRATION 1 273 | #define ADC_ANALOG 0 274 | 275 | /*DSM_CLK_CTRL*/ 276 | #define MIC_OFFSET_DIV 4 277 | #define DSM_CLK_SEL 0 278 | 279 | 280 | /*I2S_CTRL*/ 281 | #define BCLK_IOEN 7 282 | #define LRCK_IOEN 6 283 | #define SDO2_EN 5 284 | #define SDO1_EN 4 285 | #define TXEN 2 286 | #define RXEN 1 287 | #define GEN 0 288 | 289 | /*I2S_BCLK_CTRL*/ 290 | #define EDGE_TRANSFER 5 291 | #define BCLK_POLARITY 4 292 | #define BCLKDIV 0 293 | 294 | /*I2S_LRCK_CTRL1*/ 295 | #define LRCK_POLARITY 4 296 | #define LRCK_PERIODH 0 297 | 298 | /*I2S_LRCK_CTRL2*/ 299 | #define LRCK_PERIODL 0 300 | 301 | /*I2S_FMT_CTRL1*/ 302 | #define ENCD_SEL 6 303 | #define MODE_SEL 4 304 | #define TX2_OFFSET 3 305 | #define TX1_OFFSET 2 306 | #define TX_SLOT_HIZ 1 307 | #define TX_STATE 0 308 | 309 | /*I2S_FMT_CTRL2*/ 310 | #define SLOT_WIDTH_SEL 4 311 | #define SAMPLE_RESOLUTION 0 312 | 313 | /*I2S_FMT_CTRL3*/ 314 | #define TX_MLS 7 315 | #define SEXT 5 316 | #define OUT2_MUTE 4 317 | #define OUT1_MUTE 3 318 | #define LRCK_WIDTH 2 319 | #define TX_PDM 0 320 | 321 | 322 | /*I2S_TX1_CTRL1*/ 323 | #define TX1_CHSEL 0 324 | 325 | /*I2S_TX1_CTRL2*/ 326 | #define TX1_CH8_EN 7 327 | #define TX1_CH7_EN 6 328 | #define TX1_CH6_EN 5 329 | #define TX1_CH5_EN 4 330 | #define TX1_CH4_EN 3 331 | #define TX1_CH3_EN 2 332 | #define TX1_CH2_EN 1 333 | #define TX1_CH1_EN 0 334 | 335 | /*I2S_TX1_CTRL3*/ 336 | #define TX1_CH16_EN 7 337 | #define TX1_CH15_EN 6 338 | #define TX1_CH14_EN 5 339 | #define TX1_CH13_EN 4 340 | #define TX1_CH12_EN 3 341 | #define TX1_CH11_EN 2 342 | #define TX1_CH10_EN 1 343 | #define TX1_CH9_EN 0 344 | 345 | /*I2S_TX1_CHMP_CTRL1*/ 346 | #define TX1_CH4_MAP 6 347 | #define TX1_CH3_MAP 4 348 | #define TX1_CH2_MAP 2 349 | #define TX1_CH1_MAP 0 350 | 351 | /*I2S_TX1_CHMP_CTRL2*/ 352 | #define TX1_CH8_MAP 6 353 | #define TX1_CH7_MAP 4 354 | #define TX1_CH6_MAP 2 355 | #define TX1_CH5_MAP 0 356 | 357 | /*I2S_TX1_CHMP_CTRL3*/ 358 | #define TX1_CH12_MAP 6 359 | #define TX1_CH11_MAP 4 360 | #define TX1_CH10_MAP 2 361 | #define TX1_CH9_MAP 0 362 | 363 | /*I2S_TX1_CHMP_CTRL4*/ 364 | #define TX1_CH16_MAP 6 365 | #define TX1_CH15_MAP 4 366 | #define TX1_CH14_MAP 2 367 | #define TX1_CH13_MAP 0 368 | 369 | 370 | /*I2S_TX2_CTRL1*/ 371 | #define TX2_CHSEL 0 372 | 373 | /*I2S_TX2_CHMP_CTRL1*/ 374 | #define TX2_CH4_MAP 6 375 | #define TX2_CH3_MAP 4 376 | #define TX2_CH2_MAP 2 377 | #define TX2_CH1_MAP 0 378 | 379 | /*I2S_TX2_CHMP_CTRL2*/ 380 | #define TX2_CH8_MAP 6 381 | #define TX2_CH7_MAP 4 382 | #define TX2_CH6_MAP 2 383 | #define TX2_CH5_MAP 0 384 | 385 | /*I2S_TX2_CHMP_CTRL3*/ 386 | #define TX2_CH12_MAP 6 387 | #define TX2_CH11_MAP 4 388 | #define TX2_CH10_MAP 2 389 | #define TX2_CH9_MAP 0 390 | 391 | /*I2S_TX2_CHMP_CTRL4*/ 392 | #define TX2_CH16_MAP 6 393 | #define TX2_CH15_MAP 4 394 | #define TX2_CH14_MAP 2 395 | #define TX2_CH13_MAP 0 396 | 397 | 398 | /*I2S_RX1_CTRL1*/ 399 | #define RX1_CHSEL 0 400 | 401 | /*I2S_RX1_CHMP_CTRL1*/ 402 | #define RX1_CH4_MAP 6 403 | #define RX1_CH3_MAP 4 404 | #define RX1_CH2_MAP 2 405 | #define RX1_CH1_MAP 0 406 | 407 | /*I2S_RX1_CHMP_CTRL2*/ 408 | #define RX1_CH8_MAP 6 409 | #define RX1_CH7_MAP 4 410 | #define RX1_CH6_MAP 2 411 | #define RX1_CH5_MAP 0 412 | 413 | /*I2S_RX1_CHMP_CTRL3*/ 414 | #define RX1_CH12_MAP 6 415 | #define RX1_CH11_MAP 4 416 | #define RX1_CH10_MAP 2 417 | #define RX1_CH9_MAP 0 418 | 419 | /*I2S_RX1_CHMP_CTRL4*/ 420 | #define RX1_CH16_MAP 6 421 | #define RX1_CH15_MAP 4 422 | #define RX1_CH14_MAP 2 423 | #define RX1_CH13_MAP 0 424 | 425 | 426 | /*I2S_LPB_DEBUG*/ 427 | #define I2S_LPB_DEBUG_EN 0 428 | 429 | 430 | /*ADC_SPRC*/ 431 | #define ADC_FS_I2S1 0 432 | 433 | /*ADC_DIG_EN*/ 434 | #define DG_EN 4 435 | #define ENAD4 3 436 | #define ENAD3 2 437 | #define ENAD2 1 438 | #define ENAD1 0 439 | 440 | /*DMIC_EN*/ 441 | #define DMIC2_EN 1 442 | #define DMIC1_EN 0 443 | 444 | /*ADC_DSR*/ 445 | #define DIG_ADC4_SRS 6 446 | #define DIG_ADC3_SRS 4 447 | #define DIG_ADC2_SRS 2 448 | #define DIG_ADC1_SRS 0 449 | 450 | /*ADC_DDT_CTRL*/ 451 | #define ADOUT_DLY_EN 2 452 | #define ADOUT_DTS 0 453 | 454 | 455 | /*HPF_EN*/ 456 | #define DIG_ADC4_HPF_EN 3 457 | #define DIG_ADC3_HPF_EN 2 458 | #define DIG_ADC2_HPF_EN 1 459 | #define DIG_ADC1_HPF_EN 0 460 | 461 | 462 | /*ADC1_DMIX_SRC*/ 463 | #define ADC1_ADC4_DMXL_GC 7 464 | #define ADC1_ADC3_DMXL_GC 6 465 | #define ADC1_ADC2_DMXL_GC 5 466 | #define ADC1_ADC1_DMXL_GC 4 467 | #define ADC1_ADC4_DMXL_SRC 3 468 | #define ADC1_ADC3_DMXL_SRC 2 469 | #define ADC1_ADC2_DMXL_SRC 1 470 | #define ADC1_ADC1_DMXL_SRC 0 471 | 472 | /*ADC2_DMIX_SRC*/ 473 | #define ADC2_ADC4_DMXL_GC 7 474 | #define ADC2_ADC3_DMXL_GC 6 475 | #define ADC2_ADC2_DMXL_GC 5 476 | #define ADC2_ADC1_DMXL_GC 4 477 | #define ADC2_ADC4_DMXL_SRC 3 478 | #define ADC2_ADC3_DMXL_SRC 2 479 | #define ADC2_ADC2_DMXL_SRC 1 480 | #define ADC2_ADC1_DMXL_SRC 0 481 | 482 | /*ADC3_DMIX_SRC*/ 483 | #define ADC3_ADC4_DMXL_GC 7 484 | #define ADC3_ADC3_DMXL_GC 6 485 | #define ADC3_ADC2_DMXL_GC 5 486 | #define ADC3_ADC1_DMXL_GC 4 487 | #define ADC3_ADC4_DMXL_SRC 3 488 | #define ADC3_ADC3_DMXL_SRC 2 489 | #define ADC3_ADC2_DMXL_SRC 1 490 | #define ADC3_ADC1_DMXL_SRC 0 491 | 492 | /*ADC4_DMIX_SRC*/ 493 | #define ADC4_ADC4_DMXL_GC 7 494 | #define ADC4_ADC3_DMXL_GC 6 495 | #define ADC4_ADC2_DMXL_GC 5 496 | #define ADC4_ADC1_DMXL_GC 4 497 | #define ADC4_ADC4_DMXL_SRC 3 498 | #define ADC4_ADC3_DMXL_SRC 2 499 | #define ADC4_ADC2_DMXL_SRC 1 500 | #define ADC4_ADC1_DMXL_SRC 0 501 | 502 | 503 | /*ADC_DIG_DEBUG*/ 504 | #define ADC_PTN_SEL 0 505 | 506 | 507 | /*I2S_DAT_PADDRV_CTRL*/ 508 | #define TX2_DAT_DRV 4 509 | #define TX1_DAT_DRV 0 510 | 511 | /*I2S_CLK_PADDRV_CTRL*/ 512 | #define LRCK_DRV 4 513 | #define BCLK_DRV 0 514 | 515 | 516 | /*ANA_PGA1_CTRL*/ 517 | #define ADC1_ANALOG_PGA 1 518 | #define ADC1_ANALOG_PGA_STEP 0 519 | 520 | /*ANA_PGA2_CTRL*/ 521 | #define ADC2_ANALOG_PGA 1 522 | #define ADC2_ANALOG_PGA_STEP 0 523 | 524 | /*ANA_PGA3_CTRL*/ 525 | #define ADC3_ANALOG_PGA 1 526 | #define ADC3_ANALOG_PGA_STEP 0 527 | 528 | /*ANA_PGA4_CTRL*/ 529 | #define ADC4_ANALOG_PGA 1 530 | #define ADC4_ANALOG_PGA_STEP 0 531 | 532 | 533 | /*MIC_OFFSET_CTRL1*/ 534 | #define MIC_OFFSET_CAL_EN4 3 535 | #define MIC_OFFSET_CAL_EN3 2 536 | #define MIC_OFFSET_CAL_EN2 1 537 | #define MIC_OFFSET_CAL_EN1 0 538 | 539 | /*MIC_OFFSET_CTRL2*/ 540 | #define MIC_OFFSET_CAL_GAIN 3 541 | #define MIC_OFFSET_CAL_CHANNEL 1 542 | #define MIC_OFFSET_CAL_EN_ONCE 0 543 | 544 | /*MIC1_OFFSET_STATU1*/ 545 | #define MIC1_OFFSET_CAL_DONE 7 546 | #define MIC1_OFFSET_CAL_RUN_STA 6 547 | #define MIC1_OFFSET_MSB 0 548 | 549 | /*MIC1_OFFSET_STATU2*/ 550 | #define MIC1_OFFSET_LSB 0 551 | 552 | /*MIC2_OFFSET_STATU1*/ 553 | #define MIC2_OFFSET_CAL_DONE 7 554 | #define MIC2_OFFSET_CAL_RUN_STA 6 555 | #define MIC2_OFFSET_MSB 0 556 | 557 | /*MIC2_OFFSET_STATU2*/ 558 | #define MIC2_OFFSET_LSB 0 559 | 560 | /*MIC3_OFFSET_STATU1*/ 561 | #define MIC3_OFFSET_CAL_DONE 7 562 | #define MIC3_OFFSET_CAL_RUN_STA 6 563 | #define MIC3_OFFSET_MSB 0 564 | 565 | /*MIC3_OFFSET_STATU2*/ 566 | #define MIC3_OFFSET_LSB 0 567 | 568 | /*MIC4_OFFSET_STATU1*/ 569 | #define MIC4_OFFSET_CAL_DONE 7 570 | #define MIC4_OFFSET_CAL_RUN_STA 6 571 | #define MIC4_OFFSET_MSB 0 572 | 573 | /*MIC4_OFFSET_STATU2*/ 574 | #define MIC4_OFFSET_LSB 0 575 | 576 | 577 | /*ANA_ADC1_CTRL1*/ 578 | #define ADC1_PGA_BYPASS 7 579 | #define ADC1_PGA_BYP_RCM 6 580 | #define ADC1_PGA_CTRL_RCM 4 581 | #define ADC1_PGA_MUTE 3 582 | #define ADC1_DSM_ENABLE 2 583 | #define ADC1_PGA_ENABLE 1 584 | #define ADC1_MICBIAS_EN 0 585 | 586 | /*ANA_ADC1_CTRL3*/ 587 | #define ADC1_ANA_CAL_EN 5 588 | #define ADC1_SEL_OUT_EDGE 3 589 | #define ADC1_DSM_DISABLE 2 590 | #define ADC1_VREFP_DISABLE 1 591 | #define ADC1_AAF_DISABLE 0 592 | 593 | /*ANA_ADC1_CTRL6*/ 594 | #define PGA_CTRL_TC 6 595 | #define PGA_CTRL_RC 4 596 | #define PGA_CTRL_I_LIN 2 597 | #define PGA_CTRL_I_IN 0 598 | 599 | /*ANA_ADC1_CTRL7*/ 600 | #define PGA_CTRL_HI_Z 7 601 | #define PGA_CTRL_SHORT_RF 6 602 | #define PGA_CTRL_VCM_VG 4 603 | #define PGA_CTRL_VCM_IN 0 604 | 605 | 606 | /*ANA_ADC2_CTRL1*/ 607 | #define ADC2_PGA_BYPASS 7 608 | #define ADC2_PGA_BYP_RCM 6 609 | #define ADC2_PGA_CTRL_RCM 4 610 | #define ADC2_PGA_MUTE 3 611 | #define ADC2_DSM_ENABLE 2 612 | #define ADC2_PGA_ENABLE 1 613 | #define ADC2_MICBIAS_EN 0 614 | 615 | /*ANA_ADC2_CTRL3*/ 616 | #define ADC2_ANA_CAL_EN 5 617 | #define ADC2_SEL_OUT_EDGE 3 618 | #define ADC2_DSM_DISABLE 2 619 | #define ADC2_VREFP_DISABLE 1 620 | #define ADC2_AAF_DISABLE 0 621 | 622 | /*ANA_ADC2_CTRL6*/ 623 | #define PGA_CTRL_IBOOST 7 624 | #define PGA_CTRL_IQCTRL 6 625 | #define PGA_CTRL_OABIAS 4 626 | #define PGA_CTRL_CMLP_DIS 3 627 | #define PGA_CTRL_PDB_RIN 2 628 | #define PGA_CTRL_PEAKDET 0 629 | 630 | /*ANA_ADC2_CTRL7*/ 631 | #define AAF_LPMODE_EN 7 632 | #define AAF_STG2_IB_SEL 4 633 | #define AAFDSM_IB_DIV2 3 634 | #define AAF_STG1_IB_SEL 0 635 | 636 | 637 | /*ANA_ADC3_CTRL1*/ 638 | #define ADC3_PGA_BYPASS 7 639 | #define ADC3_PGA_BYP_RCM 6 640 | #define ADC3_PGA_CTRL_RCM 4 641 | #define ADC3_PGA_MUTE 3 642 | #define ADC3_DSM_ENABLE 2 643 | #define ADC3_PGA_ENABLE 1 644 | #define ADC3_MICBIAS_EN 0 645 | 646 | /*ANA_ADC3_CTRL3*/ 647 | #define ADC3_ANA_CAL_EN 5 648 | #define ADC3_INVERT_CLK 4 649 | #define ADC3_SEL_OUT_EDGE 3 650 | #define ADC3_DSM_DISABLE 2 651 | #define ADC3_VREFP_DISABLE 1 652 | #define ADC3_AAF_DISABLE 0 653 | 654 | /*ANA_ADC3_CTRL7*/ 655 | #define DSM_COMP_IB_SEL 6 656 | #define DSM_OTA_CTRL 4 657 | #define DSM_LPMODE 3 658 | #define DSM_OTA_IB_SEL 0 659 | 660 | 661 | /*ANA_ADC4_CTRL1*/ 662 | #define ADC4_PGA_BYPASS 7 663 | #define ADC4_PGA_BYP_RCM 6 664 | #define ADC4_PGA_CTRL_RCM 4 665 | #define ADC4_PGA_MUTE 3 666 | #define ADC4_DSM_ENABLE 2 667 | #define ADC4_PGA_ENABLE 1 668 | #define ADC4_MICBIAS_EN 0 669 | 670 | /*ANA_ADC4_CTRL3*/ 671 | #define ADC4_ANA_CAL_EN 5 672 | #define ADC4_SEL_OUT_EDGE 3 673 | #define ADC4_DSM_DISABLE 2 674 | #define ADC4_VREFP_DISABLE 1 675 | #define ADC4_AAF_DISABLE 0 676 | 677 | /*ANA_ADC4_CTRL6*/ 678 | #define DSM_DEMOFF 5 679 | #define DSM_EN_DITHER 4 680 | #define DSM_VREFP_LPMODE 2 681 | #define DSM_VREFP_OUTCTRL 0 682 | 683 | /*ANA_ADC4_CTRL7*/ 684 | #define CK8M_EN 5 685 | #define OSC_EN 4 686 | #define ADC4_CLK_GATING 3 687 | #define ADC3_CLK_GATING 2 688 | #define ADC2_CLK_GATING 1 689 | #define ADC1_CLK_GATING 0 690 | 691 | 692 | /*GPIO_CFG1*/ 693 | #define GPIO2_SELECT 4 694 | #define GPIO1_SELECT 0 695 | 696 | /*GPIO_CFG2*/ 697 | #define GPIO4_SELECT 4 698 | #define GPIO3_SELECT 0 699 | 700 | /*GPIO_DAT*///order??? 701 | #define GPIO4_DAT 3 702 | #define GPIO3_DAT 2 703 | #define GPIO2_DAT 1 704 | #define GPIO1_DAT 0 705 | 706 | /*GPIO_DRV*/ 707 | #define GPIO4_DRV 6 708 | #define GPIO3_DRV 4 709 | #define GPIO2_DRV 2 710 | #define GPIO1_DRV 0 711 | 712 | /*GPIO_PULL*/ 713 | #define GPIO4_PULL 6 714 | #define GPIO3_PULL 4 715 | #define GPIO2_PULL 2 716 | #define GPIO1_PULL 0 717 | 718 | /*GPIO_INT_CFG*/ 719 | #define GPIO4_EINT_CFG 6 720 | #define GPIO3_EINT_CFG 4 721 | #define GPIO2_EINT_CFG 2 722 | #define GPIO1_EINT_CFG 0 723 | 724 | /*GPIO_INT_EN*///order??? 725 | #define GPIO4_EINT_EN 3 726 | #define GPIO3_EINT_EN 2 727 | #define GPIO2_EINT_EN 1 728 | #define GPIO1_EINT_EN 0 729 | 730 | /*GPIO_INT_STATUS*///order??? 731 | #define GPIO4_EINT_STA 3 732 | #define GPIO3_EINT_STA 2 733 | #define GPIO2_EINT_STA 1 734 | #define GPIO1_EINT_STA 0 735 | 736 | 737 | /*PRNG_CLK_CTRL*/ 738 | #define PRNG_CLK_EN 1 739 | #define PRNG_CLK_POS 0 740 | 741 | 742 | 743 | /*** Some Config Value ***/ 744 | 745 | //[SYSCLK_CTRL]: PLLCLK_SRC 746 | #define PLLCLK_SRC_MCLK 0 747 | #define PLLCLK_SRC_BCLK 1 748 | #define PLLCLK_SRC_GPIO2 2 749 | #define PLLCLK_SRC_GPIO3 3 750 | 751 | //[SYSCLK_CTRL]: SYSCLK_SRC 752 | #define SYSCLK_SRC_MCLK 0 753 | #define SYSCLK_SRC_PLL 1 754 | 755 | //I2S BCLK POLARITY Control 756 | #define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0 757 | #define BCLK_INVERT_DRIVE_P_SAMPLE_N 1 758 | 759 | //I2S LRCK POLARITY Control 760 | #define LRCK_LEFT_LOW_RIGHT_HIGH 0 761 | #define LRCK_LEFT_HIGH_RIGHT_LOW 1 762 | 763 | //I2S Format Selection 764 | #define PCM_FORMAT 0 765 | #define LEFT_JUSTIFIED_FORMAT 1 766 | #define RIGHT_JUSTIFIED_FORMAT 2 767 | 768 | 769 | //I2S data protocol types 770 | 771 | #define IS_ENCODING_MODE 0 772 | 773 | #endif 774 | 775 | -------------------------------------------------------------------------------- /ac108_6mic.state: -------------------------------------------------------------------------------- 1 | state.ALSA { 2 | control.1 { 3 | iface MIXER 4 | name 'PCM Playback Volume' 5 | value 400 6 | comment { 7 | access 'read write' 8 | type INTEGER 9 | count 1 10 | range '-10239 - 400' 11 | dbmin -9999999 12 | dbmax 400 13 | dbvalue.0 400 14 | } 15 | } 16 | control.2 { 17 | iface MIXER 18 | name 'PCM Playback Switch' 19 | value true 20 | comment { 21 | access 'read write' 22 | type BOOLEAN 23 | count 1 24 | } 25 | } 26 | control.3 { 27 | iface MIXER 28 | name 'PCM Playback Route' 29 | value 1 30 | comment { 31 | access 'read write' 32 | type INTEGER 33 | count 1 34 | range '0 - 2' 35 | } 36 | } 37 | control.4 { 38 | iface PCM 39 | name 'IEC958 Playback Default' 40 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 41 | comment { 42 | access 'read write' 43 | type IEC958 44 | count 1 45 | } 46 | } 47 | control.5 { 48 | iface PCM 49 | name 'IEC958 Playback Con Mask' 50 | value '0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 51 | comment { 52 | access read 53 | type IEC958 54 | count 1 55 | } 56 | } 57 | control.6 { 58 | iface PCM 59 | name 'IEC958 Playback PCM Stream' 60 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 61 | comment { 62 | access 'read write inactive' 63 | type IEC958 64 | count 1 65 | } 66 | } 67 | } 68 | state.seeed8micvoicec { 69 | control.1 { 70 | iface MIXER 71 | name 'CH1 digital volume' 72 | value 208 73 | comment { 74 | access 'read write' 75 | type INTEGER 76 | count 1 77 | range '0 - 255' 78 | dbmin -11925 79 | dbmax 7200 80 | dbvalue.0 3675 81 | } 82 | } 83 | control.2 { 84 | iface MIXER 85 | name 'CH2 digital volume' 86 | value 208 87 | comment { 88 | access 'read write' 89 | type INTEGER 90 | count 1 91 | range '0 - 255' 92 | dbmin -11925 93 | dbmax 7200 94 | dbvalue.0 3675 95 | } 96 | } 97 | control.3 { 98 | iface MIXER 99 | name 'CH3 digital volume' 100 | value 208 101 | comment { 102 | access 'read write' 103 | type INTEGER 104 | count 1 105 | range '0 - 255' 106 | dbmin -11925 107 | dbmax 7200 108 | dbvalue.0 3675 109 | } 110 | } 111 | control.4 { 112 | iface MIXER 113 | name 'CH4 digital volume' 114 | value 208 115 | comment { 116 | access 'read write' 117 | type INTEGER 118 | count 1 119 | range '0 - 255' 120 | dbmin -11925 121 | dbmax 7200 122 | dbvalue.0 3675 123 | } 124 | } 125 | control.5 { 126 | iface MIXER 127 | name 'ADC1 PGA gain' 128 | value 0 129 | comment { 130 | access 'read write' 131 | type INTEGER 132 | count 1 133 | range '0 - 31' 134 | dbmin 0 135 | dbmax 3100 136 | dbvalue.0 0 137 | } 138 | } 139 | control.6 { 140 | iface MIXER 141 | name 'ADC2 PGA gain' 142 | value 0 143 | comment { 144 | access 'read write' 145 | type INTEGER 146 | count 1 147 | range '0 - 31' 148 | dbmin 0 149 | dbmax 3100 150 | dbvalue.0 0 151 | } 152 | } 153 | control.7 { 154 | iface MIXER 155 | name 'ADC3 PGA gain' 156 | value 0 157 | comment { 158 | access 'read write' 159 | type INTEGER 160 | count 1 161 | range '0 - 31' 162 | dbmin 0 163 | dbmax 3100 164 | dbvalue.0 0 165 | } 166 | } 167 | control.8 { 168 | iface MIXER 169 | name 'ADC4 PGA gain' 170 | value 0 171 | comment { 172 | access 'read write' 173 | type INTEGER 174 | count 1 175 | range '0 - 31' 176 | dbmin 0 177 | dbmax 3100 178 | dbvalue.0 0 179 | } 180 | } 181 | control.9 { 182 | iface MIXER 183 | name 'CH5 digital volume' 184 | value 208 185 | comment { 186 | access 'read write' 187 | type INTEGER 188 | count 1 189 | range '0 - 255' 190 | dbmin -11925 191 | dbmax 7200 192 | dbvalue.0 3675 193 | } 194 | } 195 | control.10 { 196 | iface MIXER 197 | name 'CH6 digital volume' 198 | value 208 199 | comment { 200 | access 'read write' 201 | type INTEGER 202 | count 1 203 | range '0 - 255' 204 | dbmin -11925 205 | dbmax 7200 206 | dbvalue.0 3675 207 | } 208 | } 209 | control.11 { 210 | iface MIXER 211 | name 'CH7 digital volume' 212 | value 198 213 | comment { 214 | access 'read write' 215 | type INTEGER 216 | count 1 217 | range '0 - 255' 218 | dbmin -11925 219 | dbmax 7200 220 | dbvalue.0 2925 221 | } 222 | } 223 | control.12 { 224 | iface MIXER 225 | name 'CH8 digital volume' 226 | value 198 227 | comment { 228 | access 'read write' 229 | type INTEGER 230 | count 1 231 | range '0 - 255' 232 | dbmin -11925 233 | dbmax 7200 234 | dbvalue.0 2925 235 | } 236 | } 237 | control.13 { 238 | iface MIXER 239 | name 'ADC5 PGA gain' 240 | value 0 241 | comment { 242 | access 'read write' 243 | type INTEGER 244 | count 1 245 | range '0 - 31' 246 | dbmin 0 247 | dbmax 3100 248 | dbvalue.0 0 249 | } 250 | } 251 | control.14 { 252 | iface MIXER 253 | name 'ADC6 PGA gain' 254 | value 0 255 | comment { 256 | access 'read write' 257 | type INTEGER 258 | count 1 259 | range '0 - 31' 260 | dbmin 0 261 | dbmax 3100 262 | dbvalue.0 0 263 | } 264 | } 265 | control.15 { 266 | iface MIXER 267 | name 'ADC7 PGA gain' 268 | value 0 269 | comment { 270 | access 'read write' 271 | type INTEGER 272 | count 1 273 | range '0 - 31' 274 | dbmin 0 275 | dbmax 3100 276 | dbvalue.0 0 277 | } 278 | } 279 | control.16 { 280 | iface MIXER 281 | name 'ADC8 PGA gain' 282 | value 0 283 | comment { 284 | access 'read write' 285 | type INTEGER 286 | count 1 287 | range '0 - 31' 288 | dbmin 0 289 | dbmax 3100 290 | dbvalue.0 0 291 | } 292 | } 293 | control.17 { 294 | iface MIXER 295 | name 'DAC volume' 296 | value.0 0 297 | value.1 0 298 | comment { 299 | access 'read write' 300 | type INTEGER 301 | count 2 302 | range '0 - 255' 303 | dbmin -11925 304 | dbmax 7200 305 | dbvalue.0 -11925 306 | dbvalue.1 -11925 307 | } 308 | } 309 | control.18 { 310 | iface MIXER 311 | name 'DAC mixer gain' 312 | value.0 0 313 | value.1 0 314 | comment { 315 | access 'read write' 316 | type INTEGER 317 | count 2 318 | range '0 - 15' 319 | dbmin -600 320 | dbmax 8400 321 | dbvalue.0 -600 322 | dbvalue.1 -600 323 | } 324 | } 325 | control.19 { 326 | iface MIXER 327 | name 'digital volume' 328 | value 51 329 | comment { 330 | access 'read write' 331 | type INTEGER 332 | count 1 333 | range '0 - 63' 334 | dbmin -7308 335 | dbmax 0 336 | dbvalue.0 -1392 337 | } 338 | } 339 | control.20 { 340 | iface MIXER 341 | name 'Speaker Playback Volume' 342 | value 25 343 | comment { 344 | access 'read write' 345 | type INTEGER 346 | count 1 347 | range '0 - 31' 348 | dbmin -4800 349 | dbmax -150 350 | dbvalue.0 -1050 351 | } 352 | } 353 | control.21 { 354 | iface MIXER 355 | name 'Headphone Playback Volume' 356 | value 52 357 | comment { 358 | access 'read write' 359 | type INTEGER 360 | count 1 361 | range '0 - 63' 362 | dbmin -6300 363 | dbmax 0 364 | dbvalue.0 -1100 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /ac108_asound.state: -------------------------------------------------------------------------------- 1 | state.ALSA { 2 | control.1 { 3 | iface MIXER 4 | name 'PCM Playback Volume' 5 | value 400 6 | comment { 7 | access 'read write' 8 | type INTEGER 9 | count 1 10 | range '-10239 - 400' 11 | dbmin -9999999 12 | dbmax 400 13 | dbvalue.0 400 14 | } 15 | } 16 | control.2 { 17 | iface MIXER 18 | name 'PCM Playback Switch' 19 | value true 20 | comment { 21 | access 'read write' 22 | type BOOLEAN 23 | count 1 24 | } 25 | } 26 | control.3 { 27 | iface MIXER 28 | name 'PCM Playback Route' 29 | value 0 30 | comment { 31 | access 'read write' 32 | type INTEGER 33 | count 1 34 | range '0 - 2' 35 | } 36 | } 37 | control.4 { 38 | iface PCM 39 | name 'IEC958 Playback Default' 40 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 41 | comment { 42 | access 'read write' 43 | type IEC958 44 | count 1 45 | } 46 | } 47 | control.5 { 48 | iface PCM 49 | name 'IEC958 Playback Con Mask' 50 | value '0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 51 | comment { 52 | access read 53 | type IEC958 54 | count 1 55 | } 56 | } 57 | control.6 { 58 | iface PCM 59 | name 'IEC958 Playback PCM Stream' 60 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 61 | comment { 62 | access 'read write inactive' 63 | type IEC958 64 | count 1 65 | } 66 | } 67 | } 68 | state.seeed4micvoicec { 69 | control.1 { 70 | iface MIXER 71 | name 'CH1 digital volume' 72 | value 222 73 | comment { 74 | access 'read write' 75 | type INTEGER 76 | count 1 77 | range '0 - 255' 78 | dbmin -11925 79 | dbmax 7200 80 | dbvalue.0 4725 81 | } 82 | } 83 | control.2 { 84 | iface MIXER 85 | name 'CH2 digital volume' 86 | value 222 87 | comment { 88 | access 'read write' 89 | type INTEGER 90 | count 1 91 | range '0 - 255' 92 | dbmin -11925 93 | dbmax 7200 94 | dbvalue.0 4725 95 | } 96 | } 97 | control.3 { 98 | iface MIXER 99 | name 'CH3 digital volume' 100 | value 222 101 | comment { 102 | access 'read write' 103 | type INTEGER 104 | count 1 105 | range '0 - 255' 106 | dbmin -11925 107 | dbmax 7200 108 | dbvalue.0 4725 109 | } 110 | } 111 | control.4 { 112 | iface MIXER 113 | name 'CH4 digital volume' 114 | value 222 115 | comment { 116 | access 'read write' 117 | type INTEGER 118 | count 1 119 | range '0 - 255' 120 | dbmin -11925 121 | dbmax 7200 122 | dbvalue.0 4725 123 | } 124 | } 125 | control.5 { 126 | iface MIXER 127 | name 'ADC1 PGA gain' 128 | value 0 129 | comment { 130 | access 'read write' 131 | type INTEGER 132 | count 1 133 | range '0 - 31' 134 | dbmin 0 135 | dbmax 3100 136 | dbvalue.0 0 137 | } 138 | } 139 | control.6 { 140 | iface MIXER 141 | name 'ADC2 PGA gain' 142 | value 0 143 | comment { 144 | access 'read write' 145 | type INTEGER 146 | count 1 147 | range '0 - 31' 148 | dbmin 0 149 | dbmax 3100 150 | dbvalue.0 0 151 | } 152 | } 153 | control.7 { 154 | iface MIXER 155 | name 'ADC3 PGA gain' 156 | value 0 157 | comment { 158 | access 'read write' 159 | type INTEGER 160 | count 1 161 | range '0 - 31' 162 | dbmin 0 163 | dbmax 3100 164 | dbvalue.0 0 165 | } 166 | } 167 | control.8 { 168 | iface MIXER 169 | name 'ADC4 PGA gain' 170 | value 0 171 | comment { 172 | access 'read write' 173 | type INTEGER 174 | count 1 175 | range '0 - 31' 176 | dbmin 0 177 | dbmax 3100 178 | dbvalue.0 0 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ac108_plugin/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Quiet (set to @ for a quite compile) 3 | Q ?= @ 4 | #Q ?= 5 | 6 | # Build Tools 7 | CC := gcc 8 | CFLAGS += -I. -Wall -funroll-loops -ffast-math -fPIC -DPIC -O0 -g 9 | LD := gcc 10 | LDFLAGS += -Wall -shared -lasound 11 | 12 | SND_PCM_OBJECTS = pcm_ac108.o ac108_help.o 13 | SND_PCM_LIBS = 14 | SND_PCM_BIN = libasound_module_pcm_ac108.so 15 | 16 | #SND_CTL_OBJECTS = ctl_ac108.o ladspa_utils.o 17 | #SND_CTL_LIBS = 18 | #SND_CTL_BIN = libasound_module_ctl_ac108.so 19 | 20 | MULTIARCH:=$(shell gcc --print-multiarch) 21 | LIBDIR = lib/$(MULTIARCH) 22 | 23 | .PHONY: all clean dep load_default 24 | 25 | all: Makefile $(SND_PCM_BIN) $(SND_CTL_BIN) 26 | 27 | dep: 28 | @echo DEP $@ 29 | $(Q)for i in *.c; do $(CC) -MM $(CFLAGS) "$${i}" ; done > makefile.dep 30 | 31 | -include makefile.dep 32 | 33 | $(SND_PCM_BIN): $(SND_PCM_OBJECTS) 34 | @echo LD $@ 35 | $(Q)$(LD) $(LDFLAGS) $(SND_PCM_LIBS) $(SND_PCM_OBJECTS) -o $(SND_PCM_BIN) 36 | 37 | #$(SND_CTL_BIN): $(SND_CTL_OBJECTS) 38 | # @echo LD $@ 39 | # $(Q)$(LD) $(LDFLAGS) $(SND_CTL_LIBS) $(SND_CTL_OBJECTS) -o $(SND_CTL_BIN) 40 | 41 | %.o: %.c 42 | @echo GCC $< 43 | $(Q)$(CC) -c $(CFLAGS) $(CPPFLAGS) $< 44 | 45 | clean: 46 | @echo Cleaning... 47 | $(Q)rm -vf *.o *.so 48 | 49 | install: all 50 | @echo Installing... 51 | $(Q)mkdir -p ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ 52 | $(Q)install -m 644 $(SND_PCM_BIN) ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ 53 | #$(Q)install -m 644 $(SND_CTL_BIN) ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ 54 | 55 | uninstall: 56 | @echo Un-installing... 57 | $(Q)rm ${DESTDIR}/usr/lib/alsa-lib/$(SND_PCM_BIN) 58 | #$(Q)rm ${DESTDIR}/usr/lib/alsa-lib/$(SND_CTL_BIN) 59 | -------------------------------------------------------------------------------- /ac108_plugin/README.md: -------------------------------------------------------------------------------- 1 | #seeed-4mic-voicecard alsa plugin 2 | ``` 3 | sudo apt install libasound2-dev 4 | make && sudo make install 5 | ``` -------------------------------------------------------------------------------- /ac108_plugin/ac108_help.c: -------------------------------------------------------------------------------- 1 | #include "ac108_help.h" 2 | 3 | void generate_sine(const snd_pcm_channel_area_t *areas, 4 | snd_pcm_uframes_t offset, 5 | int count, double *_phase) 6 | { 7 | snd_pcm_format_t format = SND_PCM_FORMAT_S32; /* sample format */ 8 | unsigned int rate = 16000; /* stream rate */ 9 | unsigned int channels = 4; /* count of channels */ 10 | unsigned int buffer_time = 500000; /* ring buffer length in us */ 11 | unsigned int period_time = 100000; /* period time in us */ 12 | double freq = 160; /* sinusoidal wave frequency in Hz */ 13 | int verbose = 0; /* verbose flag */ 14 | int resample = 1; /* enable alsa-lib resampling */ 15 | int period_event = 0; /* produce poll event after each period */ 16 | 17 | static double max_phase = 2. * M_PI; 18 | double phase = *_phase; 19 | double step = max_phase*freq/(double)rate; 20 | unsigned char *samples[channels]; 21 | int steps[channels]; 22 | unsigned int chn; 23 | int format_bits = snd_pcm_format_width(format); 24 | unsigned int maxval = (1 << (format_bits - 1)) - 1; 25 | int bps = format_bits / 8; /* bytes per sample */ 26 | int phys_bps = snd_pcm_format_physical_width(format) / 8; 27 | int big_endian = snd_pcm_format_big_endian(format) == 1; 28 | int to_unsigned = snd_pcm_format_unsigned(format) == 1; 29 | int is_float = (format == SND_PCM_FORMAT_FLOAT_LE || 30 | format == SND_PCM_FORMAT_FLOAT_BE); 31 | 32 | /* verify and prepare the contents of areas */ 33 | for (chn = 0; chn < channels; chn++) { 34 | if ((areas[chn].first % 8) != 0) { 35 | printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first); 36 | exit(EXIT_FAILURE); 37 | } 38 | samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8)); 39 | if ((areas[chn].step % 16) != 0) { 40 | printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step); 41 | exit(EXIT_FAILURE); 42 | } 43 | steps[chn] = areas[chn].step / 8; 44 | samples[chn] += offset * steps[chn]; 45 | } 46 | /* fill the channel areas */ 47 | while (count-- > 0) { 48 | union { 49 | float f; 50 | int i; 51 | } fval; 52 | int res, i; 53 | if (is_float) { 54 | fval.f = sin(phase); 55 | res = fval.i; 56 | } else 57 | res = sin(phase) * maxval; 58 | if (to_unsigned) 59 | res ^= 1U << (format_bits - 1); 60 | for (chn = 0; chn < channels; chn++) { 61 | /* Generate data in native endian format */ 62 | if (big_endian) { 63 | for (i = 0; i < bps; i++) 64 | *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff; 65 | } else { 66 | for (i = 0; i < bps; i++) 67 | *(samples[chn] + i) = (res >> i * 8) & 0xff; 68 | } 69 | samples[chn] += steps[chn]; 70 | } 71 | phase += step; 72 | if (phase >= max_phase) 73 | phase -= max_phase; 74 | } 75 | *_phase = phase; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /ac108_plugin/ac108_help.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void generate_sine(const snd_pcm_channel_area_t *areas, 10 | snd_pcm_uframes_t offset, 11 | int count, double *_phase); 12 | -------------------------------------------------------------------------------- /ac108_plugin/libasound_module_pcm_ac108.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/ac108_plugin/libasound_module_pcm_ac108.so -------------------------------------------------------------------------------- /ac108_plugin/pcm_ac108.c: -------------------------------------------------------------------------------- 1 | //https://github.com/HazouPH/android_device_motorola_smi-plus/blob/48029b4afc307c73181b108a5b0155b9f20856ca/smi-modules/alsa-lib_module_voice/pcm_voice.c 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ac108_help.h" 9 | #include 10 | 11 | #define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) 12 | #define AC108_FRAME_SIZE 40960 13 | struct ac108_t { 14 | snd_pcm_ioplug_t io; 15 | snd_pcm_t *pcm; 16 | snd_pcm_hw_params_t *hw_params; 17 | unsigned int last_size; 18 | unsigned int ptr; 19 | unsigned int latency; // Delay in usec 20 | unsigned int bufferSize; // Size of sample buffer 21 | }; 22 | static unsigned char capture_buf[AC108_FRAME_SIZE]; 23 | /* set up the fixed parameters of pcm PCM hw_parmas */ 24 | static int ac108_slave_hw_params_half(struct ac108_t *capture, unsigned int rate,snd_pcm_format_t format) { 25 | int err; 26 | snd_pcm_uframes_t bufferSize = capture->bufferSize; 27 | unsigned int latency = capture->latency; 28 | 29 | unsigned int buffer_time = 0; 30 | unsigned int period_time = 0; 31 | if ((err = snd_pcm_hw_params_malloc(&capture->hw_params)) < 0) return err; 32 | 33 | if ((err = snd_pcm_hw_params_any(capture->pcm, capture->hw_params)) < 0) { 34 | SNDERR("Cannot get pcm hw_params"); 35 | goto out; 36 | } 37 | if ((err = snd_pcm_hw_params_set_access(capture->pcm, capture->hw_params, 38 | SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 39 | SNDERR("Cannot set pcm access RW_INTERLEAVED"); 40 | goto out; 41 | } 42 | if ((err = snd_pcm_hw_params_set_channels(capture->pcm, capture->hw_params, 2)) < 0) { 43 | SNDERR("Cannot set pcm channels 2"); 44 | goto out; 45 | } 46 | if ((err = snd_pcm_hw_params_set_format(capture->pcm, capture->hw_params, 47 | format)) < 0) { 48 | SNDERR("Cannot set pcm format"); 49 | goto out; 50 | } 51 | if ((err = snd_pcm_hw_params_set_rate(capture->pcm, capture->hw_params, rate, 0)) < 0) { 52 | SNDERR("Cannot set pcm rate %d", rate); 53 | goto out; 54 | } 55 | 56 | err = snd_pcm_hw_params_get_buffer_time_max(capture->hw_params, 57 | &buffer_time, 0); 58 | if (buffer_time > 80000) 59 | buffer_time = 80000; 60 | period_time = buffer_time / 4; 61 | 62 | err = snd_pcm_hw_params_set_period_time_near(capture->pcm, capture->hw_params, 63 | &period_time, 0); 64 | if (err < 0) { 65 | SNDERR("Unable to set_period_time_near"); 66 | goto out; 67 | } 68 | err = snd_pcm_hw_params_set_buffer_time_near(capture->pcm, capture->hw_params, 69 | &buffer_time, 0); 70 | if (err < 0) { 71 | SNDERR("Unable to set_buffer_time_near"); 72 | goto out; 73 | } 74 | 75 | capture->bufferSize = bufferSize; 76 | capture->latency = latency; 77 | 78 | return 0; 79 | 80 | out: 81 | free(capture->hw_params); 82 | capture->hw_params = NULL; 83 | return err; 84 | } 85 | 86 | /* 87 | * start and stop callbacks - just trigger pcm PCM 88 | */ 89 | static int ac108_start(snd_pcm_ioplug_t *io) { 90 | struct ac108_t *capture = io->private_data; 91 | if(!capture->pcm) { 92 | SNDERR( "pcm is lost\n"); 93 | } 94 | 95 | return snd_pcm_start(capture->pcm); 96 | } 97 | 98 | static int ac108_stop(snd_pcm_ioplug_t *io) { 99 | struct ac108_t *capture = io->private_data; 100 | 101 | return snd_pcm_drop(capture->pcm); 102 | } 103 | /* 104 | * pointer callback 105 | * 106 | * Calculate the current position from the delay of pcm PCM 107 | */ 108 | static snd_pcm_sframes_t ac108_pointer(snd_pcm_ioplug_t *io) { 109 | 110 | struct ac108_t *capture = io->private_data; 111 | int size; 112 | 113 | assert(capture); 114 | 115 | 116 | size = snd_pcm_avail(capture->pcm); 117 | if (size < 0) 118 | return size; 119 | 120 | size = size/2; 121 | 122 | if (size > capture->last_size) { 123 | capture->ptr += size - capture->last_size; 124 | capture->ptr %= io->buffer_size; 125 | } 126 | 127 | //fprintf(stderr, "%s :%d %d %d %d %d %d\n", __func__,capture->ptr ,capture->last_size,size, io->buffer_size,io->appl_ptr, io->hw_ptr); 128 | capture->last_size = size; 129 | 130 | return capture->ptr; 131 | } 132 | 133 | /* 134 | * transfer callback 135 | */ 136 | static snd_pcm_sframes_t ac108_transfer(snd_pcm_ioplug_t *io, 137 | const snd_pcm_channel_area_t *dst_areas, 138 | snd_pcm_uframes_t dst_offset, 139 | snd_pcm_uframes_t size) { 140 | struct ac108_t *capture = io->private_data; 141 | int chn; 142 | unsigned char *dst_samples[io->channels]; 143 | int dst_steps[io->channels]; 144 | int bps = snd_pcm_format_width(io->format) / 8; /* bytes per sample */ 145 | int i; 146 | int count = 0; 147 | int err = 0; 148 | unsigned char *src_buf; 149 | unsigned char src_data[4][4]; 150 | 151 | 152 | memset(capture_buf,0,AC108_FRAME_SIZE); 153 | 154 | if(snd_pcm_avail(capture->pcm) > size*2){ 155 | if ((err = snd_pcm_readi (capture->pcm, capture_buf, size*2)) != size*2) { 156 | SNDERR("read from audio interface failed %ld %d %s!\n",size,err,snd_strerror (err)); 157 | exit(EXIT_FAILURE); 158 | size = 0 ; 159 | } 160 | }else{ 161 | size = 0; 162 | } 163 | #if 1 164 | /* verify and prepare the contents of areas */ 165 | for (chn = 0; chn < io->channels; chn++) { 166 | if ((dst_areas[chn].first % 8) != 0) { 167 | SNDERR("dst_areas[%i].first == %i, aborting...\n", chn, dst_areas[chn].first); 168 | exit(EXIT_FAILURE); 169 | } 170 | dst_samples[chn] = /*(signed short *)*/(((unsigned char *)dst_areas[chn].addr) + (dst_areas[chn].first / 8)); 171 | if ((dst_areas[chn].step % 16) != 0) { 172 | SNDERR("dst_areas[%i].step == %i, aborting...\n", chn, dst_areas[chn].step); 173 | exit(EXIT_FAILURE); 174 | } 175 | dst_steps[chn] = dst_areas[chn].step / 8; 176 | dst_samples[chn] += dst_offset * dst_steps[chn]; 177 | } 178 | #endif 179 | // for(i = 0; i < size*2*bps;i++){ 180 | // fprintf(stderr,"%x ",capture_buf[i]); 181 | // if(i%4 == 0) 182 | // fprintf(stderr,"\n"); 183 | // } 184 | 185 | //generate_sine(dst_areas, dst_offset,size, &count); 186 | src_buf = capture_buf; 187 | #if 1 188 | while(count < size){ 189 | for(chn = 0; chn < 4; chn++){ 190 | for (i = 0; i < bps; i++){ 191 | src_data[chn][i] = src_buf[i]; 192 | } 193 | src_buf += bps ; 194 | } 195 | 196 | for(chn = 0; chn < io->channels; chn++){ 197 | for (i = 0; i < bps; i++){ 198 | *(dst_samples[chn] + i) = src_data[chn][i]; 199 | //fprintf(stderr,"%x ",*(dst_samples[chn] + i)); 200 | } 201 | //fprintf(stderr,"\n"); 202 | dst_samples[chn] += dst_steps[chn]; 203 | } 204 | count++; 205 | } 206 | 207 | #endif 208 | 209 | capture->last_size -= size; 210 | 211 | return size; 212 | } 213 | 214 | /* 215 | * poll-related callbacks - just pass to pcm PCM 216 | */ 217 | static int ac108_poll_descriptors_count(snd_pcm_ioplug_t *io) { 218 | struct ac108_t *capture = io->private_data; 219 | 220 | return snd_pcm_poll_descriptors_count(capture->pcm); 221 | } 222 | 223 | static int ac108_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, 224 | unsigned int space) { 225 | struct ac108_t *capture = io->private_data; 226 | 227 | return snd_pcm_poll_descriptors(capture->pcm, pfd, space); 228 | } 229 | 230 | static int ac108_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd, 231 | unsigned int nfds, unsigned short *revents) { 232 | struct ac108_t *capture = io->private_data; 233 | 234 | return snd_pcm_poll_descriptors_revents(capture->pcm, pfd, nfds, revents); 235 | } 236 | 237 | /* 238 | * close callback 239 | */ 240 | static int ac108_close(snd_pcm_ioplug_t *io) { 241 | struct ac108_t *capture = io->private_data; 242 | if (capture->pcm) 243 | snd_pcm_close(capture->pcm); 244 | 245 | return 0; 246 | } 247 | 248 | static int setSoftwareParams(struct ac108_t *capture) { 249 | snd_pcm_sw_params_t *softwareParams; 250 | int err; 251 | 252 | snd_pcm_uframes_t bufferSize = 0; 253 | snd_pcm_uframes_t periodSize = 0; 254 | snd_pcm_uframes_t startThreshold, stopThreshold; 255 | snd_pcm_sw_params_alloca(&softwareParams); 256 | 257 | // Get the current software parameters 258 | err = snd_pcm_sw_params_current(capture->pcm, softwareParams); 259 | if (err < 0) { 260 | SNDERR("Unable to get software parameters: %s", snd_strerror(err)); 261 | goto done; 262 | } 263 | 264 | // Configure ALSA to start the transfer when the buffer is almost full. 265 | snd_pcm_get_params(capture->pcm, &bufferSize, &periodSize); 266 | 267 | 268 | startThreshold = 1; 269 | stopThreshold = bufferSize; 270 | 271 | 272 | err = snd_pcm_sw_params_set_start_threshold(capture->pcm, softwareParams, 273 | startThreshold); 274 | if (err < 0) { 275 | SNDERR("Unable to set start threshold to %lu frames: %s", 276 | startThreshold, snd_strerror(err)); 277 | goto done; 278 | } 279 | 280 | err = snd_pcm_sw_params_set_stop_threshold(capture->pcm, softwareParams, 281 | stopThreshold); 282 | if (err < 0) { 283 | SNDERR("Unable to set stop threshold to %lu frames: %s", 284 | stopThreshold, snd_strerror(err)); 285 | goto done; 286 | } 287 | // Allow the transfer to start when at least periodSize samples can be 288 | // processed. 289 | err = snd_pcm_sw_params_set_avail_min(capture->pcm, softwareParams, 290 | periodSize); 291 | if (err < 0) { 292 | SNDERR("Unable to configure available minimum to %lu: %s", 293 | periodSize, snd_strerror(err)); 294 | goto done; 295 | } 296 | 297 | // Commit the software parameters back to the device. 298 | err = snd_pcm_sw_params(capture->pcm, softwareParams); 299 | if (err < 0) 300 | SNDERR("Unable to configure software parameters: %s",snd_strerror(err)); 301 | 302 | 303 | 304 | return 0; 305 | done: 306 | snd_pcm_sw_params_free(softwareParams); 307 | 308 | return err; 309 | } 310 | /* 311 | * hw_params callback 312 | * 313 | * Set up pcm PCM according to the current parameters 314 | */ 315 | static int ac108_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { 316 | struct ac108_t *capture = io->private_data; 317 | snd_pcm_uframes_t period_size; 318 | snd_pcm_uframes_t buffer_size; 319 | int err; 320 | if (!capture->hw_params) { 321 | err = ac108_slave_hw_params_half(capture, 2*io->rate,io->format); 322 | if (err < 0) { 323 | SNDERR("ac108_slave_hw_params_half error\n"); 324 | return err; 325 | } 326 | } 327 | period_size = io->period_size; 328 | if ((err = snd_pcm_hw_params_set_period_size_near(capture->pcm, capture->hw_params, 329 | &period_size, NULL)) < 0) { 330 | SNDERR("Cannot set pcm period size %ld", period_size); 331 | return err; 332 | } 333 | buffer_size = io->buffer_size; 334 | if ((err = snd_pcm_hw_params_set_buffer_size_near(capture->pcm, capture->hw_params, 335 | &buffer_size)) < 0) { 336 | SNDERR("Cannot set pcm buffer size %ld", buffer_size); 337 | return err; 338 | } 339 | if ((err = snd_pcm_hw_params(capture->pcm, capture->hw_params)) < 0) { 340 | SNDERR("Cannot set pcm hw_params"); 341 | return err; 342 | } 343 | setSoftwareParams(capture); 344 | return 0; 345 | } 346 | /* 347 | * hw_free callback 348 | */ 349 | static int ac108_hw_free(snd_pcm_ioplug_t *io) { 350 | struct ac108_t *capture = io->private_data; 351 | free(capture->hw_params); 352 | capture->hw_params = NULL; 353 | 354 | return snd_pcm_hw_free(capture->pcm); 355 | 356 | } 357 | 358 | 359 | static int ac108_prepare(snd_pcm_ioplug_t *io) { 360 | struct ac108_t *capture = io->private_data; 361 | capture->ptr = 0; 362 | capture->last_size =0; 363 | return snd_pcm_prepare(capture->pcm); 364 | } 365 | static int ac108_drain(snd_pcm_ioplug_t *io) { 366 | struct ac108_t *capture = io->private_data; 367 | 368 | return snd_pcm_drain(capture->pcm); 369 | } 370 | #if 0 371 | static int ac108_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params) { 372 | return 0; 373 | } 374 | #endif 375 | static int ac108_delay(snd_pcm_ioplug_t * io, snd_pcm_sframes_t * delayp){ 376 | 377 | return 0; 378 | } 379 | /* 380 | * callback table 381 | */ 382 | static snd_pcm_ioplug_callback_t a108_ops = { 383 | .start = ac108_start, 384 | .stop = ac108_stop, 385 | .pointer = ac108_pointer, 386 | .transfer = ac108_transfer, 387 | .poll_descriptors_count = ac108_poll_descriptors_count, 388 | .poll_descriptors = ac108_poll_descriptors, 389 | .poll_revents = ac108_poll_revents, 390 | .close = ac108_close, 391 | .hw_params = ac108_hw_params, 392 | .hw_free = ac108_hw_free, 393 | // .sw_params = ac108_sw_params, 394 | .prepare = ac108_prepare, 395 | .drain = ac108_drain, 396 | .delay = ac108_delay, 397 | }; 398 | 399 | 400 | static int ac108_set_hw_constraint(struct ac108_t *capture) { 401 | static unsigned int accesses[] = { 402 | SND_PCM_ACCESS_RW_INTERLEAVED 403 | }; 404 | unsigned int formats[] = { SND_PCM_FORMAT_S32, 405 | SND_PCM_FORMAT_S16 }; 406 | 407 | unsigned int rates[] = { 408 | 8000, 409 | 16000, 410 | 32000, 411 | 44100, 412 | 48000, 413 | 96000 414 | }; 415 | int err; 416 | 417 | 418 | err = snd_pcm_ioplug_set_param_list(&capture->io, 419 | SND_PCM_IOPLUG_HW_ACCESS, 420 | ARRAY_SIZE(accesses), 421 | accesses); 422 | if (err < 0){ 423 | SNDERR("ioplug cannot set ac108 hw access"); 424 | return err; 425 | } 426 | 427 | if ((err = snd_pcm_ioplug_set_param_list(&capture->io, SND_PCM_IOPLUG_HW_FORMAT, 428 | ARRAY_SIZE(formats), formats)) < 0 || 429 | (err = snd_pcm_ioplug_set_param_minmax(&capture->io, SND_PCM_IOPLUG_HW_CHANNELS, 430 | 1, 4)) < 0 || 431 | (err = snd_pcm_ioplug_set_param_list(&capture->io, SND_PCM_IOPLUG_HW_RATE, 432 | ARRAY_SIZE(rates), rates)) < 0) 433 | { 434 | SNDERR("ioplug cannot set ac108 format channel rate!"); 435 | return err; 436 | } 437 | err = snd_pcm_ioplug_set_param_minmax(&capture->io,SND_PCM_IOPLUG_HW_BUFFER_BYTES, 438 | 1, 4 * 1024 * 1024); 439 | if (err < 0){ 440 | SNDERR("ioplug cannot set ac108 hw buffer bytes"); 441 | return err; 442 | } 443 | 444 | err = snd_pcm_ioplug_set_param_minmax(&capture->io,SND_PCM_IOPLUG_HW_PERIOD_BYTES, 445 | 128, 2 * 1024 * 1024); 446 | if (err < 0) { 447 | SNDERR("ioplug cannot set ac108 hw period bytes"); 448 | return err; 449 | } 450 | 451 | err = snd_pcm_ioplug_set_param_minmax(&capture->io, SND_PCM_IOPLUG_HW_PERIODS,3, 1024); 452 | if (err < 0) { 453 | SNDERR("ioplug cannot set ac108 hw periods"); 454 | return err; 455 | } 456 | return 0; 457 | } 458 | 459 | /* 460 | * Main entry point 461 | */ 462 | SND_PCM_PLUGIN_DEFINE_FUNC(ac108) { 463 | snd_config_iterator_t i, next; 464 | int err; 465 | const char *pcm_string = NULL; 466 | struct ac108_t *capture; 467 | int channels; 468 | if (stream != SND_PCM_STREAM_CAPTURE) { 469 | SNDERR("a108 is only for capture"); 470 | return -EINVAL; 471 | } 472 | 473 | snd_config_for_each(i, next, conf) { 474 | snd_config_t *n = snd_config_iterator_entry(i); 475 | const char *id; 476 | if (snd_config_get_id(n, &id) < 0) continue; 477 | if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) continue; 478 | 479 | if (strcmp(id, "slavepcm") == 0) { 480 | if (snd_config_get_string(n, &pcm_string) < 0) { 481 | SNDERR("ac108 slavepcm must be a string"); 482 | return -EINVAL; 483 | } 484 | continue; 485 | } 486 | 487 | if (strcmp(id, "channels") == 0) { 488 | long val; 489 | if (snd_config_get_integer(n, &val) < 0) { 490 | SNDERR("Invalid type for %s", id); 491 | return -EINVAL; 492 | } 493 | channels = val; 494 | if (channels != 2 && channels != 4 && channels != 6) { 495 | SNDERR("channels must be 2, 4 or 6"); 496 | return -EINVAL; 497 | } 498 | continue; 499 | } 500 | } 501 | 502 | 503 | capture = calloc(1, sizeof(*capture)); 504 | if (!capture) { 505 | SNDERR("cannot allocate"); 506 | return -ENOMEM; 507 | } 508 | err = snd_pcm_open(&capture->pcm, pcm_string, stream, mode); 509 | if (err < 0) goto error; 510 | 511 | 512 | 513 | //SND_PCM_NONBLOCK 514 | capture->io.version = SND_PCM_IOPLUG_VERSION; 515 | capture->io.name = "AC108 decode Plugin"; 516 | capture->io.mmap_rw = 0; 517 | capture->io.callback = &a108_ops; 518 | capture->io.private_data = capture; 519 | 520 | err = snd_pcm_ioplug_create(&capture->io, name, stream, mode); 521 | if (err < 0) goto error; 522 | 523 | if ((err = ac108_set_hw_constraint(capture)) < 0) { 524 | snd_pcm_ioplug_delete(&capture->io); 525 | return err; 526 | } 527 | *pcmp = capture->io.pcm; 528 | return 0; 529 | 530 | error: 531 | if (capture->pcm) snd_pcm_close(capture->pcm); 532 | free(capture); 533 | return err; 534 | } 535 | 536 | SND_PCM_PLUGIN_SYMBOL(ac108); 537 | -------------------------------------------------------------------------------- /ac10x.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ac10x.h 3 | * 4 | * (C) Copyright 2017-2018 5 | * Seeed Technology Co., Ltd. 6 | * 7 | * PeterYang 8 | * 9 | * (C) Copyright 2010-2017 10 | * Reuuimlla Technology Co., Ltd. 11 | * huangxin 12 | * 13 | * some simple description for this code 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License as 17 | * published by the Free Software Foundation; either version 2 of 18 | * the License, or (at your option) any later version. 19 | * 20 | */ 21 | #ifndef __AC10X_H__ 22 | #define __AC10X_H__ 23 | 24 | #define AC101_I2C_ID 4 25 | #define _MASTER_AC108 0 26 | #define _MASTER_AC101 1 27 | #define _MASTER_MULTI_CODEC _MASTER_AC101 28 | 29 | /* enable headset detecting & headset button pressing */ 30 | #define CONFIG_AC101_SWITCH_DETECT 31 | 32 | /* obsolete */ 33 | #define CONFIG_AC10X_TRIG_LOCK 0 34 | 35 | 36 | #ifdef AC101_DEBG 37 | #define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args) 38 | #else 39 | #define AC101_DBG(...) 40 | #endif 41 | 42 | 43 | #include "sound-compatible-4.18.h" 44 | 45 | #ifdef CONFIG_AC101_SWITCH_DETECT 46 | enum headphone_mode_u { 47 | HEADPHONE_IDLE, 48 | FOUR_HEADPHONE_PLUGIN, 49 | THREE_HEADPHONE_PLUGIN, 50 | }; 51 | #endif 52 | 53 | struct ac10x_priv { 54 | struct i2c_client *i2c[4]; 55 | struct regmap* i2cmap[4]; 56 | int codec_cnt; 57 | unsigned sysclk; 58 | #define _FREQ_24_576K 24576000 59 | #define _FREQ_22_579K 22579200 60 | unsigned mclk; /* master clock or aif_clock/aclk */ 61 | int clk_id; 62 | unsigned char i2s_mode; 63 | unsigned char data_protocol; 64 | struct delayed_work dlywork; 65 | int tdm_chips_cnt; 66 | int sysclk_en; 67 | 68 | /* member for ac101 .begin */ 69 | struct snd_soc_codec *codec; 70 | struct i2c_client *i2c101; 71 | struct regmap* regmap101; 72 | 73 | struct mutex dac_mutex; 74 | u8 dac_enable; 75 | spinlock_t lock; 76 | u8 aif1_clken; 77 | 78 | struct work_struct codec_resume; 79 | struct gpio_desc* gpiod_spk_amp_gate; 80 | 81 | #ifdef CONFIG_AC101_SWITCH_DETECT 82 | struct gpio_desc* gpiod_irq; 83 | long irq; 84 | volatile int irq_cntr; 85 | volatile int pullout_cntr; 86 | volatile int state; 87 | 88 | enum headphone_mode_u mode; 89 | struct work_struct work_switch; 90 | struct work_struct work_clear_irq; 91 | 92 | struct input_dev* inpdev; 93 | #endif 94 | /* member for ac101 .end */ 95 | }; 96 | 97 | 98 | /* AC101 DAI operations */ 99 | int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); 100 | void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); 101 | int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); 102 | int ac101_hw_params(struct snd_pcm_substream *substream, 103 | struct snd_pcm_hw_params *params, 104 | struct snd_soc_dai *codec_dai); 105 | int ac101_trigger(struct snd_pcm_substream *substream, int cmd, 106 | struct snd_soc_dai *dai); 107 | int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute); 108 | 109 | /* codec driver specific */ 110 | int ac101_codec_probe(struct snd_soc_codec *codec); 111 | int ac101_codec_remove(struct snd_soc_codec *codec); 112 | int ac101_codec_suspend(struct snd_soc_codec *codec); 113 | int ac101_codec_resume(struct snd_soc_codec *codec); 114 | int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); 115 | 116 | /* i2c device specific */ 117 | int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id); 118 | void ac101_shutdown(struct i2c_client *i2c); 119 | int ac101_remove(struct i2c_client *i2c); 120 | 121 | /* seeed voice card export */ 122 | int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int, struct snd_pcm_substream *, int, struct snd_soc_dai *)); 123 | 124 | int ac10x_fill_regcache(struct device* dev, struct regmap* map); 125 | 126 | #endif//__AC10X_H__ 127 | -------------------------------------------------------------------------------- /asound_2mic.conf: -------------------------------------------------------------------------------- 1 | # The IPC key of dmix or dsnoop plugin must be unique 2 | # If 555555 or 666666 is used by other processes, use another one 3 | 4 | 5 | # use samplerate to resample as speexdsp resample is bad 6 | defaults.pcm.rate_converter "samplerate" 7 | 8 | pcm.!default { 9 | type asym 10 | playback.pcm "playback" 11 | capture.pcm "capture" 12 | } 13 | 14 | pcm.playback { 15 | type plug 16 | slave.pcm "dmixed" 17 | } 18 | 19 | pcm.capture { 20 | type plug 21 | slave.pcm "array" 22 | } 23 | 24 | pcm.dmixed { 25 | type dmix 26 | slave.pcm "hw:seeed2micvoicec" 27 | ipc_key 555555 28 | } 29 | 30 | pcm.array { 31 | type dsnoop 32 | slave { 33 | pcm "hw:seeed2micvoicec" 34 | channels 2 35 | } 36 | ipc_key 666666 37 | } 38 | 39 | -------------------------------------------------------------------------------- /asound_4mic.conf: -------------------------------------------------------------------------------- 1 | # The IPC key of dmix or dsnoop plugin must be unique 2 | # If 555555 or 666666 is used by other processes, use another one 3 | 4 | # use samplerate to resample as speexdsp resample is bad 5 | defaults.pcm.rate_converter "samplerate" 6 | 7 | pcm.!default { 8 | type asym 9 | playback.pcm "playback" 10 | capture.pcm "ac108" 11 | } 12 | 13 | pcm.playback { 14 | type plug 15 | slave.pcm "hw:ALSA" 16 | } 17 | 18 | # pcm.dmixed { 19 | # type dmix 20 | # slave.pcm "hw:0,0" 21 | # ipc_key 555555 22 | # } 23 | 24 | pcm.ac108 { 25 | type plug 26 | slave.pcm "hw:seeed4micvoicec" 27 | } 28 | 29 | # pcm.multiapps { 30 | # type dsnoop 31 | # ac108-slavepcm "hw:1,0" 32 | # ipc_key 666666 33 | # } 34 | -------------------------------------------------------------------------------- /asound_6mic.conf: -------------------------------------------------------------------------------- 1 | # The IPC key of dmix or dsnoop plugin must be unique 2 | # If 555555 or 666666 is used by other processes, use another one 3 | 4 | # use samplerate to resample as speexdsp resample is broken 5 | defaults.pcm.rate_converter "samplerate" 6 | 7 | pcm.!default { 8 | type asym 9 | playback.pcm "dmixer" 10 | capture.pcm "ac108" 11 | } 12 | 13 | 14 | pcm.ac108 { 15 | type plug 16 | slave { 17 | rate 48000 18 | format S32_LE 19 | pcm "hw:seeed8micvoicec" 20 | } 21 | } 22 | 23 | # pcm.multiapps { 24 | # type plug 25 | # slave.pcm { 26 | # type dsnoop 27 | # slave { 28 | # rate 48000 29 | # format S32_LE 30 | # pcm "hw:seeed8micvoicec" 31 | # } 32 | # ipc_key 666666 33 | # } 34 | # } 35 | 36 | pcm.dmixer { 37 | type plug 38 | slave { 39 | pcm { 40 | type dmix 41 | ipc_key 555555 42 | slave { 43 | pcm "hw:seeed8micvoicec" 44 | format S32_LE 45 | channels 8 46 | } 47 | bindings { 48 | 0 0 49 | 1 1 50 | 2 2 51 | 3 3 52 | 4 4 53 | 5 5 54 | 6 6 55 | 7 7 56 | } 57 | } 58 | channels 8 59 | format S32_LE 60 | rate 48000 61 | } 62 | ttable.0.0 1 63 | ttable.1.1 1 64 | ttable.0.2 1 65 | ttable.1.3 1 66 | ttable.0.4 1 67 | ttable.1.5 1 68 | ttable.0.6 1 69 | ttable.1.7 1 70 | } 71 | 72 | pcm.ac101 { 73 | type plug 74 | slave { 75 | pcm "hw:seeed8micvoicec" 76 | channels 8 77 | format S32_LE 78 | rate 48000 79 | } 80 | ttable.0.0 1 81 | ttable.1.1 1 82 | ttable.0.2 1 83 | ttable.1.3 1 84 | ttable.0.4 1 85 | ttable.1.5 1 86 | ttable.0.6 1 87 | ttable.1.7 1 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /builddtbo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #dtoverlay -r seeed-2mic-voicecard 3 | DTC_FLAGS="-b 0 -Wno-unit_address_vs_reg -I dts -O dtb" 4 | 5 | dtc -@ $DTC_FLAGS -o seeed-2mic-voicecard.dtbo seeed-2mic-voicecard-overlay.dts 6 | dtc -@ $DTC_FLAGS -o seeed-4mic-voicecard.dtbo seeed-4mic-voicecard-overlay.dts 7 | dtc -@ $DTC_FLAGS -o seeed-8mic-voicecard.dtbo seeed-8mic-voicecard-overlay.dts 8 | 9 | # cp *.dtbo /boot/overlays 10 | # dtoverlay seeed-2mic-voicecard 11 | -------------------------------------------------------------------------------- /default.pa: -------------------------------------------------------------------------------- 1 | #!/usr/bin/pulseaudio -nF 2 | # 3 | # This file is part of PulseAudio. 4 | # 5 | # PulseAudio is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # PulseAudio is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with PulseAudio; if not, see . 17 | 18 | # This startup script is used only if PulseAudio is started per-user 19 | # (i.e. not in system mode) 20 | 21 | .fail 22 | 23 | ### Automatically restore the volume of streams and devices 24 | load-module module-device-restore 25 | load-module module-stream-restore 26 | load-module module-card-restore 27 | 28 | ### Automatically augment property information from .desktop files 29 | ### stored in /usr/share/application 30 | load-module module-augment-properties 31 | 32 | ### Should be after module-*-restore but before module-*-detect 33 | load-module module-switch-on-port-available 34 | 35 | ### Load audio drivers statically 36 | ### (it's probably better to not load these drivers manually, but instead 37 | ### use module-udev-detect -- see below -- for doing this automatically) 38 | load-module module-alsa-sink device=hw:0,0 39 | load-module module-alsa-source device=hw:0,0 40 | #load-module module-oss device="/dev/dsp" sink_name=output source_name=input 41 | #load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input 42 | #load-module module-null-sink 43 | #load-module module-pipe-sink 44 | 45 | ### Automatically load driver modules depending on the hardware available 46 | #.ifexists module-udev-detect.so 47 | #load-module module-udev-detect 48 | #.else 49 | ### Use the static hardware detection module (for systems that lack udev support) 50 | #load-module module-detect 51 | #.endif 52 | 53 | ### Automatically connect sink and source if JACK server is present 54 | .ifexists module-jackdbus-detect.so 55 | .nofail 56 | load-module module-jackdbus-detect channels=2 57 | .fail 58 | .endif 59 | 60 | ### Automatically load driver modules for Bluetooth hardware 61 | .ifexists module-bluetooth-policy.so 62 | load-module module-bluetooth-policy 63 | .endif 64 | 65 | .ifexists module-bluetooth-discover.so 66 | load-module module-bluetooth-discover 67 | .endif 68 | 69 | ### Load several protocols 70 | .ifexists module-esound-protocol-unix.so 71 | load-module module-esound-protocol-unix 72 | .endif 73 | load-module module-native-protocol-unix 74 | 75 | ### Network access (may be configured with paprefs, so leave this commented 76 | ### here if you plan to use paprefs) 77 | #load-module module-esound-protocol-tcp 78 | #load-module module-native-protocol-tcp 79 | #load-module module-zeroconf-publish 80 | 81 | ### Load the RTP receiver module (also configured via paprefs, see above) 82 | #load-module module-rtp-recv 83 | 84 | ### Load the RTP sender module (also configured via paprefs, see above) 85 | #load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'" 86 | #load-module module-rtp-send source=rtp.monitor 87 | 88 | ### Load additional modules from GConf settings. This can be configured with the paprefs tool. 89 | ### Please keep in mind that the modules configured by paprefs might conflict with manually 90 | ### loaded modules. 91 | .ifexists module-gconf.so 92 | .nofail 93 | load-module module-gconf 94 | .fail 95 | .endif 96 | 97 | ### Automatically restore the default sink/source when changed by the user 98 | ### during runtime 99 | ### NOTE: This should be loaded as early as possible so that subsequent modules 100 | ### that look up the default sink/source get the right value 101 | load-module module-default-device-restore 102 | 103 | ### Automatically move streams to the default sink if the sink they are 104 | ### connected to dies, similar for sources 105 | load-module module-rescue-streams 106 | 107 | ### Make sure we always have a sink around, even if it is a null sink. 108 | load-module module-always-sink 109 | 110 | ### Honour intended role device property 111 | load-module module-intended-roles 112 | 113 | ### Automatically suspend sinks/sources that become idle for too long 114 | load-module module-suspend-on-idle 115 | 116 | ### If autoexit on idle is enabled we want to make sure we only quit 117 | ### when no local session needs us anymore. 118 | .ifexists module-console-kit.so 119 | load-module module-console-kit 120 | .endif 121 | .ifexists module-systemd-login.so 122 | load-module module-systemd-login 123 | .endif 124 | 125 | ### Enable positioned event sounds 126 | load-module module-position-event-sounds 127 | 128 | ### Cork music/video streams when a phone stream is active 129 | load-module module-role-cork 130 | 131 | ### Modules to allow autoloading of filters (such as echo cancellation) 132 | ### on demand. module-filter-heuristics tries to determine what filters 133 | ### make sense, and module-filter-apply does the heavy-lifting of 134 | ### loading modules and rerouting streams. 135 | load-module module-filter-heuristics 136 | load-module module-filter-apply 137 | 138 | ### Make some devices default 139 | #set-default-sink output 140 | #set-default-source input 141 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="seeed-voicecard" 2 | PACKAGE_VERSION="0.3" 3 | BUILT_MODULE_NAME[0]="snd-soc-wm8960" 4 | BUILT_MODULE_NAME[1]="snd-soc-ac108" 5 | BUILT_MODULE_NAME[2]="snd-soc-seeed-voicecard" 6 | DEST_MODULE_LOCATION[0]="/kernel/sound/soc/codecs" 7 | DEST_MODULE_LOCATION[1]="/kernel/sound/soc/codecs" 8 | DEST_MODULE_LOCATION[2]="/kernel/sound/soc/bcm" 9 | PATCH[0]="back-to-v4.19.diff" 10 | PATCH[1]="back-to-v5.4.diff" 11 | PATCH[2]="back-to-v5.8.diff" 12 | PATCH_MATCH[0]="^4\.19\.*" 13 | PATCH_MATCH[1]="^5\.4\.*" 14 | PATCH_MATCH[2]="^5\.8\.*" 15 | AUTOINSTALL="yes" 16 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $EUID -ne 0 ]]; then 4 | echo "This script must be run as root (use sudo)" 1>&2 5 | exit 1 6 | fi 7 | 8 | # Check for enough space on /boot volume 9 | boot_line=$(df -h | grep /boot | head -n 1) 10 | if [ "x${boot_line}" = "x" ]; then 11 | echo "Warning: /boot volume not found .." 12 | else 13 | boot_space=$(echo $boot_line | awk '{print $4;}') 14 | free_space=$(echo "${boot_space%?}") 15 | unit="${boot_space: -1}" 16 | if [[ "$unit" = "K" ]]; then 17 | echo "Error: Not enough space left ($boot_space) on /boot" 18 | exit 1 19 | elif [[ "$unit" = "M" ]]; then 20 | if [ "$free_space" -lt "25" ]; then 21 | echo "Error: Not enough space left ($boot_space) on /boot" 22 | exit 1 23 | fi 24 | fi 25 | fi 26 | 27 | # 28 | # make sure that we are on something ARM/Raspberry related 29 | # either a bare metal Raspberry or a qemu session with 30 | # Raspberry stuff available 31 | # - check for /boot/overlays 32 | # - dtparam and dtoverlay is available 33 | errorFound=0 34 | OVERLAYS=/boot/overlays 35 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 36 | 37 | if [ ! -d $OVERLAYS ] ; then 38 | echo "$OVERLAYS not found or not a directory" 1>&2 39 | errorFound=1 40 | fi 41 | # should we also check for alsactl and amixer used in seeed-voicecard? 42 | PATH=$PATH:/opt/vc/bin 43 | for cmd in dtparam dtoverlay ; do 44 | if ! which $cmd &>/dev/null ; then 45 | echo "$cmd not found" 1>&2 46 | echo "You may need to run ./ubuntu-prerequisite.sh" 47 | errorFound=1 48 | fi 49 | done 50 | if [ $errorFound = 1 ] ; then 51 | echo "Errors found, exiting." 1>&2 52 | exit 1 53 | fi 54 | 55 | ver="0.3" 56 | uname_r=$(uname -r) 57 | 58 | # we create a dir with this version to ensure that 'dkms remove' won't delete 59 | # the sources during kernel updates 60 | marker="0.0.0" 61 | 62 | _VER_RUN= 63 | function get_kernel_version() { 64 | local ZIMAGE IMG_OFFSET 65 | 66 | _VER_RUN="" 67 | [ -z "$_VER_RUN" ] && { 68 | ZIMAGE=/boot/kernel.img 69 | [ -f /boot/firmware/vmlinuz ] && ZIMAGE=/boot/firmware/vmlinuz 70 | # 64-bit-only kernel package 71 | [ ! -f /boot/kernel.img ] && [ -f /boot/kernel8.img ] && ZIMAGE=/boot/kernel8.img 72 | IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1) 73 | _VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) 2>/dev/null | zcat | grep -a -m1 "Linux version" | LC_ALL=C sed -e 's/^.*Linux/Linux/' | strings | awk '{ print $3; }') 74 | } 75 | echo "$_VER_RUN" 76 | return 0 77 | } 78 | 79 | function check_kernel_headers() { 80 | VER_RUN=$(get_kernel_version) 81 | VER_HDR=$(dpkg -L raspberrypi-kernel-headers | egrep -m1 "/lib/modules/[^\/]+/build" | awk -F'/' '{ print $4; }') 82 | [ "X$VER_RUN" == "X$VER_HDR" ] && { 83 | return 0 84 | } 85 | VER_HDR=$(dpkg -L linux-headers-$VER_RUN | egrep -m1 "/lib/modules/[^\/]+/build" | awk -F'/' '{ print $4; }') 86 | [ "X$VER_RUN" == "X$VER_HDR" ] && { 87 | return 0 88 | } 89 | 90 | # echo RUN=$VER_RUN HDR=$VER_HDR 91 | echo " !!! Your kernel version is $VER_RUN" 92 | echo " Not found *** corresponding *** kernel headers with apt-get." 93 | echo " This may occur if you have ran 'rpi-update'." 94 | echo " Choose *** y *** will revert the kernel to version $VER_HDR then continue." 95 | echo " Choose *** N *** will exit without this driver support, by default." 96 | read -p "Would you like to proceed? (y/N)" -n 1 -r -s 97 | echo 98 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 99 | exit 1; 100 | fi 101 | 102 | apt-get -y --reinstall install raspberrypi-kernel 103 | } 104 | 105 | # update and install required packages 106 | which apt &>/dev/null 107 | if [[ $? -eq 0 ]]; then 108 | apt update -y 109 | # Raspbian kernel packages 110 | apt-get -y install raspberrypi-kernel-headers raspberrypi-kernel 111 | # Recent Raspbian has 64-bit kernel on 32-bit userspace 112 | apt-get -y install gcc-aarch64-linux-gnu 113 | # Ubuntu kernel packages 114 | apt-get -y install linux-raspi linux-headers-raspi linux-image-raspi 115 | apt-get -y install dkms git i2c-tools libasound2-plugins 116 | # rpi-update checker 117 | check_kernel_headers 118 | fi 119 | 120 | # Arch Linux 121 | which pacman &>/dev/null 122 | if [[ $? -eq 0 ]]; then 123 | pacman -Syu --needed git gcc automake make dkms linux-raspberrypi-headers i2c-tools 124 | fi 125 | 126 | # locate currently installed kernels (may be different to running kernel if 127 | # it's just been updated) 128 | base_ver=$(get_kernel_version) 129 | base_ver=${base_ver%%[-+]*} 130 | #kernels="${base_ver}+ ${base_ver}-v7+ ${base_ver}-v7l+" 131 | kernels=$(uname -r) 132 | 133 | function install_module { 134 | local _i 135 | 136 | src=$1 137 | mod=$2 138 | 139 | if [[ -d /var/lib/dkms/$mod/$ver/$marker ]]; then 140 | rmdir /var/lib/dkms/$mod/$ver/$marker 141 | fi 142 | 143 | if [[ -e /usr/src/$mod-$ver || -e /var/lib/dkms/$mod/$ver ]]; then 144 | dkms remove --force -m $mod -v $ver --all 145 | rm -rf /usr/src/$mod-$ver 146 | fi 147 | 148 | mkdir -p /usr/src/$mod-$ver 149 | cp -a $src/* /usr/src/$mod-$ver/ 150 | 151 | dkms add -m $mod -v $ver 152 | for _i in $kernels; do 153 | dkms build -k $_i -m $mod -v $ver && { 154 | dkms install --force -k $_i -m $mod -v $ver 155 | } 156 | done 157 | 158 | mkdir -p /var/lib/dkms/$mod/$ver/$marker 159 | } 160 | 161 | install_module "./" "seeed-voicecard" 162 | 163 | 164 | # install dtbos 165 | cp seeed-2mic-voicecard.dtbo $OVERLAYS 166 | cp seeed-4mic-voicecard.dtbo $OVERLAYS 167 | cp seeed-8mic-voicecard.dtbo $OVERLAYS 168 | 169 | #install alsa plugins 170 | # no need this plugin now 171 | # install -D ac108_plugin/libasound_module_pcm_ac108.so /usr/lib/arm-linux-gnueabihf/alsa-lib/ 172 | rm -f /usr/lib/arm-linux-gnueabihf/alsa-lib/libasound_module_pcm_ac108.so 173 | 174 | #set kernel modules 175 | grep -q "^snd-soc-seeed-voicecard$" /etc/modules || \ 176 | echo "snd-soc-seeed-voicecard" >> /etc/modules 177 | grep -q "^snd-soc-ac108$" /etc/modules || \ 178 | echo "snd-soc-ac108" >> /etc/modules 179 | grep -q "^snd-soc-wm8960$" /etc/modules || \ 180 | echo "snd-soc-wm8960" >> /etc/modules 181 | 182 | #set dtoverlays 183 | CONFIG=/boot/config.txt 184 | [ -f /boot/firmware/config.txt ] && CONFIG=/boot/firmware/config.txt 185 | [ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt 186 | 187 | sed -i -e 's:#dtparam=i2c_arm=on:dtparam=i2c_arm=on:g' $CONFIG || true 188 | grep -q "^dtoverlay=i2s-mmap$" $CONFIG || \ 189 | echo "dtoverlay=i2s-mmap" >> $CONFIG 190 | 191 | 192 | grep -q "^dtparam=i2s=on$" $CONFIG || \ 193 | echo "dtparam=i2s=on" >> $CONFIG 194 | 195 | #install config files 196 | mkdir /etc/voicecard || true 197 | cp *.conf /etc/voicecard 198 | cp *.state /etc/voicecard 199 | 200 | #create git repo 201 | git_email=$(git config --global --get user.email) 202 | git_name=$(git config --global --get user.name) 203 | if [ "x${git_email}" == "x" ] || [ "x${git_name}" == "x" ] ; then 204 | echo "setup git config" 205 | git config --global user.email "respeaker@seeed.cc" 206 | git config --global user.name "respeaker" 207 | fi 208 | echo "git init" 209 | git --git-dir=/etc/voicecard/.git init 210 | echo "git add --all" 211 | git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ add --all 212 | echo "git commit -m \"origin configures\"" 213 | git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ commit -m "origin configures" 214 | 215 | cp seeed-voicecard /usr/bin/ 216 | cp seeed-voicecard.service /lib/systemd/system/ 217 | systemctl enable seeed-voicecard.service 218 | systemctl start seeed-voicecard 219 | 220 | echo "------------------------------------------------------" 221 | echo "Please reboot your raspberry pi to apply all settings" 222 | echo "Enjoy!" 223 | echo "------------------------------------------------------" 224 | -------------------------------------------------------------------------------- /patches/back-to-v4.19.diff: -------------------------------------------------------------------------------- 1 | diff --git a/ac101.c b/ac101.c 2 | index 23837a7..41c15f3 100644 3 | --- a/ac101.c 4 | +++ b/ac101.c 5 | @@ -955,10 +955,10 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai 6 | 7 | AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", 8 | snd_pcm_stream_str(substream), 9 | - codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], 10 | - snd_soc_dai_active(codec_dai)); 11 | + codec_dai->playback_active, codec_dai->capture_active, 12 | + codec_dai->active); 13 | 14 | - if (!snd_soc_dai_active(codec_dai)) { 15 | + if (!codec_dai->active) { 16 | ac10x->aif1_clken = 1; 17 | ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); 18 | } else { 19 | @@ -1080,7 +1080,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream, 20 | freq_out = _FREQ_24_576K; 21 | for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) { 22 | if (codec_aif1_fs[i].samp_rate == params_rate(params)) { 23 | - if (codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && dmic_used && codec_aif1_fs[i].samp_rate == 44100) { 24 | + if (codec_dai->capture_active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) { 25 | ac101_update_bits(codec, AIF_SR_CTRL, (0xf<dev, "%s() stream=%s play:%d capt:%d +++\n", __func__, 35 | snd_pcm_stream_str(substream), 36 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]); 37 | + dai->playback_active, dai->capture_active); 38 | 39 | if (ac10x->i2c101) { 40 | ret = ac101_hw_params(substream, params, dai); 41 | @@ -664,8 +664,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h 42 | } 43 | } 44 | 45 | - if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) 46 | - || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream_active[SNDRV_PCM_STREAM_CAPTURE])) { 47 | + if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active) 48 | + || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) { 49 | /* not configure hw_param twice */ 50 | /* return 0; */ 51 | } 52 | @@ -810,9 +810,6 @@ static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int fr 53 | 54 | struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai); 55 | 56 | - if (freq != 24000000 || clk_id != SYSCLK_SRC_PLL) 57 | - dev_warn(dai->dev, "ac108_set_sysclk freq = %d clk = %d\n", freq, clk_id); 58 | - 59 | freq = 24000000; 60 | clk_id = SYSCLK_SRC_PLL; 61 | 62 | @@ -1124,7 +1121,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream, 63 | } 64 | } 65 | 66 | -int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) { 67 | +int ac108_aif_mute(struct snd_soc_dai *dai, int mute) { 68 | struct snd_soc_codec *codec = dai->codec; 69 | struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); 70 | 71 | @@ -1145,13 +1142,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = { 72 | .hw_params = ac108_hw_params, 73 | .prepare = ac108_prepare, 74 | .trigger = ac108_trigger, 75 | - .mute_stream = ac108_aif_mute, 76 | + .digital_mute = ac108_aif_mute, 77 | 78 | /*DAI format configuration*/ 79 | .set_fmt = ac108_set_fmt, 80 | 81 | // .hw_free = ac108_hw_free, 82 | - .no_capture_mute = 1, 83 | }; 84 | 85 | static struct snd_soc_dai_driver ac108_dai0 = { 86 | diff --git a/seeed-voicecard.c b/seeed-voicecard.c 87 | index b90af93..af6db74 100644 88 | --- a/seeed-voicecard.c 89 | +++ b/seeed-voicecard.c 90 | @@ -28,8 +28,6 @@ 91 | #include 92 | #include "ac10x.h" 93 | 94 | -#define LINUX_VERSION_IS_GEQ(x1,x2,x3) (LINUX_VERSION_CODE >= KERNEL_VERSION(x1,x2,x3)) 95 | - 96 | /* 97 | * single codec: 98 | * 0 - allow multi codec 99 | @@ -42,9 +40,6 @@ struct seeed_card_data { 100 | struct seeed_dai_props { 101 | struct asoc_simple_dai cpu_dai; 102 | struct asoc_simple_dai codec_dai; 103 | - struct snd_soc_dai_link_component cpus; /* single cpu */ 104 | - struct snd_soc_dai_link_component codecs; /* single codec */ 105 | - struct snd_soc_dai_link_component platforms; 106 | unsigned int mclk_fs; 107 | } *dai_props; 108 | unsigned int mclk_fs; 109 | @@ -97,16 +92,16 @@ static int seeed_voice_card_startup(struct snd_pcm_substream *substream) 110 | if (ret) 111 | clk_disable_unprepare(dai_props->cpu_dai.clk); 112 | 113 | - if (asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) { 114 | - priv->channels_playback_default = asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min; 115 | + if (rtd->cpu_dai->driver->playback.channels_min) { 116 | + priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min; 117 | } 118 | - if (asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) { 119 | - priv->channels_capture_default = asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min; 120 | + if (rtd->cpu_dai->driver->capture.channels_min) { 121 | + priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min; 122 | } 123 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override; 124 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override; 125 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override; 126 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override; 127 | + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override; 128 | + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override; 129 | + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override; 130 | + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override; 131 | 132 | return ret; 133 | } 134 | @@ -118,10 +113,10 @@ static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) 135 | struct seeed_dai_props *dai_props = 136 | seeed_priv_to_props(priv, rtd->num); 137 | 138 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default; 139 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default; 140 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default; 141 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default; 142 | + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default; 143 | + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default; 144 | + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default; 145 | + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default; 146 | 147 | clk_disable_unprepare(dai_props->cpu_dai.clk); 148 | 149 | @@ -132,8 +127,8 @@ static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, 150 | struct snd_pcm_hw_params *params) 151 | { 152 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 153 | - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 154 | - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 155 | + struct snd_soc_dai *codec_dai = rtd->codec_dai; 156 | + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 157 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 158 | struct seeed_dai_props *dai_props = 159 | seeed_priv_to_props(priv, rtd->num); 160 | @@ -197,7 +192,7 @@ static void work_cb_codec_clk(struct work_struct *work) 161 | static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) 162 | { 163 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 164 | - struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); 165 | + struct snd_soc_dai *dai = rtd->codec_dai; 166 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 167 | #if CONFIG_AC10X_TRIG_LOCK 168 | unsigned long flags; 169 | @@ -206,7 +201,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 170 | 171 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", 172 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 173 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]); 174 | + dai->playback_active, dai->capture_active); 175 | 176 | switch (cmd) { 177 | case SNDRV_PCM_TRIGGER_START: 178 | @@ -228,7 +223,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 179 | case SNDRV_PCM_TRIGGER_SUSPEND: 180 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 181 | /* capture channel resync, if overrun */ 182 | - if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 183 | + if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 184 | break; 185 | } 186 | 187 | @@ -248,7 +243,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 188 | 189 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n", 190 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 191 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], ret); 192 | + dai->playback_active, dai->capture_active, ret); 193 | 194 | return ret; 195 | } 196 | @@ -387,8 +382,8 @@ static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd) 197 | static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) 198 | { 199 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 200 | - struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0); 201 | - struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0); 202 | + struct snd_soc_dai *codec = rtd->codec_dai; 203 | + struct snd_soc_dai *cpu = rtd->cpu_dai; 204 | struct seeed_dai_props *dai_props = 205 | seeed_priv_to_props(priv, rtd->num); 206 | int ret; 207 | @@ -453,19 +448,20 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, 208 | goto dai_link_of_err; 209 | } 210 | 211 | - ret = asoc_simple_parse_daifmt(dev, node, codec, 212 | + ret = asoc_simple_card_parse_daifmt(dev, node, codec, 213 | prefix, &dai_link->dai_fmt); 214 | if (ret < 0) 215 | goto dai_link_of_err; 216 | 217 | of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); 218 | 219 | - ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu); 220 | + ret = asoc_simple_card_parse_cpu(cpu, dai_link, 221 | + DAI, CELL, &single_cpu); 222 | if (ret < 0) 223 | goto dai_link_of_err; 224 | 225 | #if _SINGLE_CODEC 226 | - ret = asoc_simple_parse_codec(codec, dai_link); 227 | + ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL); 228 | if (ret < 0) 229 | goto dai_link_of_err; 230 | #else 231 | @@ -477,7 +473,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, 232 | dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); 233 | #endif 234 | 235 | - ret = asoc_simple_parse_platform(plat, dai_link); 236 | + ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL); 237 | if (ret < 0) 238 | goto dai_link_of_err; 239 | 240 | @@ -502,7 +498,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, 241 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 242 | ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); 243 | #else 244 | - ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 245 | + ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 246 | #endif 247 | if (ret < 0) 248 | goto dai_link_of_err; 249 | @@ -510,16 +506,16 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, 250 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 251 | ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); 252 | #else 253 | - ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); 254 | + ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai); 255 | #endif 256 | if (ret < 0) 257 | goto dai_link_of_err; 258 | 259 | - ret = asoc_simple_set_dailink_name(dev, dai_link, 260 | + ret = asoc_simple_card_set_dailink_name(dev, dai_link, 261 | "%s-%s", 262 | - dai_link->cpus->dai_name, 263 | + dai_link->cpu_dai_name, 264 | #if _SINGLE_CODEC 265 | - dai_link->codecs->dai_name 266 | + dai_link->codec_dai_name 267 | #else 268 | dai_link->codecs[0].dai_name 269 | #endif 270 | @@ -533,19 +529,21 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, 271 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); 272 | dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); 273 | dev_dbg(dev, "\tcpu : %s / %d\n", 274 | - dai_link->cpus->dai_name, 275 | + dai_link->cpu_dai_name, 276 | dai_props->cpu_dai.sysclk); 277 | dev_dbg(dev, "\tcodec : %s / %d\n", 278 | #if _SINGLE_CODEC 279 | - dai_link->codecs->dai_name, 280 | + dai_link->codec_dai_name, 281 | #else 282 | dai_link->codecs[0].dai_name, 283 | #endif 284 | dai_props->codec_dai.sysclk); 285 | 286 | - asoc_simple_canonicalize_cpu(dai_link, single_cpu); 287 | + asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); 288 | #if _SINGLE_CODEC 289 | - asoc_simple_canonicalize_platform(dai_link); 290 | + ret = asoc_simple_card_canonicalize_dailink(dai_link); 291 | + if (ret < 0) 292 | + goto dai_link_of_err; 293 | #endif 294 | 295 | dai_link_of_err: 296 | @@ -578,7 +576,7 @@ static int seeed_voice_card_parse_aux_devs(struct device_node *node, 297 | aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); 298 | if (!aux_node) 299 | return -EINVAL; 300 | - priv->snd_card.aux_dev[i].dlc.of_node = aux_node; 301 | + priv->snd_card.aux_dev[i].codec_of_node = aux_node; 302 | } 303 | 304 | priv->snd_card.num_aux_devs = n; 305 | @@ -638,7 +636,7 @@ static int seeed_voice_card_parse_of(struct device_node *node, 306 | goto card_parse_end; 307 | } 308 | 309 | - ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX); 310 | + ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); 311 | if (ret < 0) 312 | goto card_parse_end; 313 | 314 | @@ -743,7 +741,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 315 | struct seeed_dai_props *dai_props; 316 | struct device_node *np = pdev->dev.of_node; 317 | struct device *dev = &pdev->dev; 318 | - int num, ret, i; 319 | + int num, ret; 320 | 321 | /* Get the number of DAI links */ 322 | if (np && of_get_child_by_name(np, PREFIX "dai-link")) 323 | @@ -761,25 +759,6 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 324 | if (!dai_props || !dai_link) 325 | return -ENOMEM; 326 | 327 | - /* 328 | - * Use snd_soc_dai_link_component instead of legacy style 329 | - * It is codec only. but cpu/platform will be supported in the future. 330 | - * see 331 | - * soc-core.c :: snd_soc_init_multicodec() 332 | - * 333 | - * "platform" might be removed 334 | - * see 335 | - * simple-card-utils.c :: asoc_simple_canonicalize_platform() 336 | - */ 337 | - for (i = 0; i < num; i++) { 338 | - dai_link[i].cpus = &dai_props[i].cpus; 339 | - dai_link[i].num_cpus = 1; 340 | - dai_link[i].codecs = &dai_props[i].codecs; 341 | - dai_link[i].num_codecs = 1; 342 | - dai_link[i].platforms = &dai_props[i].platforms; 343 | - dai_link[i].num_platforms = 1; 344 | - } 345 | - 346 | priv->dai_props = dai_props; 347 | priv->dai_link = dai_link; 348 | 349 | @@ -798,9 +777,6 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 350 | } 351 | } else { 352 | struct seeed_card_info *cinfo; 353 | - struct snd_soc_dai_link_component *cpus; 354 | - struct snd_soc_dai_link_component *codecs; 355 | - struct snd_soc_dai_link_component *platform; 356 | 357 | cinfo = dev->platform_data; 358 | if (!cinfo) { 359 | @@ -817,19 +793,13 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 360 | return -EINVAL; 361 | } 362 | 363 | - cpus = dai_link->cpus; 364 | - cpus->dai_name = cinfo->cpu_dai.name; 365 | - 366 | - codecs = dai_link->codecs; 367 | - codecs->name = cinfo->codec; 368 | - codecs->dai_name = cinfo->codec_dai.name; 369 | - 370 | - platform = dai_link->platforms; 371 | - platform->name = cinfo->platform; 372 | - 373 | priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; 374 | dai_link->name = cinfo->name; 375 | dai_link->stream_name = cinfo->name; 376 | + dai_link->platform_name = cinfo->platform; 377 | + dai_link->codec_name = cinfo->codec; 378 | + dai_link->cpu_dai_name = cinfo->cpu_dai.name; 379 | + dai_link->codec_dai_name = cinfo->codec_dai.name; 380 | dai_link->dai_fmt = cinfo->daifmt; 381 | dai_link->init = seeed_voice_card_dai_init; 382 | memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, 383 | @@ -853,7 +823,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 384 | return ret; 385 | 386 | err: 387 | - asoc_simple_clean_reference(&priv->snd_card); 388 | + asoc_simple_card_clean_reference(&priv->snd_card); 389 | 390 | return ret; 391 | } 392 | @@ -865,7 +835,7 @@ static int seeed_voice_card_remove(struct platform_device *pdev) 393 | 394 | if (cancel_work_sync(&priv->work_codec_clk) != 0) { 395 | } 396 | - return asoc_simple_clean_reference(card); 397 | + return asoc_simple_card_clean_reference(card); 398 | } 399 | 400 | static const struct of_device_id seeed_voice_of_match[] = { 401 | diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h 402 | index 550b3a7..6c1a014 100644 403 | --- a/sound-compatible-4.18.h 404 | +++ b/sound-compatible-4.18.h 405 | @@ -16,6 +16,10 @@ 406 | #endif 407 | 408 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) 409 | +#ifndef __has_attribute 410 | +# define __has_attribute(x) __GCC4_has_attribute_##x 411 | +# define __GCC4_has_attribute___fallthrough__ 0 412 | +#endif 413 | #if __has_attribute(__fallthrough__) 414 | # define fallthrough __attribute__((__fallthrough__)) 415 | #else 416 | @@ -31,11 +35,7 @@ 417 | #define snd_soc_codec_get_dapm snd_soc_component_get_dapm 418 | #define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level 419 | #define snd_soc_kcontrol_codec snd_soc_kcontrol_component 420 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) 421 | -#define snd_soc_read snd_soc_component_read 422 | -#else 423 | #define snd_soc_read snd_soc_component_read32 424 | -#endif 425 | #define snd_soc_register_codec devm_snd_soc_register_component 426 | #define snd_soc_unregister_codec snd_soc_unregister_component 427 | #define snd_soc_update_bits snd_soc_component_update_bits 428 | diff --git a/wm8960.c b/wm8960.c 429 | index 465c6dc..34d4dad 100644 430 | --- a/wm8960.c 431 | +++ b/wm8960.c 432 | @@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream, 433 | return 0; 434 | } 435 | 436 | -static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction) 437 | +static int wm8960_mute(struct snd_soc_dai *dai, int mute) 438 | { 439 | struct snd_soc_codec *codec = dai->codec; 440 | 441 | @@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 442 | static const struct snd_soc_dai_ops wm8960_dai_ops = { 443 | .hw_params = wm8960_hw_params, 444 | .hw_free = wm8960_hw_free, 445 | - .mute_stream = wm8960_mute, 446 | + .digital_mute = wm8960_mute, 447 | .set_fmt = wm8960_set_dai_fmt, 448 | .set_clkdiv = wm8960_set_dai_clkdiv, 449 | .set_pll = wm8960_set_dai_pll, 450 | .set_sysclk = wm8960_set_dai_sysclk, 451 | - .no_capture_mute = 1, 452 | }; 453 | 454 | static struct snd_soc_dai_driver wm8960_dai = { 455 | -------------------------------------------------------------------------------- /patches/back-to-v5.4.diff: -------------------------------------------------------------------------------- 1 | diff --git a/ac101.c b/ac101.c 2 | index be9b1d8..343f030 100644 3 | --- a/ac101.c 4 | +++ b/ac101.c 5 | @@ -955,10 +955,10 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai 6 | 7 | AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", 8 | snd_pcm_stream_str(substream), 9 | - codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], 10 | - snd_soc_dai_active(codec_dai)); 11 | + codec_dai->playback_active, codec_dai->capture_active, 12 | + codec_dai->active); 13 | 14 | - if (!snd_soc_dai_active(codec_dai)) { 15 | + if (!codec_dai->active) { 16 | ac10x->aif1_clken = 1; 17 | ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); 18 | } else { 19 | @@ -1080,7 +1080,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream, 20 | freq_out = _FREQ_24_576K; 21 | for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) { 22 | if (codec_aif1_fs[i].samp_rate == params_rate(params)) { 23 | - if (codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && dmic_used && codec_aif1_fs[i].samp_rate == 44100) { 24 | + if (codec_dai->capture_active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) { 25 | ac101_update_bits(codec, AIF_SR_CTRL, (0xf<dev, "%s() stream=%s play:%d capt:%d +++\n", __func__, 35 | snd_pcm_stream_str(substream), 36 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]); 37 | + dai->playback_active, dai->capture_active); 38 | 39 | if (ac10x->i2c101) { 40 | ret = ac101_hw_params(substream, params, dai); 41 | @@ -664,8 +664,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h 42 | } 43 | } 44 | 45 | - if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) 46 | - || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream_active[SNDRV_PCM_STREAM_CAPTURE])) { 47 | + if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active) 48 | + || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) { 49 | /* not configure hw_param twice */ 50 | /* return 0; */ 51 | } 52 | @@ -1124,7 +1124,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream, 53 | } 54 | } 55 | 56 | -int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) { 57 | +int ac108_aif_mute(struct snd_soc_dai *dai, int mute) { 58 | struct snd_soc_codec *codec = dai->codec; 59 | struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); 60 | 61 | @@ -1145,13 +1145,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = { 62 | .hw_params = ac108_hw_params, 63 | .prepare = ac108_prepare, 64 | .trigger = ac108_trigger, 65 | - .mute_stream = ac108_aif_mute, 66 | + .digital_mute = ac108_aif_mute, 67 | 68 | /*DAI format configuration*/ 69 | .set_fmt = ac108_set_fmt, 70 | 71 | // .hw_free = ac108_hw_free, 72 | - .no_capture_mute = 1, 73 | }; 74 | 75 | static struct snd_soc_dai_driver ac108_dai0 = { 76 | diff --git a/seeed-voicecard.c b/seeed-voicecard.c 77 | index c6d9048..43535aa 100644 78 | --- a/seeed-voicecard.c 79 | +++ b/seeed-voicecard.c 80 | @@ -96,16 +96,16 @@ static int seeed_voice_card_startup(struct snd_pcm_substream *substream) 81 | if (ret) 82 | clk_disable_unprepare(dai_props->cpu_dai.clk); 83 | 84 | - if (asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) { 85 | - priv->channels_playback_default = asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min; 86 | + if (rtd->cpu_dai->driver->playback.channels_min) { 87 | + priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min; 88 | } 89 | - if (asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) { 90 | - priv->channels_capture_default = asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min; 91 | + if (rtd->cpu_dai->driver->capture.channels_min) { 92 | + priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min; 93 | } 94 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override; 95 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override; 96 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override; 97 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override; 98 | + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override; 99 | + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override; 100 | + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override; 101 | + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override; 102 | 103 | return ret; 104 | } 105 | @@ -117,10 +117,10 @@ static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) 106 | struct seeed_dai_props *dai_props = 107 | seeed_priv_to_props(priv, rtd->num); 108 | 109 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default; 110 | - asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default; 111 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default; 112 | - asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default; 113 | + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default; 114 | + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default; 115 | + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default; 116 | + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default; 117 | 118 | clk_disable_unprepare(dai_props->cpu_dai.clk); 119 | 120 | @@ -131,8 +131,8 @@ static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, 121 | struct snd_pcm_hw_params *params) 122 | { 123 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 124 | - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 125 | - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 126 | + struct snd_soc_dai *codec_dai = rtd->codec_dai; 127 | + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 128 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 129 | struct seeed_dai_props *dai_props = 130 | seeed_priv_to_props(priv, rtd->num); 131 | @@ -196,7 +196,7 @@ static void work_cb_codec_clk(struct work_struct *work) 132 | static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) 133 | { 134 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 135 | - struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); 136 | + struct snd_soc_dai *dai = rtd->codec_dai; 137 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 138 | #if CONFIG_AC10X_TRIG_LOCK 139 | unsigned long flags; 140 | @@ -205,7 +205,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 141 | 142 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", 143 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 144 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]); 145 | + dai->playback_active, dai->capture_active); 146 | 147 | switch (cmd) { 148 | case SNDRV_PCM_TRIGGER_START: 149 | @@ -227,7 +227,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 150 | case SNDRV_PCM_TRIGGER_SUSPEND: 151 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 152 | /* capture channel resync, if overrun */ 153 | - if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 154 | + if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 155 | break; 156 | } 157 | 158 | @@ -252,7 +247,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd 159 | 160 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n", 161 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 162 | - dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], ret); 163 | + dai->playback_active, dai->capture_active, ret); 164 | 165 | return ret; 166 | } 167 | @@ -337,8 +337,8 @@ static int asoc_simple_init_dai(struct snd_soc_dai *dai, 168 | static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) 169 | { 170 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 171 | - struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0); 172 | - struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0); 173 | + struct snd_soc_dai *codec = rtd->codec_dai; 174 | + struct snd_soc_dai *cpu = rtd->cpu_dai; 175 | struct seeed_dai_props *dai_props = 176 | seeed_priv_to_props(priv, rtd->num); 177 | int ret; 178 | @@ -636,11 +636,11 @@ static int seeed_voice_card_probe(struct platform_device *pdev) 179 | * Use snd_soc_dai_link_component instead of legacy style 180 | * It is codec only. but cpu/platform will be supported in the future. 181 | * see 182 | - * soc-core.c :: snd_soc_init_multicodec() 183 | + * soc-core.c :: snd_soc_init_multicodec() 184 | * 185 | * "platform" might be removed 186 | * see 187 | - * simple-card-utils.c :: asoc_simple_canonicalize_platform() 188 | + * simple-card-utils.c :: asoc_simple_canonicalize_platform() 189 | */ 190 | for (i = 0; i < num; i++) { 191 | dai_link[i].cpus = &dai_props[i].cpus; 192 | diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h 193 | index 080325b..eefa7de 100644 194 | --- a/sound-compatible-4.18.h 195 | +++ b/sound-compatible-4.18.h 196 | @@ -16,6 +16,10 @@ 197 | #endif 198 | 199 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) 200 | +#ifndef __has_attribute 201 | +# define __has_attribute(x) __GCC4_has_attribute_##x 202 | +# define __GCC4_has_attribute___fallthrough__ 0 203 | +#endif 204 | #if __has_attribute(__fallthrough__) 205 | # define fallthrough __attribute__((__fallthrough__)) 206 | #else 207 | @@ -31,11 +35,7 @@ 208 | #define snd_soc_codec_get_dapm snd_soc_component_get_dapm 209 | #define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level 210 | #define snd_soc_kcontrol_codec snd_soc_kcontrol_component 211 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) 212 | -#define snd_soc_read snd_soc_component_read 213 | -#else 214 | #define snd_soc_read snd_soc_component_read32 215 | -#endif 216 | #define snd_soc_register_codec devm_snd_soc_register_component 217 | #define snd_soc_unregister_codec snd_soc_unregister_component 218 | #define snd_soc_update_bits snd_soc_component_update_bits 219 | diff --git a/wm8960.c b/wm8960.c 220 | index 465c6dc..34d4dad 100644 221 | --- a/wm8960.c 222 | +++ b/wm8960.c 223 | @@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream, 224 | return 0; 225 | } 226 | 227 | -static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction) 228 | +static int wm8960_mute(struct snd_soc_dai *dai, int mute) 229 | { 230 | struct snd_soc_codec *codec = dai->codec; 231 | 232 | @@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 233 | static const struct snd_soc_dai_ops wm8960_dai_ops = { 234 | .hw_params = wm8960_hw_params, 235 | .hw_free = wm8960_hw_free, 236 | - .mute_stream = wm8960_mute, 237 | + .digital_mute = wm8960_mute, 238 | .set_fmt = wm8960_set_dai_fmt, 239 | .set_clkdiv = wm8960_set_dai_clkdiv, 240 | .set_pll = wm8960_set_dai_pll, 241 | .set_sysclk = wm8960_set_dai_sysclk, 242 | - .no_capture_mute = 1, 243 | }; 244 | 245 | static struct snd_soc_dai_driver wm8960_dai = { 246 | -------------------------------------------------------------------------------- /patches/back-to-v5.8.diff: -------------------------------------------------------------------------------- 1 | diff --git a/ac108.c b/ac108.c 2 | index 4663df0..67edeae 100644 3 | --- a/ac108.c 4 | +++ b/ac108.c 5 | @@ -1124,7 +1124,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream, 6 | } 7 | } 8 | 9 | -int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) { 10 | +int ac108_aif_mute(struct snd_soc_dai *dai, int mute) { 11 | struct snd_soc_codec *codec = dai->codec; 12 | struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); 13 | 14 | @@ -1145,13 +1145,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = { 15 | .hw_params = ac108_hw_params, 16 | .prepare = ac108_prepare, 17 | .trigger = ac108_trigger, 18 | - .mute_stream = ac108_aif_mute, 19 | + .digital_mute = ac108_aif_mute, 20 | 21 | /*DAI format configuration*/ 22 | .set_fmt = ac108_set_fmt, 23 | 24 | // .hw_free = ac108_hw_free, 25 | - .no_capture_mute = 1, 26 | }; 27 | 28 | static struct snd_soc_dai_driver ac108_dai0 = { 29 | diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h 30 | index 080325b..faed848 100644 31 | --- a/sound-compatible-4.18.h 32 | +++ b/sound-compatible-4.18.h 33 | @@ -31,11 +31,7 @@ 34 | #define snd_soc_codec_get_dapm snd_soc_component_get_dapm 35 | #define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level 36 | #define snd_soc_kcontrol_codec snd_soc_kcontrol_component 37 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) 38 | -#define snd_soc_read snd_soc_component_read 39 | -#else 40 | #define snd_soc_read snd_soc_component_read32 41 | -#endif 42 | #define snd_soc_register_codec devm_snd_soc_register_component 43 | #define snd_soc_unregister_codec snd_soc_unregister_component 44 | #define snd_soc_update_bits snd_soc_component_update_bits 45 | diff --git a/wm8960.c b/wm8960.c 46 | index 465c6dc..34d4dad 100644 47 | --- a/wm8960.c 48 | +++ b/wm8960.c 49 | @@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream, 50 | return 0; 51 | } 52 | 53 | -static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction) 54 | +static int wm8960_mute(struct snd_soc_dai *dai, int mute) 55 | { 56 | struct snd_soc_codec *codec = dai->codec; 57 | 58 | @@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 59 | static const struct snd_soc_dai_ops wm8960_dai_ops = { 60 | .hw_params = wm8960_hw_params, 61 | .hw_free = wm8960_hw_free, 62 | - .mute_stream = wm8960_mute, 63 | + .digital_mute = wm8960_mute, 64 | .set_fmt = wm8960_set_dai_fmt, 65 | .set_clkdiv = wm8960_set_dai_clkdiv, 66 | .set_pll = wm8960_set_dai_pll, 67 | .set_sysclk = wm8960_set_dai_sysclk, 68 | - .no_capture_mute = 1, 69 | }; 70 | 71 | static struct snd_soc_dai_driver wm8960_dai = { 72 | -------------------------------------------------------------------------------- /pulseaudio/91-seeedvoicecard.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM!="sound", GOTO="seeedvoicecard_end" 2 | ACTION!="change", GOTO="seeedvoicecard_end" 3 | KERNEL!="card*", GOTO="seeedvoicecard_end" 4 | 5 | ATTR{id}=="seeed4micvoicec",ENV{PULSE_PROFILE_SET}="seeed-voicecard-4mic.conf" 6 | ATTR{id}=="seeed8micvoicec",ENV{PULSE_PROFILE_SET}="seeed-voicecard-8mic.conf" 7 | 8 | LABEL="seeedvoicecard_end" -------------------------------------------------------------------------------- /pulseaudio/README.md: -------------------------------------------------------------------------------- 1 | # PulseAudio Configuration for seeed-voicecard 2 | 3 | Follow this guide if you want to use your seeed-voicecard as a default source/sink of pulseaudio. 4 | 5 | ### Prerequisites 6 | 7 | 1. Download PulseAudio 8 | ``` 9 | sudo apt install -y pulseaudio 10 | ``` 11 | 12 | 2. PulseAudio Profiles 13 | ``` 14 | cd seeed-voicecard/pulseaudio 15 | sudo cp pulse_config_4mic/seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard-4mic.conf 16 | sudo cp pulse_config_6mic/seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard-8mic.conf 17 | ``` 18 | 19 | 3. Add `udev` Rules 20 | 21 | During the system start, when the card "seeed4micvoicec" is detected, the PULSE_PROFILE_SET variable will be set in the udev database, and PulseAudio will be forced to use `seeed-voicecard-4mic.conf`. Similarly, if the card "seeed8micvoicec" is detected, PulseAudio will be forced to use `seeed-voicecard-8mic.conf`. 22 | 23 | ``` 24 | sudo cp 91-seeedvoicecard.rules /etc/udev/rules.d/91-seeedvoicecard.rules 25 | ``` 26 | 27 | ### ReSpeaker 4 Mic Array 28 | 29 | 98 | 99 | 1. config `default.pa` and `daemon.conf` 100 | ``` 101 | sudo cp pulse_config_4mic/default.pa /etc/pulse/ 102 | sudo cp pulse_config_4mic/daemon.conf /etc/pulse/ 103 | ``` 104 | 105 | 2. reboot raspberry pi and check 106 | ``` 107 | sudo reboot 108 | pulseaudio --start # start pulse at first 109 | pactl info # check the setting 110 | 111 | Server String: /run/user/1000/pulse/native 112 | Library Protocol Version: 32 113 | Server Protocol Version: 32 114 | Is Local: yes 115 | Client Index: 18 116 | Tile Size: 65496 117 | User Name: pi 118 | Host Name: raspberrypi 119 | Server Name: pulseaudio 120 | Server Version: 10.0 121 | Default Sample Specification: s16le 4ch 96000Hz 122 | Default Channel Map: front-left,front-center,front-right,rear-center 123 | Default Sink: alsa_output.platform-soc_audio.analog-stereo 124 | Default Source: alsa_input.platform-soc_sound.seeed-source 125 | Cookie: 3b12:70b3 126 | ``` 127 | 128 | ### 6-Mics Circular Array Kit and 4-Mics Linear Array 129 | 130 | 199 | 200 | 1. config `default.pa` and `daemon.conf` 201 | ``` 202 | sudo cp pulse_config_6mic/default.pa /etc/pulse/ 203 | sudo cp pulse_config_6mic/daemon.conf /etc/pulse/ 204 | ``` 205 | 206 | 2. reboot raspberry pi and check 207 | ``` 208 | sudo reboot 209 | pulseaudio --start # start pulse at first 210 | pactl info # check the setting 211 | 212 | # The output should be like this 213 | # You could see the default sink is seeed-2ch and default source is seeed-8ch 214 | pi@raspberrypi:~ $ pactl info 215 | Server String: /run/user/1000/pulse/native 216 | Library Protocol Version: 32 217 | Server Protocol Version: 32 218 | Is Local: yes 219 | Client Index: 6 220 | Tile Size: 65496 221 | User Name: pi 222 | Host Name: raspberrypi 223 | Server Name: pulseaudio 224 | Server Version: 10.0 225 | Default Sample Specification: s32le 8ch 96000Hz 226 | Default Channel Map: front-left,front-left-of-center,front-center,front-right,front-right-of-center,rear-center,aux0,aux1 227 | Default Sink: alsa_output.platform-soc_sound.seeed-2ch 228 | Default Source: alsa_input.platform-soc_sound.seeed-8ch 229 | Cookie: 3523:e5af 230 | ``` 231 | 232 | ### FAQ 233 | 234 | 1. Default Sink/Source not right 235 | 236 | Make sure there is no any other daemon or using the audio device. Then check profile and udev rules. 237 | 238 | `pacmd list-sinks` and `pacmd list-sources` can be used to show the avaiable sinks/sources, after pulseaudio is started. 239 | 240 | 2. Can't start PulseAudio 241 | 242 | Check `default.pa` and `daemon.conf` 243 | 244 | 3. How to get PulseAudio started automatically 245 | 246 | Normally the PulseAudio server is started automatically. If you want to disable it, you can set `autospawn = no` in `~/.config/pulse/client.conf` or `/etc/pulse/client.conf`. 247 | [Click this for more details](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Running/). 248 | 249 | 4. Why the default sample rate is 96000? What if my audio's sample rate is not the same as the default? 250 | 251 | For the other sample rate audio, PulseAudio will resample it into 96K, which means that if your audio's sample rate is lower than 96K, it will get smoothing. -------------------------------------------------------------------------------- /pulseaudio/pulse_config_4mic/daemon.conf: -------------------------------------------------------------------------------- 1 | # This file is part of PulseAudio. 2 | # 3 | # PulseAudio is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation; either version 2 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # PulseAudio is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with PulseAudio; if not, see . 15 | 16 | ## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for 17 | ## more information. Default values are commented out. Use either ; or # for 18 | ## commenting. 19 | 20 | ; daemonize = no 21 | ; fail = yes 22 | ; allow-module-loading = yes 23 | ; allow-exit = yes 24 | ; use-pid-file = yes 25 | ; system-instance = no 26 | ; local-server-type = user 27 | ; enable-shm = yes 28 | ; enable-memfd = yes 29 | ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB 30 | ; lock-memory = no 31 | ; cpu-limit = no 32 | 33 | ; high-priority = yes 34 | ; nice-level = -11 35 | 36 | ; realtime-scheduling = yes 37 | ; realtime-priority = 5 38 | 39 | ; exit-idle-time = 20 40 | ; scache-idle-time = 20 41 | 42 | ; dl-search-path = (depends on architecture) 43 | 44 | ; load-default-script-file = yes 45 | ; default-script-file = /etc/pulse/default.pa 46 | 47 | ; log-target = auto 48 | ; log-level = notice 49 | ; log-meta = no 50 | ; log-time = no 51 | ; log-backtrace = 0 52 | 53 | ; resample-method = speex-float-1 54 | ; enable-remixing = yes 55 | ; enable-lfe-remixing = no 56 | ; lfe-crossover-freq = 0 57 | 58 | ; flat-volumes = yes 59 | 60 | ; rlimit-fsize = -1 61 | ; rlimit-data = -1 62 | ; rlimit-stack = -1 63 | ; rlimit-core = -1 64 | ; rlimit-as = -1 65 | ; rlimit-rss = -1 66 | ; rlimit-nproc = -1 67 | ; rlimit-nofile = 256 68 | ; rlimit-memlock = -1 69 | ; rlimit-locks = -1 70 | ; rlimit-sigpending = -1 71 | ; rlimit-msgqueue = -1 72 | ; rlimit-nice = 31 73 | ; rlimit-rtprio = 9 74 | ; rlimit-rttime = 200000 75 | 76 | default-sample-format = s16le 77 | default-sample-rate = 96000 78 | ; alternate-sample-rate = 48000 79 | default-sample-channels = 4 80 | ; default-channel-map = front-left,front-right 81 | 82 | ; default-fragments = 4 83 | ; default-fragment-size-msec = 25 84 | 85 | ; enable-deferred-volume = yes 86 | ; deferred-volume-safety-margin-usec = 8000 87 | ; deferred-volume-extra-delay-usec = 0 88 | -------------------------------------------------------------------------------- /pulseaudio/pulse_config_4mic/default.pa: -------------------------------------------------------------------------------- 1 | #!/usr/bin/pulseaudio -nF 2 | # 3 | # This file is part of PulseAudio. 4 | # 5 | # PulseAudio is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # PulseAudio is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with PulseAudio; if not, see . 17 | 18 | # This startup script is used only if PulseAudio is started per-user 19 | # (i.e. not in system mode) 20 | 21 | .fail 22 | 23 | ### Automatically restore the volume of streams and devices 24 | load-module module-device-restore 25 | load-module module-stream-restore 26 | load-module module-card-restore 27 | 28 | ### Automatically augment property information from .desktop files 29 | ### stored in /usr/share/application 30 | load-module module-augment-properties 31 | 32 | ### Should be after module-*-restore but before module-*-detect 33 | load-module module-switch-on-port-available 34 | 35 | ### Load audio drivers statically 36 | ### (it's probably better to not load these drivers manually, but instead 37 | ### use module-udev-detect -- see below -- for doing this automatically) 38 | #load-module module-alsa-sink device="hw:1,0" channels=8 rate=48000 format=s32le 39 | #load-module module-alsa-source device="hw:1,0" channels=8 rate=48000 format=s32le 40 | #load-module module-oss device="/dev/dsp" sink_name=output source_name=input 41 | #load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input 42 | #load-module module-null-sink 43 | #load-module module-pipe-sink 44 | 45 | ### Automatically load driver modules depending on the hardware available 46 | .ifexists module-udev-detect.so 47 | load-module module-udev-detect 48 | #channels=8 rate=48000 format=s32le 49 | .else 50 | ### Use the static hardware detection module (for systems that lack udev support) 51 | load-module module-detect 52 | .endif 53 | 54 | ### Automatically connect sink and source if JACK server is present 55 | .ifexists module-jackdbus-detect.so 56 | .nofail 57 | load-module module-jackdbus-detect channels=2 58 | .fail 59 | .endif 60 | 61 | ### Automatically load driver modules for Bluetooth hardware 62 | .ifexists module-bluetooth-policy.so 63 | load-module module-bluetooth-policy 64 | .endif 65 | 66 | .ifexists module-bluetooth-discover.so 67 | load-module module-bluetooth-discover 68 | .endif 69 | 70 | ### Load several protocols 71 | .ifexists module-esound-protocol-unix.so 72 | load-module module-esound-protocol-unix 73 | .endif 74 | load-module module-native-protocol-unix 75 | 76 | ### Network access (may be configured with paprefs, so leave this commented 77 | ### here if you plan to use paprefs) 78 | #load-module module-esound-protocol-tcp 79 | #load-module module-native-protocol-tcp 80 | #load-module module-zeroconf-publish 81 | 82 | ### Load the RTP receiver module (also configured via paprefs, see above) 83 | #load-module module-rtp-recv 84 | 85 | ### Load the RTP sender module (also configured via paprefs, see above) 86 | #load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'" 87 | #load-module module-rtp-send source=rtp.monitor 88 | 89 | ### Load additional modules from GConf settings. This can be configured with the paprefs tool. 90 | ### Please keep in mind that the modules configured by paprefs might conflict with manually 91 | ### loaded modules. 92 | .ifexists module-gconf.so 93 | .nofail 94 | load-module module-gconf 95 | .fail 96 | .endif 97 | 98 | ### Automatically restore the default sink/source when changed by the user 99 | ### during runtime 100 | ### NOTE: This should be loaded as early as possible so that subsequent modules 101 | ### that look up the default sink/source get the right value 102 | load-module module-default-device-restore 103 | 104 | ### Automatically move streams to the default sink if the sink they are 105 | ### connected to dies, similar for sources 106 | load-module module-rescue-streams 107 | 108 | ### Make sure we always have a sink around, even if it is a null sink. 109 | load-module module-always-sink 110 | 111 | ### Honour intended role device property 112 | load-module module-intended-roles 113 | 114 | ### Automatically suspend sinks/sources that become idle for too long 115 | load-module module-suspend-on-idle 116 | 117 | ### If autoexit on idle is enabled we want to make sure we only quit 118 | ### when no local session needs us anymore. 119 | .ifexists module-console-kit.so 120 | load-module module-console-kit 121 | .endif 122 | .ifexists module-systemd-login.so 123 | load-module module-systemd-login 124 | .endif 125 | 126 | ### Enable positioned event sounds 127 | load-module module-position-event-sounds 128 | 129 | ### Cork music/video streams when a phone stream is active 130 | load-module module-role-cork 131 | 132 | ### Modules to allow autoloading of filters (such as echo cancellation) 133 | ### on demand. module-filter-heuristics tries to determine what filters 134 | ### make sense, and module-filter-apply does the heavy-lifting of 135 | ### loading modules and rerouting streams. 136 | load-module module-filter-heuristics 137 | load-module module-filter-apply 138 | 139 | ### Make some devices default 140 | #set-default-sink output 141 | #set-default-source input 142 | set-default-source alsa_input.platform-soc_sound.seeed-source 143 | set-default-sink alsa_output.platform-soc_sound.seeed-sink 144 | 145 | -------------------------------------------------------------------------------- /pulseaudio/pulse_config_4mic/seeed-voicecard.conf: -------------------------------------------------------------------------------- 1 | # /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard.conf 2 | 3 | [General] 4 | auto-profiles = no 5 | [Mapping seeed-source] 6 | device-strings = hw:%f 7 | channel-map = front-left,front-right,rear-left,rear-right 8 | exact-channels = false 9 | fallback = yes 10 | paths-input = seeed-source 11 | priority = 3 12 | direction = input 13 | 14 | [Profile input:seeed-source] 15 | input-mappings = seeed-source 16 | priority = 5 17 | skip-probe = yes 18 | -------------------------------------------------------------------------------- /pulseaudio/pulse_config_6mic/daemon.conf: -------------------------------------------------------------------------------- 1 | # This file is part of PulseAudio. 2 | # 3 | # PulseAudio is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation; either version 2 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # PulseAudio is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with PulseAudio; if not, see . 15 | 16 | ## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for 17 | ## more information. Default values are commented out. Use either ; or # for 18 | ## commenting. 19 | 20 | ; daemonize = no 21 | ; fail = yes 22 | ; allow-module-loading = yes 23 | ; allow-exit = yes 24 | ; use-pid-file = yes 25 | ; system-instance = no 26 | ; local-server-type = user 27 | ; enable-shm = yes 28 | ; enable-memfd = yes 29 | ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB 30 | ; lock-memory = no 31 | ; cpu-limit = no 32 | 33 | ; high-priority = yes 34 | ; nice-level = -11 35 | 36 | ; realtime-scheduling = yes 37 | ; realtime-priority = 5 38 | 39 | ; exit-idle-time = 20 40 | ; scache-idle-time = 20 41 | 42 | ; dl-search-path = (depends on architecture) 43 | 44 | ; load-default-script-file = yes 45 | ; default-script-file = /etc/pulse/default.pa 46 | 47 | ; log-target = auto 48 | ; log-level = notice 49 | ; log-meta = no 50 | ; log-time = no 51 | ; log-backtrace = 0 52 | 53 | ; resample-method = speex-float-1 54 | ; enable-remixing = yes 55 | ; enable-lfe-remixing = no 56 | ; lfe-crossover-freq = 0 57 | 58 | ; flat-volumes = yes 59 | 60 | ; rlimit-fsize = -1 61 | ; rlimit-data = -1 62 | ; rlimit-stack = -1 63 | ; rlimit-core = -1 64 | ; rlimit-as = -1 65 | ; rlimit-rss = -1 66 | ; rlimit-nproc = -1 67 | ; rlimit-nofile = 256 68 | ; rlimit-memlock = -1 69 | ; rlimit-locks = -1 70 | ; rlimit-sigpending = -1 71 | ; rlimit-msgqueue = -1 72 | ; rlimit-nice = 31 73 | ; rlimit-rtprio = 9 74 | ; rlimit-rttime = 200000 75 | 76 | default-sample-format = s32le 77 | default-sample-rate = 96000 78 | ; alternate-sample-rate = 48000 79 | default-sample-channels = 8 80 | ; default-channel-map = front-left,front-right 81 | 82 | ; default-fragments = 4 83 | ; default-fragment-size-msec = 25 84 | 85 | ; enable-deferred-volume = yes 86 | ; deferred-volume-safety-margin-usec = 8000 87 | ; deferred-volume-extra-delay-usec = 0 88 | -------------------------------------------------------------------------------- /pulseaudio/pulse_config_6mic/default.pa: -------------------------------------------------------------------------------- 1 | #!/usr/bin/pulseaudio -nF 2 | # 3 | # This file is part of PulseAudio. 4 | # 5 | # PulseAudio is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # PulseAudio is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with PulseAudio; if not, see . 17 | 18 | # This startup script is used only if PulseAudio is started per-user 19 | # (i.e. not in system mode) 20 | 21 | .fail 22 | 23 | ### Automatically restore the volume of streams and devices 24 | load-module module-device-restore 25 | load-module module-stream-restore 26 | load-module module-card-restore 27 | 28 | ### Automatically augment property information from .desktop files 29 | ### stored in /usr/share/application 30 | load-module module-augment-properties 31 | 32 | ### Should be after module-*-restore but before module-*-detect 33 | load-module module-switch-on-port-available 34 | 35 | ### Load audio drivers statically 36 | ### (it's probably better to not load these drivers manually, but instead 37 | ### use module-udev-detect -- see below -- for doing this automatically) 38 | #load-module module-alsa-sink device="hw:1,0" channels=8 rate=48000 format=s32le 39 | #load-module module-alsa-source device="hw:1,0" channels=8 rate=48000 format=s32le 40 | #load-module module-oss device="/dev/dsp" sink_name=output source_name=input 41 | #load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input 42 | #load-module module-null-sink 43 | #load-module module-pipe-sink 44 | 45 | ### Automatically load driver modules depending on the hardware available 46 | .ifexists module-udev-detect.so 47 | load-module module-udev-detect 48 | #channels=8 rate=48000 format=s32le 49 | .else 50 | ### Use the static hardware detection module (for systems that lack udev support) 51 | load-module module-detect 52 | .endif 53 | 54 | ### Automatically connect sink and source if JACK server is present 55 | .ifexists module-jackdbus-detect.so 56 | .nofail 57 | load-module module-jackdbus-detect channels=2 58 | .fail 59 | .endif 60 | 61 | ### Automatically load driver modules for Bluetooth hardware 62 | .ifexists module-bluetooth-policy.so 63 | load-module module-bluetooth-policy 64 | .endif 65 | 66 | .ifexists module-bluetooth-discover.so 67 | load-module module-bluetooth-discover 68 | .endif 69 | 70 | ### Load several protocols 71 | .ifexists module-esound-protocol-unix.so 72 | load-module module-esound-protocol-unix 73 | .endif 74 | load-module module-native-protocol-unix 75 | 76 | ### Network access (may be configured with paprefs, so leave this commented 77 | ### here if you plan to use paprefs) 78 | #load-module module-esound-protocol-tcp 79 | #load-module module-native-protocol-tcp 80 | #load-module module-zeroconf-publish 81 | 82 | ### Load the RTP receiver module (also configured via paprefs, see above) 83 | #load-module module-rtp-recv 84 | 85 | ### Load the RTP sender module (also configured via paprefs, see above) 86 | #load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'" 87 | #load-module module-rtp-send source=rtp.monitor 88 | 89 | ### Load additional modules from GConf settings. This can be configured with the paprefs tool. 90 | ### Please keep in mind that the modules configured by paprefs might conflict with manually 91 | ### loaded modules. 92 | .ifexists module-gconf.so 93 | .nofail 94 | load-module module-gconf 95 | .fail 96 | .endif 97 | 98 | ### Automatically restore the default sink/source when changed by the user 99 | ### during runtime 100 | ### NOTE: This should be loaded as early as possible so that subsequent modules 101 | ### that look up the default sink/source get the right value 102 | load-module module-default-device-restore 103 | 104 | ### Automatically move streams to the default sink if the sink they are 105 | ### connected to dies, similar for sources 106 | load-module module-rescue-streams 107 | 108 | ### Make sure we always have a sink around, even if it is a null sink. 109 | load-module module-always-sink 110 | 111 | ### Honour intended role device property 112 | load-module module-intended-roles 113 | 114 | ### Automatically suspend sinks/sources that become idle for too long 115 | load-module module-suspend-on-idle 116 | 117 | ### If autoexit on idle is enabled we want to make sure we only quit 118 | ### when no local session needs us anymore. 119 | .ifexists module-console-kit.so 120 | load-module module-console-kit 121 | .endif 122 | .ifexists module-systemd-login.so 123 | load-module module-systemd-login 124 | .endif 125 | 126 | ### Enable positioned event sounds 127 | load-module module-position-event-sounds 128 | 129 | ### Cork music/video streams when a phone stream is active 130 | load-module module-role-cork 131 | 132 | ### Modules to allow autoloading of filters (such as echo cancellation) 133 | ### on demand. module-filter-heuristics tries to determine what filters 134 | ### make sense, and module-filter-apply does the heavy-lifting of 135 | ### loading modules and rerouting streams. 136 | load-module module-filter-heuristics 137 | load-module module-filter-apply 138 | 139 | ### Make some devices default 140 | #set-default-sink output 141 | #set-default-source input 142 | set-default-source alsa_input.platform-soc_sound.seeed-8ch 143 | set-default-sink alsa_output.platform-soc_sound.seeed-2ch 144 | -------------------------------------------------------------------------------- /pulseaudio/pulse_config_6mic/seeed-voicecard.conf: -------------------------------------------------------------------------------- 1 | # /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voiced.conf 2 | 3 | [General] 4 | auto-profiles = no 5 | [Mapping seeed-8ch] 6 | device-strings = hw:%f 7 | channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right 8 | exact-channels = false 9 | fallback = yes 10 | paths-input = seeed-8ch 11 | priority = 3 12 | direction = input 13 | [Mapping seeed-2ch] 14 | device-strings = hw:%f 15 | channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right 16 | exact-channels = false 17 | exact-channels = false 18 | fallback = yes 19 | paths-output = seeed-2ch 20 | direction = output 21 | priority = 2 22 | [Profile output:seeed-2ch+input:seeed-8ch] 23 | output-mappings = seeed-2ch 24 | input-mappings = seeed-8ch 25 | priority = 100 26 | skip-probe = yes 27 | [Profile output:seeed-2ch] 28 | output-mappings = seeed-2ch 29 | priority = 4 30 | skip-probe = yes 31 | [Profile input:seeed-8ch] 32 | input-mappings = seeed-8ch 33 | priority = 5 34 | skip-probe = yes -------------------------------------------------------------------------------- /pulseaudio/udev_rules_4mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/pulseaudio/udev_rules_4mic.png -------------------------------------------------------------------------------- /pulseaudio/udev_rules_6mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/pulseaudio/udev_rules_6mic.png -------------------------------------------------------------------------------- /seeed-2mic-voicecard-overlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | 4 | / { 5 | compatible = "brcm,bcm2708"; 6 | 7 | fragment@0 { 8 | target = <&i2s>; 9 | __overlay__ { 10 | status = "okay"; 11 | }; 12 | }; 13 | fragment@1 { 14 | target-path="/"; 15 | __overlay__ { 16 | wm8960_mclk: wm8960_mclk { 17 | compatible = "fixed-clock"; 18 | #clock-cells = <0>; 19 | clock-frequency = <12288000>; 20 | }; 21 | 22 | }; 23 | }; 24 | fragment@2 { 25 | target = <&i2c1>; 26 | __overlay__ { 27 | #address-cells = <1>; 28 | #size-cells = <0>; 29 | status = "okay"; 30 | 31 | wm8960: wm8960{ 32 | compatible = "wlf,wm8960"; 33 | reg = <0x1a>; 34 | #sound-dai-cells = <0>; 35 | AVDD-supply = <&vdd_5v0_reg>; 36 | DVDD-supply = <&vdd_3v3_reg>; 37 | }; 38 | }; 39 | }; 40 | 41 | 42 | fragment@3 { 43 | target = <&sound>; 44 | slave_overlay: __overlay__ { 45 | compatible = "simple-audio-card"; 46 | simple-audio-card,format = "i2s"; 47 | simple-audio-card,name = "seeed-2mic-voicecard"; 48 | status = "okay"; 49 | simple-audio-card,widgets = 50 | "Microphone", "Mic Jack", 51 | "Line", "Line In", 52 | "Line", "Line Out", 53 | "Speaker", "Speaker", 54 | "Headphone", "Headphone Jack"; 55 | simple-audio-card,routing = 56 | "Headphone Jack", "HP_L", 57 | "Headphone Jack", "HP_R", 58 | "Speaker", "SPK_LP", 59 | "Speaker", "SPK_LN", 60 | "LINPUT1", "Mic Jack", 61 | "LINPUT3", "Mic Jack", 62 | "RINPUT1", "Mic Jack", 63 | "RINPUT2", "Mic Jack"; 64 | 65 | 66 | 67 | 68 | simple-audio-card,cpu { 69 | sound-dai = <&i2s>; 70 | }; 71 | dailink0_slave: simple-audio-card,codec { 72 | sound-dai = <&wm8960>; 73 | clocks = <&wm8960_mclk>; 74 | clock-names = "mclk"; 75 | 76 | }; 77 | }; 78 | }; 79 | 80 | __overrides__ { 81 | alsaname = <&slave_overlay>,"simple-audio-card,name"; 82 | compatible = <&wm8960>,"compatible"; 83 | master = <0>,"=2!3"; 84 | }; 85 | }; 86 | 87 | -------------------------------------------------------------------------------- /seeed-2mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/seeed-2mic-voicecard.dtbo -------------------------------------------------------------------------------- /seeed-4mic-voicecard-overlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | 4 | / { 5 | compatible = "brcm,bcm2708"; 6 | 7 | fragment@0 { 8 | target = <&i2s>; 9 | __overlay__ { 10 | #sound-dai-cells = <0>; 11 | status = "okay"; 12 | }; 13 | }; 14 | 15 | fragment@1 { 16 | target-path = "/"; 17 | __overlay__ { 18 | ac108_mclk: codec-mclk { 19 | compatible = "fixed-clock"; 20 | #clock-cells = <0>; 21 | clock-frequency = <24000000>; 22 | }; 23 | }; 24 | }; 25 | 26 | fragment@2 { 27 | target = <&i2c1>; 28 | __overlay__ { 29 | #address-cells = <1>; 30 | #size-cells = <0>; 31 | status = "okay"; 32 | 33 | ac108_a: ac108@3b{ 34 | compatible = "x-power,ac108_0"; 35 | reg = <0x3b>; 36 | #sound-dai-cells = <0>; 37 | data-protocol = <0>; 38 | }; 39 | }; 40 | }; 41 | 42 | 43 | fragment@3 { 44 | target = <&sound>; 45 | 46 | sound_overlay: __overlay__ { 47 | compatible = "seeed-voicecard"; 48 | seeed-voice-card,format = "dsp_a"; 49 | seeed-voice-card,name = "seeed-4mic-voicecard"; 50 | status = "okay"; 51 | 52 | seeed-voice-card,bitclock-master = <&codec_dai>; 53 | seeed-voice-card,frame-master = <&codec_dai>; 54 | seeed-voice-card,channels-playback-override = <4>; 55 | seeed-voice-card,channels-capture-override = <4>; 56 | 57 | cpu_dai: seeed-voice-card,cpu { 58 | sound-dai = <&i2s>; 59 | dai-tdm-slot-num = <2>; 60 | dai-tdm-slot-width = <32>; 61 | dai-tdm-slot-tx-mask = <1 1 0 0>; 62 | dai-tdm-slot-rx-mask = <1 1 0 0>; 63 | }; 64 | codec_dai: seeed-voice-card,codec { 65 | sound-dai = <&ac108_a>; 66 | clocks = <&ac108_mclk>; 67 | }; 68 | }; 69 | }; 70 | 71 | __overrides__ { 72 | card-name = <&sound_overlay>,"seeed-voice-card,name"; 73 | }; 74 | }; 75 | 76 | -------------------------------------------------------------------------------- /seeed-4mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/seeed-4mic-voicecard.dtbo -------------------------------------------------------------------------------- /seeed-8mic-voicecard-overlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | 4 | / { 5 | compatible = "brcm,bcm2708"; 6 | 7 | fragment@0 { 8 | target = <&i2s>; 9 | __overlay__ { 10 | #sound-dai-cells = <0>; 11 | status = "okay"; 12 | }; 13 | }; 14 | 15 | fragment@1 { 16 | target-path = "/"; 17 | __overlay__ { 18 | ac10x_mclk: codec-mclk { 19 | compatible = "fixed-clock"; 20 | #clock-cells = <0>; 21 | clock-frequency = <24000000>; 22 | }; 23 | }; 24 | }; 25 | 26 | fragment@2 { 27 | target = <&gpio>; 28 | __overlay__ { 29 | spk_amp_pins: spk_pins { 30 | brcm,pins = <17 22>; 31 | brcm,function = <1 0>; /* out in */ 32 | brcm,pull = <0 0>; /* - - */ 33 | }; 34 | gpclk0_pins: gpclk0_pins { 35 | brcm,pins = <4>; 36 | brcm,function = <4>; /* alt func 0 */ 37 | brcm,pull = <0>; /* - */ 38 | }; 39 | }; 40 | }; 41 | 42 | fragment@3 { 43 | target = <&i2c1>; 44 | __overlay__ { 45 | #address-cells = <1>; 46 | #size-cells = <0>; 47 | status = "okay"; 48 | 49 | ac101: ac101@1a{ 50 | compatible = "x-power,ac101"; 51 | pinctrl-names = "default"; 52 | pinctrl-0 = <&spk_amp_pins &gpclk0_pins>; 53 | spk-amp-switch-gpios = <&gpio 17 0>; 54 | switch-irq-gpios = <&gpio 22 0>; 55 | reg = <0x1a>; 56 | #sound-dai-cells = <0>; 57 | }; 58 | 59 | ac108_a: ac108@35{ 60 | compatible = "x-power,ac108_0"; 61 | reg = <0x35>; 62 | #sound-dai-cells = <0>; 63 | data-protocol = <0>; 64 | tdm-chips-count = <2>; 65 | }; 66 | 67 | ac108_b: ac108@3b{ 68 | compatible = "x-power,ac108_1"; 69 | reg = <0x3b>; 70 | #sound-dai-cells = <0>; 71 | data-protocol = <0>; 72 | tdm-chips-count = <2>; 73 | }; 74 | }; 75 | }; 76 | 77 | fragment@4 { 78 | target = <&sound>; 79 | 80 | sound_overlay: __overlay__ { 81 | compatible = "seeed-voicecard"; 82 | seeed-voice-card,name = "seeed-8mic-voicecard"; 83 | seeed-voice-card,channels-playback-override = <8>; 84 | seeed-voice-card,channels-capture-override = <8>; 85 | #address-cells = <1>; 86 | #size-cells = <0>; 87 | status = "okay"; 88 | 89 | seeed-voice-card,dai-link@0 { 90 | format = "dsp_a"; 91 | bitclock-master = <&codec0_dai>; 92 | frame-master = <&codec0_dai>; 93 | /* bitclock-inversion; */ 94 | /* frame-inversion; */ 95 | reg = <0>; 96 | 97 | cpu { 98 | sound-dai = <&i2s>; 99 | dai-tdm-slot-num = <2>; 100 | dai-tdm-slot-width = <32>; 101 | dai-tdm-slot-tx-mask = <1 1 0 0>; 102 | dai-tdm-slot-rx-mask = <1 1 0 0>; 103 | }; 104 | 105 | codec0_dai: codec { 106 | sound-dai = <&ac108_a>; 107 | clocks = <&ac10x_mclk>; 108 | system-clock-id = <1>; 109 | }; 110 | }; 111 | }; 112 | }; 113 | 114 | __overrides__ { 115 | card-name = <&sound_overlay>,"seeed-voice-card,name"; 116 | }; 117 | }; 118 | 119 | -------------------------------------------------------------------------------- /seeed-8mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HinTak/seeed-voicecard/1a07cfa0e17d27b29a9af0c40a3bfb0d364d8c48/seeed-8mic-voicecard.dtbo -------------------------------------------------------------------------------- /seeed-voicecard: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2018 Baozhu Zuo 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | set -x 24 | #exec 1>/var/log/$(basename $0).log 2>&1 25 | 26 | export PATH=$PATH:/opt/vc/bin 27 | OVERLAYS=/boot/overlays 28 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 29 | 30 | #enable i2c interface 31 | dtparam -d $OVERLAYS i2c_arm=on 32 | modprobe i2c-dev 33 | 34 | #enable spi interface 35 | dtparam -d $OVERLAYS spi=on 36 | 37 | _VER_RUN= 38 | function get_kernel_version() { 39 | local ZIMAGE IMG_OFFSET 40 | 41 | _VER_RUN="" 42 | [ -z "$_VER_RUN" ] && { 43 | ZIMAGE=/boot/kernel.img 44 | IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1) 45 | # 64-bit-only kernel package 46 | [ ! -f /boot/kernel.img ] && [ -f /boot/kernel8.img ] && ZIMAGE=/boot/kernel8.img 47 | _VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) 2>/dev/null | zcat | grep -a -m1 "Linux version" | LC_ALL=C sed -e 's/^.*Linux/Linux/' | strings | awk '{ print $3; }') 48 | } 49 | echo "$_VER_RUN" 50 | return 0 51 | } 52 | 53 | CONFIG=/boot/config.txt 54 | [ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt 55 | 56 | get_overlay() { 57 | ov=$1 58 | if grep -q -E "^dtoverlay=$ov" $CONFIG; then 59 | echo 0 60 | else 61 | echo 1 62 | fi 63 | } 64 | 65 | do_overlay() { 66 | ov=$1 67 | RET=$2 68 | DEFAULT=--defaultno 69 | CURRENT=0 70 | if [ $(get_overlay $ov) -eq 0 ]; then 71 | DEFAULT= 72 | CURRENT=1 73 | fi 74 | if [ $RET -eq $CURRENT ]; then 75 | ASK_TO_REBOOT=1 76 | fi 77 | if [ $RET -eq 0 ]; then 78 | sed $CONFIG -i -e "s/^#dtoverlay=$ov/dtoverlay=$ov/" 79 | if ! grep -q -E "^dtoverlay=$ov" $CONFIG; then 80 | printf "dtoverlay=$ov\n" >> $CONFIG 81 | fi 82 | STATUS=enabled 83 | elif [ $RET -eq 1 ]; then 84 | sed $CONFIG -i -e "s/^dtoverlay=$ov/#dtoverlay=$ov/" 85 | STATUS=disabled 86 | else 87 | return $RET 88 | fi 89 | } 90 | 91 | 92 | is_1a=$(i2cdetect -y 1 0x1a 0x1a | egrep "(1a|UU)" | awk '{print $2}') 93 | is_35=$(i2cdetect -y 1 0x35 0x35 | egrep "(35|UU)" | awk '{print $2}') 94 | is_3b=$(i2cdetect -y 1 0x3b 0x3b | egrep "(3b|UU)" | awk '{print $2}') 95 | 96 | RPI_HATS="seeed-2mic-voicecard seeed-4mic-voicecard seeed-8mic-voicecard" 97 | overlay="" 98 | 99 | if [ "x${is_1a}" != "x" ] && [ "x${is_35}" == "x" ] ; then 100 | echo "install 2mic" 101 | overlay=seeed-2mic-voicecard 102 | asound_conf=/etc/voicecard/asound_2mic.conf 103 | asound_state=/etc/voicecard/wm8960_asound.state 104 | fi 105 | 106 | if [ "x${is_3b}" != "x" ] && [ "x${is_35}" == "x" ] ; then 107 | echo "install 4mic" 108 | overlay=seeed-4mic-voicecard 109 | asound_conf=/etc/voicecard/asound_4mic.conf 110 | asound_state=/etc/voicecard/ac108_asound.state 111 | fi 112 | 113 | if [ "x${is_3b}" != "x" ] && [ "x${is_35}" != "x" ] ; then 114 | echo "install 6mic" 115 | overlay=seeed-8mic-voicecard 116 | asound_conf=/etc/voicecard/asound_6mic.conf 117 | asound_state=/etc/voicecard/ac108_6mic.state 118 | fi 119 | 120 | if [ "$overlay" ]; then 121 | echo Install $overlay ... 122 | 123 | # Remove old configuration 124 | rm /etc/asound.conf 125 | rm /var/lib/alsa/asound.state 126 | 127 | kernel_ver=$(uname -r) # get_kernel_version) 128 | # echo kernel_ver=$kernel_ver 129 | 130 | # TODO: dynamic dtoverlay Bug of v4.19.x 131 | # no DT node phandle inserted. 132 | if [[ "$kernel_ver" =~ ^4\.19.*$ || "$kernel_ver" =~ ^5\.*$ ]]; then 133 | for i in $RPI_HATS; do 134 | if [ "$i" == "$overlay" ]; then 135 | /bin/true #do_overlay $overlay 0 136 | else 137 | echo Uninstall $i ... 138 | /bin/true #do_overlay $i 1 139 | fi 140 | done 141 | fi 142 | #make sure the driver loads correctly 143 | dtoverlay -d $OVERLAYS $overlay || true 144 | 145 | 146 | echo "create $overlay asound configure file" 147 | ln -s $asound_conf /etc/asound.conf 148 | echo "create $overlay asound status file" 149 | ln -s $asound_state /var/lib/alsa/asound.state 150 | fi 151 | 152 | alsactl restore 153 | 154 | #Force 3.5mm ('headphone') jack 155 | # The Raspberry Pi 4, released on 24th Jun 2019, has two HDMI ports, 156 | # and can drive two displays with audios for two users simultaneously, 157 | # in a "multiseat" configuration. The earlier single virtual ALSA 158 | # option for re-directing audio playback between headphone jack and HDMI 159 | # via a 'Routing' mixer setting was turned off eventually to allow 160 | # simultaneous usage of all 3 playback devices. 161 | if aplay -l | grep -q "bcm2835 ALSA"; then 162 | amixer cset numid=3 1 || true 163 | fi 164 | -------------------------------------------------------------------------------- /seeed-voicecard.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SEEED voice card 3 | * 4 | * (C) Copyright 2017-2018 5 | * Seeed Technology Co., Ltd. 6 | * 7 | * base on ASoC simple sound card support 8 | * 9 | * Copyright (C) 2012 Renesas Solutions Corp. 10 | * Kuninori Morimoto 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License version 2 as 14 | * published by the Free Software Foundation. 15 | */ 16 | /* #undef DEBUG */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "ac10x.h" 30 | 31 | #define LINUX_VERSION_IS_GEQ(x1,x2,x3) (LINUX_VERSION_CODE >= KERNEL_VERSION(x1,x2,x3)) 32 | 33 | 34 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0) 35 | #define simple_util_parse_clk_cpu(dev, node, dai_link, simple_dai) \ 36 | simple_util_parse_clk(dev, node, simple_dai, dai_link->cpus) 37 | #define simple_util_parse_clk_codec(dev, node, dai_link, simple_dai) \ 38 | simple_util_parse_clk(dev, node, simple_dai, dai_link->codecs) 39 | #define simple_util_parse_cpu(node, dai_link, is_single_link) \ 40 | simple_util_parse_dai(node, dai_link->cpus, is_single_link) 41 | #define simple_util_parse_codec(node, dai_link) \ 42 | simple_util_parse_dai(node, dai_link->codecs, NULL) 43 | #define simple_util_parse_platform(node, dai_link) \ 44 | simple_util_parse_dai(node, dai_link->platforms, NULL) 45 | #endif 46 | 47 | /* 48 | * single codec: 49 | * 0 - allow multi codec 50 | * 1 - yes 51 | */ 52 | #define _SINGLE_CODEC 1 53 | 54 | struct seeed_card_data { 55 | struct snd_soc_card snd_card; 56 | struct seeed_dai_props { 57 | struct simple_util_dai cpu_dai; 58 | struct simple_util_dai codec_dai; 59 | struct snd_soc_dai_link_component cpus; /* single cpu */ 60 | struct snd_soc_dai_link_component codecs; /* single codec */ 61 | struct snd_soc_dai_link_component platforms; 62 | unsigned int mclk_fs; 63 | } *dai_props; 64 | unsigned int mclk_fs; 65 | unsigned channels_playback_default; 66 | unsigned channels_playback_override; 67 | unsigned channels_capture_default; 68 | unsigned channels_capture_override; 69 | struct snd_soc_dai_link *dai_link; 70 | #if CONFIG_AC10X_TRIG_LOCK 71 | spinlock_t lock; 72 | #endif 73 | struct work_struct work_codec_clk; 74 | #define TRY_STOP_MAX 3 75 | int try_stop; 76 | }; 77 | 78 | struct seeed_card_info { 79 | const char *name; 80 | const char *card; 81 | const char *codec; 82 | const char *platform; 83 | 84 | unsigned int daifmt; 85 | struct simple_util_dai cpu_dai; 86 | struct simple_util_dai codec_dai; 87 | }; 88 | 89 | #define seeed_priv_to_card(priv) (&(priv)->snd_card) 90 | #define seeed_priv_to_dev(priv) ((priv)->snd_card.dev) 91 | #define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) 92 | #define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i)) 93 | 94 | #define DAI "sound-dai" 95 | #define CELL "#sound-dai-cells" 96 | #define PREFIX "seeed-voice-card," 97 | 98 | static int seeed_voice_card_startup(struct snd_pcm_substream *substream) 99 | { 100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 101 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 102 | struct seeed_dai_props *dai_props = 103 | seeed_priv_to_props(priv, rtd->id); 104 | int ret; 105 | 106 | ret = clk_prepare_enable(dai_props->cpu_dai.clk); 107 | if (ret) 108 | return ret; 109 | 110 | ret = clk_prepare_enable(dai_props->codec_dai.clk); 111 | if (ret) 112 | clk_disable_unprepare(dai_props->cpu_dai.clk); 113 | 114 | if (snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) { 115 | priv->channels_playback_default = snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min; 116 | } 117 | if (snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) { 118 | priv->channels_capture_default = snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min; 119 | } 120 | snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override; 121 | snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override; 122 | snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override; 123 | snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override; 124 | 125 | return ret; 126 | } 127 | 128 | static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) 129 | { 130 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 131 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 132 | struct seeed_dai_props *dai_props = 133 | seeed_priv_to_props(priv, rtd->id); 134 | 135 | snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default; 136 | snd_soc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default; 137 | snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default; 138 | snd_soc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default; 139 | 140 | clk_disable_unprepare(dai_props->cpu_dai.clk); 141 | 142 | clk_disable_unprepare(dai_props->codec_dai.clk); 143 | } 144 | 145 | static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, 146 | struct snd_pcm_hw_params *params) 147 | { 148 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 149 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 150 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 151 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 152 | struct seeed_dai_props *dai_props = 153 | seeed_priv_to_props(priv, rtd->id); 154 | unsigned int mclk, mclk_fs = 0; 155 | int ret = 0; 156 | 157 | if (priv->mclk_fs) 158 | mclk_fs = priv->mclk_fs; 159 | else if (dai_props->mclk_fs) 160 | mclk_fs = dai_props->mclk_fs; 161 | 162 | if (mclk_fs) { 163 | mclk = params_rate(params) * mclk_fs; 164 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 165 | SND_SOC_CLOCK_IN); 166 | if (ret && ret != -ENOTSUPP) 167 | goto err; 168 | 169 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, 170 | SND_SOC_CLOCK_OUT); 171 | if (ret && ret != -ENOTSUPP) 172 | goto err; 173 | } 174 | return 0; 175 | err: 176 | return ret; 177 | } 178 | 179 | #define _SET_CLOCK_CNT 2 180 | static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop, struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai); 181 | 182 | int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int, struct snd_pcm_substream *, int, struct snd_soc_dai *)) { 183 | if (! _set_clock[stream]) { 184 | _set_clock[stream] = set_clock; 185 | } 186 | return 0; 187 | } 188 | EXPORT_SYMBOL(seeed_voice_card_register_set_clock); 189 | 190 | /* 191 | * work_cb_codec_clk: clear audio codec inner clock. 192 | */ 193 | static void work_cb_codec_clk(struct work_struct *work) 194 | { 195 | struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk); 196 | int r = 0; 197 | 198 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) { 199 | r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */ 200 | } 201 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) { 202 | r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */ 203 | } 204 | 205 | if (r && priv->try_stop++ < TRY_STOP_MAX) { 206 | if (0 != schedule_work(&priv->work_codec_clk)) {} 207 | } 208 | return; 209 | } 210 | 211 | static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) 212 | { 213 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 214 | struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); 215 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 216 | #if CONFIG_AC10X_TRIG_LOCK 217 | unsigned long flags; 218 | #endif 219 | int ret = 0; 220 | 221 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", 222 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 223 | dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, dai->stream[SNDRV_PCM_STREAM_CAPTURE].active); 224 | 225 | switch (cmd) { 226 | case SNDRV_PCM_TRIGGER_START: 227 | case SNDRV_PCM_TRIGGER_RESUME: 228 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 229 | if (cancel_work_sync(&priv->work_codec_clk) != 0) {} 230 | #if CONFIG_AC10X_TRIG_LOCK 231 | /* I know it will degrades performance, but I have no choice */ 232 | spin_lock_irqsave(&priv->lock, flags); 233 | #endif 234 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1, substream, cmd, dai); 235 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1, substream, cmd, dai); 236 | #if CONFIG_AC10X_TRIG_LOCK 237 | spin_unlock_irqrestore(&priv->lock, flags); 238 | #endif 239 | break; 240 | 241 | case SNDRV_PCM_TRIGGER_STOP: 242 | case SNDRV_PCM_TRIGGER_SUSPEND: 243 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 244 | /* capture channel resync, if overrun */ 245 | if (dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 246 | break; 247 | } 248 | 249 | /* interrupt environment */ 250 | if (in_irq() || in_nmi() || in_serving_softirq()) { 251 | priv->try_stop = 0; 252 | if (0 != schedule_work(&priv->work_codec_clk)) { 253 | } 254 | } else { 255 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */ 256 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */ 257 | } 258 | break; 259 | default: 260 | ret = -EINVAL; 261 | } 262 | 263 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n", 264 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 265 | dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, dai->stream[SNDRV_PCM_STREAM_CAPTURE].active, ret); 266 | 267 | return ret; 268 | } 269 | 270 | static struct snd_soc_ops seeed_voice_card_ops = { 271 | .startup = seeed_voice_card_startup, 272 | .shutdown = seeed_voice_card_shutdown, 273 | .hw_params = seeed_voice_card_hw_params, 274 | .trigger = seeed_voice_card_trigger, 275 | }; 276 | 277 | static int simple_util_parse_dai(struct device_node *node, 278 | struct snd_soc_dai_link_component *dlc, 279 | int *is_single_link) 280 | { 281 | struct of_phandle_args args; 282 | int ret; 283 | 284 | if (!node) 285 | return 0; 286 | 287 | /* 288 | * Get node via "sound-dai = <&phandle port>" 289 | * it will be used as xxx_of_node on soc_bind_dai_link() 290 | */ 291 | ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); 292 | if (ret) 293 | return ret; 294 | 295 | /* 296 | * FIXME 297 | * 298 | * Here, dlc->dai_name is pointer to CPU/Codec DAI name. 299 | * If user unbinded CPU or Codec driver, but not for Sound Card, 300 | * dlc->dai_name is keeping unbinded CPU or Codec 301 | * driver's pointer. 302 | * 303 | * If user re-bind CPU or Codec driver again, ALSA SoC will try 304 | * to rebind Card via snd_soc_try_rebind_card(), but because of 305 | * above reason, it might can't bind Sound Card. 306 | * Because Sound Card is pointing to released dai_name pointer. 307 | * 308 | * To avoid this rebind Card issue, 309 | * 1) It needs to alloc memory to keep dai_name eventhough 310 | * CPU or Codec driver was unbinded, or 311 | * 2) user need to rebind Sound Card everytime 312 | * if he unbinded CPU or Codec. 313 | */ 314 | ret = snd_soc_of_get_dai_name(node, &dlc->dai_name, 0); 315 | if (ret < 0) 316 | return ret; 317 | 318 | dlc->of_node = args.np; 319 | 320 | if (is_single_link) 321 | *is_single_link = !args.args_count; 322 | 323 | return 0; 324 | } 325 | 326 | static int simple_util_init_dai(struct snd_soc_dai *dai, 327 | struct simple_util_dai *simple_dai) 328 | { 329 | int ret; 330 | 331 | if (!simple_dai) 332 | return 0; 333 | 334 | if (simple_dai->sysclk) { 335 | ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 336 | simple_dai->clk_direction); 337 | if (ret && ret != -ENOTSUPP) { 338 | dev_err(dai->dev, "simple-card: set_sysclk error\n"); 339 | return ret; 340 | } 341 | } 342 | 343 | if (simple_dai->slots) { 344 | ret = snd_soc_dai_set_bclk_ratio(dai, 345 | simple_dai->slots * 346 | simple_dai->slot_width); 347 | if (ret && ret != -ENOTSUPP) { 348 | dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); 349 | return ret; 350 | } 351 | } 352 | 353 | return 0; 354 | } 355 | 356 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 357 | static inline int simple_util_component_is_codec(struct snd_soc_component *component) 358 | { 359 | return component->driver->endianness; 360 | } 361 | 362 | static int simple_util_init_dai_link_params(struct snd_soc_pcm_runtime *rtd) 363 | { 364 | struct snd_soc_dai_link *dai_link = rtd->dai_link; 365 | struct snd_soc_component *component; 366 | struct snd_soc_pcm_stream *params; 367 | struct snd_pcm_hardware hw; 368 | int i, ret, stream; 369 | 370 | /* Only Codecs */ 371 | for_each_rtd_components(rtd, i, component) { 372 | if (!simple_util_component_is_codec(component)) 373 | return 0; 374 | } 375 | 376 | /* Assumes the capabilities are the same for all supported streams */ 377 | for (stream = 0; stream < 2; stream++) { 378 | ret = snd_soc_runtime_calc_hw(rtd, &hw, stream); 379 | if (ret == 0) 380 | break; 381 | } 382 | 383 | if (ret < 0) { 384 | dev_err(rtd->dev, "simple-card: no valid dai_link params\n"); 385 | return ret; 386 | } 387 | 388 | params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL); 389 | if (!params) 390 | return -ENOMEM; 391 | 392 | params->formats = hw.formats; 393 | params->rates = hw.rates; 394 | params->rate_min = hw.rate_min; 395 | params->rate_max = hw.rate_max; 396 | params->channels_min = hw.channels_min; 397 | params->channels_max = hw.channels_max; 398 | 399 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,4,0) 400 | dai_link->c2c_params = params; 401 | dai_link->num_c2c_params = 1; 402 | #else 403 | /* apparently this goes back to 5.6.x */ 404 | dai_link->params = params; 405 | dai_link->num_params = 1; 406 | #endif 407 | 408 | return 0; 409 | } 410 | #endif 411 | 412 | static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) 413 | { 414 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 415 | struct snd_soc_dai *codec = snd_soc_rtd_to_codec(rtd, 0); 416 | struct snd_soc_dai *cpu = snd_soc_rtd_to_cpu(rtd, 0); 417 | struct seeed_dai_props *dai_props = 418 | seeed_priv_to_props(priv, rtd->id); 419 | int ret; 420 | 421 | ret = simple_util_init_dai(codec, &dai_props->codec_dai); 422 | if (ret < 0) 423 | return ret; 424 | 425 | ret = simple_util_init_dai(cpu, &dai_props->cpu_dai); 426 | if (ret < 0) 427 | return ret; 428 | 429 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 430 | ret = simple_util_init_dai_link_params(rtd); 431 | if (ret < 0) 432 | return ret; 433 | #endif 434 | 435 | dev_dbg(rtd->card->dev, "codec \"%s\" mapping to cpu \"%s\"\n", codec->name, cpu->name); 436 | return 0; 437 | } 438 | 439 | static int seeed_voice_card_dai_link_of(struct device_node *node, 440 | struct seeed_card_data *priv, 441 | int idx, 442 | bool is_top_level_node) 443 | { 444 | struct device *dev = seeed_priv_to_dev(priv); 445 | struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx); 446 | struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx); 447 | struct simple_util_dai *cpu_dai = &dai_props->cpu_dai; 448 | struct simple_util_dai *codec_dai = &dai_props->codec_dai; 449 | struct device_node *cpu = NULL; 450 | struct device_node *plat = NULL; 451 | struct device_node *codec = NULL; 452 | char prop[128]; 453 | char *prefix = ""; 454 | int ret, single_cpu; 455 | 456 | /* For single DAI link & old style of DT node */ 457 | if (is_top_level_node) 458 | prefix = PREFIX; 459 | 460 | snprintf(prop, sizeof(prop), "%scpu", prefix); 461 | cpu = of_get_child_by_name(node, prop); 462 | 463 | if (!cpu) { 464 | ret = -EINVAL; 465 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 466 | goto dai_link_of_err; 467 | } 468 | 469 | snprintf(prop, sizeof(prop), "%splat", prefix); 470 | plat = of_get_child_by_name(node, prop); 471 | 472 | snprintf(prop, sizeof(prop), "%scodec", prefix); 473 | codec = of_get_child_by_name(node, prop); 474 | 475 | if (!codec) { 476 | ret = -EINVAL; 477 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 478 | goto dai_link_of_err; 479 | } 480 | 481 | ret = simple_util_parse_daifmt(dev, node, codec, 482 | prefix, &dai_link->dai_fmt); 483 | if (ret < 0) 484 | goto dai_link_of_err; 485 | 486 | of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); 487 | 488 | ret = simple_util_parse_cpu(cpu, dai_link, &single_cpu); 489 | if (ret < 0) 490 | goto dai_link_of_err; 491 | 492 | #if _SINGLE_CODEC 493 | ret = simple_util_parse_codec(codec, dai_link); 494 | if (ret < 0) 495 | goto dai_link_of_err; 496 | #else 497 | ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); 498 | if (ret < 0) { 499 | dev_err(dev, "parse codec info error %d\n", ret); 500 | goto dai_link_of_err; 501 | } 502 | dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); 503 | #endif 504 | 505 | ret = simple_util_parse_platform(plat, dai_link); 506 | if (ret < 0) 507 | goto dai_link_of_err; 508 | 509 | ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, 510 | &cpu_dai->rx_slot_mask, 511 | &cpu_dai->slots, 512 | &cpu_dai->slot_width); 513 | dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n", 514 | cpu_dai->slots, cpu_dai->slot_width, 515 | cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask 516 | ); 517 | if (ret < 0) 518 | goto dai_link_of_err; 519 | 520 | ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, 521 | &codec_dai->rx_slot_mask, 522 | &codec_dai->slots, 523 | &codec_dai->slot_width); 524 | if (ret < 0) 525 | goto dai_link_of_err; 526 | 527 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 528 | ret = simple_util_card_parse_clk_cpu(cpu, dai_link, cpu_dai); 529 | #else 530 | ret = simple_util_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 531 | #endif 532 | if (ret < 0) 533 | goto dai_link_of_err; 534 | 535 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 536 | ret = simple_util_card_parse_clk_codec(codec, dai_link, codec_dai); 537 | #else 538 | ret = simple_util_parse_clk_codec(dev, codec, dai_link, codec_dai); 539 | #endif 540 | if (ret < 0) 541 | goto dai_link_of_err; 542 | 543 | ret = simple_util_set_dailink_name(dev, dai_link, 544 | "%s-%s", 545 | dai_link->cpus->dai_name, 546 | #if _SINGLE_CODEC 547 | dai_link->codecs->dai_name 548 | #else 549 | dai_link->codecs[0].dai_name 550 | #endif 551 | ); 552 | if (ret < 0) 553 | goto dai_link_of_err; 554 | 555 | dai_link->ops = &seeed_voice_card_ops; 556 | dai_link->init = seeed_voice_card_dai_init; 557 | 558 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); 559 | dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); 560 | dev_dbg(dev, "\tcpu : %s / %d\n", 561 | dai_link->cpus->dai_name, 562 | dai_props->cpu_dai.sysclk); 563 | dev_dbg(dev, "\tcodec : %s / %d\n", 564 | #if _SINGLE_CODEC 565 | dai_link->codecs->dai_name, 566 | #else 567 | dai_link->codecs[0].dai_name, 568 | #endif 569 | dai_props->codec_dai.sysclk); 570 | 571 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0) 572 | simple_util_canonicalize_cpu(dai_link->cpus, single_cpu); 573 | #if _SINGLE_CODEC 574 | simple_util_canonicalize_platform(dai_link->platforms, dai_link->cpus); 575 | #endif 576 | #else 577 | simple_util_canonicalize_cpu(dai_link, single_cpu); 578 | #if _SINGLE_CODEC 579 | simple_util_canonicalize_platform(dai_link); 580 | #endif 581 | #endif 582 | 583 | dai_link_of_err: 584 | of_node_put(cpu); 585 | of_node_put(codec); 586 | 587 | return ret; 588 | } 589 | 590 | static int seeed_voice_card_parse_aux_devs(struct device_node *node, 591 | struct seeed_card_data *priv) 592 | { 593 | struct device *dev = seeed_priv_to_dev(priv); 594 | struct device_node *aux_node; 595 | int i, n, len; 596 | 597 | if (!of_find_property(node, PREFIX "aux-devs", &len)) 598 | return 0; /* Ok to have no aux-devs */ 599 | 600 | n = len / sizeof(__be32); 601 | if (n <= 0) 602 | return -EINVAL; 603 | 604 | priv->snd_card.aux_dev = devm_kzalloc(dev, 605 | n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); 606 | if (!priv->snd_card.aux_dev) 607 | return -ENOMEM; 608 | 609 | for (i = 0; i < n; i++) { 610 | aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); 611 | if (!aux_node) 612 | return -EINVAL; 613 | priv->snd_card.aux_dev[i].dlc.of_node = aux_node; 614 | } 615 | 616 | priv->snd_card.num_aux_devs = n; 617 | return 0; 618 | } 619 | 620 | static int seeed_voice_card_parse_of(struct device_node *node, 621 | struct seeed_card_data *priv) 622 | { 623 | struct device *dev = seeed_priv_to_dev(priv); 624 | struct device_node *dai_link; 625 | int ret; 626 | 627 | if (!node) 628 | return -EINVAL; 629 | 630 | dai_link = of_get_child_by_name(node, PREFIX "dai-link"); 631 | 632 | /* The off-codec widgets */ 633 | if (of_property_read_bool(node, PREFIX "widgets")) { 634 | ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, 635 | PREFIX "widgets"); 636 | if (ret) 637 | goto card_parse_end; 638 | } 639 | 640 | /* DAPM routes */ 641 | if (of_property_read_bool(node, PREFIX "routing")) { 642 | ret = snd_soc_of_parse_audio_routing(&priv->snd_card, 643 | PREFIX "routing"); 644 | if (ret) 645 | goto card_parse_end; 646 | } 647 | 648 | /* Factor to mclk, used in hw_params() */ 649 | of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); 650 | 651 | /* Single/Muti DAI link(s) & New style of DT node */ 652 | if (dai_link) { 653 | struct device_node *np = NULL; 654 | int i = 0; 655 | 656 | for_each_child_of_node(node, np) { 657 | dev_dbg(dev, "\tlink %d:\n", i); 658 | ret = seeed_voice_card_dai_link_of(np, priv, 659 | i, false); 660 | if (ret < 0) { 661 | of_node_put(np); 662 | goto card_parse_end; 663 | } 664 | i++; 665 | } 666 | } else { 667 | /* For single DAI link & old style of DT node */ 668 | ret = seeed_voice_card_dai_link_of(node, priv, 0, true); 669 | if (ret < 0) 670 | goto card_parse_end; 671 | } 672 | 673 | ret = simple_util_parse_card_name(&priv->snd_card, PREFIX); 674 | if (ret < 0) 675 | goto card_parse_end; 676 | 677 | ret = seeed_voice_card_parse_aux_devs(node, priv); 678 | 679 | priv->channels_playback_default = 0; 680 | priv->channels_playback_override = 2; 681 | priv->channels_capture_default = 0; 682 | priv->channels_capture_override = 2; 683 | of_property_read_u32(node, PREFIX "channels-playback-default", 684 | &priv->channels_playback_default); 685 | of_property_read_u32(node, PREFIX "channels-playback-override", 686 | &priv->channels_playback_override); 687 | of_property_read_u32(node, PREFIX "channels-capture-default", 688 | &priv->channels_capture_default); 689 | of_property_read_u32(node, PREFIX "channels-capture-override", 690 | &priv->channels_capture_override); 691 | 692 | card_parse_end: 693 | of_node_put(dai_link); 694 | 695 | return ret; 696 | } 697 | 698 | #ifdef DEBUG 699 | inline void seeed_debug_dai(struct seeed_card_data *priv, 700 | char *name, 701 | struct simple_util_dai *dai) 702 | { 703 | struct device *dev = seeed_priv_to_dev(priv); 704 | 705 | if (dai->name) 706 | dev_dbg(dev, "%s dai name = %s\n", 707 | name, dai->name); 708 | if (dai->sysclk) 709 | dev_dbg(dev, "%s sysclk = %d\n", 710 | name, dai->sysclk); 711 | 712 | dev_dbg(dev, "%s direction = %s\n", 713 | name, dai->clk_direction ? "OUT" : "IN"); 714 | 715 | if (dai->slots) 716 | dev_dbg(dev, "%s slots = %d\n", name, dai->slots); 717 | if (dai->slot_width) 718 | dev_dbg(dev, "%s slot width = %d\n", name, dai->slot_width); 719 | if (dai->tx_slot_mask) 720 | dev_dbg(dev, "%s tx slot mask = %d\n", name, dai->tx_slot_mask); 721 | if (dai->rx_slot_mask) 722 | dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask); 723 | if (dai->clk) 724 | dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk)); 725 | } 726 | 727 | inline void seeed_debug_info(struct seeed_card_data *priv) 728 | { 729 | struct snd_soc_card *card = seeed_priv_to_card(priv); 730 | struct device *dev = seeed_priv_to_dev(priv); 731 | 732 | int i; 733 | 734 | if (card->name) 735 | dev_dbg(dev, "Card Name: %s\n", card->name); 736 | 737 | for (i = 0; i < card->num_links; i++) { 738 | struct seeed_dai_props *props = seeed_priv_to_props(priv, i); 739 | struct snd_soc_dai_link *link = seeed_priv_to_link(priv, i); 740 | 741 | dev_dbg(dev, "DAI%d\n", i); 742 | 743 | seeed_debug_dai(priv, "cpu", &props->cpu_dai); 744 | seeed_debug_dai(priv, "codec", &props->codec_dai); 745 | 746 | if (link->name) 747 | dev_dbg(dev, "dai name = %s\n", link->name); 748 | 749 | dev_dbg(dev, "dai format = %04x\n", link->dai_fmt); 750 | 751 | /* 752 | if (props->adata.convert_rate) 753 | dev_dbg(dev, "convert_rate = %d\n", 754 | props->adata.convert_rate); 755 | if (props->adata.convert_channels) 756 | dev_dbg(dev, "convert_channels = %d\n", 757 | props->adata.convert_channels); 758 | if (props->codec_conf && props->codec_conf->name_prefix) 759 | dev_dbg(dev, "name prefix = %s\n", 760 | props->codec_conf->name_prefix); 761 | */ 762 | if (props->mclk_fs) 763 | dev_dbg(dev, "mclk-fs = %d\n", 764 | props->mclk_fs); 765 | } 766 | } 767 | #else 768 | #define seeed_debug_info(priv) 769 | #endif /* DEBUG */ 770 | 771 | static int seeed_voice_card_probe(struct platform_device *pdev) 772 | { 773 | struct seeed_card_data *priv; 774 | struct snd_soc_dai_link *dai_link; 775 | struct seeed_dai_props *dai_props; 776 | struct device_node *np = pdev->dev.of_node; 777 | struct device *dev = &pdev->dev; 778 | int num, ret, i; 779 | 780 | /* Get the number of DAI links */ 781 | if (np && of_get_child_by_name(np, PREFIX "dai-link")) 782 | num = of_get_child_count(np); 783 | else 784 | num = 1; 785 | 786 | /* Allocate the private data and the DAI link array */ 787 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 788 | if (!priv) 789 | return -ENOMEM; 790 | 791 | dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); 792 | dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); 793 | if (!dai_props || !dai_link) 794 | return -ENOMEM; 795 | 796 | /* 797 | * Use snd_soc_dai_link_component instead of legacy style 798 | * It is codec only. but cpu/platform will be supported in the future. 799 | * see 800 | * soc-core.c :: snd_soc_init_multicodec() 801 | * 802 | * "platform" might be removed 803 | * see 804 | * simple-card-utils.c :: simple_util_canonicalize_platform() 805 | */ 806 | for (i = 0; i < num; i++) { 807 | dai_link[i].cpus = &dai_props[i].cpus; 808 | dai_link[i].num_cpus = 1; 809 | dai_link[i].codecs = &dai_props[i].codecs; 810 | dai_link[i].num_codecs = 1; 811 | dai_link[i].platforms = &dai_props[i].platforms; 812 | dai_link[i].num_platforms = 1; 813 | } 814 | 815 | priv->dai_props = dai_props; 816 | priv->dai_link = dai_link; 817 | 818 | /* Init snd_soc_card */ 819 | priv->snd_card.owner = THIS_MODULE; 820 | priv->snd_card.dev = dev; 821 | priv->snd_card.dai_link = priv->dai_link; 822 | priv->snd_card.num_links = num; 823 | 824 | if (np && of_device_is_available(np)) { 825 | ret = seeed_voice_card_parse_of(np, priv); 826 | if (ret < 0) { 827 | if (ret != -EPROBE_DEFER) 828 | dev_err(dev, "parse error %d\n", ret); 829 | goto err; 830 | } 831 | } else { 832 | struct seeed_card_info *cinfo; 833 | struct snd_soc_dai_link_component *cpus; 834 | struct snd_soc_dai_link_component *codecs; 835 | struct snd_soc_dai_link_component *platform; 836 | 837 | cinfo = dev->platform_data; 838 | if (!cinfo) { 839 | dev_err(dev, "no info for seeed-voice-card\n"); 840 | return -EINVAL; 841 | } 842 | 843 | if (!cinfo->name || 844 | !cinfo->codec_dai.name || 845 | !cinfo->codec || 846 | !cinfo->platform || 847 | !cinfo->cpu_dai.name) { 848 | dev_err(dev, "insufficient seeed_voice_card_info settings\n"); 849 | return -EINVAL; 850 | } 851 | 852 | cpus = dai_link->cpus; 853 | cpus->dai_name = cinfo->cpu_dai.name; 854 | 855 | codecs = dai_link->codecs; 856 | codecs->name = cinfo->codec; 857 | codecs->dai_name = cinfo->codec_dai.name; 858 | 859 | platform = dai_link->platforms; 860 | platform->name = cinfo->platform; 861 | 862 | priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; 863 | dai_link->name = cinfo->name; 864 | dai_link->stream_name = cinfo->name; 865 | dai_link->dai_fmt = cinfo->daifmt; 866 | dai_link->init = seeed_voice_card_dai_init; 867 | memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, 868 | sizeof(priv->dai_props->cpu_dai)); 869 | memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, 870 | sizeof(priv->dai_props->codec_dai)); 871 | } 872 | 873 | snd_soc_card_set_drvdata(&priv->snd_card, priv); 874 | 875 | #if CONFIG_AC10X_TRIG_LOCK 876 | spin_lock_init(&priv->lock); 877 | #endif 878 | 879 | INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk); 880 | 881 | seeed_debug_info(priv); 882 | 883 | ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); 884 | if (ret >= 0) 885 | return ret; 886 | 887 | err: 888 | simple_util_clean_reference(&priv->snd_card); 889 | 890 | return ret; 891 | } 892 | 893 | static void seeed_voice_card_remove(struct platform_device *pdev) 894 | { 895 | struct snd_soc_card *card = platform_get_drvdata(pdev); 896 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(card); 897 | 898 | if (cancel_work_sync(&priv->work_codec_clk) != 0) { 899 | } 900 | simple_util_clean_reference(card); 901 | 902 | return; 903 | } 904 | 905 | static const struct of_device_id seeed_voice_of_match[] = { 906 | { .compatible = "seeed-voicecard", }, 907 | {}, 908 | }; 909 | MODULE_DEVICE_TABLE(of, seeed_voice_of_match); 910 | 911 | static struct platform_driver seeed_voice_card = { 912 | .driver = { 913 | .name = "seeed-voicecard", 914 | .pm = &snd_soc_pm_ops, 915 | .of_match_table = seeed_voice_of_match, 916 | }, 917 | .probe = seeed_voice_card_probe, 918 | .remove = seeed_voice_card_remove, 919 | }; 920 | 921 | module_platform_driver(seeed_voice_card); 922 | 923 | MODULE_ALIAS("platform:seeed-voice-card"); 924 | MODULE_LICENSE("GPL v2"); 925 | MODULE_DESCRIPTION("ASoC SEEED Voice Card"); 926 | MODULE_AUTHOR("PeterYang"); 927 | -------------------------------------------------------------------------------- /seeed-voicecard.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Seeed Voicecard service 3 | After=alsa-restore.service 4 | 5 | [Service] 6 | Type=oneshot 7 | RemainAfterExit=yes 8 | ExecStart=/usr/bin/seeed-voicecard 9 | User=root 10 | 11 | [Install] 12 | WantedBy=sysinit.target 13 | -------------------------------------------------------------------------------- /sound-compatible-4.18.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017-2018 3 | * Seeed Technology Co., Ltd. 4 | * 5 | * PeterYang 6 | */ 7 | #ifndef __SOUND_COMPATIBLE_4_18_H__ 8 | #define __SOUND_COMPATIBLE_4_18_H__ 9 | 10 | #include 11 | 12 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0) 13 | #define __NO_SND_SOC_CODEC_DRV 1 14 | #else 15 | #define __NO_SND_SOC_CODEC_DRV 0 16 | #endif 17 | 18 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) 19 | #if __has_attribute(__fallthrough__) 20 | # define fallthrough __attribute__((__fallthrough__)) 21 | #else 22 | # define fallthrough do {} while (0) /* fallthrough */ 23 | #endif 24 | #endif 25 | 26 | #if __NO_SND_SOC_CODEC_DRV 27 | #define codec component 28 | #define snd_soc_codec snd_soc_component 29 | #define snd_soc_codec_driver snd_soc_component_driver 30 | #define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata 31 | #define snd_soc_codec_get_dapm snd_soc_component_get_dapm 32 | #define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level 33 | #define snd_soc_kcontrol_codec snd_soc_kcontrol_component 34 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) 35 | #define snd_soc_read snd_soc_component_read 36 | #else 37 | #define snd_soc_read snd_soc_component_read32 38 | #endif 39 | #define snd_soc_register_codec devm_snd_soc_register_component 40 | #define snd_soc_unregister_codec snd_soc_unregister_component 41 | #define snd_soc_update_bits snd_soc_component_update_bits 42 | #define snd_soc_write snd_soc_component_write 43 | #define snd_soc_add_codec_controls snd_soc_add_component_controls 44 | #endif 45 | 46 | #endif//__SOUND_COMPATIBLE_4_18_H__ 47 | 48 | -------------------------------------------------------------------------------- /tools/coherence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Estimate the magnitude squared coherence estimate, 3 | 4 | - requirements 5 | sudo apt install python-numpy python-scipy python-matplotlib 6 | """ 7 | 8 | 9 | import sys 10 | import wave 11 | import numpy as np 12 | from scipy import signal 13 | import matplotlib.pyplot as plt 14 | 15 | if len(sys.argv) < 2: 16 | print('Usage: python {} audio.wav'.format(sys.argv[0])) 17 | sys.exit(1) 18 | 19 | wav = wave.open(sys.argv[1], 'rb') 20 | channels = wav.getnchannels() 21 | frames = wav.readframes(wav.getnframes()) 22 | fs = wav.getframerate() 23 | wav.close() 24 | 25 | print("channels: %d" % channels) 26 | print("rate : %d" % fs) 27 | print("frames : %d" % wav.getnframes()) 28 | 29 | array = np.fromstring(frames, dtype='int16') 30 | 31 | ch0 = array[0::channels] 32 | 33 | fig, ax = plt.subplots() 34 | 35 | for ch in range(1, channels): 36 | f, c = signal.coherence(ch0, array[ch::channels], fs, nperseg=1024) 37 | ax.semilogy(f, c, label="CO 1-%d" % (ch + 1)) 38 | 39 | legend = ax.legend(loc='lower right', shadow=True, fontsize='small') 40 | 41 | plt.xlabel('frequency [Hz]') 42 | plt.ylabel('Coherence') 43 | plt.show() 44 | 45 | -------------------------------------------------------------------------------- /tools/phase_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import wave 3 | import numpy as np 4 | 5 | 6 | if len(sys.argv) != 2: 7 | print('Usage: {} multi.wav'.format(sys.argv[0])) 8 | sys.exit(1) 9 | 10 | 11 | multi = wave.open(sys.argv[1], 'rb') 12 | rate = multi.getframerate() 13 | channels = multi.getnchannels() 14 | 15 | if channels <= 1: 16 | sys.exit(1) 17 | 18 | N = rate 19 | 20 | window = np.hanning(N) 21 | 22 | interp = 4*8 23 | max_offset = int(rate * 0.1 / 340 * interp) 24 | 25 | def gcc_phat(sig, refsig, fs=1, max_tau=None, interp=16): 26 | ''' 27 | This function computes the offset between the signal sig and the reference signal refsig 28 | using the Generalized Cross Correlation - Phase Transform (GCC-PHAT)method. 29 | ''' 30 | 31 | # make sure the length for the FFT is larger or equal than len(sig) + len(refsig) 32 | n = sig.shape[0] + refsig.shape[0] 33 | 34 | # Generalized Cross Correlation Phase Transform 35 | SIG = np.fft.rfft(sig, n=n) 36 | REFSIG = np.fft.rfft(refsig, n=n) 37 | R = SIG * np.conj(REFSIG) 38 | #R /= np.abs(R) 39 | 40 | cc = np.fft.irfft(R, n=(interp * n)) 41 | 42 | max_shift = int(interp * n / 2) 43 | if max_tau: 44 | max_shift = np.minimum(int(interp * fs * max_tau), max_shift) 45 | 46 | cc = np.concatenate((cc[-max_shift:], cc[:max_shift+1])) 47 | 48 | # find max cross correlation index 49 | shift = np.argmax(np.abs(cc)) - max_shift 50 | 51 | tau = shift / float(interp * fs) 52 | 53 | return tau, cc 54 | 55 | 56 | print(multi.getsampwidth()) 57 | 58 | while True: 59 | data = multi.readframes(N) 60 | 61 | if len(data) != multi.getsampwidth() * N * channels: 62 | print("done") 63 | break 64 | 65 | if multi.getsampwidth() == 2: 66 | data = np.fromstring(data, dtype='int16') 67 | else: 68 | data = np.fromstring(data, dtype='int32') 69 | ref_buf = data[0::channels] 70 | 71 | offsets = [] 72 | for ch in range(1, channels): 73 | sig_buf = data[ch::channels] 74 | tau, _ = gcc_phat(sig_buf * window, ref_buf * window, fs=1, max_tau=max_offset, interp=interp) 75 | # tau, _ = gcc_phat(sig_buf, ref_buf, fs=rate, max_tau=1) 76 | 77 | offsets.append(tau) 78 | 79 | print(offsets) 80 | 81 | print(multi.getframerate()) 82 | 83 | multi.close() 84 | -------------------------------------------------------------------------------- /ubuntu-prerequisite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Hin-Tak Leung 2020 4 | # 5 | # Overview: 6 | # This script compiles and install the Broadcom VideoCore tools, 7 | # configure the dynamic loader for the non-standard library location, 8 | # and update the loader cache. 9 | # 10 | # A few steps explicitly requires root privilege, which are 11 | # marked with "sudo". The rest is just checking for duplicate/previous 12 | # action. 13 | # 14 | # This derived from my command history on ubuntu 20.04.1 .YMMV 15 | 16 | sudo apt install -y git gcc g++ make alsa-utils cmake 17 | 18 | git clone git://github.com/raspberrypi/userland.git 19 | pushd userland/ 20 | 21 | arch=$(uname -m) 22 | if [[ "$arch" =~ aarch64 ]]; then 23 | ./buildme --aarch64 24 | else 25 | ./buildme 26 | fi 27 | # ./buildme already includes "sudo make install" at the end 28 | 29 | popd 30 | 31 | # matches Raspbian's location: 32 | if [ ! -f /etc/ld.so.conf.d/00-vmcs.conf ] ; then 33 | echo "/opt/vc/lib" | sudo tee -a /etc/ld.so.conf.d/00-vmcs.conf 34 | sudo ldconfig -v 35 | else 36 | echo "/etc/ld.so.conf.d/00-vmcs.conf exists - no need to update ld.cache!" 37 | fi 38 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $EUID -ne 0 ]]; then 4 | echo "This script must be run as root (use sudo)" 1>&2 5 | exit 1 6 | fi 7 | 8 | is_Raspberry=$(cat /proc/device-tree/model | awk '{print $1}') 9 | if [ "x${is_Raspberry}" != "xRaspberry" ] ; then 10 | echo "Sorry, this drivers only works on raspberry pi" 11 | exit 1 12 | fi 13 | 14 | uname_r=$(uname -r) 15 | 16 | CONFIG=/boot/config.txt 17 | [ -f /boot/firmware/config.txt ] && CONFIG=/boot/firmware/config.txt 18 | [ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt 19 | 20 | get_overlay() { 21 | ov=$1 22 | if grep -q -E "^dtoverlay=$ov" $CONFIG; then 23 | echo 0 24 | else 25 | echo 1 26 | fi 27 | } 28 | 29 | do_overlay() { 30 | ov=$1 31 | RET=$2 32 | DEFAULT=--defaultno 33 | CURRENT=0 34 | if [ $(get_overlay $ov) -eq 0 ]; then 35 | DEFAULT= 36 | CURRENT=1 37 | fi 38 | if [ $RET -eq $CURRENT ]; then 39 | ASK_TO_REBOOT=1 40 | fi 41 | if [ $RET -eq 0 ]; then 42 | sed $CONFIG -i -e "s/^#dtoverlay=$ov/dtoverlay=$ov/" 43 | if ! grep -q -E "^dtoverlay=$ov" $CONFIG; then 44 | printf "dtoverlay=$ov\n" >> $CONFIG 45 | fi 46 | STATUS=enabled 47 | elif [ $RET -eq 1 ]; then 48 | sed $CONFIG -i -e "s/^dtoverlay=$ov/#dtoverlay=$ov/" 49 | STATUS=disabled 50 | else 51 | return $RET 52 | fi 53 | } 54 | 55 | RPI_HATS="seeed-2mic-voicecard seeed-4mic-voicecard seeed-8mic-voicecard" 56 | 57 | PATH=$PATH:/opt/vc/bin 58 | echo "remove dtbos" 59 | for i in $RPI_HATS; do 60 | dtoverlay -r $i 61 | done 62 | OVERLAYS=/boot/overlays 63 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 64 | 65 | rm ${OVERLAYS}/seeed-2mic-voicecard.dtbo || true 66 | rm ${OVERLAYS}/seeed-4mic-voicecard.dtbo || true 67 | rm ${OVERLAYS}/seeed-8mic-voicecard.dtbo || true 68 | 69 | echo "remove alsa configs" 70 | rm -rf /etc/voicecard/ || true 71 | 72 | echo "disabled seeed-voicecard.service " 73 | systemctl stop seeed-voicecard.service 74 | systemctl disable seeed-voicecard.service 75 | 76 | echo "remove seeed-voicecard" 77 | rm /usr/bin/seeed-voicecard || true 78 | rm /lib/systemd/system/seeed-voicecard.service || true 79 | 80 | echo "remove dkms" 81 | rm -rf /var/lib/dkms/seeed-voicecard || true 82 | 83 | echo "remove kernel modules" 84 | rm /lib/modules/*/kernel/sound/soc/codecs/snd-soc-wm8960.ko || true 85 | rm /lib/modules/*/kernel/sound/soc/codecs/snd-soc-ac108.ko || true 86 | rm /lib/modules/*/kernel/sound/soc/bcm/snd-soc-seeed-voicecard.ko || true 87 | rm /lib/modules/*/updates/dkms/snd-soc-wm8960.ko || true 88 | rm /lib/modules/*/updates/dkms/snd-soc-ac108.ko || true 89 | rm /lib/modules/*/updates/dkms/snd-soc-seeed-voicecard.ko || true 90 | 91 | echo "remove $CONFIG configuration" 92 | for i in $RPI_HATS; do 93 | echo Uninstall $i ... 94 | do_overlay $i 1 95 | done 96 | 97 | echo "------------------------------------------------------" 98 | echo "Please reboot your raspberry pi to apply all settings" 99 | echo "Thank you!" 100 | echo "------------------------------------------------------" 101 | -------------------------------------------------------------------------------- /wm8960.h: -------------------------------------------------------------------------------- 1 | /* 2 | * wm8960.h -- WM8960 Soc Audio driver 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | #ifndef _WM8960_H 10 | #define _WM8960_H 11 | 12 | /* WM8960 register space */ 13 | 14 | 15 | #define WM8960_CACHEREGNUM 56 16 | 17 | #define WM8960_LINVOL 0x0 18 | #define WM8960_RINVOL 0x1 19 | #define WM8960_LOUT1 0x2 20 | #define WM8960_ROUT1 0x3 21 | #define WM8960_CLOCK1 0x4 22 | #define WM8960_DACCTL1 0x5 23 | #define WM8960_DACCTL2 0x6 24 | #define WM8960_IFACE1 0x7 25 | #define WM8960_CLOCK2 0x8 26 | #define WM8960_IFACE2 0x9 27 | #define WM8960_LDAC 0xa 28 | #define WM8960_RDAC 0xb 29 | 30 | #define WM8960_RESET 0xf 31 | #define WM8960_3D 0x10 32 | #define WM8960_ALC1 0x11 33 | #define WM8960_ALC2 0x12 34 | #define WM8960_ALC3 0x13 35 | #define WM8960_NOISEG 0x14 36 | #define WM8960_LADC 0x15 37 | #define WM8960_RADC 0x16 38 | #define WM8960_ADDCTL1 0x17 39 | #define WM8960_ADDCTL2 0x18 40 | #define WM8960_POWER1 0x19 41 | #define WM8960_POWER2 0x1a 42 | #define WM8960_ADDCTL3 0x1b 43 | #define WM8960_APOP1 0x1c 44 | #define WM8960_APOP2 0x1d 45 | 46 | #define WM8960_LINPATH 0x20 47 | #define WM8960_RINPATH 0x21 48 | #define WM8960_LOUTMIX 0x22 49 | 50 | #define WM8960_ROUTMIX 0x25 51 | #define WM8960_MONOMIX1 0x26 52 | #define WM8960_MONOMIX2 0x27 53 | #define WM8960_LOUT2 0x28 54 | #define WM8960_ROUT2 0x29 55 | #define WM8960_MONO 0x2a 56 | #define WM8960_INBMIX1 0x2b 57 | #define WM8960_INBMIX2 0x2c 58 | #define WM8960_BYPASS1 0x2d 59 | #define WM8960_BYPASS2 0x2e 60 | #define WM8960_POWER3 0x2f 61 | #define WM8960_ADDCTL4 0x30 62 | #define WM8960_CLASSD1 0x31 63 | 64 | #define WM8960_CLASSD3 0x33 65 | #define WM8960_PLL1 0x34 66 | #define WM8960_PLL2 0x35 67 | #define WM8960_PLL3 0x36 68 | #define WM8960_PLL4 0x37 69 | 70 | 71 | /* 72 | * WM8960 Clock dividers 73 | */ 74 | #define WM8960_SYSCLKDIV 0 75 | #define WM8960_DACDIV 1 76 | #define WM8960_OPCLKDIV 2 77 | #define WM8960_DCLKDIV 3 78 | #define WM8960_TOCLKSEL 4 79 | 80 | #define WM8960_SYSCLK_DIV_1 (0 << 1) 81 | #define WM8960_SYSCLK_DIV_2 (2 << 1) 82 | 83 | #define WM8960_SYSCLK_MCLK (0 << 0) 84 | #define WM8960_SYSCLK_PLL (1 << 0) 85 | #define WM8960_SYSCLK_AUTO (2 << 0) 86 | 87 | #define WM8960_DAC_DIV_1 (0 << 3) 88 | #define WM8960_DAC_DIV_1_5 (1 << 3) 89 | #define WM8960_DAC_DIV_2 (2 << 3) 90 | #define WM8960_DAC_DIV_3 (3 << 3) 91 | #define WM8960_DAC_DIV_4 (4 << 3) 92 | #define WM8960_DAC_DIV_5_5 (5 << 3) 93 | #define WM8960_DAC_DIV_6 (6 << 3) 94 | 95 | #define WM8960_DCLK_DIV_1_5 (0 << 6) 96 | #define WM8960_DCLK_DIV_2 (1 << 6) 97 | #define WM8960_DCLK_DIV_3 (2 << 6) 98 | #define WM8960_DCLK_DIV_4 (3 << 6) 99 | #define WM8960_DCLK_DIV_6 (4 << 6) 100 | #define WM8960_DCLK_DIV_8 (5 << 6) 101 | #define WM8960_DCLK_DIV_12 (6 << 6) 102 | #define WM8960_DCLK_DIV_16 (7 << 6) 103 | 104 | #define WM8960_TOCLK_F19 (0 << 1) 105 | #define WM8960_TOCLK_F21 (1 << 1) 106 | 107 | #define WM8960_OPCLK_DIV_1 (0 << 0) 108 | #define WM8960_OPCLK_DIV_2 (1 << 0) 109 | #define WM8960_OPCLK_DIV_3 (2 << 0) 110 | #define WM8960_OPCLK_DIV_4 (3 << 0) 111 | #define WM8960_OPCLK_DIV_5_5 (4 << 0) 112 | #define WM8960_OPCLK_DIV_6 (5 << 0) 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /wm8960_asound.state: -------------------------------------------------------------------------------- 1 | state.ALSA { 2 | control.1 { 3 | iface MIXER 4 | name 'PCM Playback Volume' 5 | value -1994 6 | comment { 7 | access 'read write' 8 | type INTEGER 9 | count 1 10 | range '-10239 - 400' 11 | dbmin -9999999 12 | dbmax 400 13 | dbvalue.0 -1994 14 | } 15 | } 16 | control.2 { 17 | iface MIXER 18 | name 'PCM Playback Switch' 19 | value true 20 | comment { 21 | access 'read write' 22 | type BOOLEAN 23 | count 1 24 | } 25 | } 26 | control.3 { 27 | iface MIXER 28 | name 'PCM Playback Route' 29 | value 0 30 | comment { 31 | access 'read write' 32 | type INTEGER 33 | count 1 34 | range '0 - 2' 35 | } 36 | } 37 | control.4 { 38 | iface PCM 39 | name 'IEC958 Playback Default' 40 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 41 | comment { 42 | access 'read write' 43 | type IEC958 44 | count 1 45 | } 46 | } 47 | control.5 { 48 | iface PCM 49 | name 'IEC958 Playback Con Mask' 50 | value '0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 51 | comment { 52 | access read 53 | type IEC958 54 | count 1 55 | } 56 | } 57 | control.6 { 58 | iface PCM 59 | name 'IEC958 Playback PCM Stream' 60 | value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 61 | comment { 62 | access 'read write inactive' 63 | type IEC958 64 | count 1 65 | } 66 | } 67 | } 68 | state.seeed2micvoicec { 69 | control.1 { 70 | iface MIXER 71 | name 'Capture Volume' 72 | value.0 39 73 | value.1 39 74 | comment { 75 | access 'read write' 76 | type INTEGER 77 | count 2 78 | range '0 - 63' 79 | dbmin -1725 80 | dbmax 3000 81 | dbvalue.0 1200 82 | dbvalue.1 1200 83 | } 84 | } 85 | control.2 { 86 | iface MIXER 87 | name 'Capture Volume ZC Switch' 88 | value.0 0 89 | value.1 0 90 | comment { 91 | access 'read write' 92 | type INTEGER 93 | count 2 94 | range '0 - 1' 95 | } 96 | } 97 | control.3 { 98 | iface MIXER 99 | name 'Capture Switch' 100 | value.0 true 101 | value.1 true 102 | comment { 103 | access 'read write' 104 | type BOOLEAN 105 | count 2 106 | } 107 | } 108 | control.4 { 109 | iface MIXER 110 | name 'Left Input Boost Mixer LINPUT3 Volume' 111 | value 0 112 | comment { 113 | access 'read write' 114 | type INTEGER 115 | count 1 116 | range '0 - 7' 117 | dbmin -9999999 118 | dbmax 600 119 | dbvalue.0 -9999999 120 | } 121 | } 122 | control.5 { 123 | iface MIXER 124 | name 'Left Input Boost Mixer LINPUT2 Volume' 125 | value 0 126 | comment { 127 | access 'read write' 128 | type INTEGER 129 | count 1 130 | range '0 - 7' 131 | dbmin -9999999 132 | dbmax 600 133 | dbvalue.0 -9999999 134 | } 135 | } 136 | control.6 { 137 | iface MIXER 138 | name 'Right Input Boost Mixer RINPUT3 Volume' 139 | value 0 140 | comment { 141 | access 'read write' 142 | type INTEGER 143 | count 1 144 | range '0 - 7' 145 | dbmin -9999999 146 | dbmax 600 147 | dbvalue.0 -9999999 148 | } 149 | } 150 | control.7 { 151 | iface MIXER 152 | name 'Right Input Boost Mixer RINPUT2 Volume' 153 | value 0 154 | comment { 155 | access 'read write' 156 | type INTEGER 157 | count 1 158 | range '0 - 7' 159 | dbmin -9999999 160 | dbmax 600 161 | dbvalue.0 -9999999 162 | } 163 | } 164 | control.8 { 165 | iface MIXER 166 | name 'Right Input Boost Mixer RINPUT1 Volume' 167 | value 3 168 | comment { 169 | access 'read write' 170 | type INTEGER 171 | count 1 172 | range '0 - 3' 173 | dbmin 0 174 | dbmax 2900 175 | dbvalue.0 2900 176 | } 177 | } 178 | control.9 { 179 | iface MIXER 180 | name 'Left Input Boost Mixer LINPUT1 Volume' 181 | value 3 182 | comment { 183 | access 'read write' 184 | type INTEGER 185 | count 1 186 | range '0 - 3' 187 | dbmin 0 188 | dbmax 2900 189 | dbvalue.0 2900 190 | } 191 | } 192 | control.10 { 193 | iface MIXER 194 | name 'Playback Volume' 195 | value.0 255 196 | value.1 255 197 | comment { 198 | access 'read write' 199 | type INTEGER 200 | count 2 201 | range '0 - 255' 202 | dbmin -9999999 203 | dbmax 0 204 | dbvalue.0 0 205 | dbvalue.1 0 206 | } 207 | } 208 | control.11 { 209 | iface MIXER 210 | name 'Headphone Playback Volume' 211 | value.0 127 212 | value.1 127 213 | comment { 214 | access 'read write' 215 | type INTEGER 216 | count 2 217 | range '0 - 127' 218 | dbmin -9999999 219 | dbmax 600 220 | dbvalue.0 600 221 | dbvalue.1 600 222 | } 223 | } 224 | control.12 { 225 | iface MIXER 226 | name 'Headphone Playback ZC Switch' 227 | value.0 false 228 | value.1 false 229 | comment { 230 | access 'read write' 231 | type BOOLEAN 232 | count 2 233 | } 234 | } 235 | control.13 { 236 | iface MIXER 237 | name 'Speaker Playback Volume' 238 | value.0 127 239 | value.1 127 240 | comment { 241 | access 'read write' 242 | type INTEGER 243 | count 2 244 | range '0 - 127' 245 | dbmin -9999999 246 | dbmax 600 247 | dbvalue.0 600 248 | dbvalue.1 600 249 | } 250 | } 251 | control.14 { 252 | iface MIXER 253 | name 'Speaker Playback ZC Switch' 254 | value.0 false 255 | value.1 false 256 | comment { 257 | access 'read write' 258 | type BOOLEAN 259 | count 2 260 | } 261 | } 262 | control.15 { 263 | iface MIXER 264 | name 'Speaker DC Volume' 265 | value 4 266 | comment { 267 | access 'read write' 268 | type INTEGER 269 | count 1 270 | range '0 - 5' 271 | } 272 | } 273 | control.16 { 274 | iface MIXER 275 | name 'Speaker AC Volume' 276 | value 5 277 | comment { 278 | access 'read write' 279 | type INTEGER 280 | count 1 281 | range '0 - 5' 282 | } 283 | } 284 | control.17 { 285 | iface MIXER 286 | name 'PCM Playback -6dB Switch' 287 | value false 288 | comment { 289 | access 'read write' 290 | type BOOLEAN 291 | count 1 292 | } 293 | } 294 | control.18 { 295 | iface MIXER 296 | name 'ADC Polarity' 297 | value 'No Inversion' 298 | comment { 299 | access 'read write' 300 | type ENUMERATED 301 | count 1 302 | item.0 'No Inversion' 303 | item.1 'Left Inverted' 304 | item.2 'Right Inverted' 305 | item.3 'Stereo Inversion' 306 | } 307 | } 308 | control.19 { 309 | iface MIXER 310 | name 'ADC High Pass Filter Switch' 311 | value false 312 | comment { 313 | access 'read write' 314 | type BOOLEAN 315 | count 1 316 | } 317 | } 318 | control.20 { 319 | iface MIXER 320 | name 'DAC Polarity' 321 | value 'No Inversion' 322 | comment { 323 | access 'read write' 324 | type ENUMERATED 325 | count 1 326 | item.0 'No Inversion' 327 | item.1 'Left Inverted' 328 | item.2 'Right Inverted' 329 | item.3 'Stereo Inversion' 330 | } 331 | } 332 | control.21 { 333 | iface MIXER 334 | name 'DAC Deemphasis Switch' 335 | value false 336 | comment { 337 | access 'read write' 338 | type BOOLEAN 339 | count 1 340 | } 341 | } 342 | control.22 { 343 | iface MIXER 344 | name '3D Filter Upper Cut-Off' 345 | value High 346 | comment { 347 | access 'read write' 348 | type ENUMERATED 349 | count 1 350 | item.0 High 351 | item.1 Low 352 | } 353 | } 354 | control.23 { 355 | iface MIXER 356 | name '3D Filter Lower Cut-Off' 357 | value Low 358 | comment { 359 | access 'read write' 360 | type ENUMERATED 361 | count 1 362 | item.0 Low 363 | item.1 High 364 | } 365 | } 366 | control.24 { 367 | iface MIXER 368 | name '3D Volume' 369 | value 0 370 | comment { 371 | access 'read write' 372 | type INTEGER 373 | count 1 374 | range '0 - 15' 375 | } 376 | } 377 | control.25 { 378 | iface MIXER 379 | name '3D Switch' 380 | value false 381 | comment { 382 | access 'read write' 383 | type BOOLEAN 384 | count 1 385 | } 386 | } 387 | control.26 { 388 | iface MIXER 389 | name 'ALC Function' 390 | value Off 391 | comment { 392 | access 'read write' 393 | type ENUMERATED 394 | count 1 395 | item.0 Off 396 | item.1 Right 397 | item.2 Left 398 | item.3 Stereo 399 | } 400 | } 401 | control.27 { 402 | iface MIXER 403 | name 'ALC Max Gain' 404 | value 7 405 | comment { 406 | access 'read write' 407 | type INTEGER 408 | count 1 409 | range '0 - 7' 410 | } 411 | } 412 | control.28 { 413 | iface MIXER 414 | name 'ALC Target' 415 | value 4 416 | comment { 417 | access 'read write' 418 | type INTEGER 419 | count 1 420 | range '0 - 15' 421 | } 422 | } 423 | control.29 { 424 | iface MIXER 425 | name 'ALC Min Gain' 426 | value 0 427 | comment { 428 | access 'read write' 429 | type INTEGER 430 | count 1 431 | range '0 - 7' 432 | } 433 | } 434 | control.30 { 435 | iface MIXER 436 | name 'ALC Hold Time' 437 | value 0 438 | comment { 439 | access 'read write' 440 | type INTEGER 441 | count 1 442 | range '0 - 15' 443 | } 444 | } 445 | control.31 { 446 | iface MIXER 447 | name 'ALC Mode' 448 | value ALC 449 | comment { 450 | access 'read write' 451 | type ENUMERATED 452 | count 1 453 | item.0 ALC 454 | item.1 Limiter 455 | } 456 | } 457 | control.32 { 458 | iface MIXER 459 | name 'ALC Decay' 460 | value 3 461 | comment { 462 | access 'read write' 463 | type INTEGER 464 | count 1 465 | range '0 - 15' 466 | } 467 | } 468 | control.33 { 469 | iface MIXER 470 | name 'ALC Attack' 471 | value 2 472 | comment { 473 | access 'read write' 474 | type INTEGER 475 | count 1 476 | range '0 - 15' 477 | } 478 | } 479 | control.34 { 480 | iface MIXER 481 | name 'Noise Gate Threshold' 482 | value 0 483 | comment { 484 | access 'read write' 485 | type INTEGER 486 | count 1 487 | range '0 - 31' 488 | } 489 | } 490 | control.35 { 491 | iface MIXER 492 | name 'Noise Gate Switch' 493 | value false 494 | comment { 495 | access 'read write' 496 | type BOOLEAN 497 | count 1 498 | } 499 | } 500 | control.36 { 501 | iface MIXER 502 | name 'ADC PCM Capture Volume' 503 | value.0 195 504 | value.1 195 505 | comment { 506 | access 'read write' 507 | type INTEGER 508 | count 2 509 | range '0 - 255' 510 | dbmin -9999999 511 | dbmax 3000 512 | dbvalue.0 0 513 | dbvalue.1 0 514 | } 515 | } 516 | control.37 { 517 | iface MIXER 518 | name 'Left Output Mixer Boost Bypass Volume' 519 | value 0 520 | comment { 521 | access 'read write' 522 | type INTEGER 523 | count 1 524 | range '0 - 7' 525 | dbmin -2100 526 | dbmax 0 527 | dbvalue.0 -2100 528 | } 529 | } 530 | control.38 { 531 | iface MIXER 532 | name 'Left Output Mixer LINPUT3 Volume' 533 | value 0 534 | comment { 535 | access 'read write' 536 | type INTEGER 537 | count 1 538 | range '0 - 7' 539 | dbmin -2100 540 | dbmax 0 541 | dbvalue.0 -2100 542 | } 543 | } 544 | control.39 { 545 | iface MIXER 546 | name 'Right Output Mixer Boost Bypass Volume' 547 | value 5 548 | comment { 549 | access 'read write' 550 | type INTEGER 551 | count 1 552 | range '0 - 7' 553 | dbmin -2100 554 | dbmax 0 555 | dbvalue.0 -600 556 | } 557 | } 558 | control.40 { 559 | iface MIXER 560 | name 'Right Output Mixer RINPUT3 Volume' 561 | value 2 562 | comment { 563 | access 'read write' 564 | type INTEGER 565 | count 1 566 | range '0 - 7' 567 | dbmin -2100 568 | dbmax 0 569 | dbvalue.0 -1500 570 | } 571 | } 572 | control.41 { 573 | iface MIXER 574 | name 'ADC Data Output Select' 575 | value 'Left Data = Left ADC; Right Data = Right ADC' 576 | comment { 577 | access 'read write' 578 | type ENUMERATED 579 | count 1 580 | item.0 'Left Data = Left ADC; Right Data = Right ADC' 581 | item.1 'Left Data = Left ADC; Right Data = Left ADC' 582 | item.2 'Left Data = Right ADC; Right Data = Right ADC' 583 | item.3 'Left Data = Right ADC; Right Data = Left ADC' 584 | } 585 | } 586 | control.42 { 587 | iface MIXER 588 | name 'DAC Mono Mix' 589 | value Stereo 590 | comment { 591 | access 'read write' 592 | type ENUMERATED 593 | count 1 594 | item.0 Stereo 595 | item.1 Mono 596 | } 597 | } 598 | control.43 { 599 | iface MIXER 600 | name 'Left Boost Mixer LINPUT2 Switch' 601 | value false 602 | comment { 603 | access 'read write' 604 | type BOOLEAN 605 | count 1 606 | } 607 | } 608 | control.44 { 609 | iface MIXER 610 | name 'Left Boost Mixer LINPUT3 Switch' 611 | value false 612 | comment { 613 | access 'read write' 614 | type BOOLEAN 615 | count 1 616 | } 617 | } 618 | control.45 { 619 | iface MIXER 620 | name 'Left Boost Mixer LINPUT1 Switch' 621 | value true 622 | comment { 623 | access 'read write' 624 | type BOOLEAN 625 | count 1 626 | } 627 | } 628 | control.46 { 629 | iface MIXER 630 | name 'Right Boost Mixer RINPUT2 Switch' 631 | value false 632 | comment { 633 | access 'read write' 634 | type BOOLEAN 635 | count 1 636 | } 637 | } 638 | control.47 { 639 | iface MIXER 640 | name 'Right Boost Mixer RINPUT3 Switch' 641 | value false 642 | comment { 643 | access 'read write' 644 | type BOOLEAN 645 | count 1 646 | } 647 | } 648 | control.48 { 649 | iface MIXER 650 | name 'Right Boost Mixer RINPUT1 Switch' 651 | value true 652 | comment { 653 | access 'read write' 654 | type BOOLEAN 655 | count 1 656 | } 657 | } 658 | control.49 { 659 | iface MIXER 660 | name 'Left Input Mixer Boost Switch' 661 | value true 662 | comment { 663 | access 'read write' 664 | type BOOLEAN 665 | count 1 666 | } 667 | } 668 | control.50 { 669 | iface MIXER 670 | name 'Right Input Mixer Boost Switch' 671 | value true 672 | comment { 673 | access 'read write' 674 | type BOOLEAN 675 | count 1 676 | } 677 | } 678 | control.51 { 679 | iface MIXER 680 | name 'Left Output Mixer PCM Playback Switch' 681 | value true 682 | comment { 683 | access 'read write' 684 | type BOOLEAN 685 | count 1 686 | } 687 | } 688 | control.52 { 689 | iface MIXER 690 | name 'Left Output Mixer LINPUT3 Switch' 691 | value false 692 | comment { 693 | access 'read write' 694 | type BOOLEAN 695 | count 1 696 | } 697 | } 698 | control.53 { 699 | iface MIXER 700 | name 'Left Output Mixer Boost Bypass Switch' 701 | value false 702 | comment { 703 | access 'read write' 704 | type BOOLEAN 705 | count 1 706 | } 707 | } 708 | control.54 { 709 | iface MIXER 710 | name 'Right Output Mixer PCM Playback Switch' 711 | value true 712 | comment { 713 | access 'read write' 714 | type BOOLEAN 715 | count 1 716 | } 717 | } 718 | control.55 { 719 | iface MIXER 720 | name 'Right Output Mixer RINPUT3 Switch' 721 | value false 722 | comment { 723 | access 'read write' 724 | type BOOLEAN 725 | count 1 726 | } 727 | } 728 | control.56 { 729 | iface MIXER 730 | name 'Right Output Mixer Boost Bypass Switch' 731 | value false 732 | comment { 733 | access 'read write' 734 | type BOOLEAN 735 | count 1 736 | } 737 | } 738 | control.57 { 739 | iface MIXER 740 | name 'Mono Output Mixer Left Switch' 741 | value false 742 | comment { 743 | access 'read write' 744 | type BOOLEAN 745 | count 1 746 | } 747 | } 748 | control.58 { 749 | iface MIXER 750 | name 'Mono Output Mixer Right Switch' 751 | value false 752 | comment { 753 | access 'read write' 754 | type BOOLEAN 755 | count 1 756 | } 757 | } 758 | } 759 | --------------------------------------------------------------------------------