Compare commits
272 commits
v3.0
...
v6.6-order
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c3f0119ae9 | ||
![]() |
c693d203d9 | ||
![]() |
005fc23648 | ||
![]() |
58082f3fe8 | ||
![]() |
cc37c8fb52 | ||
![]() |
c3a2d96ba2 | ||
![]() |
7aef82f3be | ||
![]() |
4ab8158c18 | ||
![]() |
8f377d1eaa | ||
![]() |
aaa2090dbf | ||
![]() |
f25e76508f | ||
![]() |
a46c3f6324 | ||
![]() |
2a40c7dbff | ||
![]() |
f69370b841 | ||
![]() |
a03624a0ce | ||
![]() |
0bb3ca6c98 | ||
![]() |
18e297d2d8 | ||
![]() |
280bb390a2 | ||
![]() |
19ca5aeb50 | ||
![]() |
ebd1fa605c | ||
![]() |
a57389693c | ||
![]() |
977a7ff321 | ||
![]() |
4d0e36d426 | ||
![]() |
99bbf880e9 | ||
![]() |
8dbe3726b7 | ||
![]() |
e452172d8c | ||
![]() |
a8ee041f21 | ||
![]() |
6bf1dbd04e | ||
![]() |
b093f59250 | ||
![]() |
8fa56e18bb | ||
![]() |
dd9391fb78 | ||
![]() |
d840f77541 | ||
![]() |
bd7623e9fb | ||
![]() |
17e2875d4d | ||
![]() |
b595b95b21 | ||
![]() |
48dbb3f815 | ||
![]() |
41a09a3b67 | ||
![]() |
81e0f0879d | ||
![]() |
d841b2a0fb | ||
![]() |
f9427ea449 | ||
![]() |
efa999f0f3 | ||
![]() |
d688a02f14 | ||
![]() |
a1a51f8aab | ||
![]() |
31d750c981 | ||
![]() |
c8d97904ce | ||
![]() |
c5f3836dd8 | ||
![]() |
19067f3333 | ||
![]() |
5793cf6bf0 | ||
![]() |
ffcdafb798 | ||
![]() |
e553d4f851 | ||
![]() |
de70691526 | ||
![]() |
1774a54301 | ||
![]() |
af484de3af | ||
![]() |
9796ad49ff | ||
![]() |
43b6034266 | ||
![]() |
eab694c170 | ||
![]() |
fdd67ea2c4 | ||
![]() |
f8d47b5c64 | ||
![]() |
47d09dba0e | ||
![]() |
d692927067 | ||
![]() |
14317817f8 | ||
![]() |
3ba6aec5bb | ||
![]() |
8b2094fc23 | ||
![]() |
c84b644247 | ||
![]() |
8e1c59be7e | ||
![]() |
f289f5586e | ||
![]() |
3ad10c131c | ||
![]() |
cbb5e082f2 | ||
![]() |
43532cfada | ||
![]() |
eea6066d70 | ||
![]() |
44d47226dc | ||
![]() |
50dbe24b78 | ||
![]() |
ebcf755c1a | ||
![]() |
484c03d4c8 | ||
![]() |
a79b7d94a5 | ||
![]() |
87f42ed64d | ||
![]() |
379f791b3c | ||
![]() |
8cce4e8ffa | ||
![]() |
ac2bf5f638 | ||
![]() |
4b7559b320 | ||
![]() |
526d0ddef9 | ||
![]() |
6fb0c63581 | ||
![]() |
06e054aa06 | ||
![]() |
1a7fbee50e | ||
![]() |
7e635f5d57 | ||
![]() |
7461921407 | ||
![]() |
794d7a3527 | ||
![]() |
c206a43374 | ||
![]() |
e0f9a554fc | ||
![]() |
e85006b775 | ||
![]() |
921ac8fba5 | ||
![]() |
30ceb546fa | ||
![]() |
e1110617b2 | ||
![]() |
a891e0260e | ||
![]() |
c1c2e5ca56 | ||
![]() |
f1a46d63cc | ||
![]() |
2e8586d547 | ||
![]() |
ac1dddeaed | ||
![]() |
6a30a3efc6 | ||
![]() |
84247b0037 | ||
![]() |
9bb3afb358 | ||
![]() |
2baddde5ba | ||
![]() |
b5def1af8e | ||
![]() |
0abc6ed072 | ||
![]() |
30216a9c7c | ||
![]() |
6af23967a5 | ||
![]() |
74620fbdbf | ||
![]() |
4cec9e73df | ||
![]() |
957b1298d5 | ||
![]() |
901b451bd5 | ||
![]() |
b83519eb82 | ||
![]() |
1372794704 | ||
![]() |
19cfed5bd7 | ||
![]() |
6875869cb2 | ||
![]() |
9285159a7d | ||
![]() |
90144a2f2e | ||
![]() |
cbd6da78fc | ||
![]() |
cf98b67079 | ||
![]() |
c4c112dcaf | ||
![]() |
9722cb01e4 | ||
![]() |
f4f303f9bd | ||
![]() |
de23c61210 | ||
![]() |
cca7bbfe2a | ||
![]() |
5ca9ab45df | ||
![]() |
8c25d81890 | ||
![]() |
f47375e54d | ||
![]() |
1f7323417b | ||
![]() |
e82e6fde67 | ||
![]() |
cd899a83d5 | ||
![]() |
e750de0ecc | ||
![]() |
efaa210d79 | ||
![]() |
e6e5240922 | ||
![]() |
8ea751c4e9 | ||
![]() |
e2529f9eee | ||
![]() |
6aff6e0b33 | ||
![]() |
bb09e9d24b | ||
![]() |
edddb2fbe4 | ||
![]() |
655d7fe57b | ||
![]() |
d64e994982 | ||
![]() |
673305fc4e | ||
![]() |
47d97e7bd5 | ||
![]() |
955707d944 | ||
![]() |
cf2a5fe807 | ||
![]() |
471f88b337 | ||
![]() |
d5b71a009d | ||
![]() |
bccf557d62 | ||
![]() |
a5095cd7b2 | ||
![]() |
bcdc10e193 | ||
![]() |
ebb9a9d82a | ||
![]() |
783eff8779 | ||
![]() |
ec5fc9d9b8 | ||
![]() |
bb1422182b | ||
![]() |
f2b2516426 | ||
![]() |
e26006d40c | ||
![]() |
138d22226e | ||
![]() |
a24da444df | ||
![]() |
c3adcde556 | ||
![]() |
1452d85e0f | ||
![]() |
329c8090ea | ||
![]() |
53c0db449b | ||
![]() |
89f8a76646 | ||
![]() |
39c134f430 | ||
![]() |
17944791bb | ||
![]() |
df68ab22d6 | ||
![]() |
88aae48c41 | ||
![]() |
6bcd4cad2f | ||
![]() |
7f88e11fd0 | ||
![]() |
60e90d6fae | ||
![]() |
5cefec4cca | ||
![]() |
1f9c1464ab | ||
![]() |
71f01070d8 | ||
![]() |
19d1e6eb88 | ||
![]() |
07dc49259a | ||
![]() |
d8775ccb93 | ||
![]() |
c3ddbb521d | ||
![]() |
1456785779 | ||
![]() |
02eccbcd55 | ||
![]() |
131ee8187d | ||
![]() |
ed615d2318 | ||
![]() |
2cc009619e | ||
![]() |
aaf0cc2c61 | ||
![]() |
a6db04e724 | ||
![]() |
0be1de9750 | ||
![]() |
fcbc3b6d6b | ||
![]() |
c2b419c286 | ||
![]() |
ed094dece5 | ||
![]() |
51966603da | ||
![]() |
6afe18224a | ||
![]() |
953fa50f4f | ||
![]() |
4396e4f834 | ||
![]() |
26754b0821 | ||
![]() |
af8668ff97 | ||
![]() |
2d08bb0b35 | ||
![]() |
c2f81c1f23 | ||
![]() |
63fa92db39 | ||
![]() |
d2c0f1b5c9 | ||
![]() |
775005d865 | ||
![]() |
8c80018c3e | ||
![]() |
076d12f7fb | ||
![]() |
c0f1dfa8bb | ||
![]() |
67f7942cfd | ||
![]() |
86c198d7dc | ||
![]() |
1d4abfa41d | ||
![]() |
f52ed1b363 | ||
![]() |
97146337cb | ||
![]() |
1046e30108 | ||
![]() |
97fd3f2eab | ||
![]() |
60d2a5129d | ||
![]() |
6b4133b5f0 | ||
![]() |
0ed11aa9a4 | ||
![]() |
2e1940bb0f | ||
![]() |
6c6dfaa7d3 | ||
![]() |
468b40eee5 | ||
![]() |
07a6c4659e | ||
![]() |
ad4d66d3f7 | ||
![]() |
add12477e9 | ||
![]() |
74d65bfcbd | ||
![]() |
deed034f31 | ||
![]() |
2563020e29 | ||
![]() |
30276c80bc | ||
![]() |
82c8d1024a | ||
![]() |
476378c156 | ||
![]() |
4c15036368 | ||
![]() |
cb9bf1c913 | ||
![]() |
37a37a6d16 | ||
![]() |
de22f92298 | ||
![]() |
72d3c9e2f6 | ||
![]() |
b9272dcee8 | ||
![]() |
61022875b3 | ||
![]() |
41c71f1b45 | ||
![]() |
8c5ffaeed3 | ||
![]() |
920ef83b63 | ||
![]() |
48463b9a50 | ||
![]() |
2da704e872 | ||
![]() |
d6f808b278 | ||
![]() |
873580cfd5 | ||
![]() |
ae32476755 | ||
![]() |
277aeacb81 | ||
![]() |
08f0d96d68 | ||
![]() |
da5f90473b | ||
![]() |
0ea5d2bd98 | ||
![]() |
c7026a4048 | ||
![]() |
11e9db79b5 | ||
![]() |
90d2673610 | ||
![]() |
09728d1c82 | ||
![]() |
b98fce84e0 | ||
![]() |
6c6b9ab7de | ||
![]() |
27b6cdf252 | ||
![]() |
f5b82b6b7d | ||
![]() |
5744fd1bbb | ||
![]() |
95f4fe0688 | ||
![]() |
3392bf394b | ||
![]() |
0965139ead | ||
![]() |
be0812c70b | ||
![]() |
dfc7b404a5 | ||
![]() |
deeddaabb4 | ||
![]() |
bfe6978ef4 | ||
![]() |
67b4504964 | ||
![]() |
bca16c68c4 | ||
![]() |
ab0b28d08f | ||
![]() |
a73335b8d0 | ||
![]() |
58c6233325 | ||
![]() |
f85c98c4e0 | ||
![]() |
7009515ec9 | ||
![]() |
8c499c2268 | ||
![]() |
9a9f4368b4 | ||
![]() |
3d56af9d3c | ||
![]() |
3074de1f86 | ||
![]() |
33ddbd39d3 | ||
![]() |
983d4834bb | ||
![]() |
a8d0a23583 | ||
![]() |
ccac09b842 |
37 changed files with 2875 additions and 643 deletions
35
Makefile
35
Makefile
|
@ -1,3 +1,17 @@
|
|||
#
|
||||
# Peter Yang <turmary@126.com>
|
||||
# Copyright (c) 2019 Seeed Studio
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
|
||||
uname_r=$(shell uname -r)
|
||||
|
||||
# If KERNELRELEASE is defined, we've been invoked from the
|
||||
# kernel build system and can use its language
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
# $(warning KERNELVERSION=$(KERNELVERSION))
|
||||
|
||||
snd-soc-wm8960-objs := wm8960.o
|
||||
snd-soc-ac108-objs := ac108.o ac101.o
|
||||
snd-soc-seeed-voicecard-objs := seeed-voicecard.o
|
||||
|
@ -14,14 +28,25 @@ endif
|
|||
endif
|
||||
|
||||
|
||||
|
||||
else
|
||||
|
||||
DEST := /lib/modules/$(uname_r)/kernel
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
make -C /lib/modules/$(uname_r)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
make -C /lib/modules/$(uname_r)/build M=$(PWD) clean
|
||||
|
||||
install:
|
||||
sudo cp snd-soc-ac108.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/
|
||||
sudo cp snd-soc-wm8960.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/
|
||||
sudo cp snd-soc-seeed-voicecard.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/bcm/
|
||||
sudo cp snd-soc-ac108.ko ${DEST}/sound/soc/codecs/
|
||||
sudo cp snd-soc-wm8960.ko ${DEST}/sound/soc/codecs/
|
||||
sudo cp snd-soc-seeed-voicecard.ko ${DEST}/sound/soc/bcm/
|
||||
sudo depmod -a
|
||||
|
||||
|
||||
.PHONY: all clean install
|
||||
|
||||
endif
|
||||
|
||||
|
|
243
README.md
243
README.md
|
@ -1,231 +1,20 @@
|
|||
# seeed-voicecard
|
||||
|
||||
[](https://gitter.im/seeed-voicecard/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
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.
|
||||
The drivers for [ReSpeaker Mic Hat](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html), [ReSpeaker 4 Mic Array](https://www.seeedstudio.com/ReSpeaker-4-Mic-Array-for-Raspberry-Pi-p-2941.html), [6-Mics Circular Array Kit](), and [4-Mics Linear Array Kit]() for Raspberry Pi.
|
||||
|
||||
### Install seeed-voicecard
|
||||
Get the seeed voice card source code. and install all linux kernel drivers
|
||||
Get the seeed voice card source code and install all linux kernel drivers
|
||||
```bash
|
||||
git clone https://github.com/respeaker/seeed-voicecard
|
||||
git clone https://github.com/HinTak/seeed-voicecard
|
||||
cd seeed-voicecard
|
||||
sudo ./install.sh
|
||||
sudo reboot
|
||||
```
|
||||
## ReSpeaker Documentation
|
||||
|
||||
## ReSpeaker Mic Hat
|
||||
Up to date documentation for reSpeaker products can be found in [Seeed Studio Wiki](https://wiki.seeedstudio.com/ReSpeaker/)!
|
||||

|
||||
|
||||
[](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html)
|
||||
|
||||
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.
|
||||
|
||||
Check that the sound card name matches the source code seeed-voicecard.
|
||||
|
||||
```bash
|
||||
#for ReSpeaker 2-mic
|
||||
pi@raspberrypi:~/seeed-voicecard $ aplay -l
|
||||
**** List of PLAYBACK Hardware Devices ****
|
||||
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
|
||||
Subdevices: 8/8
|
||||
Subdevice #0: subdevice #0
|
||||
Subdevice #1: subdevice #1
|
||||
Subdevice #2: subdevice #2
|
||||
Subdevice #3: subdevice #3
|
||||
Subdevice #4: subdevice #4
|
||||
Subdevice #5: subdevice #5
|
||||
Subdevice #6: subdevice #6
|
||||
Subdevice #7: subdevice #7
|
||||
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
card 1: seeed2micvoicec [seeed-2mic-voicecard], device 0: bcm2835-i2s-wm8960-hifi wm8960-hifi-0 []
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
pi@raspberrypi:~/seeed-voicecard $ arecord -l
|
||||
**** List of CAPTURE Hardware Devices ****
|
||||
card 1: seeed2micvoicec [seeed-2mic-voicecard], device 0: bcm2835-i2s-wm8960-hifi wm8960-hifi-0 []
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
pi@raspberrypi:~/seeed-voicecard $
|
||||
```
|
||||
If you want to change the alsa settings, You can use `sudo alsactl --file=/etc/voicecard/wm8960_asound.state store` to save it.
|
||||
|
||||
|
||||
|
||||
#### Next step
|
||||
Go to https://github.com/respeaker/mic_hat to build voice enabled projects with Google Assistant SDK or Alexa Voice Service.
|
||||
|
||||
## ReSpeaker 4 Mic Array
|
||||
|
||||
[](https://www.seeedstudio.com/ReSpeaker-4-Mic-Array-for-Raspberry-Pi-p-2941.html)
|
||||
|
||||
The 4 Mic Array uses ac108 which includes 4 ADCs, we also write ac108 rapberry pi linux kernel driver.
|
||||
|
||||
Check that the sound card name matches the source code seeed-voicecard.
|
||||
|
||||
```bash
|
||||
#for ReSpeaker 4 Mic Array
|
||||
pi@raspberrypi:~ $ arecord -L
|
||||
null
|
||||
Discard all samples (playback) or generate zero samples (capture)
|
||||
playback
|
||||
capture
|
||||
dmixed
|
||||
array
|
||||
ac108
|
||||
default:CARD=seeed4micvoicec
|
||||
seeed-4mic-voicecard,
|
||||
Default Audio Device
|
||||
sysdefault:CARD=seeed4micvoicec
|
||||
seeed-4mic-voicecard,
|
||||
Default Audio Device
|
||||
dmix:CARD=seeed4micvoicec,DEV=0
|
||||
seeed-4mic-voicecard,
|
||||
Direct sample mixing device
|
||||
dsnoop:CARD=seeed4micvoicec,DEV=0
|
||||
seeed-4mic-voicecard,
|
||||
Direct sample snooping device
|
||||
hw:CARD=seeed4micvoicec,DEV=0
|
||||
seeed-4mic-voicecard,
|
||||
Direct hardware device without any conversions
|
||||
plughw:CARD=seeed4micvoicec,DEV=0
|
||||
seeed-4mic-voicecard,
|
||||
Hardware device with all software conversions
|
||||
pi@raspberrypi:~ $
|
||||
```
|
||||
If you want to change the alsa settings, You can use `sudo alsactl --file=/etc/voicecard/ac108_asound.state store` to save it.
|
||||
|
||||
## 6-Mics Circular Array Kit
|
||||
|
||||
[]()
|
||||
|
||||
The 6 Mics Circular Array Kit uses ac108 x 2 / ac101 x 1 / micphones x 6, includes 8 ADCs and 2 DACs.
|
||||
|
||||
The driver is implemented with 8 input channels & 8 output channels.
|
||||
>**The first 6 input channel are MIC recording data,
|
||||
the rest 2 input channel are echo channel of playback
|
||||
The first 2 output channel are playing data, the rest 6 output channel are dummy**
|
||||
|
||||
|
||||
Check that the sound card name matches the source code seeed-voicecard.
|
||||
```bash
|
||||
#for 6 Mic Circular Array
|
||||
pi@raspberrypi:~ $ arecord -L
|
||||
null
|
||||
Discard all samples (playback) or generate zero samples (capture)
|
||||
default
|
||||
playback
|
||||
dmixed
|
||||
ac108
|
||||
multiapps
|
||||
ac101
|
||||
sysdefault:CARD=seeed8micvoicec
|
||||
seeed-8mic-voicecard,
|
||||
Default Audio Device
|
||||
dmix:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct sample mixing device
|
||||
dsnoop:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct sample snooping device
|
||||
hw:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct hardware device without any conversions
|
||||
plughw:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Hardware device with all software conversions
|
||||
|
||||
pi@raspberrypi:~ $ aplay -L
|
||||
null
|
||||
Discard all samples (playback) or generate zero samples (capture)
|
||||
default
|
||||
playback
|
||||
dmixed
|
||||
ac108
|
||||
multiapps
|
||||
ac101
|
||||
sysdefault:CARD=ALSA
|
||||
bcm2835 ALSA, bcm2835 ALSA
|
||||
Default Audio Device
|
||||
dmix:CARD=ALSA,DEV=0
|
||||
bcm2835 ALSA, bcm2835 ALSA
|
||||
Direct sample mixing device
|
||||
dmix:CARD=ALSA,DEV=1
|
||||
bcm2835 ALSA, bcm2835 IEC958/HDMI
|
||||
Direct sample mixing device
|
||||
dsnoop:CARD=ALSA,DEV=0
|
||||
bcm2835 ALSA, bcm2835 ALSA
|
||||
Direct sample snooping device
|
||||
dsnoop:CARD=ALSA,DEV=1
|
||||
bcm2835 ALSA, bcm2835 IEC958/HDMI
|
||||
Direct sample snooping device
|
||||
hw:CARD=ALSA,DEV=0
|
||||
bcm2835 ALSA, bcm2835 ALSA
|
||||
Direct hardware device without any conversions
|
||||
hw:CARD=ALSA,DEV=1
|
||||
bcm2835 ALSA, bcm2835 IEC958/HDMI
|
||||
Direct hardware device without any conversions
|
||||
plughw:CARD=ALSA,DEV=0
|
||||
bcm2835 ALSA, bcm2835 ALSA
|
||||
Hardware device with all software conversions
|
||||
plughw:CARD=ALSA,DEV=1
|
||||
bcm2835 ALSA, bcm2835 IEC958/HDMI
|
||||
Hardware device with all software conversions
|
||||
sysdefault:CARD=seeed8micvoicec
|
||||
seeed-8mic-voicecard,
|
||||
Default Audio Device
|
||||
dmix:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct sample mixing device
|
||||
dsnoop:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct sample snooping device
|
||||
hw:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Direct hardware device without any conversions
|
||||
plughw:CARD=seeed8micvoicec,DEV=0
|
||||
seeed-8mic-voicecard,
|
||||
Hardware device with all software conversions
|
||||
```
|
||||
|
||||
## 4-Mics Linear Array Kit
|
||||
|
||||
[]()
|
||||
|
||||
In contrast to 6-Mics Circular Array Kit for Raspberry Pi,
|
||||
the difference is only first 4 input channels are valid capture data.
|
||||
|
||||
### Usage:
|
||||
```bash
|
||||
#for ReSpeaker 2-mic
|
||||
#It will capture sound an playback on hw:1
|
||||
arecord -f cd -Dhw:1 | aplay -Dhw:1
|
||||
```
|
||||
|
||||
```bash
|
||||
#for ReSpeaker 4-mic
|
||||
#It will capture sound on AC108 and save as a.wav
|
||||
arecord -Dac108 -f S32_LE -r 16000 -c 4 a.wav
|
||||
```
|
||||
|
||||
```bash
|
||||
#for 6-Mics Circular Array Kit and 4-Mics Linear Array Kit
|
||||
#It will capture sound on AC108 and save as a.wav
|
||||
arecord -Dac108 -f S32_LE -r 16000 -c 8 a.wav
|
||||
#Take care of that the captured mic audio is on the first 6 channels
|
||||
|
||||
#It will play sound file a.wav on AC101
|
||||
aplay -D ac101 a.wav
|
||||
#Do not use -D plughw:1,0 directly except your wave file is single channel only.
|
||||
|
||||
#Doing capture && playback the same time
|
||||
arecord -D hw:1,0 -f S32_LE -r 16000 -c 8 to_be_record.wav &
|
||||
#mono_to_play.wav is a mono channel wave file to play
|
||||
aplay -D plughw:1,0 -r 16000 mono_to_play.wav
|
||||
```
|
||||
**Note: Limit for developer using 6-Mics Circular Array Kit(or 4-Mics Linear Array Kit) doing capture & playback the same time:
|
||||
1. capture must be start first, or else the capture channels will possibly be disorder.
|
||||
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.**
|
||||
|
||||
### Coherence
|
||||
|
||||
|
@ -255,7 +44,21 @@ Thank you!
|
|||
------------------------------------------------------
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Enjoy !
|
||||
|
||||
### Technical support
|
||||
|
||||
For hardware testing purposes we made a Rasperry Pi OS 5.10.17-v7l+ 32-bit image with reSpeaker drivers pre-installed, which you can download by clicking on [this link](https://files.seeedstudio.com/linux/Raspberry%20Pi%204%20reSpeaker/2021-05-07-raspios-buster-armhf-lite-respeaker.img.xz).
|
||||
|
||||
We provide official support for using reSpeaker with the following OS:
|
||||
- 32-bit Raspberry Pi OS
|
||||
- 64-bit Raspberry Pi OS
|
||||
|
||||
And following hardware platforms:
|
||||
- Raspberry Pi 3 (all models), Raspberry Pi 4 (all models)
|
||||
|
||||
Anything beyond the scope of official support is considered to be community supported. Support for other OS/hardware platforms can be added, provided MOQ requirements can be met.
|
||||
|
||||
If you have a technical problem when using reSpeaker with one of the officially supported platforms/OS, feel free to create an issue on Github. For general questions or suggestions, please use [Seeed forum](https://forum.seeedstudio.com/c/products/respeaker/15).
|
||||
|
||||
|
||||
|
|
189
ac101.c
189
ac101.c
|
@ -37,6 +37,7 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include "ac101_regs.h"
|
||||
#include "ac10x.h"
|
||||
|
||||
|
@ -77,6 +78,8 @@ int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
|
|||
int r, v = 0;
|
||||
|
||||
if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) {
|
||||
dev_err(codec->dev, "read reg %02X fail\n",
|
||||
reg);
|
||||
return r;
|
||||
}
|
||||
return v;
|
||||
|
@ -84,16 +87,20 @@ int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
|
|||
|
||||
int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
int v;
|
||||
|
||||
return regmap_write(ac10x->regmap101, reg, val);
|
||||
v = regmap_write(ac10x->regmap101, reg, val);
|
||||
return v;
|
||||
}
|
||||
|
||||
int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg,
|
||||
unsigned mask, unsigned value
|
||||
) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
int v;
|
||||
|
||||
return regmap_update_bits(ac10x->regmap101, reg, mask, value);
|
||||
v = regmap_update_bits(ac10x->regmap101, reg, mask, value);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
@ -112,7 +119,7 @@ static void switch_hw_config(struct snd_soc_codec *codec)
|
|||
{
|
||||
int r;
|
||||
|
||||
AC101_DBG("%s,line:%d\n",__func__,__LINE__);
|
||||
AC101_DBG();
|
||||
|
||||
/*HMIC/MMIC BIAS voltage level select:2.5v*/
|
||||
ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<<BIASVOLTAGE), (0xf<<BIASVOLTAGE));
|
||||
|
@ -155,7 +162,7 @@ static void switch_hw_config(struct snd_soc_codec *codec)
|
|||
*/
|
||||
static void switch_status_update(struct ac10x_priv *ac10x)
|
||||
{
|
||||
AC101_DBG("%s,line:%d,ac10x->state:%d\n", __func__, __LINE__, ac10x->state);
|
||||
AC101_DBG("ac10x->state:%d\n", ac10x->state);
|
||||
|
||||
input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state);
|
||||
input_sync(ac10x->inpdev);
|
||||
|
@ -176,7 +183,7 @@ static void work_cb_clear_irq(struct work_struct *work)
|
|||
reg_val = ac101_read(codec, HMIC_STS);
|
||||
if (BIT(HMIC_PULLOUT_PEND) & reg_val) {
|
||||
ac10x->pullout_cntr++;
|
||||
AC101_DBG("ac10x->pullout_cntr: %d\n",ac10x->pullout_cntr);
|
||||
AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr);
|
||||
}
|
||||
|
||||
reg_val |= HMIC_PEND_ALL;
|
||||
|
@ -210,17 +217,17 @@ static int __ac101_get_hmic_data(struct snd_soc_codec *codec) {
|
|||
#ifdef AC101_DEBG
|
||||
static long counter;
|
||||
#endif
|
||||
int r;
|
||||
int d;
|
||||
int r, d;
|
||||
|
||||
d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS));
|
||||
|
||||
r = 0x1 << HMIC_DATA_PEND;
|
||||
ac101_write(codec, HMIC_STS, r);
|
||||
|
||||
AC101_DBG("%s,line:%d HMIC_DATA(%3ld): %02X\n", __func__, __LINE__,
|
||||
counter++, d
|
||||
);
|
||||
/* prevent i2c accessing too frequently */
|
||||
usleep_range(1500, 3000);
|
||||
|
||||
AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
@ -255,7 +262,7 @@ static void work_cb_earphone_switch(struct work_struct *work)
|
|||
input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1);
|
||||
input_sync(ac10x->inpdev);
|
||||
|
||||
AC101_DBG("%s,line:%d KEY_HEADSETHOOK1\n", __func__, __LINE__);
|
||||
AC101_DBG("KEY_HEADSETHOOK1\n");
|
||||
|
||||
if (hook_flag1 != hook_flag2)
|
||||
hook_flag1 = hook_flag2 = 0;
|
||||
|
@ -273,7 +280,7 @@ static void work_cb_earphone_switch(struct work_struct *work)
|
|||
input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0);
|
||||
input_sync(ac10x->inpdev);
|
||||
|
||||
AC101_DBG("%s,line:%d HMIC_DATA: %d KEY_VOLUMEUP\n", __func__, __LINE__, t);
|
||||
AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t);
|
||||
}
|
||||
if (ac10x->pullout_cntr)
|
||||
ac10x->pullout_cntr--;
|
||||
|
@ -286,7 +293,7 @@ static void work_cb_earphone_switch(struct work_struct *work)
|
|||
input_sync(ac10x->inpdev);
|
||||
input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0);
|
||||
input_sync(ac10x->inpdev);
|
||||
AC101_DBG("%s,line:%d KEY_VOLUMEDOWN\n", __func__, __LINE__);
|
||||
AC101_DBG("KEY_VOLUMEDOWN\n");
|
||||
}
|
||||
if (ac10x->pullout_cntr)
|
||||
ac10x->pullout_cntr--;
|
||||
|
@ -302,7 +309,7 @@ static void work_cb_earphone_switch(struct work_struct *work)
|
|||
input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0);
|
||||
input_sync(ac10x->inpdev);
|
||||
|
||||
AC101_DBG("%s,line:%d KEY_HEADSETHOOK0\n", __func__, __LINE__);
|
||||
AC101_DBG("KEY_HEADSETHOOK0\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -356,7 +363,7 @@ static irqreturn_t audio_hmic_irq(int irq, void *para)
|
|||
|
||||
static int ac101_switch_probe(struct ac10x_priv *ac10x) {
|
||||
struct i2c_client *i2c = ac10x->i2c101;
|
||||
int ret;
|
||||
long ret;
|
||||
|
||||
ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN);
|
||||
if (IS_ERR(ac10x->gpiod_irq)) {
|
||||
|
@ -369,7 +376,7 @@ static int ac101_switch_probe(struct ac10x_priv *ac10x) {
|
|||
|
||||
ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq);
|
||||
if (IS_ERR_VALUE(ac10x->irq)) {
|
||||
pr_info("[ac101] map gpio to irq failed, errno = %d\n", ac10x->irq);
|
||||
pr_warn("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq);
|
||||
ac10x->irq = 0;
|
||||
goto _err_irq;
|
||||
}
|
||||
|
@ -377,7 +384,7 @@ static int ac101_switch_probe(struct ac10x_priv *ac10x) {
|
|||
/* request irq, set irq type to falling edge trigger */
|
||||
ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq, IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x);
|
||||
if (IS_ERR_VALUE(ret)) {
|
||||
pr_info("[ac101] request virq %d failed, errno = %d\n", ac10x->irq, ret);
|
||||
pr_warn("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret);
|
||||
goto _err_irq;
|
||||
}
|
||||
|
||||
|
@ -426,7 +433,7 @@ _err_input_register_device:
|
|||
_err_input_allocate_device:
|
||||
|
||||
if (ac10x->irq) {
|
||||
devm_free_irq(&i2c->dev, ac10x->irq, NULL);
|
||||
devm_free_irq(&i2c->dev, ac10x->irq, ac10x);
|
||||
ac10x->irq = 0;
|
||||
}
|
||||
_err_irq:
|
||||
|
@ -580,7 +587,7 @@ static int late_enable_dac(struct snd_soc_codec* codec, int event) {
|
|||
mutex_lock(&ac10x->dac_mutex);
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
AC101_DBG("%s,line:%d\n",__func__,__LINE__);
|
||||
AC101_DBG();
|
||||
if (ac10x->dac_enable == 0){
|
||||
/*enable dac module clk*/
|
||||
ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG), (0x1<<MOD_CLK_DAC_DIG));
|
||||
|
@ -610,7 +617,7 @@ static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
|
|||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
/*open*/
|
||||
AC101_DBG("post:open:%s,line:%d\n", __func__, __LINE__);
|
||||
AC101_DBG("post:open\n");
|
||||
ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0xf<<HPOUTPUTENABLE));
|
||||
msleep(10);
|
||||
ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x1<<HPPA_EN));
|
||||
|
@ -618,7 +625,7 @@ static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
|
|||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
/*close*/
|
||||
AC101_DBG("pre:close:%s,line:%d\n", __func__, __LINE__);
|
||||
AC101_DBG("pre:close\n");
|
||||
ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x0<<LHPPA_MUTE));
|
||||
msleep(10);
|
||||
ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0x0<<HPOUTPUTENABLE));
|
||||
|
@ -637,9 +644,7 @@ static int ac101_sysclk_started(void) {
|
|||
|
||||
static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
/* spin_lock move to machine trigger */
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
|
@ -653,9 +658,9 @@ static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
|
|||
ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
|
||||
|
||||
if (ret) {
|
||||
AC101_DBG("%s() L%d start sysclk failed\n", __func__, __LINE__);
|
||||
AC101_DBG("start sysclk failed\n");
|
||||
} else {
|
||||
AC101_DBG("%s() L%d hw sysclk enable\n", __func__, __LINE__);
|
||||
AC101_DBG("hw sysclk enable\n");
|
||||
ac10x->aif1_clken++;
|
||||
}
|
||||
}
|
||||
|
@ -669,19 +674,18 @@ static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
|
|||
ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x0<<SYSCLK_ENA));
|
||||
|
||||
if (ret) {
|
||||
AC101_DBG("%s() L%d stop sysclk failed\n", __func__, __LINE__);
|
||||
AC101_DBG("stop sysclk failed\n");
|
||||
} else {
|
||||
AC101_DBG("%s() L%d hw sysclk disable\n", __func__, __LINE__);
|
||||
AC101_DBG("hw sysclk disable\n");
|
||||
ac10x->aif1_clken = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AC101_DBG("%s() L%d event=%d pre_up/%d post_down/%d\n", __func__, __LINE__,
|
||||
event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD);
|
||||
AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -778,8 +782,8 @@ static struct snd_kcontrol_new ac101_controls[] = {
|
|||
SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv),
|
||||
SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN, 0xf, 0, dac_mix_vol_tlv),
|
||||
SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv),
|
||||
SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
|
||||
SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
|
||||
SOC_SINGLE_TLV("Speaker Playback Volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
|
||||
SOC_SINGLE_TLV("Headphone Playback Volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
|
||||
};
|
||||
|
||||
/* PLL divisors */
|
||||
|
@ -907,7 +911,7 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
|
|||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
AC101_DBG("%s() L%d mute=%d\n", __func__, __LINE__, mute);
|
||||
AC101_DBG("mute=%d\n", mute);
|
||||
|
||||
ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0);
|
||||
|
||||
|
@ -949,12 +953,12 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai
|
|||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
AC101_DBG("%s,line:%d stream = %s, play: %d, capt: %d, active: %d\n", __func__, __LINE__,
|
||||
AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n",
|
||||
snd_pcm_stream_str(substream),
|
||||
codec_dai->playback_active, codec_dai->capture_active,
|
||||
codec_dai->active);
|
||||
codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active,
|
||||
snd_soc_dai_active(codec_dai));
|
||||
|
||||
if (!codec_dai->active) {
|
||||
if (!snd_soc_dai_active(codec_dai)) {
|
||||
ac10x->aif1_clken = 1;
|
||||
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||
} else {
|
||||
|
@ -968,7 +972,7 @@ static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
|
|||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
int i, m, n_i, n_f;
|
||||
|
||||
AC101_DBG("%s, line:%d, pll_id:%d\n", __func__, __LINE__, pll_id);
|
||||
AC101_DBG("pll_id:%d\n", pll_id);
|
||||
|
||||
/* clear volatile reserved bits*/
|
||||
ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0);
|
||||
|
@ -1040,7 +1044,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
|
|||
int reg_val, freq_out;
|
||||
unsigned channels;
|
||||
|
||||
AC101_DBG("%s() L%d +++\n", __func__, __LINE__);
|
||||
AC101_DBG("+++\n");
|
||||
|
||||
if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
|
||||
/* not configure hw_param twice if stream is playback, tell the caller it's started */
|
||||
|
@ -1076,7 +1080,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
|
|||
freq_out = _FREQ_24_576K;
|
||||
for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
|
||||
if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
|
||||
if (codec_dai->capture_active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), (0x4<<AIF1_FS));
|
||||
} else {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), ((codec_aif1_fs[i].srbit)<<AIF1_FS));
|
||||
|
@ -1126,7 +1130,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
|
|||
AC101_DBG("rate: %d , channels: %d , samp_res: %d",
|
||||
params_rate(params), channels, aif1_slot_size);
|
||||
|
||||
AC101_DBG("%s() L%d ---\n", __func__, __LINE__);
|
||||
AC101_DBG("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1136,7 +1140,7 @@ int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|||
int AIF_CLK_CTRL = AIF1_CLK_CTRL;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
AC101_DBG("%s() L%d\n", __func__, __LINE__);
|
||||
AC101_DBG();
|
||||
|
||||
/*
|
||||
* master or slave selection
|
||||
|
@ -1148,14 +1152,14 @@ int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|||
switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master, ap is slave*/
|
||||
#if _MASTER_MULTI_CODEC == _MASTER_AC101
|
||||
pr_warn("AC101 as Master\n");
|
||||
pr_info("AC101 as Master\n");
|
||||
reg_val |= (0x0<<AIF1_MSTR_MOD);
|
||||
break;
|
||||
#else
|
||||
pr_warn("AC108 as Master\n");
|
||||
pr_info("AC108 as Master\n");
|
||||
#endif
|
||||
case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave, ap is master*/
|
||||
pr_warn("AC101 as Slave\n");
|
||||
pr_info("AC101 as Slave\n");
|
||||
reg_val |= (0x1<<AIF1_MSTR_MOD);
|
||||
break;
|
||||
default:
|
||||
|
@ -1225,7 +1229,7 @@ int ac101_audio_startup(struct snd_pcm_substream *substream,
|
|||
{
|
||||
// struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
AC101_DBG("\n\n%s,line:%d\n", __func__, __LINE__);
|
||||
AC101_DBG("\n\n\n");
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
}
|
||||
|
@ -1233,16 +1237,18 @@ int ac101_audio_startup(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
#if _MASTER_MULTI_CODEC == _MASTER_AC101
|
||||
static int ac101_set_clock(int y_start_n_stop) {
|
||||
static int ac101_set_clock(int y_start_n_stop, struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) {
|
||||
int r;
|
||||
|
||||
if (y_start_n_stop) {
|
||||
/* enable global clock */
|
||||
ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1);
|
||||
r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1);
|
||||
} else {
|
||||
/* disable global clock */
|
||||
static_ac10x->aif1_clken = 1;
|
||||
ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||
r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||
}
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1252,9 +1258,9 @@ int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
AC101_DBG("%s() stream=%s cmd=%d\n",
|
||||
__FUNCTION__,
|
||||
AC101_DBG("stream=%s cmd=%d\n",
|
||||
snd_pcm_stream_str(substream),
|
||||
cmd);
|
||||
|
||||
|
@ -1263,6 +1269,7 @@ int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
#if _MASTER_MULTI_CODEC == _MASTER_AC101
|
||||
spin_lock_irqsave(&ac10x->lock, flags);
|
||||
if (ac10x->aif1_clken == 0){
|
||||
/*
|
||||
* enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable'
|
||||
|
@ -1272,15 +1279,21 @@ int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
|
||||
ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
|
||||
}
|
||||
spin_unlock_irqrestore(&ac10x->lock, flags);
|
||||
ac101_set_clock(1, substream, cmd, dai);
|
||||
#endif
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ac101_set_clock(0, NULL, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
AC101_DBG("stream=%s cmd=%d;finished %d\n",
|
||||
snd_pcm_stream_str(substream),
|
||||
cmd, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1291,7 +1304,7 @@ static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
AC101_DBG("%s,line:%d, id=%d freq=%d, dir=%d\n", __func__, __LINE__,
|
||||
AC101_DBG("id=%d freq=%d, dir=%d\n",
|
||||
clk_id, freq, dir);
|
||||
|
||||
ac10x->sysclk = freq;
|
||||
|
@ -1340,7 +1353,7 @@ static void codec_resume_work(struct work_struct *work)
|
|||
struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume);
|
||||
struct snd_soc_codec *codec = ac10x->codec;
|
||||
|
||||
AC101_DBG("%s() L%d +++\n", __func__, __LINE__);
|
||||
AC101_DBG("+++\n");
|
||||
|
||||
set_configuration(codec);
|
||||
if (drc_used) {
|
||||
|
@ -1349,7 +1362,7 @@ static void codec_resume_work(struct work_struct *work)
|
|||
/*enable this bit to prevent leakage from ldoin*/
|
||||
ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
|
||||
|
||||
AC101_DBG("%s() L%d +++\n", __func__, __LINE__);
|
||||
AC101_DBG("---\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1357,13 +1370,13 @@ int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level le
|
|||
{
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
AC101_DBG("%s,line:%d, SND_SOC_BIAS_ON\n", __func__, __LINE__);
|
||||
AC101_DBG("SND_SOC_BIAS_ON\n");
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
AC101_DBG("%s,line:%d, SND_SOC_BIAS_PREPARE\n", __func__, __LINE__);
|
||||
AC101_DBG("SND_SOC_BIAS_PREPARE\n");
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
AC101_DBG("%s,line:%d, SND_SOC_BIAS_STANDBY\n", __func__, __LINE__);
|
||||
AC101_DBG("SND_SOC_BIAS_STANDBY\n");
|
||||
#ifdef CONFIG_AC101_SWITCH_DETECT
|
||||
switch_hw_config(codec);
|
||||
#endif
|
||||
|
@ -1375,7 +1388,7 @@ int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level le
|
|||
#endif
|
||||
ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0<<HPOUTPUTENABLE));
|
||||
ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0<<OSCEN));
|
||||
AC101_DBG("%s,line:%d, SND_SOC_BIAS_OFF\n", __func__, __LINE__);
|
||||
AC101_DBG("SND_SOC_BIAS_OFF\n");
|
||||
break;
|
||||
}
|
||||
snd_soc_codec_get_dapm(codec)->bias_level = level;
|
||||
|
@ -1389,7 +1402,7 @@ int ac101_codec_probe(struct snd_soc_codec *codec)
|
|||
|
||||
ac10x = dev_get_drvdata(codec->dev);
|
||||
if (ac10x == NULL) {
|
||||
AC101_DBG("not set client data %s() L%d\n", __func__, __LINE__);
|
||||
AC101_DBG("not set client data!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ac10x->codec = codec;
|
||||
|
@ -1440,12 +1453,20 @@ int ac101_codec_remove(struct snd_soc_codec *codec)
|
|||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (ac10x->irq) {
|
||||
/* devm_free_irq(codec->dev, ac10x->irq, NULL); */
|
||||
devm_free_irq(codec->dev, ac10x->irq, ac10x);
|
||||
ac10x->irq = 0;
|
||||
}
|
||||
|
||||
if (cancel_work_sync(&ac10x->work_switch) != 0) {
|
||||
}
|
||||
|
||||
if (cancel_work_sync(&ac10x->work_clear_irq) != 0) {
|
||||
}
|
||||
|
||||
if (ac10x->inpdev) {
|
||||
input_unregister_device(ac10x->inpdev);
|
||||
ac10x->inpdev = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
@ -1490,32 +1511,32 @@ int ac101_codec_resume(struct snd_soc_codec *codec)
|
|||
static ssize_t ac101_debug_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
static int val = 0, flag = 0;
|
||||
u8 reg,num,i=0;
|
||||
u16 value_w,value_r[128];
|
||||
struct ac10x_priv *ac10x = dev_get_drvdata(dev);
|
||||
int val = 0, flag = 0;
|
||||
u16 value_w, value_r;
|
||||
u8 reg, num, i=0;
|
||||
|
||||
val = simple_strtol(buf, NULL, 16);
|
||||
flag = (val >> 24) & 0xF;
|
||||
if(flag) {
|
||||
if (flag) {
|
||||
reg = (val >> 16) & 0xFF;
|
||||
value_w = val & 0xFFFF;
|
||||
ac101_write(ac10x->codec, reg, value_w);
|
||||
printk("write 0x%x to reg:0x%x\n",value_w,reg);
|
||||
printk("write 0x%x to reg:0x%x\n", value_w, reg);
|
||||
} else {
|
||||
reg =(val>>8)& 0xFF;
|
||||
num=val&0xff;
|
||||
reg = (val >> 8) & 0xFF;
|
||||
num = val & 0xff;
|
||||
printk("\n");
|
||||
printk("read:start add:0x%x,count:0x%x\n",reg,num);
|
||||
printk("read:start add:0x%x,count:0x%x\n", reg, num);
|
||||
|
||||
regcache_cache_bypass(ac10x->regmap101, true);
|
||||
do {
|
||||
value_r[i] = ac101_read(ac10x->codec, reg);
|
||||
printk("0x%x: 0x%04x ",reg,value_r[i]);
|
||||
reg+=1;
|
||||
i++;
|
||||
if(i == num)
|
||||
value_r = ac101_read(ac10x->codec, reg);
|
||||
printk("0x%x: 0x%04x ", reg++, value_r);
|
||||
if (++i % 4 == 0 || i == num)
|
||||
printk("\n");
|
||||
if(i%4==0)
|
||||
printk("\n");
|
||||
} while(i<num);
|
||||
} while (i < num);
|
||||
regcache_cache_bypass(ac10x->regmap101, false);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -1591,7 +1612,7 @@ int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
int ret = 0;
|
||||
unsigned v = 0;
|
||||
|
||||
AC101_DBG("%s,line:%d\n", __func__, __LINE__);
|
||||
AC101_DBG();
|
||||
|
||||
static_ac10x = ac10x;
|
||||
|
||||
|
@ -1602,13 +1623,13 @@ int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
|
||||
|
||||
/* Chip reset */
|
||||
/*
|
||||
regcache_cache_only(ac10x->regmap101, false);
|
||||
ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
|
||||
msleep(50);
|
||||
*/
|
||||
|
||||
/* sync regcache for FLAT type */
|
||||
ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
|
||||
|
||||
ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v);
|
||||
if (ret < 0) {
|
||||
|
@ -1643,7 +1664,7 @@ void ac101_shutdown(struct i2c_client *i2c)
|
|||
int reg_val;
|
||||
|
||||
if (codec == NULL) {
|
||||
pr_err("%s() L%d: no sound card.\n", __func__, __LINE__);
|
||||
pr_err(": no sound card.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
145
ac108.c
145
ac108.c
|
@ -173,8 +173,11 @@ static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925,75,0);
|
|||
int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm) {
|
||||
int r, v = 0;
|
||||
|
||||
r = regmap_read(i2cm, reg, &v);
|
||||
if ((r = regmap_read(i2cm, reg, &v)) < 0) {
|
||||
pr_err("ac10x_read error->[REG-0x%02x]\n", reg);
|
||||
} else {
|
||||
*rt_val = v;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -450,11 +453,13 @@ static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) {
|
|||
}
|
||||
|
||||
static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) {
|
||||
int r = 0;
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < ac10x->codec_cnt; i++) {
|
||||
ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
|
||||
r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
|
||||
}
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) {
|
||||
|
@ -648,7 +653,7 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
|||
|
||||
dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
|
||||
snd_pcm_stream_str(substream),
|
||||
dai->playback_active, dai->capture_active);
|
||||
dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, dai->stream[SNDRV_PCM_STREAM_CAPTURE].active);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ret = ac101_hw_params(substream, params, dai);
|
||||
|
@ -659,8 +664,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
|||
}
|
||||
}
|
||||
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active)
|
||||
|| (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) {
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active)
|
||||
|| (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) {
|
||||
/* not configure hw_param twice */
|
||||
/* return 0; */
|
||||
}
|
||||
|
@ -805,6 +810,9 @@ static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int fr
|
|||
|
||||
struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (freq != 24000000 || clk_id != SYSCLK_SRC_PLL)
|
||||
dev_warn(dai->dev, "ac108_set_sysclk freq = %d clk = %d\n", freq, clk_id);
|
||||
|
||||
freq = 24000000;
|
||||
clk_id = SYSCLK_SRC_PLL;
|
||||
|
||||
|
@ -860,6 +868,7 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
|
|||
/* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
|
||||
dev_dbg(dai->dev, "used as slave when AC101 is master\n");
|
||||
}
|
||||
fallthrough;
|
||||
case SND_SOC_DAIFMT_CBS_CFS: /*AC108 Slave*/
|
||||
dev_dbg(dai->dev, "AC108 set to work as Slave\n");
|
||||
/**
|
||||
|
@ -982,47 +991,50 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
|
|||
/*
|
||||
* due to miss channels order in cpu_dai, we meed defer the clock starting.
|
||||
*/
|
||||
static int ac108_set_clock(int y_start_n_stop) {
|
||||
u8 r;
|
||||
static int ac108_set_clock(int y_start_n_stop, struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) {
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
|
||||
|
||||
/* spin_lock move to machine trigger */
|
||||
|
||||
if (y_start_n_stop) {
|
||||
if (ac10x->sysclk_en == 0) {
|
||||
|
||||
if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
|
||||
ac101_trigger(substream, cmd, dai);
|
||||
}
|
||||
if (y_start_n_stop && ac10x->sysclk_en == 0) {
|
||||
/* enable lrck clock */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
if (r & (0x01 << BCLK_IOEN)) {
|
||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
if (reg & (0x01 << BCLK_IOEN)) {
|
||||
ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
}
|
||||
|
||||
/*0x10: PLL Common voltage enable, PLL enable */
|
||||
ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
||||
ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
||||
0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
|
||||
/* enable global clock */
|
||||
ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
|
||||
ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
|
||||
|
||||
ac10x->sysclk_en = 1UL;
|
||||
}
|
||||
} else if (ac10x->sysclk_en != 0) {
|
||||
} else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
|
||||
/* disable global clock */
|
||||
ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
|
||||
ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
|
||||
|
||||
/*0x10: PLL Common voltage disable, PLL disable */
|
||||
ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
||||
ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
||||
0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
|
||||
|
||||
/* disable lrck clock if it's enabled */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
if (r & (0x01 << LRCK_IOEN)) {
|
||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
if (reg & (0x01 << LRCK_IOEN)) {
|
||||
ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
}
|
||||
if (!ret) {
|
||||
ac10x->sysclk_en = 0UL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ac108_prepare(struct snd_pcm_substream *substream,
|
||||
|
@ -1049,39 +1061,33 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
snd_pcm_stream_str(substream),
|
||||
cmd);
|
||||
|
||||
spin_lock_irqsave(&ac10x->lock, flags);
|
||||
|
||||
if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
|
||||
ac101_trigger(substream, cmd, dai);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
goto __ret;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
spin_lock_irqsave(&ac10x->lock, flags);
|
||||
/* disable global clock if lrck disabled */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||
if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
|
||||
/* disable global clock */
|
||||
ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
|
||||
}
|
||||
|
||||
/* delayed clock starting, move to machine trigger() */
|
||||
spin_unlock_irqrestore(&ac10x->lock, flags);
|
||||
ac108_set_clock(1, substream, cmd, dai);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ac108_set_clock(0, substream, cmd, dai);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
__ret:
|
||||
spin_unlock_irqrestore(&ac10x->lock, flags);
|
||||
|
||||
dev_dbg(dai->dev, "%s() stream=%s cmd=%d; finished %d\n",
|
||||
__FUNCTION__,
|
||||
snd_pcm_stream_str(substream),
|
||||
cmd, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1115,7 +1121,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
|||
}
|
||||
}
|
||||
|
||||
int ac108_aif_mute(struct snd_soc_dai *dai, int mute) {
|
||||
int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
|
@ -1136,12 +1142,13 @@ static const struct snd_soc_dai_ops ac108_dai_ops = {
|
|||
.hw_params = ac108_hw_params,
|
||||
.prepare = ac108_prepare,
|
||||
.trigger = ac108_trigger,
|
||||
.digital_mute = ac108_aif_mute,
|
||||
.mute_stream = ac108_aif_mute,
|
||||
|
||||
/*DAI format configuration*/
|
||||
.set_fmt = ac108_set_fmt,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ac108_dai0 = {
|
||||
|
@ -1211,7 +1218,6 @@ static int ac108_add_widgets(struct snd_soc_codec *codec) {
|
|||
}
|
||||
|
||||
static int ac108_codec_probe(struct snd_soc_codec *codec) {
|
||||
|
||||
spin_lock_init(&ac10x->lock);
|
||||
|
||||
ac10x->codec = codec;
|
||||
|
@ -1267,6 +1273,12 @@ int ac108_codec_remove(struct snd_soc_codec *codec) {
|
|||
}
|
||||
return ac101_codec_remove(codec);
|
||||
}
|
||||
#if __NO_SND_SOC_CODEC_DRV
|
||||
void ac108_codec_remove_void(struct snd_soc_codec *codec) {
|
||||
ac108_codec_remove(codec);
|
||||
}
|
||||
#define ac108_codec_remove ac108_codec_remove_void
|
||||
#endif
|
||||
|
||||
int ac108_codec_suspend(struct snd_soc_codec *codec) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -1310,6 +1322,11 @@ static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
|
|||
.set_bias_level = ac108_set_bias_level,
|
||||
.read = ac108_codec_read,
|
||||
.write = ac108_codec_write,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
|
||||
|
@ -1325,12 +1342,16 @@ static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, co
|
|||
ac108_multi_write(reg, value_w, ac10x);
|
||||
printk("Write 0x%02x to REG:0x%02x\n", value_w, reg);
|
||||
} else {
|
||||
int k;
|
||||
|
||||
reg = (val >> 8) & 0xFF;
|
||||
num = val & 0xff;
|
||||
printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
|
||||
|
||||
for (k = 0; k < ac10x->codec_cnt; k++) {
|
||||
regcache_cache_bypass(ac10x->i2cmap[k], true);
|
||||
}
|
||||
do {
|
||||
int k;
|
||||
|
||||
memset(value_r, 0, sizeof value_r);
|
||||
|
||||
|
@ -1348,6 +1369,9 @@ static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, co
|
|||
printk("\n");
|
||||
}
|
||||
} while (i < num);
|
||||
for (k = 0; k < ac10x->codec_cnt; k++) {
|
||||
regcache_cache_bypass(ac10x->i2cmap[k], false);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -1383,7 +1407,15 @@ static const struct regmap_config ac108_regmap = {
|
|||
.max_register = 0xDF,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) {
|
||||
static const struct i2c_device_id ac108_i2c_id[] = {
|
||||
{ "ac108_0", 0 },
|
||||
{ "ac108_1", 1 },
|
||||
{ "ac108_2", 2 },
|
||||
{ "ac108_3", 3 },
|
||||
{ "ac101", AC101_I2C_ID },
|
||||
{ }
|
||||
};
|
||||
static int ac108_i2c_probe(struct i2c_client *i2c) {
|
||||
struct device_node *np = i2c->dev.of_node;
|
||||
unsigned int val = 0;
|
||||
int ret = 0, index;
|
||||
|
@ -1396,11 +1428,11 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
|||
}
|
||||
}
|
||||
|
||||
index = (int)i2c_id->driver_data;
|
||||
index = (int)i2c_match_id(ac108_i2c_id, i2c)->driver_data;
|
||||
if (index == AC101_I2C_ID) {
|
||||
ac10x->i2c101 = i2c;
|
||||
i2c_set_clientdata(i2c, ac10x);
|
||||
ret = ac101_probe(i2c, i2c_id);
|
||||
ret = ac101_probe(i2c, i2c_match_id(ac108_i2c_id, i2c));
|
||||
if (ret) {
|
||||
ac10x->i2c101 = NULL;
|
||||
return ret;
|
||||
|
@ -1418,8 +1450,8 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
|||
if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
|
||||
ac10x->tdm_chips_cnt = val;
|
||||
|
||||
pr_err(" ac10x i2c_id number: %d\n", index);
|
||||
pr_err(" ac10x data protocol: %d\n", ac10x->data_protocol);
|
||||
pr_info(" ac10x i2c_id number: %d\n", index);
|
||||
pr_info(" ac10x data protocol: %d\n", ac10x->data_protocol);
|
||||
|
||||
ac10x->i2c[index] = i2c;
|
||||
ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
|
||||
|
@ -1429,17 +1461,19 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
|||
return ret;
|
||||
}
|
||||
|
||||
ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
|
||||
|
||||
/*
|
||||
* Writing this register with 0x12
|
||||
* will resets all register to their default state.
|
||||
*/
|
||||
regcache_cache_only(ac10x->i2cmap[index], false);
|
||||
ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL);
|
||||
msleep(1);
|
||||
|
||||
/* sync regcache for FLAT type */
|
||||
ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
|
||||
|
||||
ac10x->codec_cnt++;
|
||||
pr_err(" ac10x codec count : %d\n", ac10x->codec_cnt);
|
||||
pr_info(" ac10x codec count : %d\n", ac10x->codec_cnt);
|
||||
|
||||
ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
|
||||
if (ret) {
|
||||
|
@ -1464,7 +1498,7 @@ __ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ac108_i2c_remove(struct i2c_client *i2c) {
|
||||
static void ac108_i2c_remove(struct i2c_client *i2c) {
|
||||
if (ac10x->codec != NULL) {
|
||||
snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
|
||||
ac10x->codec = NULL;
|
||||
|
@ -1489,17 +1523,8 @@ __ret:
|
|||
kfree(ac10x);
|
||||
ac10x = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ac108_i2c_id[] = {
|
||||
{ "ac108_0", 0 },
|
||||
{ "ac108_1", 1 },
|
||||
{ "ac108_2", 2 },
|
||||
{ "ac108_3", 3 },
|
||||
{ "ac101", AC101_I2C_ID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
|
||||
|
||||
static const struct of_device_id ac108_of_match[] = {
|
||||
|
|
|
@ -69,7 +69,7 @@ state.seeed8micvoicec {
|
|||
control.1 {
|
||||
iface MIXER
|
||||
name 'CH1 digital volume'
|
||||
value 166
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -77,13 +77,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 525
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.2 {
|
||||
iface MIXER
|
||||
name 'CH2 digital volume'
|
||||
value 166
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -91,13 +91,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 525
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.3 {
|
||||
iface MIXER
|
||||
name 'CH3 digital volume'
|
||||
value 166
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -105,13 +105,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 525
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.4 {
|
||||
iface MIXER
|
||||
name 'CH4 digital volume'
|
||||
value 166
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -119,13 +119,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 525
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.5 {
|
||||
iface MIXER
|
||||
name 'ADC1 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -133,13 +133,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.6 {
|
||||
iface MIXER
|
||||
name 'ADC2 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -147,13 +147,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.7 {
|
||||
iface MIXER
|
||||
name 'ADC3 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -161,13 +161,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.8 {
|
||||
iface MIXER
|
||||
name 'ADC4 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -175,13 +175,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.9 {
|
||||
iface MIXER
|
||||
name 'CH5 digital volume'
|
||||
value 165
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -189,13 +189,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 450
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.10 {
|
||||
iface MIXER
|
||||
name 'CH6 digital volume'
|
||||
value 165
|
||||
value 208
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -203,13 +203,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 450
|
||||
dbvalue.0 3675
|
||||
}
|
||||
}
|
||||
control.11 {
|
||||
iface MIXER
|
||||
name 'CH7 digital volume'
|
||||
value 165
|
||||
value 198
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -217,13 +217,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 450
|
||||
dbvalue.0 2925
|
||||
}
|
||||
}
|
||||
control.12 {
|
||||
iface MIXER
|
||||
name 'CH8 digital volume'
|
||||
value 165
|
||||
value 198
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -231,13 +231,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 450
|
||||
dbvalue.0 2925
|
||||
}
|
||||
}
|
||||
control.13 {
|
||||
iface MIXER
|
||||
name 'ADC5 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -245,13 +245,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.14 {
|
||||
iface MIXER
|
||||
name 'ADC6 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -259,13 +259,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.15 {
|
||||
iface MIXER
|
||||
name 'ADC7 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -273,13 +273,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.16 {
|
||||
iface MIXER
|
||||
name 'ADC8 PGA gain'
|
||||
value 25
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -287,7 +287,7 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 2500
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.17 {
|
||||
|
@ -325,7 +325,7 @@ state.seeed8micvoicec {
|
|||
control.19 {
|
||||
iface MIXER
|
||||
name 'digital volume'
|
||||
value 49
|
||||
value 51
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -333,13 +333,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 63'
|
||||
dbmin -7308
|
||||
dbmax 0
|
||||
dbvalue.0 -1624
|
||||
dbvalue.0 -1392
|
||||
}
|
||||
}
|
||||
control.20 {
|
||||
iface MIXER
|
||||
name 'speaker volume'
|
||||
value 18
|
||||
name 'Speaker Playback Volume'
|
||||
value 25
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -347,13 +347,13 @@ state.seeed8micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin -4800
|
||||
dbmax -150
|
||||
dbvalue.0 -2100
|
||||
dbvalue.0 -1050
|
||||
}
|
||||
}
|
||||
control.21 {
|
||||
iface MIXER
|
||||
name 'headphone volume'
|
||||
value 48
|
||||
name 'Headphone Playback Volume'
|
||||
value 52
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -361,7 +361,7 @@ state.seeed8micvoicec {
|
|||
range '0 - 63'
|
||||
dbmin -6300
|
||||
dbmax 0
|
||||
dbvalue.0 -1500
|
||||
dbvalue.0 -1100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ state.seeed4micvoicec {
|
|||
control.1 {
|
||||
iface MIXER
|
||||
name 'CH1 digital volume'
|
||||
value 172
|
||||
value 222
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -77,13 +77,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 975
|
||||
dbvalue.0 4725
|
||||
}
|
||||
}
|
||||
control.2 {
|
||||
iface MIXER
|
||||
name 'CH2 digital volume'
|
||||
value 172
|
||||
value 222
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -91,13 +91,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 975
|
||||
dbvalue.0 4725
|
||||
}
|
||||
}
|
||||
control.3 {
|
||||
iface MIXER
|
||||
name 'CH3 digital volume'
|
||||
value 172
|
||||
value 222
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -105,13 +105,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 975
|
||||
dbvalue.0 4725
|
||||
}
|
||||
}
|
||||
control.4 {
|
||||
iface MIXER
|
||||
name 'CH4 digital volume'
|
||||
value 172
|
||||
value 222
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -119,13 +119,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 255'
|
||||
dbmin -11925
|
||||
dbmax 7200
|
||||
dbvalue.0 975
|
||||
dbvalue.0 4725
|
||||
}
|
||||
}
|
||||
control.5 {
|
||||
iface MIXER
|
||||
name 'ADC1 PGA gain'
|
||||
value 31
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -133,13 +133,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 3100
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.6 {
|
||||
iface MIXER
|
||||
name 'ADC2 PGA gain'
|
||||
value 31
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -147,13 +147,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 3100
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.7 {
|
||||
iface MIXER
|
||||
name 'ADC3 PGA gain'
|
||||
value 31
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -161,13 +161,13 @@ state.seeed4micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 3100
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
control.8 {
|
||||
iface MIXER
|
||||
name 'ADC4 PGA gain'
|
||||
value 31
|
||||
value 0
|
||||
comment {
|
||||
access 'read write'
|
||||
type INTEGER
|
||||
|
@ -175,7 +175,7 @@ state.seeed4micvoicec {
|
|||
range '0 - 31'
|
||||
dbmin 0
|
||||
dbmax 3100
|
||||
dbvalue.0 3100
|
||||
dbvalue.0 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
ac10x.h
11
ac10x.h
|
@ -29,14 +29,19 @@
|
|||
/* enable headset detecting & headset button pressing */
|
||||
#define CONFIG_AC101_SWITCH_DETECT
|
||||
|
||||
/* obsolete */
|
||||
#define CONFIG_AC10X_TRIG_LOCK 0
|
||||
|
||||
|
||||
#ifdef AC101_DEBG
|
||||
#define AC101_DBG(format,args...) printk("[AC101] "format,##args)
|
||||
#define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
|
||||
#else
|
||||
#define AC101_DBG(...)
|
||||
#endif
|
||||
|
||||
|
||||
#include "sound-compatible-4.18.h"
|
||||
|
||||
#ifdef CONFIG_AC101_SWITCH_DETECT
|
||||
enum headphone_mode_u {
|
||||
HEADPHONE_IDLE,
|
||||
|
@ -75,7 +80,7 @@ struct ac10x_priv {
|
|||
|
||||
#ifdef CONFIG_AC101_SWITCH_DETECT
|
||||
struct gpio_desc* gpiod_irq;
|
||||
int irq;
|
||||
long irq;
|
||||
volatile int irq_cntr;
|
||||
volatile int pullout_cntr;
|
||||
volatile int state;
|
||||
|
@ -114,7 +119,7 @@ void ac101_shutdown(struct i2c_client *i2c);
|
|||
int ac101_remove(struct i2c_client *i2c);
|
||||
|
||||
/* seeed voice card export */
|
||||
int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int));
|
||||
int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int, struct snd_pcm_substream *, int, struct snd_soc_dai *));
|
||||
|
||||
int ac10x_fill_regcache(struct device* dev, struct regmap* map);
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# The IPC key of dmix or dsnoop plugin must be unique
|
||||
# If 555555 or 666666 is used by other processes, use another one
|
||||
|
||||
|
||||
# use samplerate to resample as speexdsp resample is bad
|
||||
defaults.pcm.rate_converter "samplerate"
|
||||
|
||||
pcm.!default {
|
||||
type asym
|
||||
playback.pcm "playback"
|
||||
|
@ -19,14 +23,14 @@ pcm.capture {
|
|||
|
||||
pcm.dmixed {
|
||||
type dmix
|
||||
slave.pcm "hw:0,0"
|
||||
slave.pcm "hw:seeed2micvoicec"
|
||||
ipc_key 555555
|
||||
}
|
||||
|
||||
pcm.array {
|
||||
type dsnoop
|
||||
slave {
|
||||
pcm "hw:0,0"
|
||||
pcm "hw:seeed2micvoicec"
|
||||
channels 2
|
||||
}
|
||||
ipc_key 666666
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# The IPC key of dmix or dsnoop plugin must be unique
|
||||
# If 555555 or 666666 is used by other processes, use another one
|
||||
|
||||
# use samplerate to resample as speexdsp resample is bad
|
||||
defaults.pcm.rate_converter "samplerate"
|
||||
|
||||
pcm.!default {
|
||||
type asym
|
||||
playback.pcm "playback"
|
||||
|
@ -9,22 +12,22 @@ pcm.!default {
|
|||
|
||||
pcm.playback {
|
||||
type plug
|
||||
slave.pcm "hw:0,0"
|
||||
slave.pcm "hw:ALSA"
|
||||
}
|
||||
|
||||
pcm.dmixed {
|
||||
type dmix
|
||||
slave.pcm "hw:0,0"
|
||||
ipc_key 555555
|
||||
}
|
||||
# pcm.dmixed {
|
||||
# type dmix
|
||||
# slave.pcm "hw:0,0"
|
||||
# ipc_key 555555
|
||||
# }
|
||||
|
||||
pcm.ac108 {
|
||||
type plug
|
||||
slave.pcm "multiapps"
|
||||
slave.pcm "hw:seeed4micvoicec"
|
||||
}
|
||||
|
||||
pcm.multiapps {
|
||||
type dsnoop
|
||||
ac108-slavepcm "hw:1,0"
|
||||
ipc_key 666666
|
||||
}
|
||||
# pcm.multiapps {
|
||||
# type dsnoop
|
||||
# ac108-slavepcm "hw:1,0"
|
||||
# ipc_key 666666
|
||||
# }
|
||||
|
|
105
asound_6mic.conf
105
asound_6mic.conf
|
@ -1,61 +1,90 @@
|
|||
# The IPC key of dmix or dsnoop plugin must be unique
|
||||
# If 555555 or 666666 is used by other processes, use another one
|
||||
|
||||
# use samplerate to resample as speexdsp resample is broken
|
||||
defaults.pcm.rate_converter "samplerate"
|
||||
|
||||
pcm.!default {
|
||||
type asym
|
||||
playback.pcm "ac101"
|
||||
playback.pcm "dmixer"
|
||||
capture.pcm "ac108"
|
||||
}
|
||||
|
||||
pcm.playback {
|
||||
type plug
|
||||
slave.pcm "hw:0,0"
|
||||
}
|
||||
|
||||
pcm.dmixed {
|
||||
type dmix
|
||||
slave.pcm "hw:0,0"
|
||||
ipc_key 555555
|
||||
}
|
||||
|
||||
pcm.ac108 {
|
||||
type plug
|
||||
slave.pcm "multiapps"
|
||||
slave {
|
||||
rate 48000
|
||||
format S32_LE
|
||||
pcm "hw:seeed8micvoicec"
|
||||
}
|
||||
}
|
||||
|
||||
pcm.multiapps {
|
||||
type dsnoop
|
||||
slave.pcm "hw:1,0"
|
||||
ipc_key 666666
|
||||
# pcm.multiapps {
|
||||
# type plug
|
||||
# slave.pcm {
|
||||
# type dsnoop
|
||||
# slave {
|
||||
# rate 48000
|
||||
# format S32_LE
|
||||
# pcm "hw:seeed8micvoicec"
|
||||
# }
|
||||
# ipc_key 666666
|
||||
# }
|
||||
# }
|
||||
|
||||
pcm.dmixer {
|
||||
type plug
|
||||
slave {
|
||||
pcm {
|
||||
type dmix
|
||||
ipc_key 555555
|
||||
slave {
|
||||
pcm "hw:seeed8micvoicec"
|
||||
format S32_LE
|
||||
channels 8
|
||||
}
|
||||
bindings {
|
||||
0 0
|
||||
1 1
|
||||
2 2
|
||||
3 3
|
||||
4 4
|
||||
5 5
|
||||
6 6
|
||||
7 7
|
||||
}
|
||||
}
|
||||
channels 8
|
||||
format S32_LE
|
||||
rate 48000
|
||||
}
|
||||
ttable.0.0 1
|
||||
ttable.1.1 1
|
||||
ttable.0.2 1
|
||||
ttable.1.3 1
|
||||
ttable.0.4 1
|
||||
ttable.1.5 1
|
||||
ttable.0.6 1
|
||||
ttable.1.7 1
|
||||
}
|
||||
|
||||
pcm.ac101 {
|
||||
type plug
|
||||
slave {
|
||||
pcm {
|
||||
type dmix
|
||||
ipc_key 1048576
|
||||
slave {
|
||||
pcm "hw:1,0"
|
||||
format S32_LE
|
||||
# rate 16000
|
||||
pcm "hw:seeed8micvoicec"
|
||||
channels 8
|
||||
format S32_LE
|
||||
rate 48000
|
||||
}
|
||||
bindings {
|
||||
# map 2 channels input to
|
||||
# first 2 channels of 8 output
|
||||
0 0
|
||||
1 1
|
||||
0 2
|
||||
1 3
|
||||
0 4
|
||||
1 5
|
||||
0 6
|
||||
1 7
|
||||
}
|
||||
}
|
||||
channels 2
|
||||
}
|
||||
ttable.0.0 1
|
||||
ttable.1.1 1
|
||||
ttable.0.2 1
|
||||
ttable.1.3 1
|
||||
ttable.0.4 1
|
||||
ttable.1.5 1
|
||||
ttable.0.6 1
|
||||
ttable.1.7 1
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#!/bin/sh
|
||||
#dtoverlay -r seeed-2mic-voicecard
|
||||
DTC_FLAGS="-b 0 -Wno-unit_address_vs_reg -I dts -O dtb"
|
||||
|
||||
dtc -@ -I dts -O dtb -o seeed-2mic-voicecard.dtbo seeed-2mic-voicecard-overlay.dts
|
||||
dtc -@ -I dts -O dtb -o seeed-4mic-voicecard.dtbo seeed-4mic-voicecard-overlay.dts
|
||||
dtc -@ -I dts -O dtb -o seeed-8mic-voicecard.dtbo seeed-8mic-voicecard-overlay.dts
|
||||
dtc -@ $DTC_FLAGS -o seeed-2mic-voicecard.dtbo seeed-2mic-voicecard-overlay.dts
|
||||
dtc -@ $DTC_FLAGS -o seeed-4mic-voicecard.dtbo seeed-4mic-voicecard-overlay.dts
|
||||
dtc -@ $DTC_FLAGS -o seeed-8mic-voicecard.dtbo seeed-8mic-voicecard-overlay.dts
|
||||
|
||||
# cp *.dtbo /boot/overlays
|
||||
# dtoverlay seeed-2mic-voicecard
|
||||
|
|
|
@ -6,4 +6,10 @@ BUILT_MODULE_NAME[2]="snd-soc-seeed-voicecard"
|
|||
DEST_MODULE_LOCATION[0]="/kernel/sound/soc/codecs"
|
||||
DEST_MODULE_LOCATION[1]="/kernel/sound/soc/codecs"
|
||||
DEST_MODULE_LOCATION[2]="/kernel/sound/soc/bcm"
|
||||
PATCH[0]="back-to-v4.19.diff"
|
||||
PATCH[1]="back-to-v5.4.diff"
|
||||
PATCH[2]="back-to-v5.8.diff"
|
||||
PATCH_MATCH[0]="^4\.19\.*"
|
||||
PATCH_MATCH[1]="^5\.4\.*"
|
||||
PATCH_MATCH[2]="^5\.8\.*"
|
||||
AUTOINSTALL="yes"
|
||||
|
|
177
install.sh
177
install.sh
|
@ -5,29 +5,134 @@ if [[ $EUID -ne 0 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
is_Raspberry=$(cat /proc/device-tree/model | awk '{print $1}')
|
||||
if [ "x${is_Raspberry}" != "xRaspberry" ] ; then
|
||||
echo "Sorry, this drivers only works on raspberry pi"
|
||||
# Check for enough space on /boot volume
|
||||
boot_line=$(df -h | grep /boot | head -n 1)
|
||||
if [ "x${boot_line}" = "x" ]; then
|
||||
echo "Warning: /boot volume not found .."
|
||||
else
|
||||
boot_space=$(echo $boot_line | awk '{print $4;}')
|
||||
free_space=$(echo "${boot_space%?}")
|
||||
unit="${boot_space: -1}"
|
||||
if [[ "$unit" = "K" ]]; then
|
||||
echo "Error: Not enough space left ($boot_space) on /boot"
|
||||
exit 1
|
||||
elif [[ "$unit" = "M" ]]; then
|
||||
if [ "$free_space" -lt "25" ]; then
|
||||
echo "Error: Not enough space left ($boot_space) on /boot"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# make sure that we are on something ARM/Raspberry related
|
||||
# either a bare metal Raspberry or a qemu session with
|
||||
# Raspberry stuff available
|
||||
# - check for /boot/overlays
|
||||
# - dtparam and dtoverlay is available
|
||||
errorFound=0
|
||||
OVERLAYS=/boot/overlays
|
||||
[ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays
|
||||
|
||||
if [ ! -d $OVERLAYS ] ; then
|
||||
echo "$OVERLAYS not found or not a directory" 1>&2
|
||||
errorFound=1
|
||||
fi
|
||||
# should we also check for alsactl and amixer used in seeed-voicecard?
|
||||
PATH=$PATH:/opt/vc/bin
|
||||
for cmd in dtparam dtoverlay ; do
|
||||
if ! which $cmd &>/dev/null ; then
|
||||
echo "$cmd not found" 1>&2
|
||||
echo "You may need to run ./ubuntu-prerequisite.sh"
|
||||
errorFound=1
|
||||
fi
|
||||
done
|
||||
if [ $errorFound = 1 ] ; then
|
||||
echo "Errors found, exiting." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ver="0.3"
|
||||
|
||||
uname_r=$(uname -r)
|
||||
|
||||
# we create a dir with this version to ensure that 'dkms remove' won't delete
|
||||
# the sources during kernel updates
|
||||
marker="0.0.0"
|
||||
|
||||
apt update
|
||||
apt-get -y install raspberrypi-kernel-headers raspberrypi-kernel
|
||||
apt-get -y install dkms git i2c-tools
|
||||
_VER_RUN=
|
||||
function get_kernel_version() {
|
||||
local ZIMAGE IMG_OFFSET
|
||||
|
||||
_VER_RUN=""
|
||||
[ -z "$_VER_RUN" ] && {
|
||||
ZIMAGE=/boot/kernel.img
|
||||
[ -f /boot/firmware/vmlinuz ] && ZIMAGE=/boot/firmware/vmlinuz
|
||||
# 64-bit-only kernel package
|
||||
[ ! -f /boot/kernel.img ] && [ -f /boot/kernel8.img ] && ZIMAGE=/boot/kernel8.img
|
||||
IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1)
|
||||
_VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) 2>/dev/null | zcat | grep -a -m1 "Linux version" | LC_ALL=C sed -e 's/^.*Linux/Linux/' | strings | awk '{ print $3; }')
|
||||
}
|
||||
echo "$_VER_RUN"
|
||||
return 0
|
||||
}
|
||||
|
||||
function check_kernel_headers() {
|
||||
VER_RUN=$(get_kernel_version)
|
||||
VER_HDR=$(dpkg -L raspberrypi-kernel-headers | egrep -m1 "/lib/modules/[^\/]+/build" | awk -F'/' '{ print $4; }')
|
||||
[ "X$VER_RUN" == "X$VER_HDR" ] && {
|
||||
return 0
|
||||
}
|
||||
VER_HDR=$(dpkg -L linux-headers-$VER_RUN | egrep -m1 "/lib/modules/[^\/]+/build" | awk -F'/' '{ print $4; }')
|
||||
[ "X$VER_RUN" == "X$VER_HDR" ] && {
|
||||
return 0
|
||||
}
|
||||
|
||||
# echo RUN=$VER_RUN HDR=$VER_HDR
|
||||
echo " !!! Your kernel version is $VER_RUN"
|
||||
echo " Not found *** corresponding *** kernel headers with apt-get."
|
||||
echo " This may occur if you have ran 'rpi-update'."
|
||||
echo " Choose *** y *** will revert the kernel to version $VER_HDR then continue."
|
||||
echo " Choose *** N *** will exit without this driver support, by default."
|
||||
read -p "Would you like to proceed? (y/N)" -n 1 -r -s
|
||||
echo
|
||||
if ! [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
apt-get -y --reinstall install raspberrypi-kernel
|
||||
}
|
||||
|
||||
# update and install required packages
|
||||
which apt &>/dev/null
|
||||
if [[ $? -eq 0 ]]; then
|
||||
apt update -y
|
||||
# Raspbian kernel packages
|
||||
apt-get -y install raspberrypi-kernel-headers raspberrypi-kernel
|
||||
# Recent Raspbian has 64-bit kernel on 32-bit userspace
|
||||
apt-get -y install gcc-aarch64-linux-gnu
|
||||
# Ubuntu kernel packages
|
||||
apt-get -y install linux-raspi linux-headers-raspi linux-image-raspi
|
||||
apt-get -y install dkms git i2c-tools libasound2-plugins
|
||||
# rpi-update checker
|
||||
check_kernel_headers
|
||||
fi
|
||||
|
||||
# Arch Linux
|
||||
which pacman &>/dev/null
|
||||
if [[ $? -eq 0 ]]; then
|
||||
pacman -Syu --needed git gcc automake make dkms linux-raspberrypi-headers i2c-tools
|
||||
fi
|
||||
|
||||
# locate currently installed kernels (may be different to running kernel if
|
||||
# it's just been updated)
|
||||
kernels=$(ls /lib/modules | sed "s/^/-k /")
|
||||
uname_r=$(uname -r)
|
||||
base_ver=$(get_kernel_version)
|
||||
base_ver=${base_ver%%[-+]*}
|
||||
#kernels="${base_ver}+ ${base_ver}-v7+ ${base_ver}-v7l+"
|
||||
kernels=$(uname -r)
|
||||
|
||||
function install_module {
|
||||
local _i
|
||||
|
||||
src=$1
|
||||
mod=$2
|
||||
|
||||
|
@ -39,10 +144,16 @@ function install_module {
|
|||
dkms remove --force -m $mod -v $ver --all
|
||||
rm -rf /usr/src/$mod-$ver
|
||||
fi
|
||||
|
||||
mkdir -p /usr/src/$mod-$ver
|
||||
cp -a $src/* /usr/src/$mod-$ver/
|
||||
|
||||
dkms add -m $mod -v $ver
|
||||
dkms build $kernels -m $mod -v $ver && dkms install --force $kernels -m $mod -v $ver
|
||||
for _i in $kernels; do
|
||||
dkms build -k $_i -m $mod -v $ver && {
|
||||
dkms install --force -k $_i -m $mod -v $ver
|
||||
}
|
||||
done
|
||||
|
||||
mkdir -p /var/lib/dkms/$mod/$ver/$marker
|
||||
}
|
||||
|
@ -51,40 +162,60 @@ install_module "./" "seeed-voicecard"
|
|||
|
||||
|
||||
# install dtbos
|
||||
cp seeed-2mic-voicecard.dtbo /boot/overlays
|
||||
cp seeed-4mic-voicecard.dtbo /boot/overlays
|
||||
cp seeed-8mic-voicecard.dtbo /boot/overlays
|
||||
cp seeed-2mic-voicecard.dtbo $OVERLAYS
|
||||
cp seeed-4mic-voicecard.dtbo $OVERLAYS
|
||||
cp seeed-8mic-voicecard.dtbo $OVERLAYS
|
||||
|
||||
#install alsa plugins
|
||||
# no need this plugin now
|
||||
# install -D ac108_plugin/libasound_module_pcm_ac108.so /usr/lib/arm-linux-gnueabihf/alsa-lib/libasound_module_pcm_ac108.so
|
||||
# install -D ac108_plugin/libasound_module_pcm_ac108.so /usr/lib/arm-linux-gnueabihf/alsa-lib/
|
||||
rm -f /usr/lib/arm-linux-gnueabihf/alsa-lib/libasound_module_pcm_ac108.so
|
||||
|
||||
#set kernel moduels
|
||||
grep -q "snd-soc-seeed-voicecard" /etc/modules || \
|
||||
#set kernel modules
|
||||
grep -q "^snd-soc-seeed-voicecard$" /etc/modules || \
|
||||
echo "snd-soc-seeed-voicecard" >> /etc/modules
|
||||
grep -q "snd-soc-ac108" /etc/modules || \
|
||||
grep -q "^snd-soc-ac108$" /etc/modules || \
|
||||
echo "snd-soc-ac108" >> /etc/modules
|
||||
grep -q "snd-soc-wm8960" /etc/modules || \
|
||||
grep -q "^snd-soc-wm8960$" /etc/modules || \
|
||||
echo "snd-soc-wm8960" >> /etc/modules
|
||||
|
||||
#set dtoverlays
|
||||
sed -i -e 's:#dtparam=i2c_arm=on:dtparam=i2c_arm=on:g' /boot/config.txt || true
|
||||
grep -q "dtoverlay=i2s-mmap" /boot/config.txt || \
|
||||
echo "dtoverlay=i2s-mmap" >> /boot/config.txt
|
||||
CONFIG=/boot/config.txt
|
||||
[ -f /boot/firmware/config.txt ] && CONFIG=/boot/firmware/config.txt
|
||||
[ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt
|
||||
|
||||
sed -i -e 's:#dtparam=i2c_arm=on:dtparam=i2c_arm=on:g' $CONFIG || true
|
||||
grep -q "^dtoverlay=i2s-mmap$" $CONFIG || \
|
||||
echo "dtoverlay=i2s-mmap" >> $CONFIG
|
||||
|
||||
|
||||
grep -q "dtparam=i2s=on" /boot/config.txt || \
|
||||
echo "dtparam=i2s=on" >> /boot/config.txt
|
||||
grep -q "^dtparam=i2s=on$" $CONFIG || \
|
||||
echo "dtparam=i2s=on" >> $CONFIG
|
||||
|
||||
#install config files
|
||||
mkdir /etc/voicecard || true
|
||||
cp *.conf /etc/voicecard
|
||||
cp *.state /etc/voicecard
|
||||
|
||||
#create git repo
|
||||
git_email=$(git config --global --get user.email)
|
||||
git_name=$(git config --global --get user.name)
|
||||
if [ "x${git_email}" == "x" ] || [ "x${git_name}" == "x" ] ; then
|
||||
echo "setup git config"
|
||||
git config --global user.email "respeaker@seeed.cc"
|
||||
git config --global user.name "respeaker"
|
||||
fi
|
||||
echo "git init"
|
||||
git --git-dir=/etc/voicecard/.git init
|
||||
echo "git add --all"
|
||||
git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ add --all
|
||||
echo "git commit -m \"origin configures\""
|
||||
git --git-dir=/etc/voicecard/.git --work-tree=/etc/voicecard/ commit -m "origin configures"
|
||||
|
||||
cp seeed-voicecard /usr/bin/
|
||||
cp seeed-voicecard.service /lib/systemd/system/
|
||||
systemctl enable seeed-voicecard.service
|
||||
systemctl start seeed-voicecard
|
||||
|
||||
echo "------------------------------------------------------"
|
||||
echo "Please reboot your raspberry pi to apply all settings"
|
||||
|
|
454
patches/back-to-v4.19.diff
Normal file
454
patches/back-to-v4.19.diff
Normal file
|
@ -0,0 +1,454 @@
|
|||
diff --git a/ac101.c b/ac101.c
|
||||
index 23837a7..41c15f3 100644
|
||||
--- a/ac101.c
|
||||
+++ b/ac101.c
|
||||
@@ -955,10 +955,10 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai
|
||||
|
||||
AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n",
|
||||
snd_pcm_stream_str(substream),
|
||||
- codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE],
|
||||
- snd_soc_dai_active(codec_dai));
|
||||
+ codec_dai->playback_active, codec_dai->capture_active,
|
||||
+ codec_dai->active);
|
||||
|
||||
- if (!snd_soc_dai_active(codec_dai)) {
|
||||
+ if (!codec_dai->active) {
|
||||
ac10x->aif1_clken = 1;
|
||||
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||
} else {
|
||||
@@ -1080,7 +1080,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
|
||||
freq_out = _FREQ_24_576K;
|
||||
for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
|
||||
if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
|
||||
- if (codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
+ if (codec_dai->capture_active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), (0x4<<AIF1_FS));
|
||||
} else {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), ((codec_aif1_fs[i].srbit)<<AIF1_FS));
|
||||
diff --git a/ac108.c b/ac108.c
|
||||
index d5dd12d..0a8c462 100644
|
||||
--- a/ac108.c
|
||||
+++ b/ac108.c
|
||||
@@ -653,7 +653,7 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
||||
|
||||
dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
|
||||
snd_pcm_stream_str(substream),
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
+ dai->playback_active, dai->capture_active);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ret = ac101_hw_params(substream, params, dai);
|
||||
@@ -664,8 +664,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
||||
}
|
||||
}
|
||||
|
||||
- if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK])
|
||||
- || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream_active[SNDRV_PCM_STREAM_CAPTURE])) {
|
||||
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active)
|
||||
+ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) {
|
||||
/* not configure hw_param twice */
|
||||
/* return 0; */
|
||||
}
|
||||
@@ -810,9 +810,6 @@ static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int fr
|
||||
|
||||
struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
- if (freq != 24000000 || clk_id != SYSCLK_SRC_PLL)
|
||||
- dev_warn(dai->dev, "ac108_set_sysclk freq = %d clk = %d\n", freq, clk_id);
|
||||
-
|
||||
freq = 24000000;
|
||||
clk_id = SYSCLK_SRC_PLL;
|
||||
|
||||
@@ -1124,7 +1121,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
||||
}
|
||||
}
|
||||
|
||||
-int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) {
|
||||
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
@@ -1145,13 +1142,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = {
|
||||
.hw_params = ac108_hw_params,
|
||||
.prepare = ac108_prepare,
|
||||
.trigger = ac108_trigger,
|
||||
- .mute_stream = ac108_aif_mute,
|
||||
+ .digital_mute = ac108_aif_mute,
|
||||
|
||||
/*DAI format configuration*/
|
||||
.set_fmt = ac108_set_fmt,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ac108_dai0 = {
|
||||
diff --git a/seeed-voicecard.c b/seeed-voicecard.c
|
||||
index b90af93..af6db74 100644
|
||||
--- a/seeed-voicecard.c
|
||||
+++ b/seeed-voicecard.c
|
||||
@@ -28,8 +28,6 @@
|
||||
#include <sound/simple_card_utils.h>
|
||||
#include "ac10x.h"
|
||||
|
||||
-#define LINUX_VERSION_IS_GEQ(x1,x2,x3) (LINUX_VERSION_CODE >= KERNEL_VERSION(x1,x2,x3))
|
||||
-
|
||||
/*
|
||||
* single codec:
|
||||
* 0 - allow multi codec
|
||||
@@ -42,9 +40,6 @@ struct seeed_card_data {
|
||||
struct seeed_dai_props {
|
||||
struct asoc_simple_dai cpu_dai;
|
||||
struct asoc_simple_dai codec_dai;
|
||||
- struct snd_soc_dai_link_component cpus; /* single cpu */
|
||||
- struct snd_soc_dai_link_component codecs; /* single codec */
|
||||
- struct snd_soc_dai_link_component platforms;
|
||||
unsigned int mclk_fs;
|
||||
} *dai_props;
|
||||
unsigned int mclk_fs;
|
||||
@@ -97,16 +92,16 @@ static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
|
||||
if (ret)
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
- if (asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) {
|
||||
- priv->channels_playback_default = asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min;
|
||||
+ if (rtd->cpu_dai->driver->playback.channels_min) {
|
||||
+ priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min;
|
||||
}
|
||||
- if (asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) {
|
||||
- priv->channels_capture_default = asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min;
|
||||
+ if (rtd->cpu_dai->driver->capture.channels_min) {
|
||||
+ priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min;
|
||||
}
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override;
|
||||
+ rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override;
|
||||
+ rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override;
|
||||
+ rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override;
|
||||
+ rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -118,10 +113,10 @@ static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default;
|
||||
+ rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default;
|
||||
+ rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default;
|
||||
+ rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default;
|
||||
+ rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default;
|
||||
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
@@ -132,8 +127,8 @@ static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
@@ -197,7 +192,7 @@ static void work_cb_codec_clk(struct work_struct *work)
|
||||
static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
- struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
|
||||
+ struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
unsigned long flags;
|
||||
@@ -206,7 +201,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
+ dai->playback_active, dai->capture_active);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@@ -228,7 +223,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
/* capture channel resync, if overrun */
|
||||
- if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
+ if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -248,7 +243,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], ret);
|
||||
+ dai->playback_active, dai->capture_active, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -387,8 +382,8 @@ static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd)
|
||||
static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
- struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0);
|
||||
- struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0);
|
||||
+ struct snd_soc_dai *codec = rtd->codec_dai;
|
||||
+ struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
int ret;
|
||||
@@ -453,19 +448,20 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
- ret = asoc_simple_parse_daifmt(dev, node, codec,
|
||||
+ ret = asoc_simple_card_parse_daifmt(dev, node, codec,
|
||||
prefix, &dai_link->dai_fmt);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
|
||||
|
||||
- ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu);
|
||||
+ ret = asoc_simple_card_parse_cpu(cpu, dai_link,
|
||||
+ DAI, CELL, &single_cpu);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
#if _SINGLE_CODEC
|
||||
- ret = asoc_simple_parse_codec(codec, dai_link);
|
||||
+ ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
#else
|
||||
@@ -477,7 +473,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
||||
dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs);
|
||||
#endif
|
||||
|
||||
- ret = asoc_simple_parse_platform(plat, dai_link);
|
||||
+ ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
@@ -502,7 +498,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
|
||||
ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
|
||||
#else
|
||||
- ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
|
||||
+ ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
|
||||
#endif
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
@@ -510,16 +506,16 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
|
||||
ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
|
||||
#else
|
||||
- ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai);
|
||||
+ ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
|
||||
#endif
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
- ret = asoc_simple_set_dailink_name(dev, dai_link,
|
||||
+ ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
||||
"%s-%s",
|
||||
- dai_link->cpus->dai_name,
|
||||
+ dai_link->cpu_dai_name,
|
||||
#if _SINGLE_CODEC
|
||||
- dai_link->codecs->dai_name
|
||||
+ dai_link->codec_dai_name
|
||||
#else
|
||||
dai_link->codecs[0].dai_name
|
||||
#endif
|
||||
@@ -533,19 +529,21 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
||||
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
|
||||
dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
|
||||
dev_dbg(dev, "\tcpu : %s / %d\n",
|
||||
- dai_link->cpus->dai_name,
|
||||
+ dai_link->cpu_dai_name,
|
||||
dai_props->cpu_dai.sysclk);
|
||||
dev_dbg(dev, "\tcodec : %s / %d\n",
|
||||
#if _SINGLE_CODEC
|
||||
- dai_link->codecs->dai_name,
|
||||
+ dai_link->codec_dai_name,
|
||||
#else
|
||||
dai_link->codecs[0].dai_name,
|
||||
#endif
|
||||
dai_props->codec_dai.sysclk);
|
||||
|
||||
- asoc_simple_canonicalize_cpu(dai_link, single_cpu);
|
||||
+ asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
|
||||
#if _SINGLE_CODEC
|
||||
- asoc_simple_canonicalize_platform(dai_link);
|
||||
+ ret = asoc_simple_card_canonicalize_dailink(dai_link);
|
||||
+ if (ret < 0)
|
||||
+ goto dai_link_of_err;
|
||||
#endif
|
||||
|
||||
dai_link_of_err:
|
||||
@@ -578,7 +576,7 @@ static int seeed_voice_card_parse_aux_devs(struct device_node *node,
|
||||
aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
|
||||
if (!aux_node)
|
||||
return -EINVAL;
|
||||
- priv->snd_card.aux_dev[i].dlc.of_node = aux_node;
|
||||
+ priv->snd_card.aux_dev[i].codec_of_node = aux_node;
|
||||
}
|
||||
|
||||
priv->snd_card.num_aux_devs = n;
|
||||
@@ -638,7 +636,7 @@ static int seeed_voice_card_parse_of(struct device_node *node,
|
||||
goto card_parse_end;
|
||||
}
|
||||
|
||||
- ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX);
|
||||
+ ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
|
||||
if (ret < 0)
|
||||
goto card_parse_end;
|
||||
|
||||
@@ -743,7 +741,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
struct seeed_dai_props *dai_props;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
- int num, ret, i;
|
||||
+ int num, ret;
|
||||
|
||||
/* Get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, PREFIX "dai-link"))
|
||||
@@ -761,25 +759,6 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
if (!dai_props || !dai_link)
|
||||
return -ENOMEM;
|
||||
|
||||
- /*
|
||||
- * Use snd_soc_dai_link_component instead of legacy style
|
||||
- * It is codec only. but cpu/platform will be supported in the future.
|
||||
- * see
|
||||
- * soc-core.c :: snd_soc_init_multicodec()
|
||||
- *
|
||||
- * "platform" might be removed
|
||||
- * see
|
||||
- * simple-card-utils.c :: asoc_simple_canonicalize_platform()
|
||||
- */
|
||||
- for (i = 0; i < num; i++) {
|
||||
- dai_link[i].cpus = &dai_props[i].cpus;
|
||||
- dai_link[i].num_cpus = 1;
|
||||
- dai_link[i].codecs = &dai_props[i].codecs;
|
||||
- dai_link[i].num_codecs = 1;
|
||||
- dai_link[i].platforms = &dai_props[i].platforms;
|
||||
- dai_link[i].num_platforms = 1;
|
||||
- }
|
||||
-
|
||||
priv->dai_props = dai_props;
|
||||
priv->dai_link = dai_link;
|
||||
|
||||
@@ -798,9 +777,6 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
}
|
||||
} else {
|
||||
struct seeed_card_info *cinfo;
|
||||
- struct snd_soc_dai_link_component *cpus;
|
||||
- struct snd_soc_dai_link_component *codecs;
|
||||
- struct snd_soc_dai_link_component *platform;
|
||||
|
||||
cinfo = dev->platform_data;
|
||||
if (!cinfo) {
|
||||
@@ -817,19 +793,13 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- cpus = dai_link->cpus;
|
||||
- cpus->dai_name = cinfo->cpu_dai.name;
|
||||
-
|
||||
- codecs = dai_link->codecs;
|
||||
- codecs->name = cinfo->codec;
|
||||
- codecs->dai_name = cinfo->codec_dai.name;
|
||||
-
|
||||
- platform = dai_link->platforms;
|
||||
- platform->name = cinfo->platform;
|
||||
-
|
||||
priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
|
||||
dai_link->name = cinfo->name;
|
||||
dai_link->stream_name = cinfo->name;
|
||||
+ dai_link->platform_name = cinfo->platform;
|
||||
+ dai_link->codec_name = cinfo->codec;
|
||||
+ dai_link->cpu_dai_name = cinfo->cpu_dai.name;
|
||||
+ dai_link->codec_dai_name = cinfo->codec_dai.name;
|
||||
dai_link->dai_fmt = cinfo->daifmt;
|
||||
dai_link->init = seeed_voice_card_dai_init;
|
||||
memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
|
||||
@@ -853,7 +823,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
err:
|
||||
- asoc_simple_clean_reference(&priv->snd_card);
|
||||
+ asoc_simple_card_clean_reference(&priv->snd_card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -865,7 +835,7 @@ static int seeed_voice_card_remove(struct platform_device *pdev)
|
||||
|
||||
if (cancel_work_sync(&priv->work_codec_clk) != 0) {
|
||||
}
|
||||
- return asoc_simple_clean_reference(card);
|
||||
+ return asoc_simple_card_clean_reference(card);
|
||||
}
|
||||
|
||||
static const struct of_device_id seeed_voice_of_match[] = {
|
||||
diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h
|
||||
index 550b3a7..6c1a014 100644
|
||||
--- a/sound-compatible-4.18.h
|
||||
+++ b/sound-compatible-4.18.h
|
||||
@@ -16,6 +16,10 @@
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
|
||||
+#ifndef __has_attribute
|
||||
+# define __has_attribute(x) __GCC4_has_attribute_##x
|
||||
+# define __GCC4_has_attribute___fallthrough__ 0
|
||||
+#endif
|
||||
#if __has_attribute(__fallthrough__)
|
||||
# define fallthrough __attribute__((__fallthrough__))
|
||||
#else
|
||||
@@ -31,11 +35,7 @@
|
||||
#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
|
||||
#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
|
||||
#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
|
||||
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
-#define snd_soc_read snd_soc_component_read
|
||||
-#else
|
||||
#define snd_soc_read snd_soc_component_read32
|
||||
-#endif
|
||||
#define snd_soc_register_codec devm_snd_soc_register_component
|
||||
#define snd_soc_unregister_codec snd_soc_unregister_component
|
||||
#define snd_soc_update_bits snd_soc_component_update_bits
|
||||
diff --git a/wm8960.c b/wm8960.c
|
||||
index 465c6dc..34d4dad 100644
|
||||
--- a/wm8960.c
|
||||
+++ b/wm8960.c
|
||||
@@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
@@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
static const struct snd_soc_dai_ops wm8960_dai_ops = {
|
||||
.hw_params = wm8960_hw_params,
|
||||
.hw_free = wm8960_hw_free,
|
||||
- .mute_stream = wm8960_mute,
|
||||
+ .digital_mute = wm8960_mute,
|
||||
.set_fmt = wm8960_set_dai_fmt,
|
||||
.set_clkdiv = wm8960_set_dai_clkdiv,
|
||||
.set_pll = wm8960_set_dai_pll,
|
||||
.set_sysclk = wm8960_set_dai_sysclk,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver wm8960_dai = {
|
245
patches/back-to-v5.4.diff
Normal file
245
patches/back-to-v5.4.diff
Normal file
|
@ -0,0 +1,245 @@
|
|||
diff --git a/ac101.c b/ac101.c
|
||||
index be9b1d8..343f030 100644
|
||||
--- a/ac101.c
|
||||
+++ b/ac101.c
|
||||
@@ -955,10 +955,10 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai
|
||||
|
||||
AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n",
|
||||
snd_pcm_stream_str(substream),
|
||||
- codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE],
|
||||
- snd_soc_dai_active(codec_dai));
|
||||
+ codec_dai->playback_active, codec_dai->capture_active,
|
||||
+ codec_dai->active);
|
||||
|
||||
- if (!snd_soc_dai_active(codec_dai)) {
|
||||
+ if (!codec_dai->active) {
|
||||
ac10x->aif1_clken = 1;
|
||||
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||
} else {
|
||||
@@ -1080,7 +1080,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
|
||||
freq_out = _FREQ_24_576K;
|
||||
for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
|
||||
if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
|
||||
- if (codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
+ if (codec_dai->capture_active && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), (0x4<<AIF1_FS));
|
||||
} else {
|
||||
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), ((codec_aif1_fs[i].srbit)<<AIF1_FS));
|
||||
diff --git a/ac108.c b/ac108.c
|
||||
index 4663df0..12ab27b 100644
|
||||
--- a/ac108.c
|
||||
+++ b/ac108.c
|
||||
@@ -653,7 +653,7 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
||||
|
||||
dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
|
||||
snd_pcm_stream_str(substream),
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
+ dai->playback_active, dai->capture_active);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ret = ac101_hw_params(substream, params, dai);
|
||||
@@ -664,8 +664,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
||||
}
|
||||
}
|
||||
|
||||
- if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK])
|
||||
- || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream_active[SNDRV_PCM_STREAM_CAPTURE])) {
|
||||
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active)
|
||||
+ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) {
|
||||
/* not configure hw_param twice */
|
||||
/* return 0; */
|
||||
}
|
||||
@@ -1124,7 +1124,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
||||
}
|
||||
}
|
||||
|
||||
-int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) {
|
||||
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
@@ -1145,13 +1145,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = {
|
||||
.hw_params = ac108_hw_params,
|
||||
.prepare = ac108_prepare,
|
||||
.trigger = ac108_trigger,
|
||||
- .mute_stream = ac108_aif_mute,
|
||||
+ .digital_mute = ac108_aif_mute,
|
||||
|
||||
/*DAI format configuration*/
|
||||
.set_fmt = ac108_set_fmt,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ac108_dai0 = {
|
||||
diff --git a/seeed-voicecard.c b/seeed-voicecard.c
|
||||
index c6d9048..43535aa 100644
|
||||
--- a/seeed-voicecard.c
|
||||
+++ b/seeed-voicecard.c
|
||||
@@ -96,16 +96,16 @@ static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
|
||||
if (ret)
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
- if (asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) {
|
||||
- priv->channels_playback_default = asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min;
|
||||
+ if (rtd->cpu_dai->driver->playback.channels_min) {
|
||||
+ priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min;
|
||||
}
|
||||
- if (asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) {
|
||||
- priv->channels_capture_default = asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min;
|
||||
+ if (rtd->cpu_dai->driver->capture.channels_min) {
|
||||
+ priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min;
|
||||
}
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override;
|
||||
+ rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override;
|
||||
+ rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override;
|
||||
+ rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override;
|
||||
+ rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -117,10 +117,10 @@ static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default;
|
||||
- asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default;
|
||||
+ rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default;
|
||||
+ rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default;
|
||||
+ rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default;
|
||||
+ rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default;
|
||||
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
@@ -131,8 +131,8 @@ static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
@@ -196,7 +196,7 @@ static void work_cb_codec_clk(struct work_struct *work)
|
||||
static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
- struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
|
||||
+ struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
unsigned long flags;
|
||||
@@ -205,7 +205,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
+ dai->playback_active, dai->capture_active);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@@ -227,7 +227,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
/* capture channel resync, if overrun */
|
||||
- if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
+ if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -252,7 +247,7 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
- dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE], ret);
|
||||
+ dai->playback_active, dai->capture_active, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -337,8 +337,8 @@ static int asoc_simple_init_dai(struct snd_soc_dai *dai,
|
||||
static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
- struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0);
|
||||
- struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0);
|
||||
+ struct snd_soc_dai *codec = rtd->codec_dai;
|
||||
+ struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
int ret;
|
||||
@@ -636,11 +636,11 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
* Use snd_soc_dai_link_component instead of legacy style
|
||||
* It is codec only. but cpu/platform will be supported in the future.
|
||||
* see
|
||||
- * soc-core.c :: snd_soc_init_multicodec()
|
||||
+ * soc-core.c :: snd_soc_init_multicodec()
|
||||
*
|
||||
* "platform" might be removed
|
||||
* see
|
||||
- * simple-card-utils.c :: asoc_simple_canonicalize_platform()
|
||||
+ * simple-card-utils.c :: asoc_simple_canonicalize_platform()
|
||||
*/
|
||||
for (i = 0; i < num; i++) {
|
||||
dai_link[i].cpus = &dai_props[i].cpus;
|
||||
diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h
|
||||
index 080325b..eefa7de 100644
|
||||
--- a/sound-compatible-4.18.h
|
||||
+++ b/sound-compatible-4.18.h
|
||||
@@ -16,6 +16,10 @@
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
|
||||
+#ifndef __has_attribute
|
||||
+# define __has_attribute(x) __GCC4_has_attribute_##x
|
||||
+# define __GCC4_has_attribute___fallthrough__ 0
|
||||
+#endif
|
||||
#if __has_attribute(__fallthrough__)
|
||||
# define fallthrough __attribute__((__fallthrough__))
|
||||
#else
|
||||
@@ -31,11 +35,7 @@
|
||||
#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
|
||||
#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
|
||||
#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
|
||||
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
-#define snd_soc_read snd_soc_component_read
|
||||
-#else
|
||||
#define snd_soc_read snd_soc_component_read32
|
||||
-#endif
|
||||
#define snd_soc_register_codec devm_snd_soc_register_component
|
||||
#define snd_soc_unregister_codec snd_soc_unregister_component
|
||||
#define snd_soc_update_bits snd_soc_component_update_bits
|
||||
diff --git a/wm8960.c b/wm8960.c
|
||||
index 465c6dc..34d4dad 100644
|
||||
--- a/wm8960.c
|
||||
+++ b/wm8960.c
|
||||
@@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
@@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
static const struct snd_soc_dai_ops wm8960_dai_ops = {
|
||||
.hw_params = wm8960_hw_params,
|
||||
.hw_free = wm8960_hw_free,
|
||||
- .mute_stream = wm8960_mute,
|
||||
+ .digital_mute = wm8960_mute,
|
||||
.set_fmt = wm8960_set_dai_fmt,
|
||||
.set_clkdiv = wm8960_set_dai_clkdiv,
|
||||
.set_pll = wm8960_set_dai_pll,
|
||||
.set_sysclk = wm8960_set_dai_sysclk,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver wm8960_dai = {
|
71
patches/back-to-v5.8.diff
Normal file
71
patches/back-to-v5.8.diff
Normal file
|
@ -0,0 +1,71 @@
|
|||
diff --git a/ac108.c b/ac108.c
|
||||
index 4663df0..67edeae 100644
|
||||
--- a/ac108.c
|
||||
+++ b/ac108.c
|
||||
@@ -1124,7 +1124,7 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
||||
}
|
||||
}
|
||||
|
||||
-int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int direction) {
|
||||
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
@@ -1145,13 +1145,12 @@ static const struct snd_soc_dai_ops ac108_dai_ops = {
|
||||
.hw_params = ac108_hw_params,
|
||||
.prepare = ac108_prepare,
|
||||
.trigger = ac108_trigger,
|
||||
- .mute_stream = ac108_aif_mute,
|
||||
+ .digital_mute = ac108_aif_mute,
|
||||
|
||||
/*DAI format configuration*/
|
||||
.set_fmt = ac108_set_fmt,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ac108_dai0 = {
|
||||
diff --git a/sound-compatible-4.18.h b/sound-compatible-4.18.h
|
||||
index 080325b..faed848 100644
|
||||
--- a/sound-compatible-4.18.h
|
||||
+++ b/sound-compatible-4.18.h
|
||||
@@ -31,11 +31,7 @@
|
||||
#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
|
||||
#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
|
||||
#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
|
||||
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
-#define snd_soc_read snd_soc_component_read
|
||||
-#else
|
||||
#define snd_soc_read snd_soc_component_read32
|
||||
-#endif
|
||||
#define snd_soc_register_codec devm_snd_soc_register_component
|
||||
#define snd_soc_unregister_codec snd_soc_unregister_component
|
||||
#define snd_soc_update_bits snd_soc_component_update_bits
|
||||
diff --git a/wm8960.c b/wm8960.c
|
||||
index 465c6dc..34d4dad 100644
|
||||
--- a/wm8960.c
|
||||
+++ b/wm8960.c
|
||||
@@ -796,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
@@ -1236,12 +1236,11 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
static const struct snd_soc_dai_ops wm8960_dai_ops = {
|
||||
.hw_params = wm8960_hw_params,
|
||||
.hw_free = wm8960_hw_free,
|
||||
- .mute_stream = wm8960_mute,
|
||||
+ .digital_mute = wm8960_mute,
|
||||
.set_fmt = wm8960_set_dai_fmt,
|
||||
.set_clkdiv = wm8960_set_dai_clkdiv,
|
||||
.set_pll = wm8960_set_dai_pll,
|
||||
.set_sysclk = wm8960_set_dai_sysclk,
|
||||
- .no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver wm8960_dai = {
|
8
pulseaudio/91-seeedvoicecard.rules
Normal file
8
pulseaudio/91-seeedvoicecard.rules
Normal file
|
@ -0,0 +1,8 @@
|
|||
SUBSYSTEM!="sound", GOTO="seeedvoicecard_end"
|
||||
ACTION!="change", GOTO="seeedvoicecard_end"
|
||||
KERNEL!="card*", GOTO="seeedvoicecard_end"
|
||||
|
||||
ATTR{id}=="seeed4micvoicec",ENV{PULSE_PROFILE_SET}="seeed-voicecard-4mic.conf"
|
||||
ATTR{id}=="seeed8micvoicec",ENV{PULSE_PROFILE_SET}="seeed-voicecard-8mic.conf"
|
||||
|
||||
LABEL="seeedvoicecard_end"
|
251
pulseaudio/README.md
Normal file
251
pulseaudio/README.md
Normal file
|
@ -0,0 +1,251 @@
|
|||
# PulseAudio Configuration for seeed-voicecard
|
||||
|
||||
Follow this guide if you want to use your seeed-voicecard as a default source/sink of pulseaudio.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Download PulseAudio
|
||||
```
|
||||
sudo apt install -y pulseaudio
|
||||
```
|
||||
|
||||
2. PulseAudio Profiles
|
||||
```
|
||||
cd seeed-voicecard/pulseaudio
|
||||
sudo cp pulse_config_4mic/seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard-4mic.conf
|
||||
sudo cp pulse_config_6mic/seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard-8mic.conf
|
||||
```
|
||||
|
||||
3. Add `udev` Rules
|
||||
|
||||
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`.
|
||||
|
||||
```
|
||||
sudo cp 91-seeedvoicecard.rules /etc/udev/rules.d/91-seeedvoicecard.rules
|
||||
```
|
||||
|
||||
### ReSpeaker 4 Mic Array
|
||||
|
||||
<!--
|
||||
1. Download pulseaudio
|
||||
```
|
||||
sudo apt install pulseaudio
|
||||
```
|
||||
|
||||
2. First, you need to write [a profile for pulse](https://www.freedesktop.org/wiki/Software/PulseAudio/Backends/ALSA/Profiles/)
|
||||
```
|
||||
cd seeed-voicecard
|
||||
cd pulseaudio
|
||||
cd pulse_config_4mic
|
||||
sudo cp seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/
|
||||
```
|
||||
|
||||
3. Edit `udev rules`
|
||||
|
||||
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.conf`.
|
||||
|
||||
```
|
||||
# have a look at /lib/udev/rules.d/90-pulseaudio.rules
|
||||
sudo vim /lib/udev/rules.d/90-pulseaudio.rules
|
||||
# add the following lines at about line 87(behind the setting for some laptops and before the line GOTO="pulseaudio_end")
|
||||
# Seeed Voicecard
|
||||
ATTR{id}=="seeed4micvoicec",ATTR{number}=="1",ENV{PULSE_PROFILE_SET}="seeed-voicecard.conf"
|
||||
```
|
||||

|
||||
|
||||
The value of `ATTR{number}` can be found with:
|
||||
|
||||
```
|
||||
udevadm info -a -p /sys/class/sound/card0/
|
||||
# or udevadm info -a -p /sys/class/sound/card1/
|
||||
```
|
||||
|
||||
For example, in Raspberry Pi, we can find `ATTR{id}=="seeed4micvoicec"` and `ATTR{number}=="1"` with command `udevadm info -a -p /sys/class/sound/card1/`:
|
||||
|
||||
```
|
||||
pi@raspberrypi:~ $ udevadm info -a -p /sys/class/sound/card1/
|
||||
|
||||
Udevadm info starts with the device specified by the devpath and then
|
||||
walks up the chain of parent devices. It prints for every device
|
||||
found, all possible attributes in the udev rules key format.
|
||||
A rule to match, can be composed by the attributes of the device
|
||||
and the attributes from one single parent device.
|
||||
|
||||
looking at device '/devices/platform/soc/soc:sound/sound/card1':
|
||||
KERNEL=="card1"
|
||||
SUBSYSTEM=="sound"
|
||||
DRIVER==""
|
||||
ATTR{id}=="seeed4micvoicec"
|
||||
ATTR{number}=="1"
|
||||
|
||||
looking at parent device '/devices/platform/soc/soc:sound':
|
||||
KERNELS=="soc:sound"
|
||||
SUBSYSTEMS=="platform"
|
||||
DRIVERS=="seeed-voicecard"
|
||||
ATTRS{driver_override}=="(null)"
|
||||
|
||||
looking at parent device '/devices/platform/soc':
|
||||
KERNELS=="soc"
|
||||
SUBSYSTEMS=="platform"
|
||||
DRIVERS==""
|
||||
ATTRS{driver_override}=="(null)"
|
||||
|
||||
looking at parent device '/devices/platform':
|
||||
KERNELS=="platform"
|
||||
SUBSYSTEMS==""
|
||||
DRIVERS==""
|
||||
``` -->
|
||||
|
||||
1. config `default.pa` and `daemon.conf`
|
||||
```
|
||||
sudo cp pulse_config_4mic/default.pa /etc/pulse/
|
||||
sudo cp pulse_config_4mic/daemon.conf /etc/pulse/
|
||||
```
|
||||
|
||||
2. reboot raspberry pi and check
|
||||
```
|
||||
sudo reboot
|
||||
pulseaudio --start # start pulse at first
|
||||
pactl info # check the setting
|
||||
|
||||
Server String: /run/user/1000/pulse/native
|
||||
Library Protocol Version: 32
|
||||
Server Protocol Version: 32
|
||||
Is Local: yes
|
||||
Client Index: 18
|
||||
Tile Size: 65496
|
||||
User Name: pi
|
||||
Host Name: raspberrypi
|
||||
Server Name: pulseaudio
|
||||
Server Version: 10.0
|
||||
Default Sample Specification: s16le 4ch 96000Hz
|
||||
Default Channel Map: front-left,front-center,front-right,rear-center
|
||||
Default Sink: alsa_output.platform-soc_audio.analog-stereo
|
||||
Default Source: alsa_input.platform-soc_sound.seeed-source
|
||||
Cookie: 3b12:70b3
|
||||
```
|
||||
|
||||
### 6-Mics Circular Array Kit and 4-Mics Linear Array
|
||||
|
||||
<!--
|
||||
1. Download pulseaudio
|
||||
```
|
||||
sudo apt install pulseaudio
|
||||
```
|
||||
|
||||
2. First, you need to write [a profile for pulse](https://www.freedesktop.org/wiki/Software/PulseAudio/Backends/ALSA/Profiles/)
|
||||
```
|
||||
cd seeed-voicecard
|
||||
cd pulseaudio
|
||||
cd pulse_config_6mic
|
||||
sudo cp seeed-voicecard.conf /usr/share/pulseaudio/alsa-mixer/profile-sets/
|
||||
```
|
||||
|
||||
3. Edit `udev rules`
|
||||
|
||||
During the system start, when the card "seeed8micvoicec" is detected, the PULSE_PROFILE_SET variable will be set in the udev database, and PulseAudio will be forced to use `seeed-voicecard.conf`.
|
||||
|
||||
```
|
||||
# have a look at /lib/udev/rules.d/90-pulseaudio.rules
|
||||
sudo vim /lib/udev/rules.d/90-pulseaudio.rules
|
||||
# add the following lines at about line 87(behind the setting for some laptops and before the line GOTO="pulseaudio_end")
|
||||
# Seeed Voicecard
|
||||
ATTR{id}=="seeed8micvoicec",ATTR{number}=="1",ENV{PULSE_PROFILE_SET}="seeed-voicecard.conf"
|
||||
```
|
||||

|
||||
|
||||
The value of `ATTR{number}` can be found with:
|
||||
|
||||
```
|
||||
udevadm info -a -p /sys/class/sound/card0/
|
||||
# or udevadm info -a -p /sys/class/sound/card1/
|
||||
```
|
||||
|
||||
For example, in Raspberry Pi, we can find `ATTR{id}=="seeed8micvoicec"` and `ATTR{number}=="1"` with command `udevadm info -a -p /sys/class/sound/card1/`:
|
||||
|
||||
```
|
||||
pi@raspberrypi:~ $ udevadm info -a -p /sys/class/sound/card1/
|
||||
|
||||
Udevadm info starts with the device specified by the devpath and then
|
||||
walks up the chain of parent devices. It prints for every device
|
||||
found, all possible attributes in the udev rules key format.
|
||||
A rule to match, can be composed by the attributes of the device
|
||||
and the attributes from one single parent device.
|
||||
|
||||
looking at device '/devices/platform/soc/soc:sound/sound/card1':
|
||||
KERNEL=="card1"
|
||||
SUBSYSTEM=="sound"
|
||||
DRIVER==""
|
||||
ATTR{id}=="seeed8micvoicec"
|
||||
ATTR{number}=="1"
|
||||
|
||||
looking at parent device '/devices/platform/soc/soc:sound':
|
||||
KERNELS=="soc:sound"
|
||||
SUBSYSTEMS=="platform"
|
||||
DRIVERS=="seeed-voicecard"
|
||||
ATTRS{driver_override}=="(null)"
|
||||
|
||||
looking at parent device '/devices/platform/soc':
|
||||
KERNELS=="soc"
|
||||
SUBSYSTEMS=="platform"
|
||||
DRIVERS==""
|
||||
ATTRS{driver_override}=="(null)"
|
||||
|
||||
looking at parent device '/devices/platform':
|
||||
KERNELS=="platform"
|
||||
SUBSYSTEMS==""
|
||||
DRIVERS==""
|
||||
``` -->
|
||||
|
||||
1. config `default.pa` and `daemon.conf`
|
||||
```
|
||||
sudo cp pulse_config_6mic/default.pa /etc/pulse/
|
||||
sudo cp pulse_config_6mic/daemon.conf /etc/pulse/
|
||||
```
|
||||
|
||||
2. reboot raspberry pi and check
|
||||
```
|
||||
sudo reboot
|
||||
pulseaudio --start # start pulse at first
|
||||
pactl info # check the setting
|
||||
|
||||
# The output should be like this
|
||||
# You could see the default sink is seeed-2ch and default source is seeed-8ch
|
||||
pi@raspberrypi:~ $ pactl info
|
||||
Server String: /run/user/1000/pulse/native
|
||||
Library Protocol Version: 32
|
||||
Server Protocol Version: 32
|
||||
Is Local: yes
|
||||
Client Index: 6
|
||||
Tile Size: 65496
|
||||
User Name: pi
|
||||
Host Name: raspberrypi
|
||||
Server Name: pulseaudio
|
||||
Server Version: 10.0
|
||||
Default Sample Specification: s32le 8ch 96000Hz
|
||||
Default Channel Map: front-left,front-left-of-center,front-center,front-right,front-right-of-center,rear-center,aux0,aux1
|
||||
Default Sink: alsa_output.platform-soc_sound.seeed-2ch
|
||||
Default Source: alsa_input.platform-soc_sound.seeed-8ch
|
||||
Cookie: 3523:e5af
|
||||
```
|
||||
|
||||
### FAQ
|
||||
|
||||
1. Default Sink/Source not right
|
||||
|
||||
Make sure there is no any other daemon or using the audio device. Then check profile and udev rules.
|
||||
|
||||
`pacmd list-sinks` and `pacmd list-sources` can be used to show the avaiable sinks/sources, after pulseaudio is started.
|
||||
|
||||
2. Can't start PulseAudio
|
||||
|
||||
Check `default.pa` and `daemon.conf`
|
||||
|
||||
3. How to get PulseAudio started automatically
|
||||
|
||||
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`.
|
||||
[Click this for more details](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Running/).
|
||||
|
||||
4. Why the default sample rate is 96000? What if my audio's sample rate is not the same as the default?
|
||||
|
||||
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.
|
87
pulseaudio/pulse_config_4mic/daemon.conf
Normal file
87
pulseaudio/pulse_config_4mic/daemon.conf
Normal file
|
@ -0,0 +1,87 @@
|
|||
# This file is part of PulseAudio.
|
||||
#
|
||||
# PulseAudio is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PulseAudio is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
|
||||
## more information. Default values are commented out. Use either ; or # for
|
||||
## commenting.
|
||||
|
||||
; daemonize = no
|
||||
; fail = yes
|
||||
; allow-module-loading = yes
|
||||
; allow-exit = yes
|
||||
; use-pid-file = yes
|
||||
; system-instance = no
|
||||
; local-server-type = user
|
||||
; enable-shm = yes
|
||||
; enable-memfd = yes
|
||||
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
||||
; lock-memory = no
|
||||
; cpu-limit = no
|
||||
|
||||
; high-priority = yes
|
||||
; nice-level = -11
|
||||
|
||||
; realtime-scheduling = yes
|
||||
; realtime-priority = 5
|
||||
|
||||
; exit-idle-time = 20
|
||||
; scache-idle-time = 20
|
||||
|
||||
; dl-search-path = (depends on architecture)
|
||||
|
||||
; load-default-script-file = yes
|
||||
; default-script-file = /etc/pulse/default.pa
|
||||
|
||||
; log-target = auto
|
||||
; log-level = notice
|
||||
; log-meta = no
|
||||
; log-time = no
|
||||
; log-backtrace = 0
|
||||
|
||||
; resample-method = speex-float-1
|
||||
; enable-remixing = yes
|
||||
; enable-lfe-remixing = no
|
||||
; lfe-crossover-freq = 0
|
||||
|
||||
; flat-volumes = yes
|
||||
|
||||
; rlimit-fsize = -1
|
||||
; rlimit-data = -1
|
||||
; rlimit-stack = -1
|
||||
; rlimit-core = -1
|
||||
; rlimit-as = -1
|
||||
; rlimit-rss = -1
|
||||
; rlimit-nproc = -1
|
||||
; rlimit-nofile = 256
|
||||
; rlimit-memlock = -1
|
||||
; rlimit-locks = -1
|
||||
; rlimit-sigpending = -1
|
||||
; rlimit-msgqueue = -1
|
||||
; rlimit-nice = 31
|
||||
; rlimit-rtprio = 9
|
||||
; rlimit-rttime = 200000
|
||||
|
||||
default-sample-format = s16le
|
||||
default-sample-rate = 96000
|
||||
; alternate-sample-rate = 48000
|
||||
default-sample-channels = 4
|
||||
; default-channel-map = front-left,front-right
|
||||
|
||||
; default-fragments = 4
|
||||
; default-fragment-size-msec = 25
|
||||
|
||||
; enable-deferred-volume = yes
|
||||
; deferred-volume-safety-margin-usec = 8000
|
||||
; deferred-volume-extra-delay-usec = 0
|
144
pulseaudio/pulse_config_4mic/default.pa
Normal file
144
pulseaudio/pulse_config_4mic/default.pa
Normal file
|
@ -0,0 +1,144 @@
|
|||
#!/usr/bin/pulseaudio -nF
|
||||
#
|
||||
# This file is part of PulseAudio.
|
||||
#
|
||||
# PulseAudio is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PulseAudio is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This startup script is used only if PulseAudio is started per-user
|
||||
# (i.e. not in system mode)
|
||||
|
||||
.fail
|
||||
|
||||
### Automatically restore the volume of streams and devices
|
||||
load-module module-device-restore
|
||||
load-module module-stream-restore
|
||||
load-module module-card-restore
|
||||
|
||||
### Automatically augment property information from .desktop files
|
||||
### stored in /usr/share/application
|
||||
load-module module-augment-properties
|
||||
|
||||
### Should be after module-*-restore but before module-*-detect
|
||||
load-module module-switch-on-port-available
|
||||
|
||||
### Load audio drivers statically
|
||||
### (it's probably better to not load these drivers manually, but instead
|
||||
### use module-udev-detect -- see below -- for doing this automatically)
|
||||
#load-module module-alsa-sink device="hw:1,0" channels=8 rate=48000 format=s32le
|
||||
#load-module module-alsa-source device="hw:1,0" channels=8 rate=48000 format=s32le
|
||||
#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
|
||||
#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
|
||||
#load-module module-null-sink
|
||||
#load-module module-pipe-sink
|
||||
|
||||
### Automatically load driver modules depending on the hardware available
|
||||
.ifexists module-udev-detect.so
|
||||
load-module module-udev-detect
|
||||
#channels=8 rate=48000 format=s32le
|
||||
.else
|
||||
### Use the static hardware detection module (for systems that lack udev support)
|
||||
load-module module-detect
|
||||
.endif
|
||||
|
||||
### Automatically connect sink and source if JACK server is present
|
||||
.ifexists module-jackdbus-detect.so
|
||||
.nofail
|
||||
load-module module-jackdbus-detect channels=2
|
||||
.fail
|
||||
.endif
|
||||
|
||||
### Automatically load driver modules for Bluetooth hardware
|
||||
.ifexists module-bluetooth-policy.so
|
||||
load-module module-bluetooth-policy
|
||||
.endif
|
||||
|
||||
.ifexists module-bluetooth-discover.so
|
||||
load-module module-bluetooth-discover
|
||||
.endif
|
||||
|
||||
### Load several protocols
|
||||
.ifexists module-esound-protocol-unix.so
|
||||
load-module module-esound-protocol-unix
|
||||
.endif
|
||||
load-module module-native-protocol-unix
|
||||
|
||||
### Network access (may be configured with paprefs, so leave this commented
|
||||
### here if you plan to use paprefs)
|
||||
#load-module module-esound-protocol-tcp
|
||||
#load-module module-native-protocol-tcp
|
||||
#load-module module-zeroconf-publish
|
||||
|
||||
### Load the RTP receiver module (also configured via paprefs, see above)
|
||||
#load-module module-rtp-recv
|
||||
|
||||
### Load the RTP sender module (also configured via paprefs, see above)
|
||||
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
|
||||
#load-module module-rtp-send source=rtp.monitor
|
||||
|
||||
### Load additional modules from GConf settings. This can be configured with the paprefs tool.
|
||||
### Please keep in mind that the modules configured by paprefs might conflict with manually
|
||||
### loaded modules.
|
||||
.ifexists module-gconf.so
|
||||
.nofail
|
||||
load-module module-gconf
|
||||
.fail
|
||||
.endif
|
||||
|
||||
### Automatically restore the default sink/source when changed by the user
|
||||
### during runtime
|
||||
### NOTE: This should be loaded as early as possible so that subsequent modules
|
||||
### that look up the default sink/source get the right value
|
||||
load-module module-default-device-restore
|
||||
|
||||
### Automatically move streams to the default sink if the sink they are
|
||||
### connected to dies, similar for sources
|
||||
load-module module-rescue-streams
|
||||
|
||||
### Make sure we always have a sink around, even if it is a null sink.
|
||||
load-module module-always-sink
|
||||
|
||||
### Honour intended role device property
|
||||
load-module module-intended-roles
|
||||
|
||||
### Automatically suspend sinks/sources that become idle for too long
|
||||
load-module module-suspend-on-idle
|
||||
|
||||
### If autoexit on idle is enabled we want to make sure we only quit
|
||||
### when no local session needs us anymore.
|
||||
.ifexists module-console-kit.so
|
||||
load-module module-console-kit
|
||||
.endif
|
||||
.ifexists module-systemd-login.so
|
||||
load-module module-systemd-login
|
||||
.endif
|
||||
|
||||
### Enable positioned event sounds
|
||||
load-module module-position-event-sounds
|
||||
|
||||
### Cork music/video streams when a phone stream is active
|
||||
load-module module-role-cork
|
||||
|
||||
### Modules to allow autoloading of filters (such as echo cancellation)
|
||||
### on demand. module-filter-heuristics tries to determine what filters
|
||||
### make sense, and module-filter-apply does the heavy-lifting of
|
||||
### loading modules and rerouting streams.
|
||||
load-module module-filter-heuristics
|
||||
load-module module-filter-apply
|
||||
|
||||
### Make some devices default
|
||||
#set-default-sink output
|
||||
#set-default-source input
|
||||
set-default-source alsa_input.platform-soc_sound.seeed-source
|
||||
set-default-sink alsa_output.platform-soc_sound.seeed-sink
|
||||
|
17
pulseaudio/pulse_config_4mic/seeed-voicecard.conf
Normal file
17
pulseaudio/pulse_config_4mic/seeed-voicecard.conf
Normal file
|
@ -0,0 +1,17 @@
|
|||
# /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voicecard.conf
|
||||
|
||||
[General]
|
||||
auto-profiles = no
|
||||
[Mapping seeed-source]
|
||||
device-strings = hw:%f
|
||||
channel-map = front-left,front-right,rear-left,rear-right
|
||||
exact-channels = false
|
||||
fallback = yes
|
||||
paths-input = seeed-source
|
||||
priority = 3
|
||||
direction = input
|
||||
|
||||
[Profile input:seeed-source]
|
||||
input-mappings = seeed-source
|
||||
priority = 5
|
||||
skip-probe = yes
|
87
pulseaudio/pulse_config_6mic/daemon.conf
Normal file
87
pulseaudio/pulse_config_6mic/daemon.conf
Normal file
|
@ -0,0 +1,87 @@
|
|||
# This file is part of PulseAudio.
|
||||
#
|
||||
# PulseAudio is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PulseAudio is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
|
||||
## more information. Default values are commented out. Use either ; or # for
|
||||
## commenting.
|
||||
|
||||
; daemonize = no
|
||||
; fail = yes
|
||||
; allow-module-loading = yes
|
||||
; allow-exit = yes
|
||||
; use-pid-file = yes
|
||||
; system-instance = no
|
||||
; local-server-type = user
|
||||
; enable-shm = yes
|
||||
; enable-memfd = yes
|
||||
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
||||
; lock-memory = no
|
||||
; cpu-limit = no
|
||||
|
||||
; high-priority = yes
|
||||
; nice-level = -11
|
||||
|
||||
; realtime-scheduling = yes
|
||||
; realtime-priority = 5
|
||||
|
||||
; exit-idle-time = 20
|
||||
; scache-idle-time = 20
|
||||
|
||||
; dl-search-path = (depends on architecture)
|
||||
|
||||
; load-default-script-file = yes
|
||||
; default-script-file = /etc/pulse/default.pa
|
||||
|
||||
; log-target = auto
|
||||
; log-level = notice
|
||||
; log-meta = no
|
||||
; log-time = no
|
||||
; log-backtrace = 0
|
||||
|
||||
; resample-method = speex-float-1
|
||||
; enable-remixing = yes
|
||||
; enable-lfe-remixing = no
|
||||
; lfe-crossover-freq = 0
|
||||
|
||||
; flat-volumes = yes
|
||||
|
||||
; rlimit-fsize = -1
|
||||
; rlimit-data = -1
|
||||
; rlimit-stack = -1
|
||||
; rlimit-core = -1
|
||||
; rlimit-as = -1
|
||||
; rlimit-rss = -1
|
||||
; rlimit-nproc = -1
|
||||
; rlimit-nofile = 256
|
||||
; rlimit-memlock = -1
|
||||
; rlimit-locks = -1
|
||||
; rlimit-sigpending = -1
|
||||
; rlimit-msgqueue = -1
|
||||
; rlimit-nice = 31
|
||||
; rlimit-rtprio = 9
|
||||
; rlimit-rttime = 200000
|
||||
|
||||
default-sample-format = s32le
|
||||
default-sample-rate = 96000
|
||||
; alternate-sample-rate = 48000
|
||||
default-sample-channels = 8
|
||||
; default-channel-map = front-left,front-right
|
||||
|
||||
; default-fragments = 4
|
||||
; default-fragment-size-msec = 25
|
||||
|
||||
; enable-deferred-volume = yes
|
||||
; deferred-volume-safety-margin-usec = 8000
|
||||
; deferred-volume-extra-delay-usec = 0
|
143
pulseaudio/pulse_config_6mic/default.pa
Normal file
143
pulseaudio/pulse_config_6mic/default.pa
Normal file
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/pulseaudio -nF
|
||||
#
|
||||
# This file is part of PulseAudio.
|
||||
#
|
||||
# PulseAudio is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PulseAudio is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This startup script is used only if PulseAudio is started per-user
|
||||
# (i.e. not in system mode)
|
||||
|
||||
.fail
|
||||
|
||||
### Automatically restore the volume of streams and devices
|
||||
load-module module-device-restore
|
||||
load-module module-stream-restore
|
||||
load-module module-card-restore
|
||||
|
||||
### Automatically augment property information from .desktop files
|
||||
### stored in /usr/share/application
|
||||
load-module module-augment-properties
|
||||
|
||||
### Should be after module-*-restore but before module-*-detect
|
||||
load-module module-switch-on-port-available
|
||||
|
||||
### Load audio drivers statically
|
||||
### (it's probably better to not load these drivers manually, but instead
|
||||
### use module-udev-detect -- see below -- for doing this automatically)
|
||||
#load-module module-alsa-sink device="hw:1,0" channels=8 rate=48000 format=s32le
|
||||
#load-module module-alsa-source device="hw:1,0" channels=8 rate=48000 format=s32le
|
||||
#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
|
||||
#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
|
||||
#load-module module-null-sink
|
||||
#load-module module-pipe-sink
|
||||
|
||||
### Automatically load driver modules depending on the hardware available
|
||||
.ifexists module-udev-detect.so
|
||||
load-module module-udev-detect
|
||||
#channels=8 rate=48000 format=s32le
|
||||
.else
|
||||
### Use the static hardware detection module (for systems that lack udev support)
|
||||
load-module module-detect
|
||||
.endif
|
||||
|
||||
### Automatically connect sink and source if JACK server is present
|
||||
.ifexists module-jackdbus-detect.so
|
||||
.nofail
|
||||
load-module module-jackdbus-detect channels=2
|
||||
.fail
|
||||
.endif
|
||||
|
||||
### Automatically load driver modules for Bluetooth hardware
|
||||
.ifexists module-bluetooth-policy.so
|
||||
load-module module-bluetooth-policy
|
||||
.endif
|
||||
|
||||
.ifexists module-bluetooth-discover.so
|
||||
load-module module-bluetooth-discover
|
||||
.endif
|
||||
|
||||
### Load several protocols
|
||||
.ifexists module-esound-protocol-unix.so
|
||||
load-module module-esound-protocol-unix
|
||||
.endif
|
||||
load-module module-native-protocol-unix
|
||||
|
||||
### Network access (may be configured with paprefs, so leave this commented
|
||||
### here if you plan to use paprefs)
|
||||
#load-module module-esound-protocol-tcp
|
||||
#load-module module-native-protocol-tcp
|
||||
#load-module module-zeroconf-publish
|
||||
|
||||
### Load the RTP receiver module (also configured via paprefs, see above)
|
||||
#load-module module-rtp-recv
|
||||
|
||||
### Load the RTP sender module (also configured via paprefs, see above)
|
||||
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
|
||||
#load-module module-rtp-send source=rtp.monitor
|
||||
|
||||
### Load additional modules from GConf settings. This can be configured with the paprefs tool.
|
||||
### Please keep in mind that the modules configured by paprefs might conflict with manually
|
||||
### loaded modules.
|
||||
.ifexists module-gconf.so
|
||||
.nofail
|
||||
load-module module-gconf
|
||||
.fail
|
||||
.endif
|
||||
|
||||
### Automatically restore the default sink/source when changed by the user
|
||||
### during runtime
|
||||
### NOTE: This should be loaded as early as possible so that subsequent modules
|
||||
### that look up the default sink/source get the right value
|
||||
load-module module-default-device-restore
|
||||
|
||||
### Automatically move streams to the default sink if the sink they are
|
||||
### connected to dies, similar for sources
|
||||
load-module module-rescue-streams
|
||||
|
||||
### Make sure we always have a sink around, even if it is a null sink.
|
||||
load-module module-always-sink
|
||||
|
||||
### Honour intended role device property
|
||||
load-module module-intended-roles
|
||||
|
||||
### Automatically suspend sinks/sources that become idle for too long
|
||||
load-module module-suspend-on-idle
|
||||
|
||||
### If autoexit on idle is enabled we want to make sure we only quit
|
||||
### when no local session needs us anymore.
|
||||
.ifexists module-console-kit.so
|
||||
load-module module-console-kit
|
||||
.endif
|
||||
.ifexists module-systemd-login.so
|
||||
load-module module-systemd-login
|
||||
.endif
|
||||
|
||||
### Enable positioned event sounds
|
||||
load-module module-position-event-sounds
|
||||
|
||||
### Cork music/video streams when a phone stream is active
|
||||
load-module module-role-cork
|
||||
|
||||
### Modules to allow autoloading of filters (such as echo cancellation)
|
||||
### on demand. module-filter-heuristics tries to determine what filters
|
||||
### make sense, and module-filter-apply does the heavy-lifting of
|
||||
### loading modules and rerouting streams.
|
||||
load-module module-filter-heuristics
|
||||
load-module module-filter-apply
|
||||
|
||||
### Make some devices default
|
||||
#set-default-sink output
|
||||
#set-default-source input
|
||||
set-default-source alsa_input.platform-soc_sound.seeed-8ch
|
||||
set-default-sink alsa_output.platform-soc_sound.seeed-2ch
|
34
pulseaudio/pulse_config_6mic/seeed-voicecard.conf
Normal file
34
pulseaudio/pulse_config_6mic/seeed-voicecard.conf
Normal file
|
@ -0,0 +1,34 @@
|
|||
# /usr/share/pulseaudio/alsa-mixer/profile-sets/seeed-voiced.conf
|
||||
|
||||
[General]
|
||||
auto-profiles = no
|
||||
[Mapping seeed-8ch]
|
||||
device-strings = hw:%f
|
||||
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
|
||||
exact-channels = false
|
||||
fallback = yes
|
||||
paths-input = seeed-8ch
|
||||
priority = 3
|
||||
direction = input
|
||||
[Mapping seeed-2ch]
|
||||
device-strings = hw:%f
|
||||
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
|
||||
exact-channels = false
|
||||
exact-channels = false
|
||||
fallback = yes
|
||||
paths-output = seeed-2ch
|
||||
direction = output
|
||||
priority = 2
|
||||
[Profile output:seeed-2ch+input:seeed-8ch]
|
||||
output-mappings = seeed-2ch
|
||||
input-mappings = seeed-8ch
|
||||
priority = 100
|
||||
skip-probe = yes
|
||||
[Profile output:seeed-2ch]
|
||||
output-mappings = seeed-2ch
|
||||
priority = 4
|
||||
skip-probe = yes
|
||||
[Profile input:seeed-8ch]
|
||||
input-mappings = seeed-8ch
|
||||
priority = 5
|
||||
skip-probe = yes
|
BIN
pulseaudio/udev_rules_4mic.png
Normal file
BIN
pulseaudio/udev_rules_4mic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
BIN
pulseaudio/udev_rules_6mic.png
Normal file
BIN
pulseaudio/udev_rules_6mic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 483 KiB |
|
@ -13,7 +13,7 @@
|
|||
};
|
||||
|
||||
fragment@1 {
|
||||
target-path = "/clocks";
|
||||
target-path = "/";
|
||||
__overlay__ {
|
||||
ac108_mclk: codec-mclk {
|
||||
compatible = "fixed-clock";
|
||||
|
|
Binary file not shown.
|
@ -13,7 +13,7 @@
|
|||
};
|
||||
|
||||
fragment@1 {
|
||||
target-path = "/clocks";
|
||||
target-path = "/";
|
||||
__overlay__ {
|
||||
ac10x_mclk: codec-mclk {
|
||||
compatible = "fixed-clock";
|
||||
|
@ -26,13 +26,15 @@
|
|||
fragment@2 {
|
||||
target = <&gpio>;
|
||||
__overlay__ {
|
||||
spk_amp_and_irq_pins: speaker_amp_and_irq_pins {
|
||||
spk_amp_pins: spk_pins {
|
||||
brcm,pins = <17 22>;
|
||||
brcm,function = <1 0>; /* out in */
|
||||
brcm,pull = <0 0>; /* - - */
|
||||
};
|
||||
gpclk0_pins: gpclk0_pins {
|
||||
brcm,pins = <4>;
|
||||
brcm,function = <4>; /* alt func 0 */
|
||||
brcm,pull = <0>; /* - */
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -47,7 +49,7 @@
|
|||
ac101: ac101@1a{
|
||||
compatible = "x-power,ac101";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spk_amp_and_irq_pins>,<&gpclk0_pins>;
|
||||
pinctrl-0 = <&spk_amp_pins &gpclk0_pins>;
|
||||
spk-amp-switch-gpios = <&gpio 17 0>;
|
||||
switch-irq-gpios = <&gpio 22 0>;
|
||||
reg = <0x1a>;
|
||||
|
@ -80,6 +82,8 @@
|
|||
seeed-voice-card,name = "seeed-8mic-voicecard";
|
||||
seeed-voice-card,channels-playback-override = <8>;
|
||||
seeed-voice-card,channels-capture-override = <8>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
seeed-voice-card,dai-link@0 {
|
||||
|
@ -88,6 +92,7 @@
|
|||
frame-master = <&codec0_dai>;
|
||||
/* bitclock-inversion; */
|
||||
/* frame-inversion; */
|
||||
reg = <0>;
|
||||
|
||||
cpu {
|
||||
sound-dai = <&i2s>;
|
||||
|
|
Binary file not shown.
167
seeed-voicecard
167
seeed-voicecard
|
@ -21,76 +21,143 @@
|
|||
# THE SOFTWARE.
|
||||
|
||||
set -x
|
||||
exec 1>/var/log/$(basename $0).log 2>&1
|
||||
#exec 1>/var/log/$(basename $0).log 2>&1
|
||||
|
||||
export PATH=$PATH:/opt/vc/bin
|
||||
OVERLAYS=/boot/overlays
|
||||
[ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays
|
||||
|
||||
#enable i2c interface
|
||||
dtparam i2c_arm=on
|
||||
dtparam -d $OVERLAYS i2c_arm=on
|
||||
modprobe i2c-dev
|
||||
|
||||
#enable spi interface
|
||||
dtparam spi=on
|
||||
sleep 1
|
||||
dtparam -d $OVERLAYS spi=on
|
||||
|
||||
_VER_RUN=
|
||||
function get_kernel_version() {
|
||||
local ZIMAGE IMG_OFFSET
|
||||
|
||||
_VER_RUN=""
|
||||
[ -z "$_VER_RUN" ] && {
|
||||
ZIMAGE=/boot/kernel.img
|
||||
IMG_OFFSET=$(LC_ALL=C grep -abo $'\x1f\x8b\x08\x00' $ZIMAGE | head -n 1 | cut -d ':' -f 1)
|
||||
# 64-bit-only kernel package
|
||||
[ ! -f /boot/kernel.img ] && [ -f /boot/kernel8.img ] && ZIMAGE=/boot/kernel8.img
|
||||
_VER_RUN=$(dd if=$ZIMAGE obs=64K ibs=4 skip=$(( IMG_OFFSET / 4)) 2>/dev/null | zcat | grep -a -m1 "Linux version" | LC_ALL=C sed -e 's/^.*Linux/Linux/' | strings | awk '{ print $3; }')
|
||||
}
|
||||
echo "$_VER_RUN"
|
||||
return 0
|
||||
}
|
||||
|
||||
CONFIG=/boot/config.txt
|
||||
[ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt
|
||||
|
||||
get_overlay() {
|
||||
ov=$1
|
||||
if grep -q -E "^dtoverlay=$ov" $CONFIG; then
|
||||
echo 0
|
||||
else
|
||||
echo 1
|
||||
fi
|
||||
}
|
||||
|
||||
do_overlay() {
|
||||
ov=$1
|
||||
RET=$2
|
||||
DEFAULT=--defaultno
|
||||
CURRENT=0
|
||||
if [ $(get_overlay $ov) -eq 0 ]; then
|
||||
DEFAULT=
|
||||
CURRENT=1
|
||||
fi
|
||||
if [ $RET -eq $CURRENT ]; then
|
||||
ASK_TO_REBOOT=1
|
||||
fi
|
||||
if [ $RET -eq 0 ]; then
|
||||
sed $CONFIG -i -e "s/^#dtoverlay=$ov/dtoverlay=$ov/"
|
||||
if ! grep -q -E "^dtoverlay=$ov" $CONFIG; then
|
||||
printf "dtoverlay=$ov\n" >> $CONFIG
|
||||
fi
|
||||
STATUS=enabled
|
||||
elif [ $RET -eq 1 ]; then
|
||||
sed $CONFIG -i -e "s/^dtoverlay=$ov/#dtoverlay=$ov/"
|
||||
STATUS=disabled
|
||||
else
|
||||
return $RET
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
is_1a=$(i2cdetect -y 1 0x1a 0x1a | egrep "(1a|UU)" | awk '{print $2}')
|
||||
is_35=$(i2cdetect -y 1 0x35 0x35 | egrep "(35|UU)" | awk '{print $2}')
|
||||
is_3b=$(i2cdetect -y 1 0x3b 0x3b | egrep "(3b|UU)" | awk '{print $2}')
|
||||
|
||||
is_1a=$(i2cdetect -y 1 0x1a 0x1a | grep 1a | awk '{print $2}')
|
||||
is_35=$(i2cdetect -y 1 0x35 0x35 | grep 35 | awk '{print $2}')
|
||||
is_3b=$(i2cdetect -y 1 0x3b 0x3b | grep 3b | awk '{print $2}')
|
||||
RPI_HATS="seeed-2mic-voicecard seeed-4mic-voicecard seeed-8mic-voicecard"
|
||||
overlay=""
|
||||
|
||||
if [ "x${is_1a}" == "x1a" ] && [ "x${is_35}" == "x" ] ; then
|
||||
if [ "x${is_1a}" != "x" ] && [ "x${is_35}" == "x" ] ; then
|
||||
echo "install 2mic"
|
||||
dtoverlay seeed-2mic-voicecard
|
||||
sleep 1
|
||||
hw=$(aplay -l | grep seeed2micvoicec | awk '{print $2}' | sed 's/://')
|
||||
|
||||
cp /etc/voicecard/asound_2mic.conf /etc/asound.conf
|
||||
|
||||
echo "get old hw number"
|
||||
old=$(cat /etc/asound.conf | grep hw: | awk 'NR==1 {print $2}' | sed 's/\"//g')
|
||||
|
||||
echo "replace new hw:${hw},0"
|
||||
sed -i -e "s/${old}/hw:${hw},0/g" /etc/asound.conf
|
||||
|
||||
cp /etc/voicecard/wm8960_asound.state /var/lib/alsa/asound.state
|
||||
overlay=seeed-2mic-voicecard
|
||||
asound_conf=/etc/voicecard/asound_2mic.conf
|
||||
asound_state=/etc/voicecard/wm8960_asound.state
|
||||
fi
|
||||
|
||||
if [ "x${is_3b}" == "x3b" ] && [ "x${is_35}" == "x" ] ; then
|
||||
if [ "x${is_3b}" != "x" ] && [ "x${is_35}" == "x" ] ; then
|
||||
echo "install 4mic"
|
||||
dtoverlay seeed-4mic-voicecard
|
||||
sleep 1
|
||||
hw=$(arecord -l | grep seeed4micvoicec | awk '{print $2}' | sed 's/://')
|
||||
|
||||
cp /etc/voicecard/asound_4mic.conf /etc/asound.conf
|
||||
|
||||
echo "get slavepcm line number"
|
||||
line=$(grep -n ac108-slavepcm /etc/asound.conf | awk '{print $1}' | sed 's/://')
|
||||
|
||||
echo "delete ${line} slavepcm hw number"
|
||||
sed -i -e "${line}d" /etc/asound.conf
|
||||
|
||||
echo "insert slavepcm hw:${hw},0"
|
||||
sed -i "${line}i slave.pcm \"hw:${hw},0\"" /etc/asound.conf
|
||||
|
||||
cp /etc/voicecard/ac108_asound.state /var/lib/alsa/asound.state
|
||||
overlay=seeed-4mic-voicecard
|
||||
asound_conf=/etc/voicecard/asound_4mic.conf
|
||||
asound_state=/etc/voicecard/ac108_asound.state
|
||||
fi
|
||||
|
||||
if [ "x${is_3b}" == "x3b" ] && [ "x${is_35}" == "x35" ] ; then
|
||||
if [ "x${is_3b}" != "x" ] && [ "x${is_35}" != "x" ] ; then
|
||||
echo "install 6mic"
|
||||
dtoverlay seeed-8mic-voicecard
|
||||
sleep 1
|
||||
hw=$(aplay -l | grep seeed8micvoicec | awk '{print $2}' | sed 's/://')
|
||||
overlay=seeed-8mic-voicecard
|
||||
asound_conf=/etc/voicecard/asound_6mic.conf
|
||||
asound_state=/etc/voicecard/ac108_6mic.state
|
||||
fi
|
||||
|
||||
cp /etc/voicecard/asound_6mic.conf /etc/asound.conf
|
||||
if [ "$overlay" ]; then
|
||||
echo Install $overlay ...
|
||||
|
||||
old=$(cat /etc/asound.conf | grep hw: | awk 'NR==1 {print $2}' | sed 's/\"//g')
|
||||
# Remove old configuration
|
||||
rm /etc/asound.conf
|
||||
rm /var/lib/alsa/asound.state
|
||||
|
||||
sed -i -e "s/${old}/hw:${hw},0/g" /etc/asound.conf
|
||||
kernel_ver=$(uname -r) # get_kernel_version)
|
||||
# echo kernel_ver=$kernel_ver
|
||||
|
||||
cp /etc/voicecard/ac108_6mic.state /var/lib/alsa/asound.state
|
||||
# TODO: dynamic dtoverlay Bug of v4.19.x
|
||||
# no DT node phandle inserted.
|
||||
if [[ "$kernel_ver" =~ ^4\.19.*$ || "$kernel_ver" =~ ^5\.*$ ]]; then
|
||||
for i in $RPI_HATS; do
|
||||
if [ "$i" == "$overlay" ]; then
|
||||
/bin/true #do_overlay $overlay 0
|
||||
else
|
||||
echo Uninstall $i ...
|
||||
/bin/true #do_overlay $i 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
#make sure the driver loads correctly
|
||||
dtoverlay -d $OVERLAYS $overlay || true
|
||||
|
||||
|
||||
echo "create $overlay asound configure file"
|
||||
ln -s $asound_conf /etc/asound.conf
|
||||
echo "create $overlay asound status file"
|
||||
ln -s $asound_state /var/lib/alsa/asound.state
|
||||
fi
|
||||
|
||||
alsactl restore
|
||||
|
||||
#Fore 3.5mm ('headphone') jack
|
||||
amixer cset numid=3 1
|
||||
|
||||
|
||||
#Force 3.5mm ('headphone') jack
|
||||
# The Raspberry Pi 4, released on 24th Jun 2019, has two HDMI ports,
|
||||
# and can drive two displays with audios for two users simultaneously,
|
||||
# in a "multiseat" configuration. The earlier single virtual ALSA
|
||||
# option for re-directing audio playback between headphone jack and HDMI
|
||||
# via a 'Routing' mixer setting was turned off eventually to allow
|
||||
# simultaneous usage of all 3 playback devices.
|
||||
if aplay -l | grep -q "bcm2835 ALSA"; then
|
||||
amixer cset numid=3 1 || true
|
||||
fi
|
||||
|
|
|
@ -26,6 +26,23 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/simple_card_utils.h>
|
||||
#include "ac10x.h"
|
||||
|
||||
#define LINUX_VERSION_IS_GEQ(x1,x2,x3) (LINUX_VERSION_CODE >= KERNEL_VERSION(x1,x2,x3))
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0)
|
||||
#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, simple_dai, dai_link->cpus)
|
||||
#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, simple_dai, dai_link->codecs)
|
||||
#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->cpus, is_single_link)
|
||||
#define asoc_simple_parse_codec(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->codecs, NULL)
|
||||
#define asoc_simple_parse_platform(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->platforms, NULL)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* single codec:
|
||||
|
@ -39,6 +56,9 @@ struct seeed_card_data {
|
|||
struct seeed_dai_props {
|
||||
struct asoc_simple_dai cpu_dai;
|
||||
struct asoc_simple_dai codec_dai;
|
||||
struct snd_soc_dai_link_component cpus; /* single cpu */
|
||||
struct snd_soc_dai_link_component codecs; /* single codec */
|
||||
struct snd_soc_dai_link_component platforms;
|
||||
unsigned int mclk_fs;
|
||||
} *dai_props;
|
||||
unsigned int mclk_fs;
|
||||
|
@ -47,7 +67,12 @@ struct seeed_card_data {
|
|||
unsigned channels_capture_default;
|
||||
unsigned channels_capture_override;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
spinlock_t lock;
|
||||
#endif
|
||||
struct work_struct work_codec_clk;
|
||||
#define TRY_STOP_MAX 3
|
||||
int try_stop;
|
||||
};
|
||||
|
||||
struct seeed_card_info {
|
||||
|
@ -61,6 +86,7 @@ struct seeed_card_info {
|
|||
struct asoc_simple_dai codec_dai;
|
||||
};
|
||||
|
||||
#define seeed_priv_to_card(priv) (&(priv)->snd_card)
|
||||
#define seeed_priv_to_dev(priv) ((priv)->snd_card.dev)
|
||||
#define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
|
||||
#define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||
|
@ -85,16 +111,16 @@ static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
|
|||
if (ret)
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
if (rtd->cpu_dai->driver->playback.channels_min) {
|
||||
priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min;
|
||||
if (asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min) {
|
||||
priv->channels_playback_default = asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min;
|
||||
}
|
||||
if (rtd->cpu_dai->driver->capture.channels_min) {
|
||||
priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min;
|
||||
if (asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min) {
|
||||
priv->channels_capture_default = asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min;
|
||||
}
|
||||
rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override;
|
||||
rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override;
|
||||
rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override;
|
||||
rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_override;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_override;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_override;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_override;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -106,10 +132,10 @@ static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
|
|||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
|
||||
rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default;
|
||||
rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default;
|
||||
rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default;
|
||||
rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = priv->channels_playback_default;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = priv->channels_playback_default;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = priv->channels_capture_default;
|
||||
asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = priv->channels_capture_default;
|
||||
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
|
@ -120,8 +146,8 @@ static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
|
@ -151,9 +177,9 @@ err:
|
|||
}
|
||||
|
||||
#define _SET_CLOCK_CNT 2
|
||||
static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop);
|
||||
static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop, struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai);
|
||||
|
||||
int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) {
|
||||
int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int, struct snd_pcm_substream *, int, struct snd_soc_dai *)) {
|
||||
if (! _set_clock[stream]) {
|
||||
_set_clock[stream] = set_clock;
|
||||
}
|
||||
|
@ -161,44 +187,82 @@ int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) {
|
|||
}
|
||||
EXPORT_SYMBOL(seeed_voice_card_register_set_clock);
|
||||
|
||||
/*
|
||||
* work_cb_codec_clk: clear audio codec inner clock.
|
||||
*/
|
||||
static void work_cb_codec_clk(struct work_struct *work)
|
||||
{
|
||||
struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk);
|
||||
int r = 0;
|
||||
|
||||
if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) {
|
||||
r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */
|
||||
}
|
||||
if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) {
|
||||
r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */
|
||||
}
|
||||
|
||||
if (r && priv->try_stop++ < TRY_STOP_MAX) {
|
||||
if (0 != schedule_work(&priv->work_codec_clk)) {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
unsigned long flags;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
dai->playback_active, dai->capture_active);
|
||||
|
||||
/* I know it will degrades performance, but I have no choice */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, dai->stream[SNDRV_PCM_STREAM_CAPTURE].active);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1);
|
||||
if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1);
|
||||
if (cancel_work_sync(&priv->work_codec_clk) != 0) {}
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
/* I know it will degrades performance, but I have no choice */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
#endif
|
||||
// if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1, substream, cmd, dai);
|
||||
// if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1, substream, cmd, dai);
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
/* capture channel resync, if overrun */
|
||||
if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
break;
|
||||
}
|
||||
if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
|
||||
if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
|
||||
|
||||
/* interrupt environment */
|
||||
if (in_irq() || in_nmi() || in_serving_softirq()) {
|
||||
priv->try_stop = 0;
|
||||
if (0 != schedule_work(&priv->work_codec_clk)) {
|
||||
}
|
||||
} else {
|
||||
// if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */
|
||||
// if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0, NULL, 0, NULL); /* not using 2nd to 4th arg if 1st == 0 */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d;finished %d\n",
|
||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||
dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, dai->stream[SNDRV_PCM_STREAM_CAPTURE].active, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -210,23 +274,165 @@ static struct snd_soc_ops seeed_voice_card_ops = {
|
|||
.trigger = seeed_voice_card_trigger,
|
||||
};
|
||||
|
||||
static int asoc_simple_parse_dai(struct device_node *node,
|
||||
struct snd_soc_dai_link_component *dlc,
|
||||
int *is_single_link)
|
||||
{
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get node via "sound-dai = <&phandle port>"
|
||||
* it will be used as xxx_of_node on soc_bind_dai_link()
|
||||
*/
|
||||
ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* Here, dlc->dai_name is pointer to CPU/Codec DAI name.
|
||||
* If user unbinded CPU or Codec driver, but not for Sound Card,
|
||||
* dlc->dai_name is keeping unbinded CPU or Codec
|
||||
* driver's pointer.
|
||||
*
|
||||
* If user re-bind CPU or Codec driver again, ALSA SoC will try
|
||||
* to rebind Card via snd_soc_try_rebind_card(), but because of
|
||||
* above reason, it might can't bind Sound Card.
|
||||
* Because Sound Card is pointing to released dai_name pointer.
|
||||
*
|
||||
* To avoid this rebind Card issue,
|
||||
* 1) It needs to alloc memory to keep dai_name eventhough
|
||||
* CPU or Codec driver was unbinded, or
|
||||
* 2) user need to rebind Sound Card everytime
|
||||
* if he unbinded CPU or Codec.
|
||||
*/
|
||||
ret = snd_soc_of_get_dai_name(node, &dlc->dai_name, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dlc->of_node = args.np;
|
||||
|
||||
if (is_single_link)
|
||||
*is_single_link = !args.args_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_init_dai(struct snd_soc_dai *dai,
|
||||
struct asoc_simple_dai *simple_dai)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!simple_dai)
|
||||
return 0;
|
||||
|
||||
if (simple_dai->sysclk) {
|
||||
ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
|
||||
simple_dai->clk_direction);
|
||||
if (ret && ret != -ENOTSUPP) {
|
||||
dev_err(dai->dev, "simple-card: set_sysclk error\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (simple_dai->slots) {
|
||||
ret = snd_soc_dai_set_bclk_ratio(dai,
|
||||
simple_dai->slots *
|
||||
simple_dai->slot_width);
|
||||
if (ret && ret != -ENOTSUPP) {
|
||||
dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
|
||||
static inline int asoc_simple_component_is_codec(struct snd_soc_component *component)
|
||||
{
|
||||
return component->driver->endianness;
|
||||
}
|
||||
|
||||
static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_pcm_stream *params;
|
||||
struct snd_pcm_hardware hw;
|
||||
int i, ret, stream;
|
||||
|
||||
/* Only Codecs */
|
||||
for_each_rtd_components(rtd, i, component) {
|
||||
if (!asoc_simple_component_is_codec(component))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assumes the capabilities are the same for all supported streams */
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
|
||||
if (ret == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL);
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
params->formats = hw.formats;
|
||||
params->rates = hw.rates;
|
||||
params->rate_min = hw.rate_min;
|
||||
params->rate_max = hw.rate_max;
|
||||
params->channels_min = hw.channels_min;
|
||||
params->channels_max = hw.channels_max;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,4,0)
|
||||
dai_link->c2c_params = params;
|
||||
dai_link->num_c2c_params = 1;
|
||||
#else
|
||||
/* apparently this goes back to 5.6.x */
|
||||
dai_link->params = params;
|
||||
dai_link->num_params = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *codec = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct seeed_dai_props *dai_props =
|
||||
seeed_priv_to_props(priv, rtd->num);
|
||||
int ret;
|
||||
|
||||
ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
|
||||
ret = asoc_simple_init_dai(codec, &dai_props->codec_dai);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
|
||||
ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
|
||||
ret = asoc_simple_init_dai_link_params(rtd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
dev_dbg(rtd->card->dev, "codec \"%s\" mapping to cpu \"%s\"\n", codec->name, cpu->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -254,32 +460,37 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
|||
snprintf(prop, sizeof(prop), "%scpu", prefix);
|
||||
cpu = of_get_child_by_name(node, prop);
|
||||
|
||||
if (!cpu) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
snprintf(prop, sizeof(prop), "%splat", prefix);
|
||||
plat = of_get_child_by_name(node, prop);
|
||||
|
||||
snprintf(prop, sizeof(prop), "%scodec", prefix);
|
||||
codec = of_get_child_by_name(node, prop);
|
||||
|
||||
if (!cpu || !codec) {
|
||||
if (!codec) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
ret = asoc_simple_card_parse_daifmt(dev, node, codec,
|
||||
ret = asoc_simple_parse_daifmt(dev, node, codec,
|
||||
prefix, &dai_link->dai_fmt);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
|
||||
|
||||
ret = asoc_simple_card_parse_cpu(cpu, dai_link,
|
||||
DAI, CELL, &single_cpu);
|
||||
ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
#if _SINGLE_CODEC
|
||||
ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
|
||||
ret = asoc_simple_parse_codec(codec, dai_link);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
#else
|
||||
|
@ -291,7 +502,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
|||
dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs);
|
||||
#endif
|
||||
|
||||
ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
|
||||
ret = asoc_simple_parse_platform(plat, dai_link);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
|
@ -316,7 +527,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
|||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
|
||||
ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
|
||||
#else
|
||||
ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
|
||||
ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
|
||||
#endif
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
@ -324,22 +535,16 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
|||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
|
||||
ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
|
||||
#else
|
||||
ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
|
||||
ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai);
|
||||
#endif
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
#if _SINGLE_CODEC
|
||||
ret = asoc_simple_card_canonicalize_dailink(dai_link);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
#endif
|
||||
|
||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
||||
ret = asoc_simple_set_dailink_name(dev, dai_link,
|
||||
"%s-%s",
|
||||
dai_link->cpu_dai_name,
|
||||
dai_link->cpus->dai_name,
|
||||
#if _SINGLE_CODEC
|
||||
dai_link->codec_dai_name
|
||||
dai_link->codecs->dai_name
|
||||
#else
|
||||
dai_link->codecs[0].dai_name
|
||||
#endif
|
||||
|
@ -353,17 +558,27 @@ static int seeed_voice_card_dai_link_of(struct device_node *node,
|
|||
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
|
||||
dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
|
||||
dev_dbg(dev, "\tcpu : %s / %d\n",
|
||||
dai_link->cpu_dai_name,
|
||||
dai_link->cpus->dai_name,
|
||||
dai_props->cpu_dai.sysclk);
|
||||
dev_dbg(dev, "\tcodec : %s / %d\n",
|
||||
#if _SINGLE_CODEC
|
||||
dai_link->codec_dai_name,
|
||||
dai_link->codecs->dai_name,
|
||||
#else
|
||||
dai_link->codecs[0].dai_name,
|
||||
#endif
|
||||
dai_props->codec_dai.sysclk);
|
||||
|
||||
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0)
|
||||
asoc_simple_canonicalize_cpu(dai_link->cpus, single_cpu);
|
||||
#if _SINGLE_CODEC
|
||||
asoc_simple_canonicalize_platform(dai_link->platforms, dai_link->cpus);
|
||||
#endif
|
||||
#else
|
||||
asoc_simple_canonicalize_cpu(dai_link, single_cpu);
|
||||
#if _SINGLE_CODEC
|
||||
asoc_simple_canonicalize_platform(dai_link);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
dai_link_of_err:
|
||||
of_node_put(cpu);
|
||||
|
@ -395,7 +610,7 @@ static int seeed_voice_card_parse_aux_devs(struct device_node *node,
|
|||
aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
|
||||
if (!aux_node)
|
||||
return -EINVAL;
|
||||
priv->snd_card.aux_dev[i].codec_of_node = aux_node;
|
||||
priv->snd_card.aux_dev[i].dlc.of_node = aux_node;
|
||||
}
|
||||
|
||||
priv->snd_card.num_aux_devs = n;
|
||||
|
@ -455,7 +670,7 @@ static int seeed_voice_card_parse_of(struct device_node *node,
|
|||
goto card_parse_end;
|
||||
}
|
||||
|
||||
ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
|
||||
ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX);
|
||||
if (ret < 0)
|
||||
goto card_parse_end;
|
||||
|
||||
|
@ -480,6 +695,79 @@ card_parse_end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
inline void seeed_debug_dai(struct seeed_card_data *priv,
|
||||
char *name,
|
||||
struct asoc_simple_dai *dai)
|
||||
{
|
||||
struct device *dev = seeed_priv_to_dev(priv);
|
||||
|
||||
if (dai->name)
|
||||
dev_dbg(dev, "%s dai name = %s\n",
|
||||
name, dai->name);
|
||||
if (dai->sysclk)
|
||||
dev_dbg(dev, "%s sysclk = %d\n",
|
||||
name, dai->sysclk);
|
||||
|
||||
dev_dbg(dev, "%s direction = %s\n",
|
||||
name, dai->clk_direction ? "OUT" : "IN");
|
||||
|
||||
if (dai->slots)
|
||||
dev_dbg(dev, "%s slots = %d\n", name, dai->slots);
|
||||
if (dai->slot_width)
|
||||
dev_dbg(dev, "%s slot width = %d\n", name, dai->slot_width);
|
||||
if (dai->tx_slot_mask)
|
||||
dev_dbg(dev, "%s tx slot mask = %d\n", name, dai->tx_slot_mask);
|
||||
if (dai->rx_slot_mask)
|
||||
dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask);
|
||||
if (dai->clk)
|
||||
dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk));
|
||||
}
|
||||
|
||||
inline void seeed_debug_info(struct seeed_card_data *priv)
|
||||
{
|
||||
struct snd_soc_card *card = seeed_priv_to_card(priv);
|
||||
struct device *dev = seeed_priv_to_dev(priv);
|
||||
|
||||
int i;
|
||||
|
||||
if (card->name)
|
||||
dev_dbg(dev, "Card Name: %s\n", card->name);
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
struct seeed_dai_props *props = seeed_priv_to_props(priv, i);
|
||||
struct snd_soc_dai_link *link = seeed_priv_to_link(priv, i);
|
||||
|
||||
dev_dbg(dev, "DAI%d\n", i);
|
||||
|
||||
seeed_debug_dai(priv, "cpu", &props->cpu_dai);
|
||||
seeed_debug_dai(priv, "codec", &props->codec_dai);
|
||||
|
||||
if (link->name)
|
||||
dev_dbg(dev, "dai name = %s\n", link->name);
|
||||
|
||||
dev_dbg(dev, "dai format = %04x\n", link->dai_fmt);
|
||||
|
||||
/*
|
||||
if (props->adata.convert_rate)
|
||||
dev_dbg(dev, "convert_rate = %d\n",
|
||||
props->adata.convert_rate);
|
||||
if (props->adata.convert_channels)
|
||||
dev_dbg(dev, "convert_channels = %d\n",
|
||||
props->adata.convert_channels);
|
||||
if (props->codec_conf && props->codec_conf->name_prefix)
|
||||
dev_dbg(dev, "name prefix = %s\n",
|
||||
props->codec_conf->name_prefix);
|
||||
*/
|
||||
if (props->mclk_fs)
|
||||
dev_dbg(dev, "mclk-fs = %d\n",
|
||||
props->mclk_fs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define seeed_debug_info(priv)
|
||||
#endif /* DEBUG */
|
||||
|
||||
static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct seeed_card_data *priv;
|
||||
|
@ -487,7 +775,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
|||
struct seeed_dai_props *dai_props;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num, ret;
|
||||
int num, ret, i;
|
||||
|
||||
/* Get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, PREFIX "dai-link"))
|
||||
|
@ -505,6 +793,25 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
|||
if (!dai_props || !dai_link)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Use snd_soc_dai_link_component instead of legacy style
|
||||
* It is codec only. but cpu/platform will be supported in the future.
|
||||
* see
|
||||
* soc-core.c :: snd_soc_init_multicodec()
|
||||
*
|
||||
* "platform" might be removed
|
||||
* see
|
||||
* simple-card-utils.c :: asoc_simple_canonicalize_platform()
|
||||
*/
|
||||
for (i = 0; i < num; i++) {
|
||||
dai_link[i].cpus = &dai_props[i].cpus;
|
||||
dai_link[i].num_cpus = 1;
|
||||
dai_link[i].codecs = &dai_props[i].codecs;
|
||||
dai_link[i].num_codecs = 1;
|
||||
dai_link[i].platforms = &dai_props[i].platforms;
|
||||
dai_link[i].num_platforms = 1;
|
||||
}
|
||||
|
||||
priv->dai_props = dai_props;
|
||||
priv->dai_link = dai_link;
|
||||
|
||||
|
@ -523,6 +830,9 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
|||
}
|
||||
} else {
|
||||
struct seeed_card_info *cinfo;
|
||||
struct snd_soc_dai_link_component *cpus;
|
||||
struct snd_soc_dai_link_component *codecs;
|
||||
struct snd_soc_dai_link_component *platform;
|
||||
|
||||
cinfo = dev->platform_data;
|
||||
if (!cinfo) {
|
||||
|
@ -539,13 +849,19 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpus = dai_link->cpus;
|
||||
cpus->dai_name = cinfo->cpu_dai.name;
|
||||
|
||||
codecs = dai_link->codecs;
|
||||
codecs->name = cinfo->codec;
|
||||
codecs->dai_name = cinfo->codec_dai.name;
|
||||
|
||||
platform = dai_link->platforms;
|
||||
platform->name = cinfo->platform;
|
||||
|
||||
priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
|
||||
dai_link->name = cinfo->name;
|
||||
dai_link->stream_name = cinfo->name;
|
||||
dai_link->platform_name = cinfo->platform;
|
||||
dai_link->codec_name = cinfo->codec;
|
||||
dai_link->cpu_dai_name = cinfo->cpu_dai.name;
|
||||
dai_link->codec_dai_name = cinfo->codec_dai.name;
|
||||
dai_link->dai_fmt = cinfo->daifmt;
|
||||
dai_link->init = seeed_voice_card_dai_init;
|
||||
memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
|
||||
|
@ -556,14 +872,20 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
|||
|
||||
snd_soc_card_set_drvdata(&priv->snd_card, priv);
|
||||
|
||||
#if CONFIG_AC10X_TRIG_LOCK
|
||||
spin_lock_init(&priv->lock);
|
||||
#endif
|
||||
|
||||
INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk);
|
||||
|
||||
seeed_debug_info(priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
err:
|
||||
asoc_simple_card_clean_reference(&priv->snd_card);
|
||||
asoc_simple_clean_reference(&priv->snd_card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -571,8 +893,13 @@ err:
|
|||
static int seeed_voice_card_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct seeed_card_data *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
return asoc_simple_card_clean_reference(card);
|
||||
if (cancel_work_sync(&priv->work_codec_clk) != 0) {
|
||||
}
|
||||
asoc_simple_clean_reference(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id seeed_voice_of_match[] = {
|
||||
|
|
47
sound-compatible-4.18.h
Normal file
47
sound-compatible-4.18.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2018
|
||||
* Seeed Technology Co., Ltd. <www.seeedstudio.com>
|
||||
*
|
||||
* PeterYang <linsheng.yang@seeed.cc>
|
||||
*/
|
||||
#ifndef __SOUND_COMPATIBLE_4_18_H__
|
||||
#define __SOUND_COMPATIBLE_4_18_H__
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
|
||||
#define __NO_SND_SOC_CODEC_DRV 1
|
||||
#else
|
||||
#define __NO_SND_SOC_CODEC_DRV 0
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
|
||||
#if __has_attribute(__fallthrough__)
|
||||
# define fallthrough __attribute__((__fallthrough__))
|
||||
#else
|
||||
# define fallthrough do {} while (0) /* fallthrough */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __NO_SND_SOC_CODEC_DRV
|
||||
#define codec component
|
||||
#define snd_soc_codec snd_soc_component
|
||||
#define snd_soc_codec_driver snd_soc_component_driver
|
||||
#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata
|
||||
#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
|
||||
#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
|
||||
#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
#define snd_soc_read snd_soc_component_read
|
||||
#else
|
||||
#define snd_soc_read snd_soc_component_read32
|
||||
#endif
|
||||
#define snd_soc_register_codec devm_snd_soc_register_component
|
||||
#define snd_soc_unregister_codec snd_soc_unregister_component
|
||||
#define snd_soc_update_bits snd_soc_component_update_bits
|
||||
#define snd_soc_write snd_soc_component_write
|
||||
#define snd_soc_add_codec_controls snd_soc_add_component_controls
|
||||
#endif
|
||||
|
||||
#endif//__SOUND_COMPATIBLE_4_18_H__
|
||||
|
83
tools/phase_test.py
Normal file
83
tools/phase_test.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
import sys
|
||||
import wave
|
||||
import numpy as np
|
||||
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Usage: {} multi.wav'.format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
multi = wave.open(sys.argv[1], 'rb')
|
||||
rate = multi.getframerate()
|
||||
channels = multi.getnchannels()
|
||||
|
||||
if channels <= 1:
|
||||
sys.exit(1)
|
||||
|
||||
N = rate
|
||||
|
||||
window = np.hanning(N)
|
||||
|
||||
interp = 4*8
|
||||
max_offset = int(rate * 0.1 / 340 * interp)
|
||||
|
||||
def gcc_phat(sig, refsig, fs=1, max_tau=None, interp=16):
|
||||
'''
|
||||
This function computes the offset between the signal sig and the reference signal refsig
|
||||
using the Generalized Cross Correlation - Phase Transform (GCC-PHAT)method.
|
||||
'''
|
||||
|
||||
# make sure the length for the FFT is larger or equal than len(sig) + len(refsig)
|
||||
n = sig.shape[0] + refsig.shape[0]
|
||||
|
||||
# Generalized Cross Correlation Phase Transform
|
||||
SIG = np.fft.rfft(sig, n=n)
|
||||
REFSIG = np.fft.rfft(refsig, n=n)
|
||||
R = SIG * np.conj(REFSIG)
|
||||
#R /= np.abs(R)
|
||||
|
||||
cc = np.fft.irfft(R, n=(interp * n))
|
||||
|
||||
max_shift = int(interp * n / 2)
|
||||
if max_tau:
|
||||
max_shift = np.minimum(int(interp * fs * max_tau), max_shift)
|
||||
|
||||
cc = np.concatenate((cc[-max_shift:], cc[:max_shift+1]))
|
||||
|
||||
# find max cross correlation index
|
||||
shift = np.argmax(np.abs(cc)) - max_shift
|
||||
|
||||
tau = shift / float(interp * fs)
|
||||
|
||||
return tau, cc
|
||||
|
||||
|
||||
print(multi.getsampwidth())
|
||||
|
||||
while True:
|
||||
data = multi.readframes(N)
|
||||
|
||||
if len(data) != multi.getsampwidth() * N * channels:
|
||||
print("done")
|
||||
break
|
||||
|
||||
if multi.getsampwidth() == 2:
|
||||
data = np.fromstring(data, dtype='int16')
|
||||
else:
|
||||
data = np.fromstring(data, dtype='int32')
|
||||
ref_buf = data[0::channels]
|
||||
|
||||
offsets = []
|
||||
for ch in range(1, channels):
|
||||
sig_buf = data[ch::channels]
|
||||
tau, _ = gcc_phat(sig_buf * window, ref_buf * window, fs=1, max_tau=max_offset, interp=interp)
|
||||
# tau, _ = gcc_phat(sig_buf, ref_buf, fs=rate, max_tau=1)
|
||||
|
||||
offsets.append(tau)
|
||||
|
||||
print(offsets)
|
||||
|
||||
print(multi.getframerate())
|
||||
|
||||
multi.close()
|
37
ubuntu-prerequisite.sh
Executable file
37
ubuntu-prerequisite.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) Hin-Tak Leung 2020
|
||||
#
|
||||
# Overview:
|
||||
# This script compiles and install the Broadcom VideoCore tools,
|
||||
# configure the dynamic loader for the non-standard library location,
|
||||
# and update the loader cache.
|
||||
#
|
||||
# A few steps explicitly requires root privilege, which are
|
||||
# marked with "sudo". The rest is just checking for duplicate/previous
|
||||
# action.
|
||||
#
|
||||
# This derived from my command history on ubuntu 20.04.1 .YMMV
|
||||
|
||||
sudo apt install -y git gcc g++ make alsa-utils cmake
|
||||
|
||||
git clone git://github.com/raspberrypi/userland.git
|
||||
pushd userland/
|
||||
|
||||
arch=$(uname -m)
|
||||
if [[ "$arch" =~ aarch64 ]]; then
|
||||
./buildme --aarch64
|
||||
else
|
||||
./buildme
|
||||
fi
|
||||
# ./buildme already includes "sudo make install" at the end
|
||||
|
||||
popd
|
||||
|
||||
# matches Raspbian's location:
|
||||
if [ ! -f /etc/ld.so.conf.d/00-vmcs.conf ] ; then
|
||||
echo "/opt/vc/lib" | sudo tee -a /etc/ld.so.conf.d/00-vmcs.conf
|
||||
sudo ldconfig -v
|
||||
else
|
||||
echo "/etc/ld.so.conf.d/00-vmcs.conf exists - no need to update ld.cache!"
|
||||
fi
|
69
uninstall.sh
69
uninstall.sh
|
@ -13,16 +13,64 @@ fi
|
|||
|
||||
uname_r=$(uname -r)
|
||||
|
||||
CONFIG=/boot/config.txt
|
||||
[ -f /boot/firmware/config.txt ] && CONFIG=/boot/firmware/config.txt
|
||||
[ -f /boot/firmware/usercfg.txt ] && CONFIG=/boot/firmware/usercfg.txt
|
||||
|
||||
get_overlay() {
|
||||
ov=$1
|
||||
if grep -q -E "^dtoverlay=$ov" $CONFIG; then
|
||||
echo 0
|
||||
else
|
||||
echo 1
|
||||
fi
|
||||
}
|
||||
|
||||
do_overlay() {
|
||||
ov=$1
|
||||
RET=$2
|
||||
DEFAULT=--defaultno
|
||||
CURRENT=0
|
||||
if [ $(get_overlay $ov) -eq 0 ]; then
|
||||
DEFAULT=
|
||||
CURRENT=1
|
||||
fi
|
||||
if [ $RET -eq $CURRENT ]; then
|
||||
ASK_TO_REBOOT=1
|
||||
fi
|
||||
if [ $RET -eq 0 ]; then
|
||||
sed $CONFIG -i -e "s/^#dtoverlay=$ov/dtoverlay=$ov/"
|
||||
if ! grep -q -E "^dtoverlay=$ov" $CONFIG; then
|
||||
printf "dtoverlay=$ov\n" >> $CONFIG
|
||||
fi
|
||||
STATUS=enabled
|
||||
elif [ $RET -eq 1 ]; then
|
||||
sed $CONFIG -i -e "s/^dtoverlay=$ov/#dtoverlay=$ov/"
|
||||
STATUS=disabled
|
||||
else
|
||||
return $RET
|
||||
fi
|
||||
}
|
||||
|
||||
RPI_HATS="seeed-2mic-voicecard seeed-4mic-voicecard seeed-8mic-voicecard"
|
||||
|
||||
PATH=$PATH:/opt/vc/bin
|
||||
echo "remove dtbos"
|
||||
rm /boot/overlays/seeed-2mic-voicecard.dtbo || true
|
||||
rm /boot/overlays/seeed-4mic-voicecard.dtbo || true
|
||||
rm /boot/overlays/seeed-8mic-voicecard.dtbo || true
|
||||
for i in $RPI_HATS; do
|
||||
dtoverlay -r $i
|
||||
done
|
||||
OVERLAYS=/boot/overlays
|
||||
[ -d /boot/firmware/overlays ] && OVERLAYS=/boot/firmware/overlays
|
||||
|
||||
rm ${OVERLAYS}/seeed-2mic-voicecard.dtbo || true
|
||||
rm ${OVERLAYS}/seeed-4mic-voicecard.dtbo || true
|
||||
rm ${OVERLAYS}/seeed-8mic-voicecard.dtbo || true
|
||||
|
||||
echo "remove alsa configs"
|
||||
rm -rf /etc/voicecard/ || true
|
||||
|
||||
echo "disabled seeed-voicecard.service "
|
||||
systemctl stop seeed-voicecard.service
|
||||
systemctl disable seeed-voicecard.service
|
||||
|
||||
echo "remove seeed-voicecard"
|
||||
|
@ -33,9 +81,18 @@ echo "remove dkms"
|
|||
rm -rf /var/lib/dkms/seeed-voicecard || true
|
||||
|
||||
echo "remove kernel modules"
|
||||
rm /lib/modules/${uname_r}/kernel/sound/soc/codecs/snd-soc-wm8960.ko || true
|
||||
rm /lib/modules/${uname_r}/kernel/sound/soc/codecs/snd-soc-ac108.ko || true
|
||||
rm /lib/modules/${uname_r}/kernel/sound/soc/bcm/snd-soc-seeed-voicecard.ko || true
|
||||
rm /lib/modules/*/kernel/sound/soc/codecs/snd-soc-wm8960.ko || true
|
||||
rm /lib/modules/*/kernel/sound/soc/codecs/snd-soc-ac108.ko || true
|
||||
rm /lib/modules/*/kernel/sound/soc/bcm/snd-soc-seeed-voicecard.ko || true
|
||||
rm /lib/modules/*/updates/dkms/snd-soc-wm8960.ko || true
|
||||
rm /lib/modules/*/updates/dkms/snd-soc-ac108.ko || true
|
||||
rm /lib/modules/*/updates/dkms/snd-soc-seeed-voicecard.ko || true
|
||||
|
||||
echo "remove $CONFIG configuration"
|
||||
for i in $RPI_HATS; do
|
||||
echo Uninstall $i ...
|
||||
do_overlay $i 1
|
||||
done
|
||||
|
||||
echo "------------------------------------------------------"
|
||||
echo "Please reboot your raspberry pi to apply all settings"
|
||||
|
|
26
wm8960.c
26
wm8960.c
|
@ -25,6 +25,7 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/wm8960.h>
|
||||
#include "sound-compatible-4.18.h"
|
||||
|
||||
#include "wm8960.h"
|
||||
|
||||
|
@ -507,7 +508,11 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
|
|||
* list each time to find the desired power state do so now
|
||||
* and save the result.
|
||||
*/
|
||||
#if __NO_SND_SOC_CODEC_DRV
|
||||
list_for_each_entry(w, &codec->card->widgets, list) {
|
||||
#else
|
||||
list_for_each_entry(w, &codec->component.card->widgets, list) {
|
||||
#endif
|
||||
if (w->dapm != dapm)
|
||||
continue;
|
||||
if (strcmp(w->name, "LOUT1 PGA") == 0)
|
||||
|
@ -748,6 +753,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
|
|||
iface |= 0x000c;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported width %d\n",
|
||||
params_width(params));
|
||||
|
@ -790,7 +796,7 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int wm8960_mute(struct snd_soc_dai *dai, int mute)
|
||||
static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
|
@ -1230,11 +1236,12 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|||
static const struct snd_soc_dai_ops wm8960_dai_ops = {
|
||||
.hw_params = wm8960_hw_params,
|
||||
.hw_free = wm8960_hw_free,
|
||||
.digital_mute = wm8960_mute,
|
||||
.mute_stream = wm8960_mute,
|
||||
.set_fmt = wm8960_set_dai_fmt,
|
||||
.set_clkdiv = wm8960_set_dai_clkdiv,
|
||||
.set_pll = wm8960_set_dai_pll,
|
||||
.set_sysclk = wm8960_set_dai_sysclk,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver wm8960_dai = {
|
||||
|
@ -1252,7 +1259,11 @@ static struct snd_soc_dai_driver wm8960_dai = {
|
|||
.rates = WM8960_RATES,
|
||||
.formats = WM8960_FORMATS,},
|
||||
.ops = &wm8960_dai_ops,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0)
|
||||
.symmetric_rate = 1,
|
||||
#else
|
||||
.symmetric_rates = 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int wm8960_probe(struct snd_soc_codec *codec)
|
||||
|
@ -1276,6 +1287,11 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
|
|||
.probe = wm8960_probe,
|
||||
.set_bias_level = wm8960_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
#if __NO_SND_SOC_CODEC_DRV
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct regmap_config wm8960_regmap = {
|
||||
|
@ -1302,8 +1318,7 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
|
|||
pdata->shared_lrclk = true;
|
||||
}
|
||||
|
||||
static int wm8960_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
static int wm8960_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
|
||||
struct wm8960_priv *wm8960;
|
||||
|
@ -1368,10 +1383,9 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wm8960_i2c_remove(struct i2c_client *client)
|
||||
static void wm8960_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id wm8960_i2c_id[] = {
|
||||
|
|
Loading…
Add table
Reference in a new issue