@ -0,0 +1,54 @@ | |||
MOD_NAME := snd-i2smic-rpi | |||
ifneq ($(KERNELRELEASE),) | |||
# call from kernel build system | |||
all: | |||
@echo "Building from the kernel build system" | |||
@echo "Module build: $(CONFIG_SND_I2S_RPI)" | |||
@echo "Name: $(MOD_NAME)" | |||
obj-$(CONFIG_SND_I2S_RPI) := $(MOD_NAME).o | |||
else | |||
# external module build | |||
EXTRA_FLAGS += -I$(PWD) | |||
# | |||
# KDIR is a path to a directory containing kernel source. | |||
# It can be specified on the command line passed to make to enable the module to | |||
# be built and installed for a kernel other than the one currently running. | |||
# By default it is the path to the symbolic link created when | |||
# the current kernel's modules were installed, but | |||
# any valid path to the directory in which the target kernel's source is located | |||
# can be provided on the command line. | |||
# | |||
KDIR ?= /lib/modules/$(shell uname -r)/build | |||
MDIR ?= /lib/modules/$(shell uname -r) | |||
PWD := $(shell pwd) | |||
export CONFIG_SND_I2S_RPI := m | |||
all: | |||
$(MAKE) -C $(KDIR) M=$(PWD) modules | |||
clean: | |||
$(MAKE) -C $(KDIR) M=$(PWD) clean | |||
help: | |||
$(MAKE) -C $(KDIR) M=$(PWD) help | |||
install: snd-i2smic-rpi.ko | |||
rm -f ${MDIR}/kernel/sound/drivers/$(MOD_NAME).ko | |||
install -m644 -b -D $(MOD_NAME).ko ${MDIR}/kernel/sound/drivers/$(MOD_NAME).ko | |||
depmod -a | |||
uninstall: | |||
rm -rf ${MDIR}/kernel/sound/drivers/$(MOD_NAME).ko | |||
depmod -a | |||
endif | |||
.PHONY : all clean install uninstall |
@ -0,0 +1,114 @@ | |||
**Driver for the Adafruit I2S MEMS Microphone** | |||
[Product learn page on Adafruit](https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/overview). | |||
Known problems with this driver: Low vol level. While you can use the alsa magic socery to make an alsa softvol input, that approach won't work out of the box with anything that uses Pulseaudio. If you have any idea how to make this work with Pulse, please drop me a line. | |||
Installing the I2S microphone driver the easy way | |||
==================================== | |||
If you use Raspbian or any Debian-derived distribution, [go to the releases tab](https://github.com/htruong/snd-i2s_rpi/releases) and download the newest deb version. | |||
Then do the following | |||
```bash | |||
# Installing raspberrypi-kernel-headers works only if you haven't messed with | |||
# the rpi-update thing. | |||
# If you did, then you would have to do the rpi-source method | |||
# to get the kernel headers. See: | |||
# https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-and-test#kernel-compiling | |||
$ sudo apt install dkms raspberrypi-kernel-headers | |||
$ sudo dpkg -i snd-i2s-rpi-dkms_0.0.2_all.deb | |||
# For this to work, remember to modify these first: | |||
# /boot/config.txt -> dtparam=i2s=on | |||
# and | |||
# /etc/modules -> snd-bcm2835 | |||
# remember to reboot | |||
$ sudo modprobe snd-i2s_rpi rpi_platform_generation=0 | |||
# rpi_platform_generation=0 for Raspberry Pi 1 B/A/A+, 0 | |||
# do not add anything for everything else (2/3). | |||
# see if it works | |||
$ dmesg | grep i2s | |||
# it should say blah.i2s mapping OK | |||
# [ 3.519017] snd_i2s_rpi: loading out-of-tree module taints kernel. | |||
# [ 3.519881] snd-i2s_rpi: Version 0.0.2 | |||
# [ 3.519889] snd-i2s_rpi: Setting platform to 20203000.i2s | |||
# [ 7.624559] asoc-simple-card asoc-simple-card.0: ASoC: CPU DAI 20203000.i2s not registered - will retry | |||
# ... snip ... | |||
# [ 9.507142] asoc-simple-card asoc-simple-card.0: snd-soc-dummy-dai <-> 20203000.i2s mapping ok | |||
$ arecord -l | |||
# it should list your mic | |||
# note that the default vol level is very low, you need | |||
# to follow the ladyada's guide to make it hearable | |||
# If you want it to load automatically at startup | |||
# 1. Add to /etc/modules | |||
# snd-i2s_rpi | |||
# 2. If you have a Pi old-gen, you need to do this: | |||
# create file called /etc/modprobe.d/snd-i2s_rpi.conf | |||
# add this line | |||
# options snd-i2s_rpi rpi_platform_generation=0 | |||
``` | |||
Installing as a stand-alone module | |||
==================================== | |||
make | |||
sudo make install | |||
To load the driver manually, run this as root: | |||
modprobe snd-i2s_rpi | |||
You may also specify custom toolchains by using the `CROSS_COMPILE` flag: | |||
CROSS_COMPILE=/usr/local/bin/arm-eabi- | |||
Installing as a part of the kernel | |||
====================================== | |||
Instructions to come later. Who would ever want to do that? | |||
Installing as a DKMS module | |||
================================= | |||
You can have even more fun with snd-i2s\_rpi by installing it as a DKMS module has the main advantage of being auto-compiled (and thus, possibly surviving) between kernel upgrades. | |||
First, get dkms. On Raspbian this should be: | |||
sudo apt install dkms | |||
Then copy the root of this repository to `/usr/share`: | |||
sudo cp -R . /usr/src/snd-i2s_rpi-0.0.2 (or whatever version number declared on dkms.conf is) | |||
sudo dkms add -m snd-i2s_rpi -v 0.0.2 | |||
Build and load the module: | |||
sudo dkms build -m snd-i2s_rpi -v 0.0.2 | |||
sudo dkms install -m snd-i2s_rpi -v 0.0.2 | |||
Now you have a proper dkms module that will work for a long time... hopefully. | |||
@ -0,0 +1,7 @@ | |||
PACKAGE_NAME="snd-i2smic-rpi" | |||
PACKAGE_VERSION="0.1.0" | |||
MAKE="KDIR=/lib/modules/$kernelver/build MDIR=/lib/modules/$kernelver make" | |||
CLEAN="make clean" | |||
BUILT_MODULE_NAME[0]="snd-i2smic-rpi" | |||
AUTOINSTALL="yes" | |||
DEST_MODULE_LOCATION="/kernel/sound/drivers" |
@ -0,0 +1,151 @@ | |||
/* | |||
* ===================================================================================== | |||
* | |||
* Filename: snd-i2smic-rpi | |||
* | |||
* Description: I2S microphone kernel module | |||
* | |||
* Version: 0.1.0 | |||
* Created: 2020-04-14 | |||
* Revision: none | |||
* Compiler: gcc | |||
* | |||
* Pi4 Mods: Carter Nelson | |||
* Orig Author: Huan Truong (htruong@tnhh.net), originally written by Paul Creaser | |||
* | |||
* ===================================================================================== | |||
*/ | |||
#include <linux/module.h> | |||
#include <linux/moduleparam.h> | |||
#include <linux/kernel.h> | |||
#include <linux/kmod.h> | |||
#include <linux/platform_device.h> | |||
#include <sound/simple_card.h> | |||
#include <linux/delay.h> | |||
#include "snd-i2smic-rpi.h" | |||
/* | |||
* modified for linux 4.1.5 | |||
* inspired by https://github.com/msperl/spi-config | |||
* with thanks for https://github.com/notro/rpi-source/wiki | |||
* as well as Florian Meier for the rpi i2s and dma drivers | |||
* | |||
* to use a differant (simple-card compatible) codec | |||
* change the codec name string in two places and the | |||
* codec_dai name string. (see codec's source file) | |||
* | |||
* | |||
* N.B. playback vs capture is determined by the codec choice | |||
* */ | |||
static struct asoc_simple_card_info card_info; | |||
static struct platform_device card_device; | |||
/* | |||
* Setup command line parameter | |||
*/ | |||
static short rpi_platform_generation; | |||
module_param(rpi_platform_generation, short, 0); | |||
MODULE_PARM_DESC(rpi_platform_generation, "Raspberry Pi generation: 0=Pi0, 1=Pi2/3, 2=Pi4"); | |||
/* | |||
* Dummy callback for release | |||
*/ | |||
void device_release_callback(struct device *dev) { /* do nothing */ }; | |||
/* | |||
* Setup the card info | |||
*/ | |||
static struct asoc_simple_card_info default_card_info = { | |||
.card = "snd_rpi_i2s_card", // -> snd_soc_card.name | |||
.name = "simple-card_codec_link", // -> snd_soc_dai_link.name | |||
.codec = "snd-soc-dummy", // "dmic-codec", // -> snd_soc_dai_link.codec_name | |||
.platform = "not-set.i2s", | |||
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, | |||
.cpu_dai = { | |||
.name = "not-set.i2s", // -> snd_soc_dai_link.cpu_dai_name | |||
.sysclk = 0 | |||
}, | |||
.codec_dai = { | |||
.name = "snd-soc-dummy-dai", //"dmic-codec", // -> snd_soc_dai_link.codec_dai_name | |||
.sysclk = 0 | |||
}, | |||
}; | |||
/* | |||
* Setup the card device | |||
*/ | |||
static struct platform_device default_card_device = { | |||
.name = "asoc-simple-card", //module alias | |||
.id = 0, | |||
.num_resources = 0, | |||
.dev = { | |||
.release = &device_release_callback, | |||
.platform_data = &default_card_info, // *HACK ALERT* | |||
}, | |||
}; | |||
/* | |||
* Callback for module initialization | |||
*/ | |||
int i2s_mic_rpi_init(void) | |||
{ | |||
const char *dmaengine = "bcm2708-dmaengine"; //module name | |||
static char *card_platform; | |||
int ret; | |||
printk(KERN_INFO "snd-i2smic-rpi: Version %s\n", SND_I2SMIC_RPI_VERSION); | |||
// Set platform | |||
switch (rpi_platform_generation) { | |||
case 0: | |||
// Pi Zero | |||
card_platform = "20203000.i2s"; | |||
break; | |||
case 1: | |||
// Pi 2 and 3 | |||
card_platform = "3f203000.i2s"; | |||
break; | |||
case 2: | |||
default: | |||
// Pi 4 | |||
card_platform = "fe203000.i2s"; | |||
break; | |||
} | |||
printk(KERN_INFO "snd-i2smic-rpi: Setting platform to %s\n", card_platform); | |||
// request DMA engine module | |||
ret = request_module(dmaengine); | |||
pr_alert("request module load '%s': %d\n",dmaengine, ret); | |||
// update info | |||
card_info = default_card_info; | |||
card_info.platform = card_platform; | |||
card_info.cpu_dai.name = card_platform; | |||
card_device = default_card_device; | |||
card_device.dev.platform_data = &card_info; | |||
// register the card device | |||
ret = platform_device_register(&card_device); | |||
pr_alert("register platform device '%s': %d\n",card_device.name, ret); | |||
return 0; | |||
} | |||
/* | |||
* Callback for module exit | |||
*/ | |||
void i2s_mic_rpi_exit(void) | |||
{ | |||
platform_device_unregister(&card_device); | |||
pr_alert("i2s mic module unloaded\n"); | |||
} | |||
// Plumb it up | |||
module_init(i2s_mic_rpi_init); | |||
module_exit(i2s_mic_rpi_exit); | |||
MODULE_DESCRIPTION("ASoC simple-card I2S Microphone"); | |||
MODULE_AUTHOR("Carter Nelson"); | |||
MODULE_LICENSE("GPL v2"); |
@ -0,0 +1,6 @@ | |||
#ifndef _SND_I2SMIC_RPI_H_ | |||
#define _SND_I2SMIC_RPI_H_ | |||
#define SND_I2SMIC_RPI_VERSION "0.1.0" | |||
#endif |
@ -0,0 +1,120 @@ | |||
#!/bin/bash | |||
#------------------------------------------------------------------------- | |||
# Installer script for I2S microphone support on Raspberry Pi | |||
# | |||
# 2020/04/15 | |||
#------------------------------------------------------------------------- | |||
############################ Script assisters ############################ | |||
# Given a list of strings representing options, display each option | |||
# preceded by a number (1 to N), display a prompt, check input until | |||
# a valid number within the selection range is entered. | |||
selectN() { | |||
for ((i=1; i<=$#; i++)); do | |||
echo $i. ${!i} | |||
done | |||
echo | |||
REPLY="" | |||
while : | |||
do | |||
echo -n "SELECT 1-$#: " | |||
read | |||
if [[ $REPLY -ge 1 ]] && [[ $REPLY -le $# ]]; then | |||
return $REPLY | |||
fi | |||
done | |||
} | |||
function ask() { | |||
# http://djm.me/ask | |||
while true; do | |||
if [ "${2:-}" = "Y" ]; then | |||
prompt="Y/n" | |||
default=Y | |||
elif [ "${2:-}" = "N" ]; then | |||
prompt="y/N" | |||
default=N | |||
else | |||
prompt="y/n" | |||
default= | |||
fi | |||
# Ask the question | |||
read -p "$1 [$prompt] " REPLY | |||
# Default? | |||
if [ -z "$REPLY" ]; then | |||
REPLY=$default | |||
fi | |||
# Check if the reply is valid | |||
case "$REPLY" in | |||
Y*|y*) return 0 ;; | |||
N*|n*) return 1 ;; | |||
esac | |||
done | |||
} | |||
####################################################### MAIN | |||
clear | |||
echo "This script downloads and installs" | |||
echo "I2S microphone support." | |||
echo | |||
echo "Select Pi Model:" | |||
selectN "Pi 0 or 0W" \ | |||
"Pi 2 or 3" \ | |||
"Pi 4" | |||
PIMODEL_SELECT=$(($?-1)) | |||
ask "Auto load module at boot?" | |||
AUTO_LOAD=$? | |||
echo | |||
echo "Installing..." | |||
# System update and install | |||
apt-get -y update | |||
apt-get -y upgrade | |||
apt-get -y install git dkms raspberrypi-kernel-headers | |||
# Clone the repo | |||
git clone https://github.com/adafruit/Raspberry-Pi-Installer-Scripts.git | |||
# Install and build module | |||
cd Raspberry-Pi-Installer-Scripts/i2s_mic_module | |||
cp -R . /usr/src/snd-i2smic-rpi-0.1.0 | |||
dkms add -m snd-i2smic-rpi -v 0.1.0 | |||
dkms build -m snd-i2smic-rpi -v 0.1.0 | |||
dkms install -m snd-i2smic-rpi -v 0.1.0 | |||
# Setup auto load at boot if selected | |||
if [ $AUTO_LOAD = 0 ]; then | |||
cat > /etc/modules-load.d/snd-i2smic-rpi.conf<<EOF | |||
snd-i2smic-rpi | |||
EOF | |||
cat > /etc/modprobe.d/snd-i2smic-rpi.conf<<EOF | |||
options snd-i2smic-rpi rpi_platform_generation=$PIMODEL_SELECT | |||
EOF | |||
fi | |||
# Enable I2S overlay | |||
sed -i -e 's/#dtparam=i2s/dtparam=i2s/g' /boot/config.txt | |||
# Done | |||
echo "DONE." | |||
echo | |||
echo "Settings take effect on next boot." | |||
echo | |||
echo -n "REBOOT NOW? [y/N] " | |||
read | |||
if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then | |||
echo "Exiting without reboot." | |||
exit 0 | |||
fi | |||
echo "Reboot started..." | |||
reboot | |||
exit 0 |