├── seeed-2mic-voicecard.dtbo ├── seeed-4mic-voicecard.dtbo ├── seeed-8mic-voicecard.dtbo ├── pulseaudio ├── udev_rules_4mic.png ├── udev_rules_6mic.png ├── 91-seeedvoicecard.rules ├── pulse_config_4mic │ ├── seeed-voicecard.conf │ ├── daemon.conf │ └── default.pa ├── pulse_config_6mic │ ├── seeed-voicecard.conf │ ├── daemon.conf │ └── default.pa └── README.md ├── ac108_plugin ├── README.md ├── libasound_module_pcm_ac108.so ├── ac108_help.h ├── Makefile ├── ac108_help.c └── pcm_ac108.c ├── seeed-voicecard.service ├── dkms.conf ├── asound_4mic.conf ├── asound_2mic.conf ├── ubuntu-prerequisite.sh ├── tools ├── coherence.py └── phase_test.py ├── Makefile ├── asound_6mic.conf ├── sound-compatible-4.18.h ├── seeed-4mic-voicecard-overlay.dts ├── seeed-2mic-voicecard-overlay.dts ├── seeed-8mic-voicecard-overlay.dts ├── uninstall.sh ├── wm8960.h ├── ac10x.h ├── ac108_asound.state ├── default.pa ├── seeed-voicecard ├── ac108_6mic.state ├── README.md ├── ac101_regs.h ├── install.sh ├── wm8960_asound.state ├── ac108.h └── seeed-voicecard.c /seeed-2mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/seeed-2mic-voicecard.dtbo -------------------------------------------------------------------------------- /seeed-4mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/seeed-4mic-voicecard.dtbo -------------------------------------------------------------------------------- /seeed-8mic-voicecard.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/seeed-8mic-voicecard.dtbo -------------------------------------------------------------------------------- /pulseaudio/udev_rules_4mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/pulseaudio/udev_rules_4mic.png -------------------------------------------------------------------------------- /pulseaudio/udev_rules_6mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/pulseaudio/udev_rules_6mic.png -------------------------------------------------------------------------------- /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/libasound_module_pcm_ac108.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/seeed-voicecard/master/ac108_plugin/libasound_module_pcm_ac108.so -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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" -------------------------------------------------------------------------------- /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 | AUTOINSTALL="yes" 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /asound_4mic.conf: -------------------------------------------------------------------------------- 1 | pcm.dsnooper { 2 | type dsnoop 3 | ipc_key 816357492 4 | ipc_key_add_uid 0 5 | ipc_perm 0666 6 | slave { 7 | pcm "hw:1,0" 8 | channels 4 9 | } 10 | } 11 | 12 | pcm.!default { 13 | type asym 14 | playback.pcm { 15 | type plug 16 | slave.pcm "hw:0,0" 17 | } 18 | capture.pcm { 19 | type plug 20 | slave.pcm "dsnooper" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /asound_6mic.conf: -------------------------------------------------------------------------------- 1 | defaults.pcm.rate_converter "samplerate" 2 | 3 | pcm.!default { 4 | type asym 5 | playback.pcm "dmixer" 6 | capture.pcm "capture" 7 | } 8 | 9 | pcm.playback { 10 | type plug 11 | slave.pcm "dmixed" 12 | } 13 | 14 | pcm.capture { 15 | type plug 16 | slave.pcm "array" 17 | } 18 | 19 | pcm.dmixer { 20 | type plug 21 | slave { 22 | pcm { 23 | type dmix 24 | ipc_key 555555 25 | slave { 26 | pcm "hw:seeed8micvoicec" 27 | period_time 0 28 | period_size 1024 29 | buffer_size 8192 30 | format S32_LE 31 | channels 8 32 | } 33 | bindings { 34 | 0 0 35 | 1 1 36 | 2 2 37 | 3 3 38 | 4 4 39 | 5 5 40 | 6 6 41 | 7 7 42 | } 43 | } 44 | channels 8 45 | format S32_LE 46 | rate 48000 47 | } 48 | ttable.0.0 1 49 | ttable.1.1 1 50 | ttable.0.2 1 51 | ttable.1.3 1 52 | ttable.0.4 1 53 | ttable.1.5 1 54 | ttable.0.6 1 55 | ttable.1.7 1 56 | } 57 | 58 | pcm.array { 59 | type dsnoop 60 | slave { 61 | pcm "hw:seeed8micvoicec" 62 | channels 8 63 | } 64 | ipc_key 666666 65 | } 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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,18,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 | #define snd_soc_read snd_soc_component_read32 35 | #define snd_soc_register_codec snd_soc_register_component 36 | #define snd_soc_unregister_codec snd_soc_unregister_component 37 | #define snd_soc_update_bits snd_soc_component_update_bits 38 | #define snd_soc_write snd_soc_component_write 39 | #define snd_soc_add_codec_controls snd_soc_add_component_controls 40 | #endif 41 | 42 | #endif//__SOUND_COMPATIBLE_4_18_H__ 43 | 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt 18 | 19 | get_overlay() { 20 | ov=$1 21 | if grep -q -E "^dtoverlay=$ov" $CONFIG; then 22 | echo 0 23 | else 24 | echo 1 25 | fi 26 | } 27 | 28 | do_overlay() { 29 | ov=$1 30 | RET=$2 31 | DEFAULT=--defaultno 32 | CURRENT=0 33 | if [ $(get_overlay $ov) -eq 0 ]; then 34 | DEFAULT= 35 | CURRENT=1 36 | fi 37 | if [ $RET -eq $CURRENT ]; then 38 | ASK_TO_REBOOT=1 39 | fi 40 | if [ $RET -eq 0 ]; then 41 | sed $CONFIG -i -e "s/^#dtoverlay=$ov/dtoverlay=$ov/" 42 | if ! grep -q -E "^dtoverlay=$ov" $CONFIG; then 43 | printf "dtoverlay=$ov\n" >> $CONFIG 44 | fi 45 | STATUS=enabled 46 | elif [ $RET -eq 1 ]; then 47 | sed $CONFIG -i -e "s/^dtoverlay=$ov/#dtoverlay=$ov/" 48 | STATUS=disabled 49 | else 50 | return $RET 51 | fi 52 | } 53 | 54 | RPI_HATS="seeed-2mic-voicecard seeed-4mic-voicecard seeed-8mic-voicecard" 55 | 56 | PATH=$PATH:/opt/vc/bin 57 | echo -e "\n### Remove dtbos" 58 | for i in $RPI_HATS; do 59 | dtoverlay -r $i 60 | done 61 | OVERLAYS=/boot/overlays 62 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 63 | 64 | rm -v ${OVERLAYS}/seeed-2mic-voicecard.dtbo || true 65 | rm -v ${OVERLAYS}/seeed-4mic-voicecard.dtbo || true 66 | rm -v ${OVERLAYS}/seeed-8mic-voicecard.dtbo || true 67 | 68 | echo -e "\n### Remove alsa configs" 69 | rm -rfv /etc/voicecard/ || true 70 | 71 | echo -e "\n### Disable seeed-voicecard.service " 72 | systemctl stop seeed-voicecard.service 73 | systemctl disable seeed-voicecard.service 74 | 75 | echo -e "\n### Remove seeed-voicecard" 76 | rm -v /usr/bin/seeed-voicecard || true 77 | rm -v /lib/systemd/system/seeed-voicecard.service || true 78 | 79 | echo -e "\n### Remove dkms" 80 | rm -rfv /var/lib/dkms/seeed-voicecard || true 81 | 82 | echo -e "\n### Unload codec driver by modprobe -r" 83 | modprobe -r snd_soc_ac108 84 | modprobe -r snd_soc_wm8960 85 | modprobe -r snd_soc_seeed_voicecard 86 | 87 | echo -e "\n### Remove kernel modules" 88 | rm -v /lib/modules/*/kernel/sound/soc/codecs/snd-soc-wm8960.ko || true 89 | rm -v /lib/modules/*/kernel/sound/soc/codecs/snd-soc-ac108.ko || true 90 | rm -v /lib/modules/*/kernel/sound/soc/bcm/snd-soc-seeed-voicecard.ko || true 91 | rm -v /lib/modules/*/updates/dkms/snd-soc-wm8960.ko || true 92 | rm -v /lib/modules/*/updates/dkms/snd-soc-ac108.ko || true 93 | rm -v /lib/modules/*/updates/dkms/snd-soc-seeed-voicecard.ko || true 94 | 95 | echo -e "\n### Remove $CONFIG configuration" 96 | for i in $RPI_HATS; do 97 | echo Uninstall $i ... 98 | do_overlay $i 1 99 | done 100 | 101 | echo "------------------------------------------------------" 102 | echo "Please reboot your raspberry pi to apply all settings" 103 | echo "Thank you!" 104 | echo "------------------------------------------------------" 105 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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)); 123 | 124 | int ac10x_fill_regcache(struct device* dev, struct regmap* map); 125 | 126 | #endif//__AC10X_H__ 127 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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 | LOGF=/var/log/$(basename $0).log 25 | exec 1> >(tee $LOGF) 26 | exec 2>&1 27 | 28 | export PATH=$PATH:/opt/vc/bin 29 | OVERLAYS=/boot/overlays 30 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 31 | 32 | #enable i2c interface 33 | dtparam -d $OVERLAYS i2c_arm=on 34 | modprobe i2c-dev 35 | 36 | #enable spi interface 37 | dtparam -d $OVERLAYS spi=on 38 | 39 | _VER_RUN= 40 | function get_kernel_version() { 41 | local ZIMAGE IMG_OFFSET 42 | 43 | _VER_RUN="" 44 | [ -z "$_VER_RUN" ] && { 45 | ZIMAGE=/boot/kernel.img 46 | IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1) 47 | _VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) | zcat | grep -a -m1 "Linux version" | 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 | audioconfig=/home/pi/audiosetup 99 | 100 | if [ "x${is_1a}" != "x" ] && [ "x${is_35}" == "x" ] ; then 101 | echo "install 2mic" 102 | if [ -f $audioconfig ] ; then 103 | sudo rm $audioconfig 104 | fi 105 | echo 'Respeaker-2-Mic' >> $audioconfig 106 | echo "create 2mic asound configure file" 107 | overlay=seeed-2mic-voicecard 108 | asound_conf=/etc/voicecard/asound_2mic.conf 109 | asound_rc=/etc/voicecard/asound_2mic.conf 110 | asound_state=/etc/voicecard/wm8960_asound.state 111 | fi 112 | 113 | if [ "x${is_3b}" != "x" ] && [ "x${is_35}" == "x" ] ; then 114 | if [ -f $audioconfig ] ; then 115 | sudo rm $audioconfig 116 | fi 117 | echo 'Respeaker-4-Mic' >> $audioconfig 118 | echo "create 4mic asound configure file" 119 | overlay=seeed-4mic-voicecard 120 | asound_conf=/etc/voicecard/asound_4mic.conf 121 | asound_rc=/etc/voicecard/asound_4mic.conf 122 | asound_state=/etc/voicecard/ac108_asound.state 123 | if aplay -l | grep -q "bcm2835 ALSA"; then 124 | amixer cset numid=3 1 || true 125 | fi 126 | fi 127 | 128 | if [ "x${is_3b}" != "x" ] && [ "x${is_35}" != "x" ] ; then 129 | echo "install 6mic" 130 | if [ -f $audioconfig ] ; then 131 | sudo rm $audioconfig 132 | fi 133 | echo 'Respeaker-6-Mic' >> $audioconfig 134 | echo "create 6mic asound configure file" 135 | overlay=seeed-8mic-voicecard 136 | asound_conf=/etc/voicecard/asound_6mic.conf 137 | asound_rc=/etc/voicecard/asound_6mic.conf 138 | asound_state=/etc/voicecard/ac108_6mic.state 139 | fi 140 | 141 | if [ "$overlay" ]; then 142 | echo Install $overlay ... 143 | 144 | # Remove old configuration 145 | rm /etc/asound.conf 146 | rm /home/pi/.asoundrc 147 | rm /var/lib/alsa/asound.state 148 | 149 | : <<\EOF 150 | kernel_ver=$(get_kernel_version) 151 | # echo kernel_ver=$kernel_ver 152 | 153 | # TODO: dynamic dtoverlay Bug of v4.19.x 154 | # no DT node phandle inserted. 155 | if [[ "$kernel_ver" =~ ^4\.19.*$ || "$kernel_ver" =~ ^5\.*$ ]]; then 156 | for i in $RPI_HATS; do 157 | if [ "$i" == "$overlay" ]; then 158 | do_overlay $overlay 0 159 | else 160 | echo Uninstall $i ... 161 | do_overlay $i 1 162 | fi 163 | done 164 | fi 165 | EOF 166 | #make sure the driver loads correctly 167 | dtoverlay -d $OVERLAYS $overlay || true 168 | 169 | 170 | echo "create $overlay asound configure file" 171 | ln -s $asound_conf /etc/asound.conf 172 | sudo cp /etc/asound.conf /home/pi/.asoundrc 173 | echo "create $overlay asound status file" 174 | ln -s $asound_state /var/lib/alsa/asound.state 175 | fi 176 | 177 | alsactl restore 178 | 179 | #Force 3.5mm ('headphone') jack 180 | # The Raspberry Pi 4, released on 24th Jun 2019, has two HDMI ports, 181 | # and can drive two displays with audios for two users simultaneously, 182 | # in a "multiseat" configuration. The earlier single virtual ALSA 183 | # option for re-directing audio playback between headphone jack and HDMI 184 | # via a 'Routing' mixer setting was turned off eventually to allow 185 | # simultaneous usage of all 3 playback devices. 186 | if aplay -l | grep -q "bcm2835 ALSA"; then 187 | amixer cset numid=3 1 || true 188 | fi 189 | 190 | exit 0 191 | -------------------------------------------------------------------------------- /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 Playback 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 'Speaker Playback Volume' 312 | value 25 313 | comment { 314 | access 'read write' 315 | type INTEGER 316 | count 1 317 | range '0 - 31' 318 | dbmin -4800 319 | dbmax -150 320 | dbvalue.0 -1050 321 | } 322 | } 323 | control.19 { 324 | iface MIXER 325 | name 'Headphone Playback Volume' 326 | value 52 327 | comment { 328 | access 'read write' 329 | type INTEGER 330 | count 1 331 | range '0 - 63' 332 | dbmin -6300 333 | dbmax 0 334 | dbvalue.0 -1100 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # seeed-voicecard 2 | 3 | [![Join the chat at https://gitter.im/seeed-voicecard/Lobby](https://badges.gitter.im/seeed-voicecard/Lobby.svg)](https://gitter.im/seeed-voicecard/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | The drivers of [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. 6 | 7 | ### Install seeed-voicecard 8 | Get the seeed voice card source code. and install all linux kernel drivers 9 | ```bash 10 | git clone https://github.com/respeaker/seeed-voicecard 11 | cd seeed-voicecard 12 | sudo ./install.sh 13 | sudo reboot 14 | ``` 15 | It may probably happen that the driver won't compile with the latest kernel when raspbian rolls out new patches to the kernel. If so, please try `sudo ./install.sh --compat-kernel` which uses an older kernel but ensures that the driver can work. 16 | 17 | ## ReSpeaker Mic Hat 18 | 19 | [![](https://github.com/SeeedDocument/MIC_HATv1.0_for_raspberrypi/blob/master/img/mic_hatv1.0.png?raw=true)](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html) 20 | 21 | While the upstream wm8960 codec is not currently supported by current Pi kernel builds, upstream wm8960 has some bugs, we had fixed it. we must it build manually. 22 | 23 | Check that the sound card name matches the source code seeed-voicecard. 24 | 25 | ```bash 26 | #for ReSpeaker 2-mic 27 | pi@raspberrypi:~/seeed-voicecard $ aplay -l 28 | **** List of PLAYBACK Hardware Devices **** 29 | card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA] 30 | Subdevices: 8/8 31 | Subdevice #0: subdevice #0 32 | Subdevice #1: subdevice #1 33 | Subdevice #2: subdevice #2 34 | Subdevice #3: subdevice #3 35 | Subdevice #4: subdevice #4 36 | Subdevice #5: subdevice #5 37 | Subdevice #6: subdevice #6 38 | Subdevice #7: subdevice #7 39 | card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI] 40 | Subdevices: 1/1 41 | Subdevice #0: subdevice #0 42 | card 1: seeed2micvoicec [seeed-2mic-voicecard], device 0: bcm2835-i2s-wm8960-hifi wm8960-hifi-0 [] 43 | Subdevices: 1/1 44 | Subdevice #0: subdevice #0 45 | pi@raspberrypi:~/seeed-voicecard $ arecord -l 46 | **** List of CAPTURE Hardware Devices **** 47 | card 1: seeed2micvoicec [seeed-2mic-voicecard], device 0: bcm2835-i2s-wm8960-hifi wm8960-hifi-0 [] 48 | Subdevices: 1/1 49 | Subdevice #0: subdevice #0 50 | pi@raspberrypi:~/seeed-voicecard $ 51 | ``` 52 | If you want to change the alsa settings, You can use `sudo alsactl --file=/etc/voicecard/wm8960_asound.state store` to save it. 53 | 54 | 55 | 56 | #### Next step 57 | Go to https://github.com/respeaker/mic_hat to build voice enabled projects with Google Assistant SDK or Alexa Voice Service. 58 | 59 | ## ReSpeaker 4 Mic Array 60 | 61 | [![](https://github.com/SeeedDocument/ReSpeaker-4-Mic-Array-for-Raspberry-Pi/blob/master/img/features.png?raw=true)](https://www.seeedstudio.com/ReSpeaker-4-Mic-Array-for-Raspberry-Pi-p-2941.html) 62 | 63 | The 4 Mic Array uses ac108 which includes 4 ADCs, we also write ac108 rapberry pi linux kernel driver. 64 | 65 | Check that the sound card name matches the source code seeed-voicecard. 66 | 67 | ```bash 68 | #for ReSpeaker 4 Mic Array 69 | pi@raspberrypi:~ $ arecord -L 70 | null 71 | Discard all samples (playback) or generate zero samples (capture) 72 | playback 73 | capture 74 | dmixed 75 | array 76 | ac108 77 | default:CARD=seeed4micvoicec 78 | seeed-4mic-voicecard, 79 | Default Audio Device 80 | sysdefault:CARD=seeed4micvoicec 81 | seeed-4mic-voicecard, 82 | Default Audio Device 83 | dmix:CARD=seeed4micvoicec,DEV=0 84 | seeed-4mic-voicecard, 85 | Direct sample mixing device 86 | dsnoop:CARD=seeed4micvoicec,DEV=0 87 | seeed-4mic-voicecard, 88 | Direct sample snooping device 89 | hw:CARD=seeed4micvoicec,DEV=0 90 | seeed-4mic-voicecard, 91 | Direct hardware device without any conversions 92 | plughw:CARD=seeed4micvoicec,DEV=0 93 | seeed-4mic-voicecard, 94 | Hardware device with all software conversions 95 | pi@raspberrypi:~ $ 96 | ``` 97 | If you want to change the alsa settings, You can use `sudo alsactl --file=/etc/voicecard/ac108_asound.state store` to save it. 98 | 99 | ## 6-Mics Circular Array Kit 100 | 101 | [![](https://user-images.githubusercontent.com/3901856/37268348-6adef768-2600-11e8-8861-588b1c3ea142.png)]() 102 | 103 | The 6 Mics Circular Array Kit uses ac108 x 2 / ac101 x 1 / micphones x 6, includes 8 ADCs and 2 DACs. 104 | 105 | The driver is implemented with 8 input channels & 8 output channels. 106 | >**The first 6 input channel are MIC recording data, 107 | the rest 2 input channel are echo channel of playback 108 | The first 2 output channel are playing data, the rest 6 output channel are dummy** 109 | 110 | 111 | Check that the sound card name matches the source code seeed-voicecard. 112 | ```bash 113 | #for 6 Mic Circular Array 114 | pi@raspberrypi:~ $ arecord -L 115 | null 116 | Discard all samples (playback) or generate zero samples (capture) 117 | default 118 | playback 119 | dmixed 120 | ac108 121 | multiapps 122 | ac101 123 | sysdefault:CARD=seeed8micvoicec 124 | seeed-8mic-voicecard, 125 | Default Audio Device 126 | dmix:CARD=seeed8micvoicec,DEV=0 127 | seeed-8mic-voicecard, 128 | Direct sample mixing device 129 | dsnoop:CARD=seeed8micvoicec,DEV=0 130 | seeed-8mic-voicecard, 131 | Direct sample snooping device 132 | hw:CARD=seeed8micvoicec,DEV=0 133 | seeed-8mic-voicecard, 134 | Direct hardware device without any conversions 135 | plughw:CARD=seeed8micvoicec,DEV=0 136 | seeed-8mic-voicecard, 137 | Hardware device with all software conversions 138 | 139 | pi@raspberrypi:~ $ aplay -L 140 | null 141 | Discard all samples (playback) or generate zero samples (capture) 142 | default 143 | playback 144 | dmixed 145 | ac108 146 | multiapps 147 | ac101 148 | sysdefault:CARD=ALSA 149 | bcm2835 ALSA, bcm2835 ALSA 150 | Default Audio Device 151 | dmix:CARD=ALSA,DEV=0 152 | bcm2835 ALSA, bcm2835 ALSA 153 | Direct sample mixing device 154 | dmix:CARD=ALSA,DEV=1 155 | bcm2835 ALSA, bcm2835 IEC958/HDMI 156 | Direct sample mixing device 157 | dsnoop:CARD=ALSA,DEV=0 158 | bcm2835 ALSA, bcm2835 ALSA 159 | Direct sample snooping device 160 | dsnoop:CARD=ALSA,DEV=1 161 | bcm2835 ALSA, bcm2835 IEC958/HDMI 162 | Direct sample snooping device 163 | hw:CARD=ALSA,DEV=0 164 | bcm2835 ALSA, bcm2835 ALSA 165 | Direct hardware device without any conversions 166 | hw:CARD=ALSA,DEV=1 167 | bcm2835 ALSA, bcm2835 IEC958/HDMI 168 | Direct hardware device without any conversions 169 | plughw:CARD=ALSA,DEV=0 170 | bcm2835 ALSA, bcm2835 ALSA 171 | Hardware device with all software conversions 172 | plughw:CARD=ALSA,DEV=1 173 | bcm2835 ALSA, bcm2835 IEC958/HDMI 174 | Hardware device with all software conversions 175 | sysdefault:CARD=seeed8micvoicec 176 | seeed-8mic-voicecard, 177 | Default Audio Device 178 | dmix:CARD=seeed8micvoicec,DEV=0 179 | seeed-8mic-voicecard, 180 | Direct sample mixing device 181 | dsnoop:CARD=seeed8micvoicec,DEV=0 182 | seeed-8mic-voicecard, 183 | Direct sample snooping device 184 | hw:CARD=seeed8micvoicec,DEV=0 185 | seeed-8mic-voicecard, 186 | Direct hardware device without any conversions 187 | plughw:CARD=seeed8micvoicec,DEV=0 188 | seeed-8mic-voicecard, 189 | Hardware device with all software conversions 190 | ``` 191 | 192 | ## 4-Mics Linear Array Kit 193 | 194 | [![](https://user-images.githubusercontent.com/3901856/37194106-a0ccebce-23a7-11e8-88c5-ec611e44ec49.png)]() 195 | 196 | In contrast to 6-Mics Circular Array Kit for Raspberry Pi, 197 | the difference is only first 4 input channels are valid capture data. 198 | 199 | 200 | ### Usage: 201 | 202 | ```bash 203 | #Query sound card number 204 | # The sound card number used as arecord/aplay argument, 205 | # ( like '-D hw:N' and '-D plughw:N', where N is the number ) 206 | # could be found by command: 207 | aplay -l 208 | #or 209 | arecord -l 210 | # Find the line in the command output which near below form: 211 | card 1: seeed2micvoicec [seeed-2mic-voicecard], device 0: ... 212 | # The number between first word `card` and char `:` is the Sound Card Number, here it's 1. 213 | ``` 214 | 215 | ```bash 216 | #for ReSpeaker 2-mic 217 | #It will capture sound an playback on hw:1 218 | arecord -f cd -Dhw:1 | aplay -Dhw:1 219 | ``` 220 | 221 | ```bash 222 | #for ReSpeaker 4-mic 223 | #It will capture sound on AC108 and save as a.wav 224 | arecord -Dac108 -f S32_LE -r 16000 -c 4 a.wav 225 | ``` 226 | 227 | ```bash 228 | #for 6-Mics Circular Array Kit and 4-Mics Linear Array Kit 229 | #It will capture sound on AC108 and save as a.wav 230 | arecord -Dac108 -f S32_LE -r 16000 -c 8 a.wav 231 | #Take care of that the captured mic audio is on the first 6 channels 232 | 233 | #It will play a mono channel sound file mono_to_play.wav 234 | #The file to play must be mono channel or else the speaker output nothing. 235 | aplay -D plughw:1,0 mono_to_play.wav 236 | 237 | #Doing capture && playback the same time 238 | arecord -D hw:1,0 -f S32_LE -r 16000 -c 8 to_be_record.wav & 239 | #mono_to_play.wav is a mono channel wave file to play 240 | aplay -D plughw:1,0 -r 16000 mono_to_play.wav 241 | ``` 242 | **Note: Limit for developer using 6-Mics Circular Array Kit(or 4-Mics Linear Array Kit) doing capture & playback the same time: 243 | 1. capture must be start first, or else the capture channels will possibly be disorder. 244 | 2. playback output channels must fill with 8 same channels data or 4 same stereo channels data, or else the speaker or headphone will output nothing possibly.** 245 | 246 | ### Coherence 247 | 248 | Estimate the magnitude squared coherence using Welch’s method. 249 | ![4-mics-linear-array-kit coherence](https://user-images.githubusercontent.com/3901856/37277486-beb1dd96-261f-11e8-898b-84405bfc7cea.png) 250 | Note: 'CO 1-2' means the coherence between channel 1 and channel 2. 251 | 252 | ```bash 253 | # How to get the coherence of the captured audio(a.wav for example). 254 | sudo apt install python-numpy python-scipy python-matplotlib 255 | python tools/coherence.py a.wav 256 | 257 | # Requirement of the input audio file: 258 | - format: WAV(Microsoft) signed 16-bit PCM 259 | - channels: >=2 260 | ``` 261 | 262 | ### uninstall seeed-voicecard 263 | If you want to upgrade the driver , you need uninstall the driver first. 264 | 265 | ``` 266 | pi@raspberrypi:~/seeed-voicecard $ sudo ./uninstall.sh 267 | ... 268 | ------------------------------------------------------ 269 | Please reboot your raspberry pi to apply all settings 270 | Thank you! 271 | ------------------------------------------------------ 272 | ``` 273 | 274 | 275 | 276 | 277 | Enjoy ! 278 | 279 | ### FAQ 280 | 281 | When you encounter any installation and use problems when you start your ReSpeaker Pi hat, please use the following image for testing. We have installed seeed-voicecard based on the latest PI image, which can be used by burning it directly on SD. If this still cannot solve your problem, you can ask in the issue. We will try our best to solve your problem. 282 | 283 |

284 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FORCE_KERNEL="1.20200819-1" 4 | 5 | if [[ $EUID -ne 0 ]]; then 6 | echo "This script must be run as root (use sudo)" 1>&2 7 | exit 1 8 | fi 9 | 10 | # Check if enough space on /boot volume 11 | boot_line=$(df -BM | grep /boot | head -n 1) 12 | MIN_BOOT_SPC=25 # MegaBytes 13 | if [ "x${boot_line}" = "x" ]; then 14 | echo "Warning: /boot volume not found .." 15 | else 16 | boot_space=$(echo $boot_line | awk '{print $4;}') 17 | free_space=$(echo "${boot_space%?}") 18 | unit="${boot_space: -1}" 19 | if [[ "$unit" != "M" ]]; then 20 | echo "Warning: /boot volume not found .." 21 | elif [ "$free_space" -lt "$MIN_BOOT_SPC" ]; then 22 | echo "Error: Not enough space left ($boot_space) on /boot" 23 | echo " at least $MIN_BOOT_SPC MB required" 24 | exit 1 25 | fi 26 | fi 27 | 28 | # 29 | # make sure that we are on something ARM/Raspberry related 30 | # either a bare metal Raspberry or a qemu session with 31 | # Raspberry stuff available 32 | # - check for /boot/overlays 33 | # - dtparam and dtoverlay is available 34 | errorFound=0 35 | OVERLAYS=/boot/overlays 36 | [ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays 37 | 38 | if [ ! -d $OVERLAYS ] ; then 39 | echo "$OVERLAYS not found or not a directory" 1>&2 40 | errorFound=1 41 | fi 42 | # should we also check for alsactl and amixer used in seeed-voicecard? 43 | PATH=$PATH:/opt/vc/bin 44 | for cmd in dtparam dtoverlay ; do 45 | if ! which $cmd &>/dev/null ; then 46 | echo "$cmd not found" 1>&2 47 | echo "You may need to run ./ubuntu-prerequisite.sh" 48 | errorFound=1 49 | fi 50 | done 51 | 52 | if [ ! -x seeed-voicecard -o ! -f seeed-voicecard.service ]; then 53 | echo "Please run this script in the project directory" 54 | echo "which has files such as install.sh and seeed-voicecard.service" 55 | errorFound=1 56 | fi 57 | 58 | if [ $errorFound = 1 ] ; then 59 | echo "Errors found, exiting." 1>&2 60 | exit 1 61 | fi 62 | 63 | ver="0.3" 64 | uname_r=$(uname -r) 65 | arch_r=$(dpkg --print-architecture) 66 | 67 | # we create a dir with this version to ensure that 'dkms remove' won't delete 68 | # the sources during kernel updates 69 | marker="0.0.0" 70 | 71 | _VER_RUN= 72 | function get_kernel_version() { 73 | local ZIMAGE IMG_OFFSET 74 | 75 | _VER_RUN="" 76 | [ -z "$_VER_RUN" ] && { 77 | ZIMAGE=/boot/kernel.img 78 | [ -f /boot/firmware/vmlinuz ] && ZIMAGE=/boot/firmware/vmlinuz 79 | IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1) 80 | _VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) 2>/dev/null | zcat | grep -a -m1 "Linux version" | strings | awk '{ print $3; }') 81 | } 82 | echo "$_VER_RUN" 83 | return 0 84 | } 85 | 86 | function check_kernel_headers() { 87 | VER_RUN=$(get_kernel_version) 88 | VER_HDR=$(dpkg -L raspberrypi-kernel-headers | egrep -m1 "/lib/modules/[^-]+/build" | awk -F'/' '{ print $4; }') 89 | [ "X$VER_RUN" == "X$VER_HDR" ] && { 90 | return 0 91 | } 92 | VER_HDR=$(dpkg -L linux-headers-$VER_RUN | egrep -m1 "/lib/modules/[[:print:]]+/build" | awk -F'/' '{ print $4; }') 93 | [ "X$VER_RUN" == "X$VER_HDR" ] && { 94 | return 0 95 | } 96 | 97 | # echo RUN=$VER_RUN HDR=$VER_HDR 98 | echo " !!! Your kernel version is $VER_RUN" 99 | echo " Couldn't find *** corresponding *** kernel headers with apt-get." 100 | echo " This may happen if you ran 'rpi-update'." 101 | echo " Choose *** y *** to revert the kernel to version $VER_HDR and continue." 102 | echo " Choose *** N *** to exit without this driver support, by default." 103 | read -p "Would you like to proceed? (y/N)" -n 1 -r -s 104 | echo 105 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 106 | exit 1; 107 | fi 108 | 109 | apt-get -y --reinstall install raspberrypi-kernel 110 | } 111 | 112 | function download_install_debpkg() { 113 | local prefix name r pkg status _name 114 | prefix=$1 115 | name=$2 116 | pkg=${name%%_*} 117 | 118 | status=$(dpkg -l $pkg | tail -1) 119 | _name=$( echo "$status" | awk '{ printf "%s_%s_%s", $2, $3, $4; }') 120 | status=$(echo "$status" | awk '{ printf "%s", $1; }') 121 | 122 | if [ "X$status" == "Xii" -a "X${name%.deb}" == "X$_name" ]; then 123 | echo "debian package $name already installed." 124 | return 0 125 | fi 126 | 127 | for (( i = 0; i < 3; i++ )); do 128 | wget $prefix$name -O /tmp/$name && break 129 | done 130 | dpkg -i /tmp/$name; r=$? 131 | rm -f /tmp/$name 132 | return $r 133 | } 134 | 135 | function usage() { 136 | cat <<-__EOF__ 137 | usage: sudo ./install [ --compat-kernel | --keep-kernel ] [ -h | --help ] 138 | default action is update kernel & headers to latest version. 139 | --compat-kernel uses an older kernel but ensures that the driver can work. 140 | --keep-kernel don't change/update the system kernel, maybe install 141 | coressponding kernel headers. 142 | --help show this help message 143 | __EOF__ 144 | exit 1 145 | } 146 | 147 | compat_kernel= 148 | keep_kernel= 149 | # parse commandline options 150 | while [ ! -z "$1" ] ; do 151 | case $1 in 152 | -h|--help) 153 | usage 154 | ;; 155 | --compat-kernel) 156 | compat_kernel=Y 157 | ;; 158 | --keep-kernel) 159 | keep_kernel=Y 160 | ;; 161 | esac 162 | shift 163 | done 164 | 165 | if [ "X$keep_kernel" != "X" ]; then 166 | FORCE_KERNEL=$(dpkg -s raspberrypi-kernel | awk '/^Version:/{printf "%s\n",$2;}') 167 | echo -e "\n### Keep current system kernel not to change" 168 | elif [ "X$compat_kernel" != "X" ]; then 169 | echo -e "\n### will compile with a compatible kernel..." 170 | else 171 | FORCE_KERNEL="" 172 | echo -e "\n### will compile with the latest kernel..." 173 | fi 174 | [ "X$FORCE_KERNEL" != "X" ] && { 175 | echo -e "The kernel & headers use package version: $FORCE_KERNEL\r\n\r\n" 176 | } 177 | 178 | function install_kernel() { 179 | local _url _prefix 180 | 181 | # Instead of retrieving the lastest kernel & headers 182 | [ "X$FORCE_KERNEL" == "X" ] && { 183 | # Raspbian kernel packages 184 | apt-get -y --force-yes install raspberrypi-kernel-headers raspberrypi-kernel || { 185 | # Ubuntu kernel packages 186 | apt-get -y install linux-raspi linux-headers-raspi linux-image-raspi 187 | } 188 | } || { 189 | # We would like to a fixed version 190 | KERN_NAME=raspberrypi-kernel_${FORCE_KERNEL}_${arch_r}.deb 191 | HDR_NAME=raspberrypi-kernel-headers_${FORCE_KERNEL}_${arch_r}.deb 192 | _url=$(apt-get download --print-uris raspberrypi-kernel | sed -nre "s/'([^']+)'.*$/\1/g;p") 193 | _prefix=$(echo $_url | sed -nre 's/^(.*)raspberrypi-kernel_.*$/\1/g;p') 194 | 195 | download_install_debpkg "$_prefix" "$KERN_NAME" && { 196 | download_install_debpkg "$_prefix" "$HDR_NAME" 197 | } || { 198 | echo "Error: Install kernel or header failed" 199 | exit 2 200 | } 201 | } 202 | } 203 | 204 | function uninstall_module { 205 | src=$1 206 | mod=$2 207 | 208 | if [[ -d /var/lib/dkms/$mod/$ver/$marker ]]; then 209 | rmdir /var/lib/dkms/$mod/$ver/$marker 210 | fi 211 | 212 | if [[ -e /usr/src/$mod-$ver || -e /var/lib/dkms/$mod/$ver ]]; then 213 | dkms remove --force -m $mod -v $ver --all 214 | rm -rf /usr/src/$mod-$ver 215 | fi 216 | 217 | return 0 218 | } 219 | 220 | # update and install required packages 221 | which apt &>/dev/null; r=$? 222 | if [[ $r -eq 0 ]]; then 223 | echo -e "\n### Install required tool packages" 224 | apt update -y 225 | apt-get -y install dkms git i2c-tools libasound2-plugins 226 | fi 227 | 228 | echo -e "\n### Uninstall previous dkms module" 229 | uninstall_module "./" "seeed-voicecard" 230 | 231 | if [[ $r -eq 0 ]]; then 232 | echo -e "\n### Install required kernel package" 233 | install_kernel 234 | # rpi-update checker 235 | check_kernel_headers 236 | fi 237 | 238 | # Arch Linux 239 | which pacman &>/dev/null 240 | if [[ $? -eq 0 ]]; then 241 | pacman -Syu --needed git gcc automake make dkms linux-raspberrypi-headers i2c-tools 242 | fi 243 | 244 | # locate currently installed kernels (may be different to running kernel if 245 | # it's just been updated) 246 | base_ver=$(get_kernel_version) 247 | base_ver=${base_ver%%[-+]*} 248 | # kernels="${base_ver}+ ${base_ver}-v7+ ${base_ver}-v7l+" 249 | # select exact kernel postfix 250 | kernels=${base_ver}$(echo $uname_r | sed -re 's/^[0-9.]+(.*)/\1/g') 251 | 252 | function install_module { 253 | local _i 254 | 255 | src=$1 256 | mod=$2 257 | 258 | mkdir -p /usr/src/$mod-$ver 259 | cp -a $src/* /usr/src/$mod-$ver/ 260 | 261 | dkms add -m $mod -v $ver 262 | for _i in $kernels; do 263 | dkms build -k $_i -m $mod -v $ver && { 264 | dkms install --force -k $_i -m $mod -v $ver 265 | } || { 266 | echo "Can't compile with this kernel, aborting" 267 | echo "Please try to compile with the option --compat-kernel" 268 | exit 1 269 | } 270 | done 271 | 272 | mkdir -p /var/lib/dkms/$mod/$ver/$marker 273 | } 274 | 275 | echo -e "\n### Install sound card driver" 276 | install_module "./" "seeed-voicecard" 277 | 278 | # install dtbos 279 | echo -e "\n### Install device tree overlays" 280 | cp -v seeed-2mic-voicecard.dtbo $OVERLAYS 281 | cp -v seeed-4mic-voicecard.dtbo $OVERLAYS 282 | cp -v seeed-8mic-voicecard.dtbo $OVERLAYS 283 | 284 | # install alsa plugins 285 | # we don't need this plugin now 286 | # install -D ac108_plugin/libasound_module_pcm_ac108.so /usr/lib/arm-linux-gnueabihf/alsa-lib/ 287 | rm -f /usr/lib/arm-linux-gnueabihf/alsa-lib/libasound_module_pcm_ac108.so 288 | 289 | #set kernel modules 290 | echo -e "\n### Codec driver loading at startup (in /etc/modules)" 291 | grep -q "^snd-soc-seeed-voicecard$" /etc/modules || \ 292 | echo "snd-soc-seeed-voicecard" >> /etc/modules 293 | grep -q "^snd-soc-ac108$" /etc/modules || \ 294 | echo "snd-soc-ac108" >> /etc/modules 295 | grep -q "^snd-soc-wm8960$" /etc/modules || \ 296 | echo "snd-soc-wm8960" >> /etc/modules 297 | 298 | #set dtoverlays 299 | CONFIG=/boot/config.txt 300 | [ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt 301 | echo -e "\n### Found boot configuration file $CONFIG" 302 | 303 | sed -i -e 's:#dtparam=i2c_arm=on:dtparam=i2c_arm=on:g' $CONFIG || true 304 | grep -q "^dtoverlay=i2s-mmap$" $CONFIG || \ 305 | echo "dtoverlay=i2s-mmap" >> $CONFIG 306 | 307 | 308 | grep -q "^dtparam=i2s=on$" $CONFIG || \ 309 | echo "dtparam=i2s=on" >> $CONFIG 310 | 311 | #install config files 312 | echo -e "\n### Install alsa and widget configuration" 313 | mkdir /etc/voicecard || true 314 | cp -v *.conf /etc/voicecard 315 | cp -v *.state /etc/voicecard 316 | 317 | #create git repo 318 | echo -e "\n### Manage alsa configuration by git" 319 | git_email=$(git config --global --get user.email) 320 | git_name=$(git config --global --get user.name) 321 | if [ "x${git_email}" == "x" ] || [ "x${git_name}" == "x" ] ; then 322 | echo "setup git config" 323 | git config --global user.email "respeaker@seeed.cc" 324 | git config --global user.name "respeaker" 325 | fi 326 | echo "git init" 327 | git --git-dir=/etc/voicecard/.git init 328 | echo "git add --all" 329 | git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ add --all 330 | echo "git commit -m \"origin configures\"" 331 | git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ commit -m "origin configures" 332 | 333 | echo -e "\n### Start service seeed-voicecard" 334 | echo -e " see /var/log/seeed-voicecard.log for more service information" 335 | cp seeed-voicecard /usr/bin/ 336 | cp seeed-voicecard.service /lib/systemd/system/ 337 | systemctl enable seeed-voicecard.service 338 | systemctl start seeed-voicecard 339 | 340 | echo "------------------------------------------------------" 341 | echo "Please reboot your device to apply all settings" 342 | echo "Enjoy!" 343 | echo "------------------------------------------------------" 344 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | * single codec: 35 | * 0 - allow multi codec 36 | * 1 - yes 37 | */ 38 | #define _SINGLE_CODEC 1 39 | 40 | struct seeed_card_data { 41 | struct snd_soc_card snd_card; 42 | struct seeed_dai_props { 43 | struct asoc_simple_dai cpu_dai; 44 | struct asoc_simple_dai codec_dai; 45 | struct snd_soc_dai_link_component cpus; /* single cpu */ 46 | struct snd_soc_dai_link_component codecs; /* single codec */ 47 | struct snd_soc_dai_link_component platforms; 48 | unsigned int mclk_fs; 49 | } *dai_props; 50 | unsigned int mclk_fs; 51 | unsigned channels_playback_default; 52 | unsigned channels_playback_override; 53 | unsigned channels_capture_default; 54 | unsigned channels_capture_override; 55 | struct snd_soc_dai_link *dai_link; 56 | #if CONFIG_AC10X_TRIG_LOCK 57 | spinlock_t lock; 58 | #endif 59 | struct work_struct work_codec_clk; 60 | #define TRY_STOP_MAX 3 61 | int try_stop; 62 | }; 63 | 64 | struct seeed_card_info { 65 | const char *name; 66 | const char *card; 67 | const char *codec; 68 | const char *platform; 69 | 70 | unsigned int daifmt; 71 | struct asoc_simple_dai cpu_dai; 72 | struct asoc_simple_dai codec_dai; 73 | }; 74 | 75 | #define seeed_priv_to_dev(priv) ((priv)->snd_card.dev) 76 | #define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) 77 | #define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i)) 78 | 79 | #define DAI "sound-dai" 80 | #define CELL "#sound-dai-cells" 81 | #define PREFIX "seeed-voice-card," 82 | 83 | static int seeed_voice_card_startup(struct snd_pcm_substream *substream) 84 | { 85 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 86 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 87 | struct seeed_dai_props *dai_props = 88 | seeed_priv_to_props(priv, rtd->num); 89 | int ret; 90 | 91 | ret = clk_prepare_enable(dai_props->cpu_dai.clk); 92 | if (ret) 93 | return ret; 94 | 95 | ret = clk_prepare_enable(dai_props->codec_dai.clk); 96 | if (ret) 97 | clk_disable_unprepare(dai_props->cpu_dai.clk); 98 | 99 | if (rtd->cpu_dai->driver->playback.channels_min) { 100 | priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min; 101 | } 102 | if (rtd->cpu_dai->driver->capture.channels_min) { 103 | priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min; 104 | } 105 | rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override; 106 | rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override; 107 | rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override; 108 | rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override; 109 | 110 | return ret; 111 | } 112 | 113 | static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) 114 | { 115 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 116 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 117 | struct seeed_dai_props *dai_props = 118 | seeed_priv_to_props(priv, rtd->num); 119 | 120 | rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default; 121 | rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default; 122 | rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default; 123 | rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default; 124 | 125 | clk_disable_unprepare(dai_props->cpu_dai.clk); 126 | 127 | clk_disable_unprepare(dai_props->codec_dai.clk); 128 | } 129 | 130 | static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, 131 | struct snd_pcm_hw_params *params) 132 | { 133 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 134 | struct snd_soc_dai *codec_dai = rtd->codec_dai; 135 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 136 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 137 | struct seeed_dai_props *dai_props = 138 | seeed_priv_to_props(priv, rtd->num); 139 | unsigned int mclk, mclk_fs = 0; 140 | int ret = 0; 141 | 142 | if (priv->mclk_fs) 143 | mclk_fs = priv->mclk_fs; 144 | else if (dai_props->mclk_fs) 145 | mclk_fs = dai_props->mclk_fs; 146 | 147 | if (mclk_fs) { 148 | mclk = params_rate(params) * mclk_fs; 149 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 150 | SND_SOC_CLOCK_IN); 151 | if (ret && ret != -ENOTSUPP) 152 | goto err; 153 | 154 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, 155 | SND_SOC_CLOCK_OUT); 156 | if (ret && ret != -ENOTSUPP) 157 | goto err; 158 | } 159 | return 0; 160 | err: 161 | return ret; 162 | } 163 | 164 | #define _SET_CLOCK_CNT 2 165 | static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop); 166 | 167 | int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) { 168 | if (! _set_clock[stream]) { 169 | _set_clock[stream] = set_clock; 170 | } 171 | return 0; 172 | } 173 | EXPORT_SYMBOL(seeed_voice_card_register_set_clock); 174 | 175 | /* 176 | * work_cb_codec_clk: clear audio codec inner clock. 177 | */ 178 | static void work_cb_codec_clk(struct work_struct *work) 179 | { 180 | struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk); 181 | int r = 0; 182 | 183 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) { 184 | r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); 185 | } 186 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) { 187 | r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); 188 | } 189 | 190 | if (r && priv->try_stop++ < TRY_STOP_MAX) { 191 | if (0 != schedule_work(&priv->work_codec_clk)) {} 192 | } 193 | return; 194 | } 195 | 196 | static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) 197 | { 198 | struct snd_soc_pcm_runtime *rtd = substream->private_data; 199 | struct snd_soc_dai *dai = rtd->codec_dai; 200 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 201 | #if CONFIG_AC10X_TRIG_LOCK 202 | unsigned long flags; 203 | #endif 204 | int ret = 0; 205 | 206 | dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", 207 | __FUNCTION__, snd_pcm_stream_str(substream), cmd, 208 | dai->playback_active, dai->capture_active); 209 | 210 | switch (cmd) { 211 | case SNDRV_PCM_TRIGGER_START: 212 | case SNDRV_PCM_TRIGGER_RESUME: 213 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 214 | if (cancel_work_sync(&priv->work_codec_clk) != 0) {} 215 | #if CONFIG_AC10X_TRIG_LOCK 216 | /* I know it will degrades performance, but I have no choice */ 217 | spin_lock_irqsave(&priv->lock, flags); 218 | #endif 219 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1); 220 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1); 221 | #if CONFIG_AC10X_TRIG_LOCK 222 | spin_unlock_irqrestore(&priv->lock, flags); 223 | #endif 224 | break; 225 | 226 | case SNDRV_PCM_TRIGGER_STOP: 227 | case SNDRV_PCM_TRIGGER_SUSPEND: 228 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 229 | /* capture channel resync, if overrun */ 230 | if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 231 | break; 232 | } 233 | 234 | /* interrupt environment */ 235 | if (in_irq() || in_nmi() || in_serving_softirq()) { 236 | priv->try_stop = 0; 237 | if (0 != schedule_work(&priv->work_codec_clk)) { 238 | } 239 | } else { 240 | if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); 241 | if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); 242 | } 243 | break; 244 | default: 245 | ret = -EINVAL; 246 | } 247 | 248 | return ret; 249 | } 250 | 251 | static struct snd_soc_ops seeed_voice_card_ops = { 252 | .startup = seeed_voice_card_startup, 253 | .shutdown = seeed_voice_card_shutdown, 254 | .hw_params = seeed_voice_card_hw_params, 255 | .trigger = seeed_voice_card_trigger, 256 | }; 257 | 258 | static int asoc_simple_parse_dai(struct device_node *node, 259 | struct snd_soc_dai_link_component *dlc, 260 | int *is_single_link) 261 | { 262 | struct of_phandle_args args; 263 | int ret; 264 | 265 | if (!node) 266 | return 0; 267 | 268 | /* 269 | * Get node via "sound-dai = <&phandle port>" 270 | * it will be used as xxx_of_node on soc_bind_dai_link() 271 | */ 272 | ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); 273 | if (ret) 274 | return ret; 275 | 276 | /* 277 | * FIXME 278 | * 279 | * Here, dlc->dai_name is pointer to CPU/Codec DAI name. 280 | * If user unbinded CPU or Codec driver, but not for Sound Card, 281 | * dlc->dai_name is keeping unbinded CPU or Codec 282 | * driver's pointer. 283 | * 284 | * If user re-bind CPU or Codec driver again, ALSA SoC will try 285 | * to rebind Card via snd_soc_try_rebind_card(), but because of 286 | * above reason, it might can't bind Sound Card. 287 | * Because Sound Card is pointing to released dai_name pointer. 288 | * 289 | * To avoid this rebind Card issue, 290 | * 1) It needs to alloc memory to keep dai_name eventhough 291 | * CPU or Codec driver was unbinded, or 292 | * 2) user need to rebind Sound Card everytime 293 | * if he unbinded CPU or Codec. 294 | */ 295 | ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); 296 | if (ret < 0) 297 | return ret; 298 | 299 | dlc->of_node = args.np; 300 | 301 | if (is_single_link) 302 | *is_single_link = !args.args_count; 303 | 304 | return 0; 305 | } 306 | 307 | static int asoc_simple_init_dai(struct snd_soc_dai *dai, 308 | struct asoc_simple_dai *simple_dai) 309 | { 310 | int ret; 311 | 312 | if (!simple_dai) 313 | return 0; 314 | 315 | if (simple_dai->sysclk) { 316 | ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 317 | simple_dai->clk_direction); 318 | if (ret && ret != -ENOTSUPP) { 319 | dev_err(dai->dev, "simple-card: set_sysclk error\n"); 320 | return ret; 321 | } 322 | } 323 | 324 | if (simple_dai->slots) { 325 | ret = snd_soc_dai_set_bclk_ratio(dai, 326 | simple_dai->slots * 327 | simple_dai->slot_width); 328 | if (ret && ret != -ENOTSUPP) { 329 | dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); 330 | return ret; 331 | } 332 | } 333 | 334 | return 0; 335 | } 336 | 337 | static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) 338 | { 339 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 340 | struct snd_soc_dai *codec = rtd->codec_dai; 341 | struct snd_soc_dai *cpu = rtd->cpu_dai; 342 | struct seeed_dai_props *dai_props = 343 | seeed_priv_to_props(priv, rtd->num); 344 | int ret; 345 | 346 | ret = asoc_simple_init_dai(codec, &dai_props->codec_dai); 347 | if (ret < 0) 348 | return ret; 349 | 350 | ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai); 351 | if (ret < 0) 352 | return ret; 353 | 354 | return 0; 355 | } 356 | 357 | static int seeed_voice_card_dai_link_of(struct device_node *node, 358 | struct seeed_card_data *priv, 359 | int idx, 360 | bool is_top_level_node) 361 | { 362 | struct device *dev = seeed_priv_to_dev(priv); 363 | struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx); 364 | struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx); 365 | struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; 366 | struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; 367 | struct device_node *cpu = NULL; 368 | struct device_node *plat = NULL; 369 | struct device_node *codec = NULL; 370 | char prop[128]; 371 | char *prefix = ""; 372 | int ret, single_cpu; 373 | 374 | /* For single DAI link & old style of DT node */ 375 | if (is_top_level_node) 376 | prefix = PREFIX; 377 | 378 | snprintf(prop, sizeof(prop), "%scpu", prefix); 379 | cpu = of_get_child_by_name(node, prop); 380 | 381 | if (!cpu) { 382 | ret = -EINVAL; 383 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 384 | goto dai_link_of_err; 385 | } 386 | 387 | snprintf(prop, sizeof(prop), "%splat", prefix); 388 | plat = of_get_child_by_name(node, prop); 389 | 390 | snprintf(prop, sizeof(prop), "%scodec", prefix); 391 | codec = of_get_child_by_name(node, prop); 392 | 393 | if (!codec) { 394 | ret = -EINVAL; 395 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 396 | goto dai_link_of_err; 397 | } 398 | 399 | ret = asoc_simple_parse_daifmt(dev, node, codec, 400 | prefix, &dai_link->dai_fmt); 401 | if (ret < 0) 402 | goto dai_link_of_err; 403 | 404 | of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); 405 | 406 | ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu); 407 | if (ret < 0) 408 | goto dai_link_of_err; 409 | 410 | #if _SINGLE_CODEC 411 | ret = asoc_simple_parse_codec(codec, dai_link); 412 | if (ret < 0) 413 | goto dai_link_of_err; 414 | #else 415 | ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); 416 | if (ret < 0) { 417 | dev_err(dev, "parse codec info error %d\n", ret); 418 | goto dai_link_of_err; 419 | } 420 | dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); 421 | #endif 422 | 423 | ret = asoc_simple_parse_platform(plat, dai_link); 424 | if (ret < 0) 425 | goto dai_link_of_err; 426 | 427 | ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, 428 | &cpu_dai->rx_slot_mask, 429 | &cpu_dai->slots, 430 | &cpu_dai->slot_width); 431 | dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n", 432 | cpu_dai->slots, cpu_dai->slot_width, 433 | cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask 434 | ); 435 | if (ret < 0) 436 | goto dai_link_of_err; 437 | 438 | ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, 439 | &codec_dai->rx_slot_mask, 440 | &codec_dai->slots, 441 | &codec_dai->slot_width); 442 | if (ret < 0) 443 | goto dai_link_of_err; 444 | 445 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 446 | ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); 447 | #else 448 | ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 449 | #endif 450 | if (ret < 0) 451 | goto dai_link_of_err; 452 | 453 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) 454 | ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); 455 | #else 456 | ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); 457 | #endif 458 | if (ret < 0) 459 | goto dai_link_of_err; 460 | 461 | #if _SINGLE_CODEC 462 | asoc_simple_canonicalize_platform(dai_link); 463 | #endif 464 | 465 | ret = asoc_simple_set_dailink_name(dev, dai_link, 466 | "%s-%s", 467 | dai_link->cpus->dai_name, 468 | #if _SINGLE_CODEC 469 | dai_link->codecs->dai_name 470 | #else 471 | dai_link->codecs[0].dai_name 472 | #endif 473 | ); 474 | if (ret < 0) 475 | goto dai_link_of_err; 476 | 477 | dai_link->ops = &seeed_voice_card_ops; 478 | dai_link->init = seeed_voice_card_dai_init; 479 | 480 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); 481 | dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); 482 | dev_dbg(dev, "\tcpu : %s / %d\n", 483 | dai_link->cpus->dai_name, 484 | dai_props->cpu_dai.sysclk); 485 | dev_dbg(dev, "\tcodec : %s / %d\n", 486 | #if _SINGLE_CODEC 487 | dai_link->codecs->dai_name, 488 | #else 489 | dai_link->codecs[0].dai_name, 490 | #endif 491 | dai_props->codec_dai.sysclk); 492 | 493 | asoc_simple_canonicalize_cpu(dai_link, single_cpu); 494 | 495 | dai_link_of_err: 496 | of_node_put(cpu); 497 | of_node_put(codec); 498 | 499 | return ret; 500 | } 501 | 502 | static int seeed_voice_card_parse_aux_devs(struct device_node *node, 503 | struct seeed_card_data *priv) 504 | { 505 | struct device *dev = seeed_priv_to_dev(priv); 506 | struct device_node *aux_node; 507 | int i, n, len; 508 | 509 | if (!of_find_property(node, PREFIX "aux-devs", &len)) 510 | return 0; /* Ok to have no aux-devs */ 511 | 512 | n = len / sizeof(__be32); 513 | if (n <= 0) 514 | return -EINVAL; 515 | 516 | priv->snd_card.aux_dev = devm_kzalloc(dev, 517 | n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); 518 | if (!priv->snd_card.aux_dev) 519 | return -ENOMEM; 520 | 521 | for (i = 0; i < n; i++) { 522 | aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); 523 | if (!aux_node) 524 | return -EINVAL; 525 | priv->snd_card.aux_dev[i].dlc.of_node = aux_node; 526 | } 527 | 528 | priv->snd_card.num_aux_devs = n; 529 | return 0; 530 | } 531 | 532 | static int seeed_voice_card_parse_of(struct device_node *node, 533 | struct seeed_card_data *priv) 534 | { 535 | struct device *dev = seeed_priv_to_dev(priv); 536 | struct device_node *dai_link; 537 | int ret; 538 | 539 | if (!node) 540 | return -EINVAL; 541 | 542 | dai_link = of_get_child_by_name(node, PREFIX "dai-link"); 543 | 544 | /* The off-codec widgets */ 545 | if (of_property_read_bool(node, PREFIX "widgets")) { 546 | ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, 547 | PREFIX "widgets"); 548 | if (ret) 549 | goto card_parse_end; 550 | } 551 | 552 | /* DAPM routes */ 553 | if (of_property_read_bool(node, PREFIX "routing")) { 554 | ret = snd_soc_of_parse_audio_routing(&priv->snd_card, 555 | PREFIX "routing"); 556 | if (ret) 557 | goto card_parse_end; 558 | } 559 | 560 | /* Factor to mclk, used in hw_params() */ 561 | of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); 562 | 563 | /* Single/Muti DAI link(s) & New style of DT node */ 564 | if (dai_link) { 565 | struct device_node *np = NULL; 566 | int i = 0; 567 | 568 | for_each_child_of_node(node, np) { 569 | dev_dbg(dev, "\tlink %d:\n", i); 570 | ret = seeed_voice_card_dai_link_of(np, priv, 571 | i, false); 572 | if (ret < 0) { 573 | of_node_put(np); 574 | goto card_parse_end; 575 | } 576 | i++; 577 | } 578 | } else { 579 | /* For single DAI link & old style of DT node */ 580 | ret = seeed_voice_card_dai_link_of(node, priv, 0, true); 581 | if (ret < 0) 582 | goto card_parse_end; 583 | } 584 | 585 | ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX); 586 | if (ret < 0) 587 | goto card_parse_end; 588 | 589 | ret = seeed_voice_card_parse_aux_devs(node, priv); 590 | 591 | priv->channels_playback_default = 0; 592 | priv->channels_playback_override = 2; 593 | priv->channels_capture_default = 0; 594 | priv->channels_capture_override = 2; 595 | of_property_read_u32(node, PREFIX "channels-playback-default", 596 | &priv->channels_playback_default); 597 | of_property_read_u32(node, PREFIX "channels-playback-override", 598 | &priv->channels_playback_override); 599 | of_property_read_u32(node, PREFIX "channels-capture-default", 600 | &priv->channels_capture_default); 601 | of_property_read_u32(node, PREFIX "channels-capture-override", 602 | &priv->channels_capture_override); 603 | 604 | card_parse_end: 605 | of_node_put(dai_link); 606 | 607 | return ret; 608 | } 609 | 610 | static int seeed_voice_card_probe(struct platform_device *pdev) 611 | { 612 | struct seeed_card_data *priv; 613 | struct snd_soc_dai_link *dai_link; 614 | struct seeed_dai_props *dai_props; 615 | struct device_node *np = pdev->dev.of_node; 616 | struct device *dev = &pdev->dev; 617 | int num, ret, i; 618 | 619 | /* Get the number of DAI links */ 620 | if (np && of_get_child_by_name(np, PREFIX "dai-link")) 621 | num = of_get_child_count(np); 622 | else 623 | num = 1; 624 | 625 | /* Allocate the private data and the DAI link array */ 626 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 627 | if (!priv) 628 | return -ENOMEM; 629 | 630 | dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); 631 | dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); 632 | if (!dai_props || !dai_link) 633 | return -ENOMEM; 634 | 635 | /* 636 | * Use snd_soc_dai_link_component instead of legacy style 637 | * It is codec only. but cpu/platform will be supported in the future. 638 | * see 639 | * soc-core.c :: snd_soc_init_multicodec() 640 | * 641 | * "platform" might be removed 642 | * see 643 | * simple-card-utils.c :: asoc_simple_canonicalize_platform() 644 | */ 645 | for (i = 0; i < num; i++) { 646 | dai_link[i].cpus = &dai_props[i].cpus; 647 | dai_link[i].num_cpus = 1; 648 | dai_link[i].codecs = &dai_props[i].codecs; 649 | dai_link[i].num_codecs = 1; 650 | dai_link[i].platforms = &dai_props[i].platforms; 651 | dai_link[i].num_platforms = 1; 652 | } 653 | 654 | priv->dai_props = dai_props; 655 | priv->dai_link = dai_link; 656 | 657 | /* Init snd_soc_card */ 658 | priv->snd_card.owner = THIS_MODULE; 659 | priv->snd_card.dev = dev; 660 | priv->snd_card.dai_link = priv->dai_link; 661 | priv->snd_card.num_links = num; 662 | 663 | if (np && of_device_is_available(np)) { 664 | ret = seeed_voice_card_parse_of(np, priv); 665 | if (ret < 0) { 666 | if (ret != -EPROBE_DEFER) 667 | dev_err(dev, "parse error %d\n", ret); 668 | goto err; 669 | } 670 | } else { 671 | struct seeed_card_info *cinfo; 672 | struct snd_soc_dai_link_component *cpus; 673 | struct snd_soc_dai_link_component *codecs; 674 | struct snd_soc_dai_link_component *platform; 675 | 676 | cinfo = dev->platform_data; 677 | if (!cinfo) { 678 | dev_err(dev, "no info for seeed-voice-card\n"); 679 | return -EINVAL; 680 | } 681 | 682 | if (!cinfo->name || 683 | !cinfo->codec_dai.name || 684 | !cinfo->codec || 685 | !cinfo->platform || 686 | !cinfo->cpu_dai.name) { 687 | dev_err(dev, "insufficient seeed_voice_card_info settings\n"); 688 | return -EINVAL; 689 | } 690 | 691 | cpus = dai_link->cpus; 692 | cpus->dai_name = cinfo->cpu_dai.name; 693 | 694 | codecs = dai_link->codecs; 695 | codecs->name = cinfo->codec; 696 | codecs->dai_name = cinfo->codec_dai.name; 697 | 698 | platform = dai_link->platforms; 699 | platform->name = cinfo->platform; 700 | 701 | priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; 702 | dai_link->name = cinfo->name; 703 | dai_link->stream_name = cinfo->name; 704 | dai_link->dai_fmt = cinfo->daifmt; 705 | dai_link->init = seeed_voice_card_dai_init; 706 | memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, 707 | sizeof(priv->dai_props->cpu_dai)); 708 | memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, 709 | sizeof(priv->dai_props->codec_dai)); 710 | } 711 | 712 | snd_soc_card_set_drvdata(&priv->snd_card, priv); 713 | 714 | #if CONFIG_AC10X_TRIG_LOCK 715 | spin_lock_init(&priv->lock); 716 | #endif 717 | 718 | INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk); 719 | 720 | ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); 721 | if (ret >= 0) 722 | return ret; 723 | 724 | err: 725 | asoc_simple_clean_reference(&priv->snd_card); 726 | 727 | return ret; 728 | } 729 | 730 | static int seeed_voice_card_remove(struct platform_device *pdev) 731 | { 732 | struct snd_soc_card *card = platform_get_drvdata(pdev); 733 | struct seeed_card_data *priv = snd_soc_card_get_drvdata(card); 734 | 735 | if (cancel_work_sync(&priv->work_codec_clk) != 0) { 736 | } 737 | return asoc_simple_clean_reference(card); 738 | } 739 | 740 | static const struct of_device_id seeed_voice_of_match[] = { 741 | { .compatible = "seeed-voicecard", }, 742 | {}, 743 | }; 744 | MODULE_DEVICE_TABLE(of, seeed_voice_of_match); 745 | 746 | static struct platform_driver seeed_voice_card = { 747 | .driver = { 748 | .name = "seeed-voicecard", 749 | .pm = &snd_soc_pm_ops, 750 | .of_match_table = seeed_voice_of_match, 751 | }, 752 | .probe = seeed_voice_card_probe, 753 | .remove = seeed_voice_card_remove, 754 | }; 755 | 756 | module_platform_driver(seeed_voice_card); 757 | 758 | MODULE_ALIAS("platform:seeed-voice-card"); 759 | MODULE_LICENSE("GPL v2"); 760 | MODULE_DESCRIPTION("ASoC SEEED Voice Card"); 761 | MODULE_AUTHOR("PeterYang"); 762 | --------------------------------------------------------------------------------