├── .drone.yml ├── .drone └── slack.tpl ├── .gitignore ├── README.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── docs ├── matrixio-kernel-modules.dkms ├── matrixio-kernel-modules.install ├── postinst └── rules ├── kernel_modifications.txt ├── misc ├── asound-softvol-example.conf ├── asound.conf ├── audio_output_setup ├── matrixio.conf └── pyaudio_test.py └── src ├── .clang-format ├── Kbuild ├── Makefile ├── fir_coeff.h ├── matrixio-codec.c ├── matrixio-core.c ├── matrixio-core.h ├── matrixio-env.c ├── matrixio-everloop.c ├── matrixio-gpio.c ├── matrixio-imu.c ├── matrixio-mic.c ├── matrixio-pcm.h ├── matrixio-playback.c ├── matrixio-pwm.c ├── matrixio-regmap.c ├── matrixio-uart.c └── matrixio.dts /.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | # Make sure the host machine is able to emulate arm devices 3 | arm-support: 4 | when: 5 | matrix: 6 | ARCHITECTURE: armhf 7 | privileged: true 8 | image: gcr.io/admobilize-testing/gce-builder:latest 9 | commands: 10 | - set +e 11 | - mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc 12 | - test ! -e /proc/sys/fs/binfmt_misc/arm && echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:' > /proc/sys/fs/binfmt_misc/register || echo 'arm file already exists' 13 | 14 | build-debs: 15 | image: gcr.io/admobilize-testing/creator-builder-${ARCHITECTURE}-${DISTRIBUTION}-${CODENAME}:${IMAGE_TAG} 16 | privileged: true 17 | commands: 18 | - curl https://apt.matrix.one/doc/apt-key.gpg | apt-key add - 19 | - echo "deb https://apt.matrix.one/raspbian ${CODENAME} main" > /etc/apt/sources.list.d/matrixlabs.list 20 | - apt install -y dkms 21 | - ${UPDATE_CMD} 22 | - debuild -us -uc -b 23 | - mv ../*.deb . 24 | 25 | upload-debs: 26 | when: 27 | branch: master 28 | event: [ push, tag ] 29 | status: success 30 | image: gcr.io/admobilize-testing/gce-builder:latest 31 | secrets: [aws_access_key_id, aws_secret_access_key, gpg_key, gpg_pass] 32 | commands: 33 | # Prepare GPG 34 | - echo "$${GPG_KEY}" > /tmp/tmpkey && gpg --import --batch /tmp/tmpkey && rm /tmp/tmpkey 35 | - echo "personal-digest-preferences SHA512" >> /root/.gnupg/gpg.conf 36 | 37 | # Only TAG events are published to main 38 | - export COMPONENT=$([ "${DRONE_BUILD_EVENT}" = "tag" ] && echo "main" || echo "unstable") 39 | - export PKG_VER=$(dpkg-parsechangelog --show-field Version -ldebian/changelog) 40 | 41 | # Upload packages 42 | - mv matrixio-kernel-modules_$${PKG_VER}_all.deb 43 | matrixio-kernel-modules_${DISTRIBUTION}-${CODENAME}-$${PKG_VER}-$${COMPONENT}_all.deb 44 | 45 | - echo "$${GPG_PASS}" | deb-s3 upload --bucket apt.matrix.one 46 | --prefix $DISTRIBUTION 47 | --component $COMPONENT 48 | --codename $CODENAME 49 | --access-key-id $${AWS_ACCESS_KEY_ID} 50 | --secret-access-key $${AWS_SECRET_ACCESS_KEY} 51 | --sign info@matrix.one 52 | --gpg-options="--batch --passphrase-fd 0" 53 | matrixio-kernel-modules_${DISTRIBUTION}-${CODENAME}-$${PKG_VER}-$${COMPONENT}_all.deb 54 | 55 | # Render the notification template for the notify-slack step 56 | - j2 --var-delimiter '%%' .drone/slack.tpl > notification.tpl 57 | 58 | notify-always: 59 | image: plugins/slack 60 | secrets: [slack_webhook] 61 | username: drone-ci-builder 62 | channel: notifications 63 | when: 64 | status: [ success, failure ] 65 | branch: master 66 | event: [ push, tag ] 67 | template: file:///drone/src/github.com/matrix-io/matrixio-kernel-modules/notification.tpl 68 | 69 | matrix: 70 | include: 71 | - CODENAME: stretch 72 | DISTRIBUTION: raspbian 73 | ARCHITECTURE: armhf 74 | UPDATE_CMD: "apt update" 75 | IMAGE_TAG: 84d9bfb7 76 | 77 | - CODENAME: buster 78 | DISTRIBUTION: raspbian 79 | ARCHITECTURE: armhf 80 | UPDATE_CMD: "apt update -y --allow-releaseinfo-change" 81 | IMAGE_TAG: 84d9bfb7 82 | -------------------------------------------------------------------------------- /.drone/slack.tpl: -------------------------------------------------------------------------------- 1 | {{#success build.status}} 2 | {{build.author}} just built `{{repo.name}}:{{build.branch}}` from <%%DRONE_COMMIT_LINK%%|#{{truncate build.commit 8}}> 3 | :new: {{build.message}} 4 | :debian: `matrixio-kernel-modules_%%DISTRIBUTION%%-%%CODENAME%%-%%PKG_VER%%-%%COMPONENT%%_armhf.deb` 5 | Was published to `apt.matrix.one/%%DISTRIBUTION%% %%CODENAME%% %%COMPONENT%%` 6 | {{else}} 7 | {{build.author}} just broke the build of `{{repo.name}}:{{build.branch}}` with <%%DRONE_COMMIT_LINK%%|#{{truncate build.commit 8}}> 8 | :new: :zombie: {{build.message}} 9 | :debian: `matrixio-kernel-modules_%%DISTRIBUTION%%-%%CODENAME%%-%%PKG_VER%%-%%COMPONENT%%_armhf.deb` 10 | Failed to build 11 | {{/success}} 12 | :stopwatch: {{ since build.started}} 13 | :gear: {{build.link}} 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.ko 3 | *.o 4 | *.d 5 | *.symvers 6 | *.order 7 | *.mod.c 8 | *.mod 9 | *.dtbo 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # matrixio-kernel-modules 2 | 3 | MATRIXIO Kernel Modules is the kernel drivers for MATRIX Creator and MATRIX Voice. 4 | 5 | ## Warning: Kernel Version 6 | This drivers **only** works with current stock raspbian kernel. To reverting back to current stock Raspbian kernel use: 7 | 8 | ``` 9 | sudo apt-get install --reinstall raspberrypi-bootloader raspberrypi-kernel 10 | ``` 11 | 12 | ## Option 1: Package Installation 13 | ``` 14 | # Add repo and key 15 | curl -L https://apt.matrix.one/doc/apt-key.gpg | sudo apt-key add - 16 | echo "deb https://apt.matrix.one/raspbian $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/matrixlabs.list 17 | ``` 18 | ``` 19 | # Update packages and install 20 | sudo apt-get update 21 | sudo apt-get upgrade 22 | ``` 23 | ``` 24 | # Reboot in case of Kernel Updates 25 | sudo reboot 26 | ``` 27 | ``` 28 | # Installation MATRIX Packages 29 | sudo apt install matrixio-kernel-modules 30 | ``` 31 | ``` 32 | # Reboot 33 | sudo reboot 34 | ``` 35 | ## Option 2: Cloning & compiling from sources 36 | 37 | ### Install Dependencies 38 | 39 | ``` 40 | # Add repo and key 41 | curl -L https://apt.matrix.one/doc/apt-key.gpg | sudo apt-key add - 42 | echo "deb https://apt.matrix.one/raspbian $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/matrixlabs.list 43 | ``` 44 | ``` 45 | # Update packages and install 46 | sudo apt-get update 47 | sudo apt-get upgrade 48 | ``` 49 | ``` 50 | # Installation MATRIX Pacakages 51 | sudo apt install matrixio-creator-init 52 | ``` 53 | ``` 54 | # Installation Kernel Packages 55 | sudo apt-get -y install raspberrypi-kernel-headers raspberrypi-kernel git 56 | ``` 57 | ``` 58 | # Reboot 59 | sudo reboot 60 | ``` 61 | 62 | ### Cloning & Compiling 63 | ``` 64 | git clone https://github.com/matrix-io/matrixio-kernel-modules 65 | cd matrixio-kernel-modules/src 66 | make && make install 67 | ``` 68 | ### Overlay Setup 69 | 70 | Add in `/boot/config.txt` 71 | 72 | ``` 73 | dtoverlay=matrixio 74 | ``` 75 | 76 | Finally, load the remaining required modules 77 | ``` 78 | sudo cp ~/matrixio-kernel-modules/misc/matrixio.conf /etc/modules-load.d/ 79 | sudo cp ~/matrixio-kernel-modules/misc/asound.conf /etc/ 80 | sudo reboot 81 | ``` 82 | 83 | The reboot in the last step allows and activates the MATRIXIO Kernel modules. 84 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | matrixio-kernel-modules (0.2.5) unstable; urgency=low 2 | * Mic driver and device tree updated for 5.10.x kernel series 3 | 4 | -- Andres Calderon Mon, 29 Mar 2021 10:00 -0500 5 | 6 | matrixio-kernel-modules (0.2.4) unstable; urgency=low 7 | * Codec driver and device tree updated for 5.4.x kernel series 8 | 9 | -- Leonardo Urrego Wed, 5 Aug 2020 22:00:00 -0500 10 | 11 | matrixio-kernel-modules (0.2.3) unstable; urgency=low 12 | * Adding dsnoop in asound.conf to support simultaneous recording from different channels 13 | 14 | -- Samreen Islam Fri, 13 Dec 2019 10:57:00 -1057 15 | 16 | matrixio-kernel-modules (0.2.2) unstable; urgency=low 17 | * hardcoding rates for stretch compatibility 18 | 19 | -- Andres Calderon Mon, 9 Dec 2019 14:30:00 -0800 20 | 21 | matrixio-kernel-modules (0.2.1) unstable; urgency=low 22 | * Rewrite of microphone array driver. Does not crash the kernel anymore. 23 | 24 | -- Trent Piepho Thu, 21 Nov 2019 18:38:23 -0800 25 | 26 | matrixio-kernel-modules (0.2.0) unstable; urgency=low 27 | * Redo DKMS package so it works properly, uninstalls and upgrades, builds dtbo file 28 | 29 | -- Trent Piepho Thu, 21 Nov 2019 18:02:24 -0800 30 | 31 | matrixio-kernel-modules (0.1.9) unstable; urgency=low 32 | 33 | * Bug Fix: UART ttyMATRIX0 34 | * Reimplement Workqueue in mics driver to better performance 35 | 36 | -- Leonardo Urrego Fri, 16 Aug 2019 22:00:00 -0500 37 | 38 | matrixio-kernel-modules (0.1.8) unstable; urgency=low 39 | 40 | * Raspbian Buster support added 41 | * Raspbian Jessie support removed 42 | 43 | -- Andres Calderon Tue, 25 Jun 2019 22:00:00 -0500 44 | 45 | matrixio-kernel-modules (0.1.7) unstable; urgency=low 46 | 47 | * Bug Fix: compatibility with the [4.19 kernel version](https://pimylifeup.com/raspberry-pi-kernel-updated-to-4-19/) 48 | 49 | -- Leonardo Urrego Thu, 13 Jun 2019 12:00:00 -0500 50 | 51 | matrixio-kernel-modules (0.1.6) unstable; urgency=low 52 | 53 | * Added support for controlling speaker/headphone audio output via alsamixer (from osmaa) 54 | * Added support for controlling 32 steps volume via alsamixer (from osmaa) 55 | * Update Matrix Sound Card Name 56 | 57 | -- Yoel Ramos Castillo Wed, 10 Apr 2019 10:00:00 -0500 58 | 59 | matrixio-kernel-modules (0.1.5) unstable; urgency=low 60 | 61 | * Support for audio playback on MATRIX Voice. 62 | - Supported Frequencies: 8kHz, 16kHz, 44100Hz, 48kHz 63 | - Supported Bit depth: unsigned 16 bit 64 | 65 | -- Yoel Ramos Castillo Fri, 16 Nov 2018 17:00:00 -0500 66 | 67 | matrixio-kernel-modules (0.1.4) unstable; urgency=low 68 | 69 | * Bug Fix: Zwave UART Has been fixed 70 | * Bug Fix: Change led order from GRWB to RGBW and use buffer write function. 71 | * Support microphone core: 128 Taps FIR filter. 72 | 73 | -- Kevin Patino Fri, 18 May 2018 17:00:00 -0500 74 | 75 | matrixio-kernel-modules (0.1.3) unstable; urgency=low 76 | 77 | * Bug Fix: kernel overlay fixed 78 | 79 | -- Andres Calderon Sat, 14 Apr 2018 19:00:00 -0500 80 | 81 | matrixio-kernel-modules (0.1.2) unstable; urgency=low 82 | 83 | * Bug Fix: Mircrophone Array noise has been fixed 84 | 85 | -- Andres Calderon Mon, 9 Apr 2018 17:00:00 -0500 86 | 87 | matrixio-kernel-modules (0.1.1) unstable; urgency=low 88 | 89 | * Initial release 90 | 91 | -- Andres Calderon Mon, 04 Apr 2018 17:00:00 -0500 92 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: matrixio-kernel-modules 2 | Section: embedded 3 | Priority: optional 4 | Maintainer: MATRIX Labs 5 | Build-Depends: debhelper (>= 9), dkms, device-tree-compiler 6 | Standards-Version: 3.9.5 7 | Homepage: https://github.com/matrix-io/matrixio-kernel-modules 8 | 9 | Package: matrixio-kernel-modules 10 | Architecture: all 11 | Depends: ${misc:Depends}, 12 | matrixio-creator-init,raspberrypi-kernel-headers,raspberrypi-kernel, 13 | dkms, 14 | Description: Source DKMS for MATRIXIO kernel modules. 15 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: matrix-creator-init 3 | Source: https://www.admobilize.com/ 4 | 5 | Files: * 6 | Copyright: 2016-2020 7 | License: 8 | The firmware for the MCU is free and can be downloaded from this 9 | repository: https://github.com/matrix-io/matrix-creator-mcu. 10 | The firmware for the FPGA is not free, but a free version exists 11 | in this repository: 12 | https://github.com/matrix-io/matrix-creator-fpga/tree/master/creator_core 13 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrixio-kernel-modules/77da992d03a31e1449566936e5820bf276634ec0/debian/docs -------------------------------------------------------------------------------- /debian/matrixio-kernel-modules.dkms: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="matrixio-kernel-modules" 2 | 3 | PACKAGE_VERSION="#MODULE_VERSION#" 4 | 5 | BUILT_MODULE_NAME[0]="matrixio-core" 6 | BUILT_MODULE_NAME[1]="matrixio-codec" 7 | BUILT_MODULE_NAME[2]="matrixio-mic" 8 | BUILT_MODULE_NAME[3]="matrixio-playback" 9 | BUILT_MODULE_NAME[4]="matrixio-env" 10 | BUILT_MODULE_NAME[5]="matrixio-imu" 11 | BUILT_MODULE_NAME[6]="matrixio-everloop" 12 | BUILT_MODULE_NAME[7]="matrixio-gpio" 13 | BUILT_MODULE_NAME[8]="matrixio-uart" 14 | BUILT_MODULE_NAME[9]="matrixio-regmap" 15 | 16 | DEST_MODULE_LOCATION[0]="/kernel/drivers/mfd" 17 | DEST_MODULE_LOCATION[1]="/kernel/sound/soc/codecs" 18 | DEST_MODULE_LOCATION[2]="/kernel/sound/soc/codecs" 19 | DEST_MODULE_LOCATION[3]="/kernel/sound/soc/codecs" 20 | DEST_MODULE_LOCATION[4]="/kernel/drivers/mfd" 21 | DEST_MODULE_LOCATION[5]="/kernel/drivers/mfd" 22 | DEST_MODULE_LOCATION[6]="/kernel/drivers/mfd" 23 | DEST_MODULE_LOCATION[7]="/kernel/drivers/mfd" 24 | DEST_MODULE_LOCATION[8]="/kernel/drivers/mfd" 25 | DEST_MODULE_LOCATION[9]="/kernel/drivers/mfd" 26 | 27 | AUTOINSTALL="yes" 28 | -------------------------------------------------------------------------------- /debian/matrixio-kernel-modules.install: -------------------------------------------------------------------------------- 1 | kernel_modifications.txt usr/share/matrixlabs/matrixio-devices/config 2 | misc/asound.conf etc/ 3 | misc/matrixio.conf etc/modules-load.d/ -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | DKMS_NAME=matrixio-kernel-modules 5 | DKMS_PACKAGE_NAME=$DKMS_NAME-dkms 6 | DKMS_VERSION=0.2.5 7 | 8 | postinst_found=0 9 | 10 | case "$1" in 11 | configure) 12 | for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do 13 | if [ -f $DKMS_POSTINST ]; then 14 | $DKMS_POSTINST $DKMS_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2 15 | postinst_found=1 16 | break 17 | fi 18 | done 19 | if [ "$postinst_found" -eq 0 ]; then 20 | echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not" 21 | echo "built with legacy DKMS support." 22 | echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst" 23 | echo "support or upgrade DKMS to a more current version." 24 | exit 1 25 | else 26 | echo "Enable configurations in /boot/config.txt" 27 | cp /boot/config.txt /boot/config.txt.matrixio.bk \ 28 | && /usr/share/matrixlabs/matrixio-devices/matrixlabs_edit_settings.py \ 29 | /boot/config.txt.matrixio.bk \ 30 | /usr/share/matrixlabs/matrixio-devices/config/kernel_modifications.txt > /boot/config.txt 31 | fi 32 | ;; 33 | esac 34 | 35 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | #DH_VERBOSE = 1 3 | DPKG_EXPORT_BUILDFLAGS = 1 4 | 5 | include /usr/share/dpkg/pkg-info.mk 6 | 7 | %: 8 | dh $@ --with dkms 9 | 10 | override_dh_auto_build: 11 | dh_auto_build -v -D src -- matrixio.dtbo 12 | 13 | override_dh_install: 14 | dh_install src/*.c src/*.h src/Makefile src/Kbuild usr/src/matrixio-kernel-modules-$(DEB_VERSION_UPSTREAM)/ 15 | dh_install src/matrixio.dtbo boot/overlays 16 | 17 | override_dh_dkms: 18 | dh_dkms -V $(DEB_VERSION_UPSTREAM) 19 | 20 | override_dh_auto_configure: 21 | override_dh_auto_test: 22 | override_dh_auto_install: 23 | override_dh_auto_clean: 24 | -------------------------------------------------------------------------------- /kernel_modifications.txt: -------------------------------------------------------------------------------- 1 | dtoverlay=matrixio 2 | -------------------------------------------------------------------------------- /misc/asound-softvol-example.conf: -------------------------------------------------------------------------------- 1 | pcm.!default { 2 | type asym 3 | capture.pcm "mic" 4 | playback.pcm "speaker" 5 | } 6 | 7 | pcm.mic { 8 | type softvol 9 | slave { 10 | pcm "array" 11 | } 12 | control { 13 | name "MicArray Master" 14 | card 0 15 | } 16 | } 17 | 18 | pcm.speaker { 19 | type plug 20 | slave { 21 | pcm "hw:0,0" 22 | } 23 | } 24 | 25 | pcm.array { 26 | type plug 27 | slave { 28 | pcm "hw:2,0" 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /misc/asound.conf: -------------------------------------------------------------------------------- 1 | pcm.!default { 2 | type asym 3 | capture.pcm "mic" 4 | playback.pcm "speaker" 5 | } 6 | 7 | pcm.mic { 8 | type softvol 9 | slave { 10 | pcm "array" 11 | } 12 | control { 13 | name "MicArray Master" 14 | card "MATRIXIOSOUND" 15 | } 16 | } 17 | 18 | pcm.speaker { 19 | type plug 20 | slave { 21 | pcm "hw:0,0" 22 | } 23 | } 24 | 25 | pcm.array { 26 | type plug 27 | slave { 28 | pcm "hw:MATRIXIOSOUND" 29 | } 30 | } 31 | 32 | pcm.channel_1 { 33 | type dsnoop 34 | ipc_key 234884 35 | slave { 36 | pcm "hw:2,0" 37 | channels 8 38 | } 39 | bindings.0 0 40 | } 41 | 42 | pcm.channel_2 { 43 | type dsnoop 44 | ipc_key 234884 45 | slave { 46 | pcm "hw:2,0" 47 | channels 8 48 | } 49 | bindings.0 1 50 | } 51 | 52 | pcm.channel_3 { 53 | type dsnoop 54 | ipc_key 234884 55 | slave { 56 | pcm "hw:2,0" 57 | channels 8 58 | } 59 | bindings.0 2 60 | } 61 | 62 | pcm.channel_4 { 63 | type dsnoop 64 | ipc_key 234884 65 | slave { 66 | pcm "hw:2,0" 67 | channels 8 68 | } 69 | bindings.0 3 70 | } 71 | 72 | pcm.channel_5 { 73 | type dsnoop 74 | ipc_key 234884 75 | slave { 76 | pcm "hw:2,0" 77 | channels 8 78 | } 79 | bindings.0 4 80 | } 81 | 82 | pcm.channel_6 { 83 | type dsnoop 84 | ipc_key 234884 85 | slave { 86 | pcm "hw:2,0" 87 | channels 8 88 | } 89 | bindings.0 5 90 | } 91 | 92 | pcm.channel_7 { 93 | type dsnoop 94 | ipc_key 234884 95 | slave { 96 | pcm "hw:2,0" 97 | channels 8 98 | } 99 | bindings.0 6 100 | } 101 | 102 | pcm.channel_8 { 103 | type dsnoop 104 | ipc_key 234884 105 | slave { 106 | pcm "hw:2,0" 107 | channels 8 108 | } 109 | bindings.0 7 110 | } 111 | 112 | pcm.all_channels { 113 | type dsnoop 114 | ipc_key 234884 115 | slave { 116 | pcm "hw:2,0" 117 | channels 8 118 | } 119 | bindings { 120 | 0 0 121 | 0 1 122 | 0 2 123 | 0 3 124 | 0 4 125 | 0 5 126 | 0 6 127 | 0 7 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /misc/audio_output_setup: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrixio-kernel-modules/77da992d03a31e1449566936e5820bf276634ec0/misc/audio_output_setup -------------------------------------------------------------------------------- /misc/matrixio.conf: -------------------------------------------------------------------------------- 1 | matrixio-regmap 2 | matrixio-everloop 3 | matrixio-gpio 4 | matrixio-imu 5 | matrixio-env 6 | matrixio-mic 7 | matrixio-playback 8 | matrixio-codec 9 | -------------------------------------------------------------------------------- /misc/pyaudio_test.py: -------------------------------------------------------------------------------- 1 | import pyaudio 2 | import wave 3 | 4 | CHUNK = 2048 5 | FORMAT = pyaudio.paInt16 6 | CHANNELS = 8 7 | RATE = 96000 8 | RECORD_SECONDS = 5 9 | WAVE_OUTPUT_FILENAME = "output.wav" 10 | 11 | p = pyaudio.PyAudio() 12 | 13 | stream = p.open(format=FORMAT, 14 | channels=CHANNELS, 15 | rate=RATE, 16 | input=True, 17 | frames_per_buffer=CHUNK) 18 | 19 | print("* recording") 20 | 21 | frames = [] 22 | 23 | for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): 24 | data = stream.read(CHUNK) 25 | frames.append(data) 26 | 27 | print("* done recording") 28 | 29 | stream.stop_stream() 30 | stream.close() 31 | p.terminate() 32 | 33 | wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') 34 | wf.setnchannels(CHANNELS) 35 | wf.setsampwidth(p.get_sample_size(FORMAT)) 36 | wf.setframerate(RATE) 37 | wf.writeframes(b''.join(frames)) 38 | wf.close() 39 | 40 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | # Linux kernel style for C 2 | BasedOnStyle: LLVM 3 | IndentWidth: 8 4 | UseTab: Always 5 | BreakBeforeBraces: Linux 6 | AllowShortIfStatementsOnASingleLine: false 7 | IndentCaseLabels: false 8 | -------------------------------------------------------------------------------- /src/Kbuild: -------------------------------------------------------------------------------- 1 | obj-m += matrixio-core.o 2 | obj-m += matrixio-uart.o 3 | obj-m += matrixio-everloop.o 4 | obj-m += matrixio-codec.o 5 | obj-m += matrixio-mic.o 6 | obj-m += matrixio-playback.o 7 | obj-m += matrixio-gpio.o 8 | obj-m += matrixio-env.o 9 | obj-m += matrixio-imu.o 10 | obj-m += matrixio-regmap.o 11 | 12 | ccflags-y := -Wno-missing-attributes 13 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | DTC?=dtc 2 | KDIR?=/lib/modules/$(shell uname -r)/build 3 | 4 | all: matrixio.dtbo 5 | $(MAKE) -C $(KDIR) M=$(CURDIR) modules 6 | 7 | matrixio.dtbo: matrixio.dts 8 | $(DTC) -W no-unit_address_vs_reg -@ -I dts -O dtb -o matrixio.dtbo matrixio.dts 9 | 10 | install: matrixio.dtbo 11 | $(MAKE) -C $(KDIR) M=$(CURDIR) modules_install 12 | depmod -A 13 | cp matrixio.dtbo /boot/overlays 14 | 15 | clean: 16 | $(MAKE) -C $(KDIR) M=$(CURDIR) clean 17 | rm -f matrixio.dtbo 18 | -------------------------------------------------------------------------------- /src/fir_coeff.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define FIR_ORDER 128 4 | 5 | struct FIRCoeff { 6 | uint32_t rate_; 7 | int16_t coeff_[FIR_ORDER]; 8 | }; 9 | 10 | struct FIRCoeff FIR_Coeff[10] = { 11 | {8000, 12 | {-26, -2, -26, -1, -17, -19, 0, -30, 1, -13, -24, 13 | 14, -33, 10, 5, -23, 51, -20, 37, 53, -12, 116, 14 | 12, 74, 133, 0, 196, 58, 96, 224, -10, 253, 89, 15 | 55, 287, -91, 233, 65, -103, 279, -280, 85, -32, -424, 16 | 183, -588, -211, -182, -915, 32, -977, -649, -281, -1578, -62, 17 | -1368, -1229, -71, -2553, 188, -1658, -2337, 1862, -6351, 5228, 24468, 18 | 5228, -6351, 1862, -2337, -1658, 188, -2553, -71, -1229, -1368, -62, 19 | -1578, -281, -649, -977, 32, -915, -182, -211, -588, 183, -424, 20 | -32, 85, -280, 279, -103, 65, 233, -91, 287, 55, 89, 21 | 253, -10, 224, 96, 58, 196, 0, 133, 74, 12, 116, 22 | -12, 53, 37, -20, 51, -23, 5, 10, -33, 14, -24, 23 | -13, 1, -30, 0, -19, -17, -1}}, 24 | {12000, 25 | {2, 6, 2, 25, 9, 2, 31, 15, 3, 39, 22, 26 | 1, 50, 31, -1, 60, 41, -10, 68, 51, -27, 70, 27 | 59, -55, 62, 64, -95, 41, 63, -151, 3, 56, -223, 28 | -54, 43, -312, -136, 25, -416, -241, 6, -533, -372, -7, 29 | -658, -529, -8, -786, -714, 15, -910, -936, 89, -1024, -1216, 30 | 253, -1120, -1619, 613, -1194, -2377, 1574, -1241, -5149, 8368, 20600, 31 | 8368, -5149, -1241, 1574, -2377, -1194, 613, -1619, -1120, 253, -1216, 32 | -1024, 89, -936, -910, 15, -714, -786, -8, -529, -658, -7, 33 | -372, -533, 6, -241, -416, 25, -136, -312, 43, -54, -223, 34 | 56, 3, -151, 63, 41, -95, 64, 62, -55, 59, 70, 35 | -27, 51, 68, -10, 41, 60, -1, 31, 50, 1, 22, 36 | 39, 3, 15, 31, 2, 9, 25}}, 37 | {16000, 38 | {-3, 18, -3, 19, 3, 1, 21, -9, 19, 4, -7, 39 | 29, -22, 16, 5, -30, 36, -45, 0, 4, -75, 37, 40 | -82, -38, 0, -150, 24, -131, -113, -4, -257, -8, -187, 41 | -232, -9, -394, -71, -237, -401, -7, -553, -167, -262, -625, 42 | 13, -720, -303, -229, -916, 80, -877, -497, -76, -1319, 245, 43 | -1006, -820, 380, -2055, 721, -1091, -1742, 2474, -5713, 5863, 25086, 44 | 5863, -5713, 2474, -1742, -1091, 721, -2055, 380, -820, -1006, 245, 45 | -1319, -76, -497, -877, 80, -916, -229, -303, -720, 13, -625, 46 | -262, -167, -553, -7, -401, -237, -71, -394, -9, -232, -187, 47 | -8, -257, -4, -113, -131, 24, -150, 0, -38, -82, 37, 48 | -75, 4, 0, -45, 36, -30, 5, 16, -22, 29, -7, 49 | 4, 19, -9, 21, 1, 3, 19}}, 50 | {22050, 51 | {-15, 1, -15, 7, -13, -7, 2, -26, 5, -19, -21, 52 | 4, -47, 1, -29, -50, 5, -82, -17, -41, -100, 4, 53 | -128, -54, -49, -175, 1, -181, -117, -49, -277, -14, -232, 54 | -215, -35, -400, -43, -265, -354, 0, -538, -95, -261, -543, 55 | 70, -678, -182, -190, -799, 189, -807, -329, 2, -1174, 404, 56 | -911, -615, 499, -1894, 920, -979, -1518, 2622, -5551, 6084, 25202, 57 | 6084, -5551, 2622, -1518, -979, 920, -1894, 499, -615, -911, 404, 58 | -1174, 2, -329, -807, 189, -799, -190, -182, -678, 70, -543, 59 | -261, -95, -538, 0, -354, -265, -43, -400, -35, -215, -232, 60 | -14, -277, -49, -117, -181, 1, -175, -49, -54, -128, 4, 61 | -100, -41, -17, -82, 5, -50, -29, 1, -47, 4, -21, 62 | -19, 5, -26, 2, -7, -13, 7}}, 63 | {24000, 64 | {-19, 2, -19, 3, -12, -14, 3, -29, 0, -16, -31, 65 | 3, -49, -10, -23, -62, 3, -80, -33, -30, -112, 0, 66 | -119, -75, -34, -185, -7, -163, -142, -30, -279, -27, -200, 67 | -240, -12, -390, -61, -220, -376, 24, -511, -117, -203, -556, 68 | 91, -632, -206, -123, -799, 206, -742, -353, 75, -1158, 414, 69 | -830, -636, 570, -1860, 921, -886, -1534, 2684, -5499, 6075, 25293, 70 | 6075, -5499, 2684, -1534, -886, 921, -1860, 570, -636, -830, 414, 71 | -1158, 75, -353, -742, 206, -799, -123, -206, -632, 91, -556, 72 | -203, -117, -511, 24, -376, -220, -61, -390, -12, -240, -200, 73 | -27, -279, -30, -142, -163, -7, -185, -34, -75, -119, 0, 74 | -112, -30, -33, -80, 3, -62, -23, -10, -49, 3, -31, 75 | -16, 0, -29, 3, -14, -12, 3}}, 76 | {32000, 77 | {-25, -1, -25, -1, -17, -20, -1, -34, -5, -21, -36, 78 | 0, -54, -15, -28, -66, -1, -83, -36, -33, -114, -1, 79 | -119, -73, -31, -180, -1, -154, -131, -17, -264, -9, -180, 80 | -217, 12, -361, -28, -184, -337, 67, -465, -67, -150, -499, 81 | 152, -568, -138, -52, -725, 283, -661, -269, 162, -1068, 506, 82 | -735, -539, 669, -1758, 1023, -782, -1428, 2790, -5391, 6180, 25393, 83 | 6180, -5391, 2790, -1428, -782, 1023, -1758, 669, -539, -735, 506, 84 | -1068, 162, -269, -661, 283, -725, -52, -138, -568, 152, -499, 85 | -150, -67, -465, 67, -337, -184, -28, -361, 12, -217, -180, 86 | -9, -264, -17, -131, -154, -1, -180, -31, -73, -119, -1, 87 | -114, -33, -36, -83, -1, -66, -28, -15, -54, 0, -36, 88 | -21, -5, -34, -1, -20, -17, -1}}, 89 | {44100, 90 | {0, -23, 0, -14, -24, 1, -19, -29, -1, -26, -36, 0, 91 | -37, -46, 2, -52, -57, 5, -73, -70, 8, -99, -82, 12, 92 | -131, -94, 17, -170, -103, 24, -216, -108, 30, -270, -108, 38, 93 | -333, -99, 46, -406, -80, 54, -491, -46, 63, -591, 7, 70, 94 | -713, 91, 78, -870, 223, 84, -1088, 438, 89, -1436, 838, 93, 95 | -2151, 1825, 96, -4896, 8627, 21950, 8627, -4896, 96, 1825, -2151, 93, 96 | 838, -1436, 89, 438, -1088, 84, 223, -870, 78, 91, -713, 70, 97 | 7, -591, 63, -46, -491, 54, -80, -406, 46, -99, -333, 38, 98 | -108, -270, 30, -108, -216, 24, -103, -170, 17, -94, -131, 12, 99 | -82, -99, 8, -70, -73, 5, -57, -52, 2, -46, -37, 0, 100 | -36, -26, -1, -29, -19, 1, -24, -14}}, 101 | {48000, 102 | {-19, -20, -19, -1, -16, -26, -1, -12, -36, -7, -7, 103 | -48, -20, 1, -57, -43, 5, -60, -74, 7, -52, -113, 104 | -2, -32, -152, -31, 0, -181, -84, 33, -188, -162, 60, 105 | -163, -256, 62, -100, -352, 22, -2, -429, -72, 118, -460, 106 | -230, 240, -416, -447, 329, -273, -710, 344, -3, -992, 229, 107 | 421, -1262, -103, 1089, -1486, -901, 2372, -1634, -3821, 9465, 20180, 108 | 9465, -3821, -1634, 2372, -901, -1486, 1089, -103, -1262, 421, 229, 109 | -992, -3, 344, -710, -273, 329, -447, -416, 240, -230, -460, 110 | 118, -72, -429, -2, 22, -352, -100, 62, -256, -163, 60, 111 | -162, -188, 33, -84, -181, 0, -31, -152, -32, -2, -113, 112 | -52, 7, -74, -60, 5, -43, -57, 1, -20, -48, -7, 113 | -7, -36, -12, -1, -26, -16, -1}}, 114 | {96000, 115 | {-20, -11, -20, -18, -6, 4, 4, -7, -25, -31, -19, 1, 116 | 13, 1, -32, -56, -48, -10, 23, 19, -29, -86, -98, -47, 117 | 26, 54, 0, -103, -165, -120, 1, 95, 64, -84, -231, -235, 118 | -74, 120, 168, -1, -263, -384, -230, 92, 301, 177, -215, -549, 119 | -497, -46, 439, 501, -1, -699, -958, -432, 554, 1154, 658, -805, 120 | -2135, -1898, 620, 4726, 8542, 10093, 8542, 4726, 620, -1898, -2135, -805, 121 | 658, 1154, 554, -432, -958, -699, -1, 501, 439, -46, -497, -549, 122 | -215, 177, 301, 92, -230, -384, -263, -1, 168, 120, -74, -235, 123 | -231, -84, 64, 95, 1, -120, -165, -103, 0, 54, 26, -47, 124 | -98, -86, -29, 19, 23, -10, -48, -56, -32, 1, 13, 1, 125 | -19, -31, -25, -7, 4, 4, -6, -18}}, 126 | {0, {0}}}; 127 | -------------------------------------------------------------------------------- /src/matrixio-codec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrix-codec.c -- MATRIX microphone array audio driver 3 | * 4 | * Copyright 2017-2020 MATRIX Labs 5 | * 6 | * Author: Andres Calderon 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the 10 | * Free Software Foundation; either version 2 of the License, or (at your 11 | * option) any later version. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "matrixio-core.h" 29 | #include "matrixio-pcm.h" 30 | 31 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(5,3,0) 32 | static struct snd_soc_dai_link matrixio_snd_soc_dai[] = { 33 | { 34 | .name = "matrixio.mic.0", 35 | .stream_name = "matrixio.mic.0", 36 | .codec_dai_name = "snd-soc-dummy-dai", 37 | .cpu_dai_name = "matrixio-mic.0", 38 | .platform_name = "matrixio-mic", 39 | .codec_name = "snd-soc-dummy", 40 | }, 41 | { 42 | .name = "matrixio.pcm-out.0", 43 | .stream_name = "matrixio.pcm-out.0", 44 | .codec_dai_name = "snd-soc-dummy-dai", 45 | .cpu_dai_name = "matrixio-pcm-out.0", 46 | .platform_name = "matrixio-playback", 47 | .codec_name = "snd-soc-dummy", 48 | }}; 49 | #else 50 | SND_SOC_DAILINK_DEFS(matrixio_mic, 51 | DAILINK_COMP_ARRAY(COMP_CPU("matrixio-mic.0")), 52 | DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), 53 | DAILINK_COMP_ARRAY(COMP_PLATFORM("matrixio-mic"))); 54 | 55 | SND_SOC_DAILINK_DEFS(matrixio_playback, 56 | DAILINK_COMP_ARRAY(COMP_CPU("matrixio-pcm-out.0")), 57 | DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), 58 | DAILINK_COMP_ARRAY(COMP_PLATFORM("matrixio-playback"))); 59 | 60 | static struct snd_soc_dai_link matrixio_snd_soc_dai[] = { 61 | { 62 | .name = "matrixio.mic.0", 63 | .stream_name = "matrixio.mic.0", 64 | SND_SOC_DAILINK_REG(matrixio_mic), 65 | }, 66 | { 67 | .name = "matrixio.pcm-out.0", 68 | .stream_name = "matrixio.pcm-out.0", 69 | SND_SOC_DAILINK_REG(matrixio_playback), 70 | }}; 71 | #endif 72 | 73 | static struct snd_soc_card matrixio_soc_card = { 74 | .name = "MATRIXIO-SOUND", 75 | .owner = THIS_MODULE, 76 | .dai_link = matrixio_snd_soc_dai, 77 | .num_links = ARRAY_SIZE(matrixio_snd_soc_dai), 78 | .fully_routed = true, 79 | }; 80 | 81 | static const struct snd_kcontrol_new matrixio_snd_controls[] = {}; 82 | 83 | static const struct snd_soc_dapm_widget matrixio_dapm_widgets[] = {}; 84 | 85 | static const struct snd_soc_dapm_route matrixio_dapm_routes[] = {}; 86 | 87 | static int matrixio_codec_probe(struct snd_soc_component *codec) { return 0; } 88 | 89 | static const struct snd_soc_component_driver matrixio_soc_codec_driver = { 90 | 91 | .probe = matrixio_codec_probe, 92 | .controls = matrixio_snd_controls, 93 | .num_controls = ARRAY_SIZE(matrixio_snd_controls), 94 | .dapm_widgets = matrixio_dapm_widgets, 95 | .num_dapm_widgets = ARRAY_SIZE(matrixio_dapm_widgets), 96 | .dapm_routes = matrixio_dapm_routes, 97 | .num_dapm_routes = ARRAY_SIZE(matrixio_dapm_routes), 98 | }; 99 | 100 | static struct snd_soc_dai_driver matrixio_dai_driver[] = { 101 | { 102 | .name = "matrixio-pcm-out.0", 103 | .playback = 104 | { 105 | .stream_name = "matrixio-pcm-out.0", 106 | .channels_min = 2, 107 | .channels_max = 2, 108 | .rates = MATRIXIO_RATES, 109 | .rate_min = 8000, 110 | .rate_max = 48000, 111 | .formats = MATRIXIO_FORMATS, 112 | }, 113 | }, 114 | { 115 | .name = "matrixio-mic.0", 116 | .capture = 117 | { 118 | .stream_name = "matrixio-mic.0", 119 | .channels_min = 1, 120 | .channels_max = MATRIXIO_CHANNELS_MAX, 121 | .rates = MATRIXIO_RATES, 122 | .rate_min = 8000, 123 | .rate_max = 96000, 124 | .formats = MATRIXIO_FORMATS, 125 | }, 126 | }}; 127 | 128 | static int matrixio_probe(struct platform_device *pdev) 129 | { 130 | struct snd_soc_card *card = &matrixio_soc_card; 131 | int ret; 132 | 133 | card->dev = &pdev->dev; 134 | 135 | ret = devm_snd_soc_register_component( 136 | &pdev->dev, &matrixio_soc_codec_driver, matrixio_dai_driver, 137 | ARRAY_SIZE(matrixio_dai_driver)); 138 | if (ret) { 139 | dev_err(&pdev->dev, "Failed to register MATRIXIO codec: %d\n", 140 | ret); 141 | return ret; 142 | } 143 | 144 | ret = devm_snd_soc_register_card(&pdev->dev, card); 145 | if (ret) { 146 | dev_err(&pdev->dev, "Failed to register MATRIXIO card (%d)\n", 147 | ret); 148 | return ret; 149 | } 150 | 151 | return ret; 152 | } 153 | 154 | static int matrixio_codec_remove(struct platform_device *pdev) { return 0; } 155 | 156 | static const struct of_device_id snd_matrixio_codec_of_match[] = { 157 | { 158 | .compatible = "matrixio-codec", 159 | }, 160 | {}, 161 | }; 162 | MODULE_DEVICE_TABLE(of, snd_matrixio_codec_of_match); 163 | 164 | static struct platform_driver matrixio_codec_driver = { 165 | .driver = 166 | { 167 | .name = "matrixio-codec", 168 | .owner = THIS_MODULE, 169 | .of_match_table = of_match_ptr(snd_matrixio_codec_of_match), 170 | .pm = &snd_soc_pm_ops, 171 | }, 172 | .probe = matrixio_probe, 173 | .remove = matrixio_codec_remove, 174 | }; 175 | 176 | module_platform_driver(matrixio_codec_driver); 177 | 178 | MODULE_SOFTDEP("pre: matrixio-mic"); 179 | MODULE_SOFTDEP("pre: matrixio-playback"); 180 | 181 | MODULE_LICENSE("GPL"); 182 | MODULE_AUTHOR("Andres Calderon "); 183 | MODULE_DESCRIPTION("MATRIXIO audio module"); 184 | -------------------------------------------------------------------------------- /src/matrixio-core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrix-core.c -- MATRIX core functions to talk with the FPGA internal 3 | * bus 4 | * 5 | * Copyright 2017 MATRIX Labs 6 | * 7 | * Author: Andres Calderon 8 | * 9 | * This program is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the 11 | * Free Software Foundation; either version 2 of the License, or (at your 12 | * option) any later version. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "matrixio-core.h" 24 | 25 | 26 | /* We need to send the address before reading/writing the data after it. This 27 | * can be done with two transfers in one message: one for the address followed 28 | * by one for the data. Or with one transfer: with the address and data copied 29 | * in/out of a single buffer. One transfer is faster for small messages, while 30 | * two transfers are faster for large messages. The two transfer method also 31 | * avoids needing to impose a max size limit on the data as it goes directly 32 | * to/from a user supplied buffer. 33 | * 34 | * This value is the threshold where the driver switches from one xfer to the 35 | * two xfer method. It also provides the largest bounce buffer size needed. 36 | */ 37 | #define MATRIXIO_SPI_BOUNCE_SIZE 2048 38 | 39 | struct hardware_cmd { 40 | uint8_t readnwrite : 1; 41 | uint16_t reg : 15; 42 | }; 43 | 44 | /* For a large read, does not use rx_buffer to bounce the data */ 45 | static int matrixio_large_read(struct matrixio *matrixio, unsigned int add, int length, 46 | void *data) 47 | { 48 | /* Don't use stack hw_cmd as it must be dma-safe */ 49 | struct hardware_cmd* hw_cmd = (struct hardware_cmd*)matrixio->tx_buffer; 50 | struct spi_transfer t[] = { 51 | { 52 | .tx_buf = hw_cmd, 53 | .len = sizeof(*hw_cmd), 54 | }, 55 | { 56 | .rx_buf = data, 57 | .len = length, 58 | } 59 | }; 60 | struct spi_message m; 61 | 62 | hw_cmd->reg = add; 63 | hw_cmd->readnwrite = 1; 64 | spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); 65 | 66 | return spi_sync(matrixio->spi, &m); 67 | } 68 | 69 | /* For small reads, bounces the data through tx/rx buffer */ 70 | static int matrixio_small_read(struct matrixio *matrixio, unsigned int add, int length, 71 | void *data) 72 | { 73 | struct hardware_cmd* hw_cmd = (struct hardware_cmd*)matrixio->tx_buffer; 74 | struct spi_transfer t[] = { 75 | { 76 | .tx_buf = matrixio->tx_buffer, 77 | .rx_buf = matrixio->rx_buffer, 78 | .len = length + sizeof(*hw_cmd), 79 | } 80 | }; 81 | struct spi_message m; 82 | int ret; 83 | 84 | hw_cmd->reg = add; 85 | hw_cmd->readnwrite = 1; 86 | memset(matrixio->tx_buffer + sizeof(*hw_cmd), 0, length); 87 | 88 | spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); 89 | ret = spi_sync(matrixio->spi, &m); 90 | 91 | memcpy(data, matrixio->rx_buffer + sizeof(*hw_cmd), length); 92 | 93 | return ret; 94 | } 95 | 96 | int matrixio_read(struct matrixio *matrixio, unsigned int add, int length, 97 | void *data) 98 | { 99 | int ret; 100 | 101 | mutex_lock(&matrixio->reg_lock); 102 | if (length > MATRIXIO_SPI_BOUNCE_SIZE - sizeof(struct hardware_cmd)) { 103 | ret = matrixio_large_read(matrixio, add, length, data); 104 | } else { 105 | ret = matrixio_small_read(matrixio, add, length, data); 106 | } 107 | mutex_unlock(&matrixio->reg_lock); 108 | 109 | return ret; 110 | } 111 | EXPORT_SYMBOL(matrixio_read); 112 | 113 | int matrixio_write(struct matrixio *matrixio, unsigned int add, int length, 114 | void *data) 115 | { 116 | int ret; 117 | struct hardware_cmd *hw_cmd = (struct hardware_cmd*)matrixio->tx_buffer; 118 | struct spi_transfer xfers[2] = { 119 | { 120 | .tx_buf = matrixio->tx_buffer, 121 | }, 122 | }; 123 | struct spi_message m; 124 | 125 | mutex_lock(&matrixio->reg_lock); 126 | 127 | hw_cmd->reg = add; 128 | hw_cmd->readnwrite = 0; 129 | if (length > MATRIXIO_SPI_BOUNCE_SIZE - sizeof(*hw_cmd)) { 130 | xfers[0].len = sizeof(*hw_cmd); 131 | xfers[1].tx_buf = data; 132 | xfers[1].len = length; 133 | spi_message_init_with_transfers(&m, xfers, 2); 134 | } else { 135 | xfers[0].len = length + sizeof(*hw_cmd); 136 | memcpy(matrixio->tx_buffer + sizeof(*hw_cmd), data, length); 137 | spi_message_init_with_transfers(&m, xfers, 1); 138 | } 139 | 140 | ret = spi_sync(matrixio->spi, &m); 141 | mutex_unlock(&matrixio->reg_lock); 142 | 143 | return ret; 144 | } 145 | EXPORT_SYMBOL(matrixio_write); 146 | 147 | static int matrixio_reg_read(void *context, unsigned int reg, unsigned int *val) 148 | { 149 | return matrixio_read((struct matrixio *)(context), reg, sizeof(int16_t), 150 | &val); 151 | } 152 | 153 | int matrixio_reg_write(void *context, unsigned int reg, unsigned int val) 154 | { 155 | int16_t data = val; 156 | return matrixio_write((struct matrixio *)(context), reg, 157 | sizeof(int16_t), &data); 158 | } 159 | EXPORT_SYMBOL(matrixio_reg_write); 160 | 161 | static int matrixio_register_devices(struct matrixio *matrixio) 162 | { 163 | const struct mfd_cell cells[] = { 164 | { 165 | .name = "matrixio-everloop", 166 | .of_compatible = "matrixio-everloop", 167 | .platform_data = matrixio, 168 | .pdata_size = sizeof(*matrixio), 169 | }, 170 | { 171 | .name = "matrixio-mic", 172 | .of_compatible = "matrixio-mic", 173 | .platform_data = matrixio, 174 | .pdata_size = sizeof(*matrixio), 175 | }, 176 | { 177 | .name = "matrixio-playback", 178 | .of_compatible = "matrixio-playback", 179 | .platform_data = matrixio, 180 | .pdata_size = sizeof(*matrixio), 181 | }, 182 | { 183 | .name = "matrixio-codec", 184 | .of_compatible = "matrixio-codec", 185 | .platform_data = matrixio, 186 | .pdata_size = sizeof(*matrixio), 187 | }, 188 | { 189 | .name = "matrixio-uart", 190 | .of_compatible = "matrixio-uart", 191 | .platform_data = matrixio, 192 | .pdata_size = sizeof(*matrixio), 193 | }, 194 | { 195 | .name = "matrixio-gpio", 196 | .of_compatible = "matrixio-gpio", 197 | .platform_data = matrixio, 198 | .pdata_size = sizeof(*matrixio), 199 | }, 200 | { 201 | .name = "matrixio-env", 202 | .of_compatible = "matrixio-env", 203 | .platform_data = matrixio, 204 | .pdata_size = sizeof(*matrixio), 205 | }, 206 | { 207 | .name = "matrixio-imu", 208 | .of_compatible = "matrixio-imu", 209 | .platform_data = matrixio, 210 | .pdata_size = sizeof(*matrixio), 211 | }, 212 | { 213 | .name = "matrixio-regmap", 214 | .of_compatible = "matrixio-regmap", 215 | .platform_data = matrixio, 216 | .pdata_size = sizeof(*matrixio), 217 | }}; 218 | 219 | return devm_mfd_add_devices(matrixio->dev, -1, cells, ARRAY_SIZE(cells), 220 | NULL, 0, NULL); 221 | } 222 | 223 | static int matrixio_init(struct matrixio *matrixio, 224 | struct matrixio_platform_data *pdata) 225 | { 226 | int ret; 227 | 228 | dev_set_drvdata(matrixio->dev, matrixio); 229 | 230 | /* TODO: Check that this is actually a MATRIX FPGA */ 231 | ret = matrixio_register_devices(matrixio); 232 | 233 | if (ret) { 234 | dev_err(matrixio->dev, "Failed to register MATRIX FPGA \n"); 235 | return ret; 236 | } 237 | 238 | return 0; 239 | } 240 | 241 | static const struct regmap_config matrixio_regmap_config = { 242 | .reg_bits = 16, 243 | .val_bits = 16, 244 | .reg_read = matrixio_reg_read, 245 | .reg_write = matrixio_reg_write, 246 | }; 247 | 248 | static int matrixio_core_probe(struct spi_device *spi) 249 | { 250 | int ret; 251 | struct matrixio *matrixio; 252 | 253 | spi->mode = SPI_MODE_3; 254 | spi->bits_per_word = 8; 255 | ret = spi_setup(spi); 256 | 257 | if (ret) 258 | return ret; 259 | 260 | matrixio = devm_kzalloc(&spi->dev, sizeof(struct matrixio), GFP_KERNEL); 261 | 262 | if (matrixio == NULL) 263 | return -ENOMEM; 264 | 265 | matrixio->dev = &spi->dev; 266 | 267 | matrixio->spi = spi; 268 | 269 | mutex_init(&matrixio->reg_lock); 270 | 271 | matrixio->rx_buffer = devm_kzalloc(&spi->dev, MATRIXIO_SPI_BOUNCE_SIZE, GFP_KERNEL); 272 | if (matrixio->rx_buffer == NULL) 273 | return -ENOMEM; 274 | 275 | matrixio->tx_buffer = devm_kzalloc(&spi->dev, MATRIXIO_SPI_BOUNCE_SIZE, GFP_KERNEL); 276 | if (matrixio->tx_buffer == NULL) 277 | return -ENOMEM; 278 | 279 | spi_set_drvdata(spi, matrixio); 280 | 281 | matrixio->regmap = devm_regmap_init(&spi->dev, NULL, matrixio, 282 | &matrixio_regmap_config); 283 | 284 | if (IS_ERR(matrixio->regmap)) { 285 | ret = PTR_ERR(matrixio->regmap); 286 | dev_err(matrixio->dev, "Failed to allocate register map: %d\n", 287 | ret); 288 | return ret; 289 | } 290 | 291 | return matrixio_init(matrixio, dev_get_platdata(&spi->dev)); 292 | } 293 | 294 | static const struct of_device_id matrixio_core_dt_ids[] = { 295 | {.compatible = "matrixio-core", .data = (void *)0}, {}}; 296 | 297 | MODULE_DEVICE_TABLE(of, matrixio_core_dt_ids); 298 | 299 | static struct spi_driver matrixio_core_driver = { 300 | .driver = 301 | { 302 | .name = "matrixio-core", 303 | .of_match_table = of_match_ptr(matrixio_core_dt_ids), 304 | }, 305 | .probe = matrixio_core_probe}; 306 | 307 | module_spi_driver(matrixio_core_driver); 308 | 309 | MODULE_LICENSE("GPL"); 310 | MODULE_AUTHOR("Andres Calderon "); 311 | MODULE_DESCRIPTION("MATRIXIO core module"); 312 | -------------------------------------------------------------------------------- /src/matrixio-core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * matrix-core.h -- MATRIX core functions to talk with the FPGA internal 3 | * bus 4 | * 5 | * Copyright 2017 MATRIX Labs 6 | * 7 | * Author: Andres Calderon 8 | * 9 | * This program is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the 11 | * Free Software Foundation; either version 2 of the License, or (at your 12 | * option) any later version. 13 | */ 14 | 15 | #ifndef __MATRIXIO_CORE_H__ 16 | #define __MATRIXIO_CORE_H__ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define MATRIXIO_CONF_BASE 0x0000 24 | #define MATRIXIO_UART_BASE 0x1000 25 | #define MATRIXIO_MICARRAY_BASE 0x2000 26 | #define MATRIXIO_EVERLOOP_BASE 0x3000 27 | #define MATRIXIO_GPIO_BASE 0x4000 28 | #define MATRIXIO_MCU_BASE 0x5000 29 | #define MATRIXIO_PLAYBACK_BASE 0x6000 30 | 31 | struct matrixio { 32 | struct device *dev; 33 | struct regmap *regmap; 34 | struct mutex reg_lock; 35 | struct spi_device *spi; 36 | u8 *tx_buffer; 37 | u8 *rx_buffer; 38 | }; 39 | 40 | struct matrixio_platform_data { 41 | int (*platform_init)(struct device *dev); 42 | }; 43 | 44 | /* This is called directly in some places. They should use regmap */ 45 | int matrixio_reg_write(void *context, unsigned int reg, unsigned int val); 46 | 47 | int matrixio_read(struct matrixio *matrixio, unsigned int add, int length, 48 | void *data); 49 | 50 | int matrixio_write(struct matrixio *matrixio, unsigned int add, int length, 51 | void *data); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/matrixio-env.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrixio_env.c - Support for Vishay VEML6070 UV A light sensor 3 | * 4 | * Copyright 2017 Andres Calderon 5 | * 6 | * This file is subject to the terms and conditions of version 2 of 7 | * the GNU General Public License. See the file COPYING in the main 8 | * directory of this archive for more details. 9 | * 10 | * IIO driver for (MATRIX CREATOR Enviromental Sensors) 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "matrixio-core.h" 22 | 23 | #define MATRIXIO_UV_DRV_NAME "matrixio_env" 24 | 25 | #define MATRIXIO_SRAM_OFFSET_ENV 0x0 26 | 27 | struct matrixio_bus { 28 | struct matrixio *mio; 29 | struct mutex lock; 30 | }; 31 | 32 | struct matrixio_env_data { 33 | int UV; 34 | int altitude; 35 | int pressure; 36 | int temperature_mpl; 37 | int humidity; 38 | int temperature_hts; 39 | }; 40 | 41 | static const struct iio_chan_spec matrixio_env_channels[] = { 42 | { 43 | .type = IIO_INTENSITY, 44 | .modified = 1, 45 | .channel2 = IIO_MOD_LIGHT_UV, 46 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 47 | }, 48 | { 49 | .type = IIO_UVINDEX, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 50 | }, 51 | { 52 | .type = IIO_TEMP, 53 | .modified = 1, 54 | .channel2 = IIO_MOD_TEMP_OBJECT, 55 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 56 | }, 57 | { 58 | .type = IIO_PRESSURE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 59 | }, 60 | { 61 | .type = IIO_HUMIDITYRELATIVE, 62 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 63 | }, 64 | { 65 | .type = IIO_DISTANCE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 66 | }}; 67 | 68 | static int matrixio_env_to_uv_index(unsigned val) 69 | { 70 | /* 71 | * conversion of raw UV intensity values to UV index depends on 72 | * integration time (IT) and value of the resistor connected to 73 | * the RSET pin (Table for RSET:270 KOhm IT: 1 74 | */ 75 | unsigned uvi[11] = {187, 373, 560, /* low */ 76 | 746, 933, 1120, /* moderate */ 77 | 1308, 1494, /* high */ 78 | 1681, 1868, 2054}; /* very high */ 79 | int i; 80 | 81 | for (i = 0; i < ARRAY_SIZE(uvi); i++) 82 | if (val <= uvi[i] * 4) /* 4T */ 83 | return i; 84 | 85 | return 11; /* extreme */ 86 | } 87 | 88 | static void matrixio_to_int_plus_micro(int data, int *val, int *val2) 89 | { 90 | *val = data / 1000; 91 | *val2 = (data % 1000) * 1000; 92 | } 93 | 94 | static int matrixio_env_read_raw(struct iio_dev *indio_dev, 95 | struct iio_chan_spec const *chan, int *val, 96 | int *val2, long mask) 97 | { 98 | struct matrixio_bus *data = iio_priv(indio_dev); 99 | int ret; 100 | struct matrixio_env_data env_data; 101 | 102 | mutex_lock(&indio_dev->mlock); 103 | ret = matrixio_read(data->mio, 104 | MATRIXIO_MCU_BASE + (MATRIXIO_SRAM_OFFSET_ENV >> 1), 105 | sizeof(env_data), &env_data); 106 | mutex_unlock(&indio_dev->mlock); 107 | 108 | switch (mask) { 109 | case IIO_CHAN_INFO_RAW: 110 | switch (chan->type) { 111 | case IIO_INTENSITY: 112 | *val = env_data.UV; 113 | return IIO_VAL_INT; 114 | case IIO_TEMP: 115 | matrixio_to_int_plus_micro(env_data.temperature_hts, 116 | val, val2); 117 | return IIO_VAL_INT_PLUS_MICRO; 118 | case IIO_PRESSURE: 119 | matrixio_to_int_plus_micro(env_data.pressure, val, 120 | val2); 121 | return IIO_VAL_INT_PLUS_MICRO; 122 | case IIO_HUMIDITYRELATIVE: 123 | matrixio_to_int_plus_micro(env_data.humidity, val, 124 | val2); 125 | return IIO_VAL_INT_PLUS_MICRO; 126 | case IIO_DISTANCE: 127 | matrixio_to_int_plus_micro(env_data.altitude, val, 128 | val2); 129 | return IIO_VAL_INT_PLUS_MICRO; 130 | default: 131 | return -EINVAL; 132 | } 133 | case IIO_CHAN_INFO_PROCESSED: 134 | *val = matrixio_env_to_uv_index(env_data.UV); 135 | return IIO_VAL_INT; 136 | default: 137 | return -EINVAL; 138 | } 139 | } 140 | 141 | static const struct iio_info matrixio_env_info = { 142 | .read_raw = matrixio_env_read_raw, 143 | // .driver_module = THIS_MODULE, 144 | }; 145 | 146 | static int matrixio_env_probe(struct platform_device *pdev) 147 | { 148 | struct matrixio_bus *data; 149 | struct iio_dev *indio_dev; 150 | 151 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); 152 | if (!indio_dev) 153 | return -ENOMEM; 154 | 155 | data = iio_priv(indio_dev); 156 | 157 | data->mio = dev_get_drvdata(pdev->dev.parent); 158 | 159 | platform_set_drvdata(pdev, indio_dev); 160 | 161 | mutex_init(&data->lock); 162 | 163 | indio_dev->dev.parent = &pdev->dev; 164 | indio_dev->info = &matrixio_env_info; 165 | indio_dev->channels = matrixio_env_channels; 166 | indio_dev->num_channels = ARRAY_SIZE(matrixio_env_channels); 167 | indio_dev->name = MATRIXIO_UV_DRV_NAME; 168 | indio_dev->modes = INDIO_DIRECT_MODE; 169 | 170 | data->mio = dev_get_drvdata(pdev->dev.parent); 171 | 172 | return iio_device_register(indio_dev); 173 | } 174 | 175 | static int matrixio_env_remove(struct platform_device *pdev) 176 | { 177 | struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); 178 | 179 | iio_device_unregister(indio_dev); 180 | 181 | return 0; 182 | } 183 | 184 | static struct platform_driver matrixio_env_driver = { 185 | .driver = 186 | { 187 | .name = "matrixio-env", 188 | }, 189 | .probe = matrixio_env_probe, 190 | .remove = matrixio_env_remove, 191 | }; 192 | 193 | module_platform_driver(matrixio_env_driver); 194 | 195 | MODULE_LICENSE("GPL"); 196 | MODULE_AUTHOR("Andres Calderon "); 197 | MODULE_DESCRIPTION("MATRIXIO IIO ENV module"); 198 | -------------------------------------------------------------------------------- /src/matrixio-everloop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "matrixio-core.h" 9 | 10 | struct everloop_data { 11 | struct matrixio *mio; 12 | struct class *cl; 13 | dev_t devt; 14 | struct cdev cdev; 15 | struct device *device; 16 | int major; 17 | }; 18 | 19 | ssize_t matrixio_everloop_write(struct file *pfile, const char __user *buffer, 20 | size_t length, loff_t *offset) 21 | { 22 | struct everloop_data *el = pfile->private_data; 23 | 24 | matrixio_write(el->mio, MATRIXIO_EVERLOOP_BASE, length, (void *)buffer); 25 | 26 | return length; 27 | } 28 | 29 | int matrixio_everloop_open(struct inode *inode, struct file *filp) 30 | { 31 | 32 | struct everloop_data *el; 33 | el = container_of(inode->i_cdev, struct everloop_data, cdev); 34 | 35 | filp->private_data = el; /* For use elsewhere */ 36 | 37 | return 0; 38 | } 39 | 40 | struct file_operations matrixio_everloop_file_operations = { 41 | .owner = THIS_MODULE, 42 | .open = matrixio_everloop_open, 43 | .write = matrixio_everloop_write}; 44 | 45 | static int matrixio_everloop_uevent(struct device *d, 46 | struct kobj_uevent_env *env) 47 | { 48 | add_uevent_var(env, "DEVMODE=%#o", 0666); 49 | return 0; 50 | } 51 | 52 | static int matrixio_everloop_probe(struct platform_device *pdev) 53 | { 54 | struct everloop_data *el; 55 | 56 | el = devm_kzalloc(&pdev->dev, sizeof(struct everloop_data), GFP_KERNEL); 57 | 58 | if (el == NULL) 59 | return -ENOMEM; 60 | 61 | dev_set_drvdata(&pdev->dev, el); 62 | 63 | el->mio = dev_get_drvdata(pdev->dev.parent); 64 | 65 | alloc_chrdev_region(&el->devt, 0, 1, "matrixio_everloop"); 66 | el->cl = class_create(THIS_MODULE, "matrixio_everloop"); 67 | 68 | el->cl->dev_uevent = matrixio_everloop_uevent; 69 | 70 | el->device = 71 | device_create(el->cl, NULL, el->devt, NULL, "matrixio_everloop"); 72 | 73 | if (IS_ERR(el->device)) { 74 | dev_err(&pdev->dev, "Unable to create device " 75 | "for matrix; errno = %ld\n", 76 | PTR_ERR(el->device)); 77 | } 78 | cdev_init(&el->cdev, &matrixio_everloop_file_operations); 79 | cdev_add(&el->cdev, el->devt, 1); 80 | 81 | return 0; 82 | } 83 | 84 | static int matrixio_everloop_remove(struct platform_device *pdev) 85 | { 86 | struct everloop_data *el = dev_get_drvdata(&pdev->dev); 87 | 88 | unregister_chrdev(el->major, "matrixio_everloop"); 89 | 90 | return 0; 91 | } 92 | 93 | static struct platform_driver matrixio_everloop_driver = { 94 | .driver = 95 | { 96 | .name = "matrixio-everloop", 97 | }, 98 | .probe = matrixio_everloop_probe, 99 | .remove = matrixio_everloop_remove, 100 | }; 101 | 102 | module_platform_driver(matrixio_everloop_driver); 103 | 104 | MODULE_LICENSE("GPL"); 105 | MODULE_AUTHOR("Andres Calderon "); 106 | MODULE_DESCRIPTION("MATRIXIO Everloop module"); 107 | -------------------------------------------------------------------------------- /src/matrixio-gpio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "matrixio-core.h" 9 | 10 | struct matrixio_gpio { 11 | struct gpio_chip chip; 12 | struct matrixio *mio; 13 | struct mutex lock; 14 | }; 15 | 16 | static int matrixio_gpio_get_direction(struct gpio_chip *gc, unsigned offset) 17 | { 18 | struct matrixio_gpio *chip = gpiochip_get_data(gc); 19 | int gpio_direction; 20 | 21 | mutex_lock(&chip->lock); 22 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE, &gpio_direction); 23 | mutex_unlock(&chip->lock); 24 | 25 | return (gpio_direction & BIT(offset)); 26 | } 27 | 28 | static int matrixio_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 29 | { 30 | struct matrixio_gpio *chip = gpiochip_get_data(gc); 31 | int gpio_direction; 32 | 33 | mutex_lock(&chip->lock); 34 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE, &gpio_direction); 35 | 36 | gpio_direction &= ~BIT(offset); 37 | 38 | regmap_write(chip->mio->regmap, MATRIXIO_GPIO_BASE, gpio_direction); 39 | mutex_unlock(&chip->lock); 40 | 41 | return 0; 42 | } 43 | 44 | static int matrixio_gpio_direction_output(struct gpio_chip *gc, unsigned offset, 45 | int value) 46 | { 47 | struct matrixio_gpio *chip = gpiochip_get_data(gc); 48 | int gpio_direction; 49 | int gpio_value; 50 | 51 | mutex_lock(&chip->lock); 52 | 53 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE, &gpio_direction); 54 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE + 1, &gpio_value); 55 | 56 | if (value) 57 | gpio_value |= BIT(offset); 58 | else 59 | gpio_value &= ~BIT(offset); 60 | 61 | gpio_direction |= BIT(offset); 62 | 63 | regmap_write(chip->mio->regmap, MATRIXIO_GPIO_BASE, gpio_direction); 64 | regmap_write(chip->mio->regmap, MATRIXIO_GPIO_BASE + 1, gpio_value); 65 | mutex_unlock(&chip->lock); 66 | 67 | return 0; 68 | } 69 | 70 | static int matrixio_gpio_get(struct gpio_chip *gc, unsigned offset) 71 | { 72 | struct matrixio_gpio *chip = gpiochip_get_data(gc); 73 | int gpio_value; 74 | 75 | mutex_lock(&chip->lock); 76 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE + 1, &gpio_value); 77 | mutex_unlock(&chip->lock); 78 | 79 | return (gpio_value & BIT(offset)); 80 | } 81 | 82 | static void matrixio_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 83 | { 84 | struct matrixio_gpio *chip = gpiochip_get_data(gc); 85 | int gpio_value; 86 | 87 | mutex_lock(&chip->lock); 88 | regmap_read(chip->mio->regmap, MATRIXIO_GPIO_BASE + 1, &gpio_value); 89 | 90 | if (value) 91 | gpio_value |= BIT(offset); 92 | else 93 | gpio_value &= ~BIT(offset); 94 | 95 | regmap_write(chip->mio->regmap, MATRIXIO_GPIO_BASE + 1, gpio_value); 96 | mutex_unlock(&chip->lock); 97 | } 98 | 99 | static const struct gpio_chip matrixio_gpio_chip = { 100 | .label = "matrixio-gpio", 101 | .owner = THIS_MODULE, 102 | .get_direction = matrixio_gpio_get_direction, 103 | .direction_input = matrixio_gpio_direction_input, 104 | .direction_output = matrixio_gpio_direction_output, 105 | .get = matrixio_gpio_get, 106 | .set = matrixio_gpio_set, 107 | .base = -1, 108 | .ngpio = 16, 109 | .can_sleep = true, 110 | }; 111 | 112 | static int matrixio_gpio_probe(struct platform_device *pdev) 113 | { 114 | struct matrixio_gpio *gpio; 115 | int ret; 116 | 117 | gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); 118 | if (!gpio) 119 | return -ENOMEM; 120 | 121 | platform_set_drvdata(pdev, gpio); 122 | 123 | gpio->mio = dev_get_drvdata(pdev->dev.parent); 124 | gpio->chip = matrixio_gpio_chip; 125 | gpio->chip.parent = gpio->mio->dev; 126 | 127 | mutex_init(&gpio->lock); 128 | 129 | ret = gpiochip_add_data(&gpio->chip, gpio); 130 | 131 | if (ret) { 132 | dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); 133 | return ret; 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | static int matrixio_gpio_remove(struct platform_device *pdev) 140 | { 141 | 142 | struct matrixio_gpio *gpio; 143 | gpio = dev_get_drvdata(&pdev->dev); 144 | mutex_destroy(&gpio->lock); 145 | return 0; 146 | } 147 | 148 | static struct platform_driver matrixio_gpio_driver = { 149 | .driver = 150 | { 151 | .name = "matrixio-gpio", 152 | }, 153 | .probe = matrixio_gpio_probe, 154 | .remove = matrixio_gpio_remove, 155 | }; 156 | 157 | module_platform_driver(matrixio_gpio_driver); 158 | 159 | MODULE_LICENSE("GPL"); 160 | MODULE_AUTHOR("Andres Calderon "); 161 | MODULE_DESCRIPTION("MATRIXIO GPIO module"); 162 | -------------------------------------------------------------------------------- /src/matrixio-imu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrixio_imu.c - Support for Vishay VEML6070 UV A light sensor 3 | * 4 | * Copyright 2017 Andres Calderon 5 | * 6 | * This file is subject to the terms and conditions of version 2 of 7 | * the GNU General Public License. See the file COPYING in the main 8 | * directory of this archive for more details. 9 | * 10 | * IIO driver for VEML6070 (MATRIX CREATOR UV Sensor) 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "matrixio-core.h" 22 | 23 | #define MATRIXIO_UV_DRV_NAME "matrixio_imu" 24 | 25 | #define MATRIXIO_SRAM_OFFSET_IMU 0x30 26 | #define MATRIXIO_CALIB_OFFSET 6 27 | 28 | struct matrixio_bus { 29 | struct matrixio *mio; 30 | struct mutex lock; 31 | }; 32 | 33 | static const struct iio_chan_spec matrixio_imu_channels[] = { 34 | { 35 | .type = IIO_ACCEL, 36 | .modified = 1, 37 | .channel2 = IIO_MOD_X, 38 | .address = 0, 39 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 40 | }, 41 | { 42 | .type = IIO_ACCEL, 43 | .modified = 1, 44 | .channel2 = IIO_MOD_Y, 45 | .address = 2, 46 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 47 | }, 48 | { 49 | .type = IIO_ACCEL, 50 | .modified = 1, 51 | .channel2 = IIO_MOD_Z, 52 | .address = 4, 53 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 54 | }, 55 | { 56 | .type = IIO_ANGL_VEL, 57 | .modified = 1, 58 | .channel2 = IIO_MOD_X, 59 | .address = 6, 60 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 61 | }, 62 | { 63 | .type = IIO_ANGL_VEL, 64 | .modified = 1, 65 | .channel2 = IIO_MOD_Y, 66 | .address = 8, 67 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 68 | }, 69 | { 70 | .type = IIO_ANGL_VEL, 71 | .modified = 1, 72 | .channel2 = IIO_MOD_Z, 73 | .address = 10, 74 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 75 | }, 76 | { 77 | .type = IIO_MAGN, 78 | .modified = 1, 79 | .channel2 = IIO_MOD_X, 80 | .address = 12, 81 | .info_mask_separate = 82 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBBIAS), 83 | }, 84 | { 85 | .type = IIO_MAGN, 86 | .modified = 1, 87 | .channel2 = IIO_MOD_Y, 88 | .address = 14, 89 | .info_mask_separate = 90 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBBIAS), 91 | }, 92 | { 93 | .type = IIO_MAGN, 94 | .modified = 1, 95 | .channel2 = IIO_MOD_Z, 96 | .address = 16, 97 | .info_mask_separate = 98 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBBIAS), 99 | }}; 100 | 101 | static void matrixio_int_to_int_plus_micro(int data, int *val, int *val2) 102 | { 103 | *val = data / 1000; 104 | *val2 = (data % 1000) * 1000; 105 | } 106 | 107 | static int matrixio_int_plus_micro_to_int(int val, int val2) 108 | { 109 | return val * 1000 + (val2 / 1000); 110 | } 111 | 112 | static int matrixio_imu_write_raw(struct iio_dev *indio_dev, 113 | struct iio_chan_spec const *chan, int val, 114 | int val2, long mask) 115 | { 116 | struct matrixio_bus *data = iio_priv(indio_dev); 117 | int ret; 118 | int data_write; 119 | 120 | if (mask == IIO_CHAN_INFO_CALIBBIAS) { 121 | 122 | data_write = matrixio_int_plus_micro_to_int(val, val2); 123 | mutex_lock(&indio_dev->mlock); 124 | ret = matrixio_write(data->mio, 125 | MATRIXIO_MCU_BASE + 126 | (MATRIXIO_SRAM_OFFSET_IMU >> 1) + 127 | chan->address + MATRIXIO_CALIB_OFFSET, 128 | sizeof(data_write), &data_write); 129 | mutex_unlock(&indio_dev->mlock); 130 | return ret; 131 | } 132 | 133 | return -EINVAL; 134 | } 135 | 136 | static int matrixio_imu_read_raw(struct iio_dev *indio_dev, 137 | struct iio_chan_spec const *chan, int *val, 138 | int *val2, long mask) 139 | { 140 | struct matrixio_bus *data = iio_priv(indio_dev); 141 | int ret; 142 | int data_read; 143 | int offset; 144 | 145 | switch (mask) { 146 | case IIO_CHAN_INFO_CALIBBIAS: 147 | offset = chan->address + MATRIXIO_CALIB_OFFSET; 148 | break; 149 | case IIO_CHAN_INFO_RAW: 150 | offset = chan->address; 151 | break; 152 | default: 153 | return -EINVAL; 154 | } 155 | 156 | mutex_lock(&indio_dev->mlock); 157 | ret = matrixio_read(data->mio, 158 | MATRIXIO_MCU_BASE + 159 | (MATRIXIO_SRAM_OFFSET_IMU >> 1) + offset, 160 | sizeof(data_read), &data_read); 161 | 162 | mutex_unlock(&indio_dev->mlock); 163 | 164 | if (ret) 165 | return ret; 166 | 167 | matrixio_int_to_int_plus_micro(data_read, val, val2); 168 | 169 | return IIO_VAL_INT_PLUS_MICRO; 170 | } 171 | 172 | static const struct iio_info matrixio_imu_info = { 173 | .read_raw = matrixio_imu_read_raw, .write_raw = matrixio_imu_write_raw, 174 | // .driver_module = THIS_MODULE, 175 | }; 176 | 177 | static int matrixio_imu_probe(struct platform_device *pdev) 178 | { 179 | struct matrixio_bus *data; 180 | struct iio_dev *indio_dev; 181 | 182 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); 183 | if (!indio_dev) 184 | return -ENOMEM; 185 | 186 | data = iio_priv(indio_dev); 187 | 188 | data->mio = dev_get_drvdata(pdev->dev.parent); 189 | 190 | platform_set_drvdata(pdev, indio_dev); 191 | 192 | mutex_init(&data->lock); 193 | 194 | indio_dev->dev.parent = &pdev->dev; 195 | indio_dev->info = &matrixio_imu_info; 196 | indio_dev->channels = matrixio_imu_channels; 197 | indio_dev->num_channels = ARRAY_SIZE(matrixio_imu_channels); 198 | indio_dev->name = MATRIXIO_UV_DRV_NAME; 199 | indio_dev->modes = INDIO_DIRECT_MODE; 200 | 201 | data->mio = dev_get_drvdata(pdev->dev.parent); 202 | 203 | return iio_device_register(indio_dev); 204 | } 205 | 206 | static int matrixio_imu_remove(struct platform_device *pdev) 207 | { 208 | struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); 209 | 210 | iio_device_unregister(indio_dev); 211 | 212 | return 0; 213 | } 214 | 215 | static struct platform_driver matrixio_imu_driver = { 216 | .driver = 217 | { 218 | .name = "matrixio-imu", 219 | }, 220 | .probe = matrixio_imu_probe, 221 | .remove = matrixio_imu_remove, 222 | }; 223 | 224 | module_platform_driver(matrixio_imu_driver); 225 | 226 | MODULE_LICENSE("GPL"); 227 | MODULE_AUTHOR("Andres Calderon "); 228 | MODULE_DESCRIPTION("MATRIXIO IIO ENV module"); 229 | -------------------------------------------------------------------------------- /src/matrixio-mic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrix-codec.c -- MATRIX microphone array audio driver 3 | * 4 | * Copyright 2018 MATRIX Labs 5 | * 6 | * Author: Andres Calderon 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the 10 | * Free Software Foundation; either version 2 of the License, or (at your 11 | * option) any later version. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "fir_coeff.h" 30 | #include "matrixio-core.h" 31 | #include "matrixio-pcm.h" 32 | 33 | /* Single global pointer to driver state. Should use one of the driver private 34 | * data fields, but the way this is an ASoC driver but not really an ASoC driver 35 | * makes this hard! */ 36 | static struct matrixio_mic_substream *ms; 37 | 38 | static const struct { 39 | unsigned rate; 40 | unsigned short 41 | decimation; /* sample rate = (PDM clock = 3 MHz) / (decimation+1) */ 42 | unsigned short gain; /* in bits */ 43 | } matrixio_params[] = {{8000, 374, 1}, {12000, 249, 2}, {16000, 186, 3}, 44 | {22050, 135, 5}, {24000, 124, 5}, {32000, 92, 6}, 45 | {44100, 67, 7}, {48000, 61, 7}, {96000, 30, 10}}; 46 | 47 | static struct snd_pcm_hardware matrixio_pcm_capture_hw = { 48 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID | 49 | SNDRV_PCM_INFO_BLOCK_TRANSFER, 50 | .formats = MATRIXIO_FORMATS, 51 | .rates = MATRIXIO_RATES, 52 | .rate_min = 8000, 53 | .rate_max = 96000, 54 | .channels_min = 1, 55 | .channels_max = MATRIXIO_CHANNELS_MAX, 56 | .buffer_bytes_max = MATRIXIO_BUFFER_MAX, 57 | .period_bytes_min = MATRIXIO_PERIOD_BYTES_PER_CH * 1, 58 | .period_bytes_max = MATRIXIO_PERIOD_BYTES_PER_CH * MATRIXIO_CHANNELS_MAX, 59 | .periods_min = MATRIXIO_MIN_PERIODS, 60 | .periods_max = MATRIXIO_BUFFER_MAX / MATRIXIO_PERIOD_BYTES_PER_CH, 61 | }; 62 | 63 | static void matrixio_pcm_capture_work(struct work_struct *wk) 64 | { 65 | struct matrixio_mic_substream *ms = 66 | container_of(wk, struct matrixio_mic_substream, work); 67 | struct snd_pcm_substream *substream = ms->substream; 68 | struct snd_pcm_runtime *runtime = substream->runtime; 69 | unsigned long flags; 70 | unsigned long pos; 71 | uint16_t *buf; 72 | unsigned i, c; 73 | int ret; 74 | 75 | ret = 76 | matrixio_read(ms->mio, MATRIXIO_MICARRAY_BASE, 77 | snd_pcm_lib_period_bytes(substream), ms->frag_buffer); 78 | /* Clear SPI xfer in progress bit */ 79 | smp_mb__before_atomic(); 80 | clear_bit(1, &ms->flags); 81 | if (ret) { 82 | pcm_err(substream->pcm, "matrixio SPI read failed (%d)\n", ret); 83 | return; 84 | } 85 | 86 | spin_lock_irqsave(&ms->worker_lock, flags); 87 | /* Just return if we've stopped to audio process. This device has no 88 | * way to stop the interrupts. */ 89 | if (!test_bit(0, &ms->flags)) { 90 | spin_unlock_irqrestore(&ms->worker_lock, flags); 91 | return; 92 | } 93 | if (!runtime->dma_area) { 94 | /* This should not happen */ 95 | pcm_err(substream->pcm, "DMA buffer missing!"); 96 | spin_unlock_irqrestore(&ms->worker_lock, flags); 97 | return; 98 | } 99 | pos = atomic_read(&ms->position); 100 | /* Interleave data from fragment into "dma" buffer */ 101 | buf = (uint16_t *)(runtime->dma_area + frames_to_bytes(runtime, pos)); 102 | for (i = 0; i < MATRIXIO_PERIOD_FRAMES; i++) 103 | for (c = 0; c < runtime->channels; c++) 104 | *buf++ = 105 | ms->frag_buffer[c * MATRIXIO_PERIOD_FRAMES + i]; 106 | 107 | pos += MATRIXIO_PERIOD_FRAMES; 108 | if (pos >= runtime->buffer_size) 109 | pos -= runtime->buffer_size; 110 | atomic_set(&ms->position, pos); 111 | 112 | spin_unlock_irqrestore(&ms->worker_lock, flags); 113 | 114 | snd_pcm_period_elapsed(ms->substream); 115 | } 116 | 117 | static irqreturn_t matrixio_pcm_interrupt(int irq, void *irq_data) 118 | { 119 | struct matrixio_mic_substream *ms = irq_data; 120 | 121 | if (ms->substream == NULL) 122 | return IRQ_NONE; 123 | 124 | /* Have we started receive? Device will generate interrupts constantly. 125 | */ 126 | if (!test_bit(0, &ms->flags)) 127 | return IRQ_HANDLED; 128 | 129 | if (test_and_set_bit(1, &ms->flags)) { 130 | /* Buffer was not yet empty */ 131 | pcm_warn(ms->substream->pcm, 132 | "Possible overflow, work queue not keeping up\n"); 133 | } 134 | queue_work(ms->wq, &ms->work); 135 | 136 | return IRQ_HANDLED; 137 | } 138 | 139 | static int matrixio_pcm_open(struct snd_soc_component *component, 140 | struct snd_pcm_substream *substream) 141 | { 142 | struct snd_pcm_runtime *runtime = substream->runtime; 143 | int ret; 144 | 145 | snd_soc_set_runtime_hwparams(substream, &matrixio_pcm_capture_hw); 146 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 147 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 148 | MATRIXIO_PERIOD_FRAMES); 149 | snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 150 | MATRIXIO_PERIOD_FRAMES); 151 | 152 | snd_pcm_set_sync(substream); 153 | 154 | if (ms->substream != NULL) 155 | return -EBUSY; 156 | 157 | ms->substream = substream; 158 | 159 | atomic_set(&ms->position, 0); 160 | 161 | ms->wq = alloc_workqueue("matrixio-mic", WQ_HIGHPRI | WQ_UNBOUND, 1); 162 | if (!ms->wq) { 163 | ret = -ENOMEM; 164 | goto fail_substream; 165 | } 166 | 167 | INIT_WORK(&ms->work, matrixio_pcm_capture_work); 168 | 169 | /* Clear the running flag, so the irq handler will not do anything when 170 | * it starts */ 171 | clear_bit(0, &ms->flags); 172 | smp_mb__after_atomic(); 173 | ret = 174 | request_irq(ms->irq, matrixio_pcm_interrupt, 0, "matrixio-mic", ms); 175 | if (ret < 0) 176 | goto fail_workqueue; 177 | 178 | return 0; 179 | 180 | fail_workqueue: 181 | destroy_workqueue(ms->wq); 182 | fail_substream: 183 | ms->substream = NULL; 184 | 185 | return ret; 186 | } 187 | 188 | static int matrixio_pcm_close(struct snd_soc_component *component, 189 | struct snd_pcm_substream *substream) 190 | { 191 | clear_bit(0, &ms->flags); /* Should already be clear from trigger stop, 192 | but just in case */ 193 | free_irq(ms->irq, ms); 194 | destroy_workqueue(ms->wq); 195 | cancel_work_sync(&ms->work); 196 | ms->substream = NULL; 197 | 198 | return 0; 199 | } 200 | 201 | static int matrixio_pcm_trigger(struct snd_soc_component *component, 202 | struct snd_pcm_substream *substream, int cmd) 203 | { 204 | unsigned long flags; 205 | 206 | switch (cmd) { 207 | case SNDRV_PCM_TRIGGER_START: 208 | set_bit(0, &ms->flags); 209 | smp_mb__after_atomic(); 210 | return 0; 211 | case SNDRV_PCM_TRIGGER_STOP: 212 | pcm_dbg(substream->pcm, "stopping"); 213 | /* We need the lock here to insure the work function is not in 214 | * the middle of processing audio data into the dma buffer */ 215 | spin_lock_irqsave(&ms->worker_lock, flags); 216 | clear_bit(0, &ms->flags); 217 | spin_unlock_irqrestore(&ms->worker_lock, flags); 218 | pcm_dbg(substream->pcm, "stopped"); 219 | return 0; 220 | default: 221 | return -EINVAL; 222 | } 223 | } 224 | 225 | static int matrixio_pcm_hw_params(struct snd_soc_component *component, 226 | struct snd_pcm_substream *substream, 227 | struct snd_pcm_hw_params *hw_params) 228 | { 229 | int i; 230 | int rate; 231 | 232 | if (snd_pcm_format_width(params_format(hw_params)) != 16) 233 | return -EINVAL; 234 | 235 | rate = params_rate(hw_params); 236 | 237 | // This regmap write stuff should move to prepare instead of hwparams 238 | for (i = 0; i < ARRAY_SIZE(matrixio_params); i++) { 239 | if (rate == matrixio_params[i].rate) { 240 | regmap_write(ms->mio->regmap, MATRIXIO_CONF_BASE + 0x06, 241 | matrixio_params[i].decimation); 242 | 243 | regmap_write(ms->mio->regmap, MATRIXIO_CONF_BASE + 0x07, 244 | matrixio_params[i].gain); 245 | break; 246 | } 247 | } 248 | if (i == ARRAY_SIZE(matrixio_params)) 249 | return -EINVAL; 250 | 251 | // should use ARRAY_SIZE rather than sentinal 252 | for (i = 0; FIR_Coeff[i].rate_; i++) { 253 | if (FIR_Coeff[i].rate_ == rate) { 254 | matrixio_write(ms->mio, MATRIXIO_MICARRAY_BASE, 255 | MATRIXIO_FIR_TAP_SIZE, 256 | &FIR_Coeff[i].coeff_[0]); 257 | break; 258 | } 259 | } 260 | if (!FIR_Coeff[i].rate_) 261 | return -EINVAL; 262 | 263 | return 0; 264 | } 265 | 266 | static int matrixio_pcm_hw_free(struct snd_soc_component *component, 267 | struct snd_pcm_substream *substream) 268 | { 269 | /* Capture should have been stopped already */ 270 | snd_BUG_ON(test_bit(0, &ms->flags)); 271 | /* Make sure work function is finished */ 272 | flush_workqueue(ms->wq); 273 | return snd_pcm_lib_free_pages(substream); 274 | } 275 | 276 | static int matrixio_pcm_prepare(struct snd_soc_component *component, 277 | struct snd_pcm_substream *substream) 278 | { 279 | if (substream->runtime->period_size != MATRIXIO_PERIOD_FRAMES) { 280 | pcm_err(substream->pcm, "Need %u frames/period, got %lu\n", 281 | MATRIXIO_PERIOD_FRAMES, 282 | substream->runtime->period_size); 283 | return -EINVAL; 284 | } 285 | /* We don't need the lock since the work queue can not be running when 286 | * prepare is called */ 287 | atomic_set(&ms->position, 0); 288 | return 0; 289 | } 290 | 291 | static snd_pcm_uframes_t 292 | matrixio_pcm_pointer(struct snd_soc_component *component, 293 | struct snd_pcm_substream *substream) 294 | { 295 | return atomic_read(&ms->position); 296 | } 297 | 298 | static int matrixio_pcm_new(struct snd_soc_component *component, 299 | struct snd_soc_pcm_runtime *rtd) 300 | { 301 | snd_pcm_set_managed_buffer_all( 302 | rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, component->dev, 303 | matrixio_pcm_capture_hw.buffer_bytes_max, 304 | matrixio_pcm_capture_hw.buffer_bytes_max); 305 | return 0; 306 | } 307 | 308 | static int matrixio_pcm_dma_mmap(struct snd_soc_component *component, 309 | struct snd_pcm_substream *substream, 310 | struct vm_area_struct *vma) 311 | { 312 | return snd_pcm_lib_default_mmap(substream, vma); 313 | } 314 | 315 | static const struct snd_soc_component_driver matrixio_soc_platform = { 316 | .pcm_construct = matrixio_pcm_new, 317 | .open = matrixio_pcm_open, 318 | .hw_params = matrixio_pcm_hw_params, 319 | .hw_free = matrixio_pcm_hw_free, 320 | .prepare = matrixio_pcm_prepare, 321 | .pointer = matrixio_pcm_pointer, 322 | .close = matrixio_pcm_close, 323 | .mmap = matrixio_pcm_dma_mmap, 324 | .trigger = matrixio_pcm_trigger, 325 | }; 326 | 327 | static int matrixio_pcm_platform_probe(struct platform_device *pdev) 328 | { 329 | int ret; 330 | 331 | /* Yuck, assign to global variable, fix this! */ 332 | ms = devm_kzalloc(&pdev->dev, sizeof(struct matrixio_mic_substream), 333 | GFP_KERNEL); 334 | if (!ms) { 335 | dev_err(&pdev->dev, "Failed to allocate matrixio substream"); 336 | return -ENOMEM; 337 | } 338 | ms->frag_buffer = devm_kmalloc( 339 | &pdev->dev, matrixio_pcm_capture_hw.period_bytes_max, GFP_KERNEL); 340 | if (!ms->frag_buffer) { 341 | dev_err(&pdev->dev, 342 | "Failed to allocate SPI fragment buffer (%zu bytes)", 343 | matrixio_pcm_capture_hw.period_bytes_max); 344 | return -ENOMEM; 345 | } 346 | 347 | ms->mio = dev_get_drvdata(pdev->dev.parent); 348 | ms->substream = NULL; 349 | spin_lock_init(&ms->worker_lock); 350 | 351 | ms->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 352 | 353 | ret = devm_snd_soc_register_component(&pdev->dev, 354 | &matrixio_soc_platform, NULL, 0); 355 | if (ret) { 356 | dev_err(&pdev->dev, 357 | "MATRIXIO sound SoC register platform error: %d", ret); 358 | return ret; 359 | } 360 | 361 | dev_set_drvdata(&pdev->dev, ms); 362 | 363 | dev_info(&pdev->dev, "MATRIXIO mic array audio driver loaded (IRQ=%u)", 364 | ms->irq); 365 | 366 | return 0; 367 | } 368 | 369 | static const struct of_device_id snd_matrixio_pcm_of_match[] = { 370 | { 371 | .compatible = "matrixio-mic", 372 | }, 373 | {}, 374 | }; 375 | MODULE_DEVICE_TABLE(of, snd_matrixio_pcm_of_match); 376 | 377 | static struct platform_driver matrixio_codec_driver = { 378 | .driver = {.name = "matrixio-mic", 379 | .owner = THIS_MODULE, 380 | .of_match_table = snd_matrixio_pcm_of_match}, 381 | .probe = matrixio_pcm_platform_probe, 382 | }; 383 | 384 | module_platform_driver(matrixio_codec_driver); 385 | 386 | MODULE_LICENSE("GPL"); 387 | MODULE_AUTHOR("Andres Calderon "); 388 | MODULE_DESCRIPTION("MATRIXIO MIC array PCM"); 389 | -------------------------------------------------------------------------------- /src/matrixio-pcm.h: -------------------------------------------------------------------------------- 1 | #ifndef __MATRIXIO_PCM_H__ 2 | #define __MATRIXIO_PCM_H__ 3 | 4 | #include "matrixio-core.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct playback_params { 11 | uint32_t rate; 12 | uint32_t period; 13 | uint32_t bit_time; 14 | }; 15 | 16 | /* Defines for capture parameters */ 17 | #define MATRIXIO_CHANNELS_MAX 8 18 | #define MATRIXIO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ 19 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 20 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 21 | #define MATRIXIO_FORMATS SNDRV_PCM_FMTBIT_S16_LE 22 | /* 512 is 2 ** (ADDR_WIDTH_BUFFER - CHANNELS_WIDTH - 1) from mic_array.v 23 | * The FPGA has a 1024 samples x 8 channels buffer divided into two fragments. 24 | * There is an interrupt after each fragment. */ 25 | #define MATRIXIO_PERIOD_FRAMES (512u) 26 | #define MATRIXIO_PERIOD_BYTES_PER_CH (MATRIXIO_PERIOD_FRAMES * sizeof(uint16_t)) 27 | /* Enough for at least 32 periods, ~170 ms at max sample rate and channels */ 28 | #define MATRIXIO_BUFFER_MAX (1u << 18) 29 | #define MATRIXIO_FIR_TAP_SIZE (128u * sizeof(uint16_t)) 30 | /* This could be 2, but some software (pyaudio) uses the smallest buffer it can 31 | * get and provides no way to ask for a larger one. So we make the smallest 32 | * buffer enough to allow for reasonable latency. */ 33 | #define MATRIXIO_MIN_PERIODS 16 34 | 35 | /* For playback */ 36 | struct matrixio_substream { 37 | struct matrixio *mio; 38 | unsigned irq; 39 | struct mutex lock; 40 | struct snd_pcm_substream *substream; 41 | struct playback_params *playback_params; 42 | snd_pcm_uframes_t position; 43 | }; 44 | 45 | struct matrixio_mic_substream { 46 | struct matrixio *mio; 47 | unsigned irq; 48 | struct workqueue_struct *wq; 49 | struct work_struct work; 50 | struct snd_pcm_substream *substream; 51 | 52 | spinlock_t worker_lock; /* Use in atomic trigger callback, can't be mutex */ 53 | atomic_t position; /* Position in DMA buffer in frames */ 54 | uint16_t *frag_buffer; /* One interrupt worth of data's bounce buffer */ 55 | /* bit 0 - capture on 56 | * bit 1 - period SPI xfer pending */ 57 | unsigned long flags; 58 | }; 59 | 60 | /* Managing races: 61 | * 62 | * The irq handler only needs the matriox_substream itself, and the flag, work 63 | * and wq members. Do not delete these without first freeing the irq. Use the 64 | * atomic flag methods with the flags. The capture on bit is NOT enough to 65 | * prevent a race vs the irq handler; it is only meant to reduce the performance 66 | * impact of the "always on" irq design of the matrix io pcm. 67 | * 68 | * The workqueue will need various pcm data and one should not modify the 69 | * position field while the worker might be running. To do this, do not modify 70 | * position while the pcm substream is active without holding the worker_lock. 71 | * Also insure the worker is no longer running when the pcm substream stops. It 72 | * is ok to read the position without holding worker_lock, as nothing which 73 | * writes to position is permitted to set it to an incorrect value at any time. 74 | */ 75 | #endif 76 | -------------------------------------------------------------------------------- /src/matrixio-playback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * matrix-codec.c -- MATRIX microphone array audio driver 3 | * 4 | * Copyright 2018 MATRIX Labs 5 | * 6 | * Author: Andres Calderon 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the 10 | * Free Software Foundation; either version 2 of the License, or (at your 11 | * option) any later version. 12 | */ 13 | 14 | #include "matrixio-core.h" 15 | #include "matrixio-pcm.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #define MATRIXIO_MICARRAY_BUFFER_SIZE (512 * 2) 35 | #define kFIFOSize 4096 36 | #define KERNEL_FIFO_SIZE 32768 37 | 38 | static struct semaphore sem; 39 | 40 | const uint16_t kConfBaseAddress = 0x0000; 41 | 42 | static struct matrixio_substream *ms; 43 | 44 | static DECLARE_KFIFO(pcm_fifo, unsigned char, KERNEL_FIFO_SIZE); 45 | 46 | static const struct playback_params pcm_sampling_frequencies[] = { 47 | {8000, 1000000 / 8000, 975}, {16000, 1000000 / 16000, 492}, 48 | {32000, 1000000 / 32000, 245}, {44100, 1000000 / 44100, 177}, 49 | {48000, 1000000 / 48000, 163}, {88200, 1000000 / 88200, 88}, 50 | {96000, 1000000 / 96000, 81}}; 51 | 52 | static struct snd_pcm_hardware matrixio_playback_capture_hw = { 53 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE, 54 | .formats = SNDRV_PCM_FMTBIT_S16_LE, 55 | .rates = SNDRV_PCM_RATE_8000_48000, 56 | .rate_min = 8000, 57 | .rate_max = 48000, 58 | .channels_min = 2, 59 | .channels_max = 2, 60 | .buffer_bytes_max = 32768, 61 | .period_bytes_min = 4096, 62 | .period_bytes_max = 32768, 63 | .periods_min = 4, 64 | .periods_max = 8, 65 | }; 66 | 67 | struct task_struct *playback_task; 68 | 69 | static uint16_t matrixio_fifo_status(void) 70 | { 71 | uint16_t write_pointer; 72 | uint16_t read_pointer; 73 | 74 | matrixio_read(ms->mio, MATRIXIO_PLAYBACK_BASE + 0x802, sizeof(uint16_t), 75 | &read_pointer); 76 | matrixio_read(ms->mio, MATRIXIO_PLAYBACK_BASE + 0x803, sizeof(uint16_t), 77 | &write_pointer); 78 | 79 | if (write_pointer >= read_pointer) 80 | return write_pointer - read_pointer; 81 | return kFIFOSize - read_pointer + write_pointer; 82 | } 83 | 84 | static uint16_t matrixio_flush(void) 85 | { 86 | uint16_t flush_data = 0x0001; 87 | 88 | matrixio_write(ms->mio, MATRIXIO_CONF_BASE + 12, sizeof(uint16_t), 89 | &flush_data); 90 | 91 | flush_data = 0x0000; 92 | 93 | return matrixio_write(ms->mio, MATRIXIO_CONF_BASE + 12, 94 | sizeof(uint16_t), &flush_data); 95 | } 96 | 97 | static int thread_pcm_playback(void *data) 98 | { 99 | int ret; 100 | uint16_t fifo_status; 101 | static unsigned char matrixio_pb_buf[MATRIXIO_MICARRAY_BUFFER_SIZE]; 102 | 103 | while (!kthread_should_stop()) { 104 | down(&sem); 105 | 106 | if (ms->playback_params == 0) 107 | continue; 108 | 109 | do { 110 | fifo_status = matrixio_fifo_status(); 111 | 112 | /* When FIFO is empty go sem.down to wait for more 113 | * frames */ 114 | if (kfifo_len(&pcm_fifo) == 0) 115 | break; 116 | 117 | if (fifo_status > kFIFOSize * 3 / 4) { 118 | udelay(MATRIXIO_MICARRAY_BUFFER_SIZE * 119 | ms->playback_params->bit_time); 120 | continue; 121 | } 122 | 123 | ret = kfifo_out(&pcm_fifo, matrixio_pb_buf, 124 | MATRIXIO_MICARRAY_BUFFER_SIZE); 125 | 126 | matrixio_write(ms->mio, MATRIXIO_PLAYBACK_BASE, 127 | MATRIXIO_MICARRAY_BUFFER_SIZE, 128 | (void *)matrixio_pb_buf); 129 | 130 | ms->position += MATRIXIO_MICARRAY_BUFFER_SIZE; 131 | 132 | snd_pcm_period_elapsed(ms->substream); 133 | } while (kfifo_len(&pcm_fifo) >= MATRIXIO_MICARRAY_BUFFER_SIZE); 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | static int matrixio_playback_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) 140 | { 141 | snd_soc_set_runtime_hwparams(substream, &matrixio_playback_capture_hw); 142 | 143 | snd_pcm_set_sync(substream); 144 | 145 | if (ms->substream != NULL) { 146 | return -EBUSY; 147 | } 148 | 149 | ms->substream = substream; 150 | 151 | ms->position = 0; 152 | 153 | matrixio_flush(); 154 | 155 | sema_init(&sem, 1); 156 | 157 | kfifo_reset(&pcm_fifo); 158 | 159 | playback_task = 160 | kthread_run(&thread_pcm_playback, (void *)ms, "matrixio_playback"); 161 | 162 | return 0; 163 | } 164 | 165 | static int matrixio_playback_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) 166 | { 167 | up(&sem); 168 | 169 | kthread_stop(playback_task); 170 | 171 | ms->substream = 0; 172 | 173 | ms->playback_params = 0; 174 | 175 | return 0; 176 | } 177 | 178 | static int matrixio_playback_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, 179 | struct snd_pcm_hw_params *hw_params) 180 | { 181 | int i; 182 | int rate; 183 | 184 | if (snd_pcm_format_width(params_format(hw_params)) != 16) 185 | return -EINVAL; 186 | 187 | rate = params_rate(hw_params); 188 | for (i = 0; i < ARRAY_SIZE(pcm_sampling_frequencies); i++) { 189 | if (rate == pcm_sampling_frequencies[i].rate) { 190 | ms->playback_params = 191 | (struct playback_params 192 | *)&pcm_sampling_frequencies[i]; 193 | return matrixio_reg_write( 194 | ms->mio, MATRIXIO_CONF_BASE + 9, 195 | pcm_sampling_frequencies[i].bit_time); 196 | } 197 | } 198 | return -EINVAL; 199 | } 200 | 201 | static int matrixio_playback_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) 202 | { 203 | return snd_pcm_lib_free_pages(substream); 204 | } 205 | 206 | static int matrixio_playback_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) 207 | { 208 | ms->position = 0; 209 | return 0; 210 | } 211 | 212 | static snd_pcm_uframes_t 213 | matrixio_playback_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) 214 | { 215 | struct snd_pcm_runtime *runtime = substream->runtime; 216 | 217 | mutex_lock(&ms->lock); 218 | ms->position = 219 | ms->position < frames_to_bytes(runtime, runtime->buffer_size) 220 | ? ms->position 221 | : 0; 222 | 223 | mutex_unlock(&ms->lock); 224 | 225 | return bytes_to_frames(runtime, ms->position); 226 | } 227 | 228 | static int matrixio_playback_copy(struct snd_soc_component *component, struct snd_pcm_substream *substream, 229 | int channel, snd_pcm_uframes_t pos, 230 | void __user *buf, snd_pcm_uframes_t bytes) 231 | { 232 | int ret; 233 | unsigned int copied; 234 | 235 | struct snd_pcm_runtime *runtime = substream->runtime; 236 | 237 | int frame_count = bytes_to_frames(runtime, bytes); 238 | 239 | ret = kfifo_from_user(&pcm_fifo, buf, bytes, &copied); 240 | up(&sem); 241 | 242 | return frame_count; 243 | } 244 | 245 | static int matrixio_playback_select_info(struct snd_kcontrol *kcontrol, 246 | struct snd_ctl_elem_info *uinfo) 247 | { 248 | static const char *texts[2] = {"Speaker", "Headphone"}; 249 | return snd_ctl_enum_info(uinfo, 1, 2, texts); 250 | } 251 | 252 | static int matrixio_playback_select_get(struct snd_kcontrol *kcontrol, 253 | struct snd_ctl_elem_value *ucontrol) 254 | { 255 | uint16_t config = 0; 256 | matrixio_read(ms->mio, MATRIXIO_CONF_BASE + 11, sizeof(uint16_t), 257 | &config); 258 | ucontrol->value.integer.value[0] = config; 259 | return 0; 260 | } 261 | 262 | static int matrixio_playback_select_put(struct snd_kcontrol *kcontrol, 263 | struct snd_ctl_elem_value *ucontrol) 264 | { 265 | uint16_t config = ucontrol->value.integer.value[0] ? 0x0001 : 0x0000; 266 | matrixio_write(ms->mio, MATRIXIO_CONF_BASE + 11, sizeof(uint16_t), 267 | &config); 268 | return 1; 269 | } 270 | 271 | #define MAX_VOLUME 32 272 | 273 | static int matrixio_volume_info(struct snd_kcontrol *kcontrol, 274 | struct snd_ctl_elem_info *uinfo) 275 | { 276 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 277 | uinfo->count = 1; 278 | uinfo->value.integer.min = 0; 279 | uinfo->value.integer.max = MAX_VOLUME; 280 | uinfo->value.integer.step = 1; 281 | return 0; 282 | } 283 | 284 | static int matrixio_volume_get(struct snd_kcontrol *kcontrol, 285 | struct snd_ctl_elem_value *ucontrol) 286 | { 287 | uint16_t volume_regvalue; 288 | matrixio_read(ms->mio, MATRIXIO_CONF_BASE + 0x08, sizeof(uint16_t), 289 | &volume_regvalue); 290 | ucontrol->value.integer.value[0] = MAX_VOLUME - volume_regvalue; 291 | return 0; 292 | } 293 | 294 | static int matrixio_volume_put(struct snd_kcontrol *kcontrol, 295 | struct snd_ctl_elem_value *ucontrol) 296 | { 297 | uint16_t volume_regvalue = 298 | MAX_VOLUME - ucontrol->value.integer.value[0]; 299 | matrixio_write(ms->mio, MATRIXIO_CONF_BASE + 0x08, sizeof(uint16_t), 300 | &volume_regvalue); 301 | return 1; 302 | } 303 | 304 | static const struct snd_kcontrol_new matrixio_snd_controls[] = { 305 | { 306 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 307 | .name = "Master_Playback_Switch", 308 | .index = 0, 309 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 310 | .info = matrixio_playback_select_info, 311 | .get = matrixio_playback_select_get, 312 | .put = matrixio_playback_select_put, 313 | }, 314 | { 315 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 316 | .name = "Playback_Volume", 317 | .index = 1, 318 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 319 | .info = matrixio_volume_info, 320 | .get = matrixio_volume_get, 321 | .put = matrixio_volume_put, 322 | }}; 323 | 324 | static int matrixio_playback_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { return 0; } 325 | 326 | static const struct snd_soc_component_driver matrixio_soc_platform = { 327 | .pcm_construct = matrixio_playback_new, 328 | .controls = matrixio_snd_controls, 329 | .num_controls = ARRAY_SIZE(matrixio_snd_controls), 330 | .open = matrixio_playback_open, 331 | .hw_params = matrixio_playback_hw_params, 332 | .hw_free = matrixio_playback_hw_free, 333 | .prepare = matrixio_playback_prepare, 334 | .pointer = matrixio_playback_pointer, 335 | .copy_user = matrixio_playback_copy, 336 | .close = matrixio_playback_close, 337 | }; 338 | 339 | static int matrixio_playback_platform_probe(struct platform_device *pdev) 340 | { 341 | int ret; 342 | 343 | ms = devm_kzalloc(&pdev->dev, sizeof(struct matrixio_substream), 344 | GFP_KERNEL); 345 | if (!ms) { 346 | dev_err(&pdev->dev, "data allocation"); 347 | return -ENOMEM; 348 | } 349 | 350 | ms->mio = dev_get_drvdata(pdev->dev.parent); 351 | 352 | ms->substream = 0; 353 | 354 | mutex_init(&ms->lock); 355 | 356 | ret = devm_snd_soc_register_component(&pdev->dev, 357 | &matrixio_soc_platform, NULL, 0); 358 | if (ret) { 359 | dev_err(&pdev->dev, 360 | "MATRIXIO sound SoC register platform error: %d", ret); 361 | return ret; 362 | } 363 | 364 | dev_set_drvdata(&pdev->dev, ms); 365 | 366 | INIT_KFIFO(pcm_fifo); 367 | 368 | return 0; 369 | } 370 | 371 | static const struct of_device_id snd_matrixio_playback_of_match[] = { 372 | { 373 | .compatible = "matrixio-playback", 374 | }, 375 | {}, 376 | }; 377 | MODULE_DEVICE_TABLE(of, snd_matrixio_playback_of_match); 378 | 379 | static struct platform_driver matrixio_codec_driver = { 380 | .driver = {.name = "matrixio-playback", 381 | .owner = THIS_MODULE, 382 | .of_match_table = snd_matrixio_playback_of_match}, 383 | .probe = matrixio_playback_platform_probe, 384 | }; 385 | 386 | module_platform_driver(matrixio_codec_driver); 387 | 388 | MODULE_LICENSE("GPL"); 389 | MODULE_AUTHOR("Andres Calderon "); 390 | MODULE_DESCRIPTION("MATRIXIO PCM module"); 391 | -------------------------------------------------------------------------------- /src/matrixio-pwm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "matrixio-core.h" 8 | 9 | #define NUM_PWM 16 10 | 11 | struct matrixio_pwm_chip { 12 | struct matrixio *mio; 13 | struct pwm_chip chip; 14 | }; 15 | 16 | static inline struct matrixio_pwm_chip *to_matrixio(struct pwm_chip *chip) 17 | { 18 | return container_of(chip, struct matrixio_pwm_chip, chip); 19 | } 20 | 21 | static int matrixio_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 22 | { 23 | return 0; 24 | } 25 | 26 | static void matrixio_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) {} 27 | 28 | static int matrixio_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 29 | { 30 | return 0; 31 | } 32 | 33 | static void matrixio_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 34 | { 35 | } 36 | 37 | static int matrixio_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 38 | int duty_ns, int period_ns) 39 | { 40 | return 0; 41 | } 42 | 43 | static const struct pwm_ops matrixio_pwm_ops = { 44 | .request = matrixio_pwm_request, 45 | .free = matrixio_pwm_free, 46 | .config = matrixio_pwm_config, 47 | .enable = matrixio_pwm_enable, 48 | .disable = matrixio_pwm_disable, 49 | .owner = THIS_MODULE, 50 | }; 51 | 52 | static int matrixio_pwm_probe(struct platform_device *pdev) 53 | { 54 | 55 | struct matrixio_pwm_chip *matrixio_pwm; 56 | 57 | matrixio_pwm = 58 | devm_kzalloc(&pdev->dev, sizeof(*matrixio_pwm), GFP_KERNEL); 59 | if (!matrixio_pwm) 60 | return -ENOMEM; 61 | 62 | matrixio_pwm->chip.dev = &pdev->dev; 63 | matrixio_pwm->chip.ops = &matrixio_pwm_ops; 64 | matrixio_pwm->chip.npwm = NUM_PWM; 65 | matrixio_pwm->chip.base = -1; 66 | 67 | platform_set_drvdata(pdev, matrixio_pwm); 68 | 69 | return pwmchip_add(&matrixio_pwm->chip); 70 | } 71 | 72 | static int matrixio_pwm_remove(struct platform_device *pdev) 73 | { 74 | struct matrixio_pwm_chip *matrixio_pwm = platform_get_drvdata(pdev); 75 | 76 | return pwmchip_remove(&matrixio_pwm->chip); 77 | } 78 | 79 | static struct platform_driver matrixio_pwm_driver = { 80 | .driver = 81 | { 82 | .name = "matrixio-pwm", 83 | }, 84 | .probe = matrixio_pwm_probe, 85 | .remove = matrixio_pwm_remove, 86 | }; 87 | 88 | module_platform_driver(matrixio_pwm_driver); 89 | 90 | MODULE_LICENSE("GPL"); 91 | MODULE_AUTHOR("Andres Calderon "); 92 | MODULE_DESCRIPTION("MATRIXIO PWM module"); 93 | -------------------------------------------------------------------------------- /src/matrixio-regmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "matrixio-core.h" 10 | 11 | struct regmap_data { 12 | struct matrixio *mio; 13 | struct class *cl; 14 | dev_t devt; 15 | struct cdev cdev; 16 | struct device *device; 17 | int major; 18 | }; 19 | 20 | int matrixio_regmap_open(struct inode *inode, struct file *filp) 21 | { 22 | 23 | struct regmap_data *el; 24 | el = container_of(inode->i_cdev, struct regmap_data, cdev); 25 | 26 | filp->private_data = el; /* For use elsewhere */ 27 | 28 | return 0; 29 | } 30 | 31 | #define WR_VALUE 1200 32 | #define RD_VALUE 1201 33 | 34 | static long matrixio_regmap_ioctl(struct file *file, unsigned int cmd, 35 | unsigned long arg) 36 | { 37 | struct regmap_data *el = file->private_data; 38 | int32_t *user_buffer; 39 | static int32_t data[12000]; 40 | 41 | switch (cmd) { 42 | case WR_VALUE: 43 | user_buffer = (int32_t *)arg; 44 | if (copy_from_user(data, user_buffer, sizeof(int32_t) * 2)) 45 | return -EFAULT; 46 | 47 | if (copy_from_user(&data[2], user_buffer + 2, data[1])) 48 | return -EFAULT; 49 | 50 | return matrixio_write(el->mio, data[0], data[1], 51 | (void *)&data[2]); 52 | 53 | case RD_VALUE: 54 | user_buffer = (int32_t *)arg; 55 | if (copy_from_user(data, user_buffer, sizeof(int32_t) * 2)) 56 | return -EFAULT; 57 | 58 | matrixio_read(el->mio, data[0], data[1], (void *)&data[2]); 59 | if (copy_to_user(user_buffer + 2, &data[2], data[1])) 60 | return -EFAULT; 61 | return 0; 62 | } 63 | return -EINVAL; 64 | } 65 | 66 | struct file_operations matrixio_regmap_file_operations = { 67 | .owner = THIS_MODULE, 68 | .open = matrixio_regmap_open, 69 | .unlocked_ioctl = matrixio_regmap_ioctl}; 70 | 71 | static int matrixio_regmap_uevent(struct device *d, struct kobj_uevent_env *env) 72 | { 73 | add_uevent_var(env, "DEVMODE=%#o", 0666); 74 | return 0; 75 | } 76 | 77 | static int matrixio_regmap_probe(struct platform_device *pdev) 78 | { 79 | struct regmap_data *el; 80 | 81 | el = devm_kzalloc(&pdev->dev, sizeof(struct regmap_data), GFP_KERNEL); 82 | 83 | if (el == NULL) 84 | return -ENOMEM; 85 | 86 | dev_set_drvdata(&pdev->dev, el); 87 | 88 | el->mio = dev_get_drvdata(pdev->dev.parent); 89 | 90 | alloc_chrdev_region(&el->devt, 0, 1, "matrixio_regmap"); 91 | el->cl = class_create(THIS_MODULE, "matrixio_regmap"); 92 | 93 | el->cl->dev_uevent = matrixio_regmap_uevent; 94 | 95 | el->device = 96 | device_create(el->cl, NULL, el->devt, NULL, "matrixio_regmap"); 97 | 98 | if (IS_ERR(el->device)) { 99 | dev_err(&pdev->dev, "Unable to create device " 100 | "for matrix; errno = %ld\n", 101 | PTR_ERR(el->device)); 102 | } 103 | cdev_init(&el->cdev, &matrixio_regmap_file_operations); 104 | cdev_add(&el->cdev, el->devt, 1); 105 | 106 | return 0; 107 | } 108 | 109 | static int matrixio_regmap_remove(struct platform_device *pdev) 110 | { 111 | struct regmap_data *el = dev_get_drvdata(&pdev->dev); 112 | 113 | unregister_chrdev(el->major, "matrixio_regmap"); 114 | 115 | return 0; 116 | } 117 | 118 | static struct platform_driver matrixio_regmap_driver = { 119 | .driver = 120 | { 121 | .name = "matrixio-regmap", 122 | }, 123 | .probe = matrixio_regmap_probe, 124 | .remove = matrixio_regmap_remove, 125 | }; 126 | 127 | module_platform_driver(matrixio_regmap_driver); 128 | 129 | MODULE_LICENSE("GPL"); 130 | MODULE_AUTHOR("Andres Calderon "); 131 | MODULE_DESCRIPTION("MATRIXIO regmap module"); 132 | -------------------------------------------------------------------------------- /src/matrixio-uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "matrixio-core.h" 17 | 18 | static struct matrixio *matrixio; 19 | static struct uart_port port; 20 | static int irq; 21 | static struct workqueue_struct *workqueue; 22 | static struct work_struct work; 23 | static spinlock_t conf_lock; 24 | 25 | struct matrixio_uart_status { 26 | uint8_t dummy : 8; 27 | uint8_t fifo_empty : 1; 28 | uint8_t uart_ucr : 2; 29 | uint8_t uart_tx_busy : 5; 30 | }; 31 | 32 | struct matrixio_uart_data { 33 | uint8_t uart_rx; 34 | uint8_t empty; 35 | }; 36 | 37 | static const char driver_name[] = "ttyMATRIX"; 38 | static const char tty_dev_name[] = "ttyMATRIX"; 39 | 40 | static irqreturn_t uart_rxint(int irq, void *dev_id) 41 | { 42 | if (!freezing(current)) 43 | queue_work(workqueue, &work); 44 | return IRQ_HANDLED; 45 | } 46 | 47 | static void matrixio_uart_work(struct work_struct *w) 48 | { 49 | struct matrixio_uart_data uart_data; 50 | 51 | spin_lock_irq(&conf_lock); 52 | matrixio_read(matrixio, MATRIXIO_UART_BASE, sizeof(uart_data), 53 | (void *)&uart_data); 54 | 55 | if (!uart_data.empty) { 56 | tty_insert_flip_char(&port.state->port, 57 | (unsigned int)uart_data.uart_rx, 58 | TTY_NORMAL); 59 | tty_flip_buffer_push(&port.state->port); 60 | } 61 | spin_unlock_irq(&conf_lock); 62 | } 63 | 64 | static unsigned int matrixio_uart_tx_empty(struct uart_port *port) { return 1; } 65 | 66 | static void matrixio_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) 67 | { 68 | } 69 | 70 | static unsigned int matrixio_uart_get_mctrl(struct uart_port *port) 71 | { 72 | return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; 73 | } 74 | 75 | static void matrixio_uart_stop_tx(struct uart_port *port) {} 76 | 77 | static void matrixio_uart_start_tx(struct uart_port *port) 78 | { 79 | struct matrixio_uart_status uart_status; 80 | spin_lock(&conf_lock); 81 | 82 | while (1) { 83 | do { 84 | matrixio_read(matrixio, MATRIXIO_UART_BASE + 0x100, 85 | sizeof(uart_status), 86 | (void *)&uart_status); 87 | } while (uart_status.uart_tx_busy); 88 | 89 | matrixio_reg_write( 90 | matrixio, MATRIXIO_UART_BASE + 0x101, 91 | port->state->xmit.buf[port->state->xmit.tail]); 92 | port->state->xmit.tail = 93 | (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); 94 | port->icount.tx++; 95 | 96 | if (uart_circ_empty(&port->state->xmit)) 97 | break; 98 | } 99 | 100 | spin_unlock(&conf_lock); 101 | } 102 | 103 | static void matrixio_uart_stop_rx(struct uart_port *port) {} 104 | 105 | static void matrixio_uart_enable_ms(struct uart_port *port) {} 106 | 107 | static void matrixio_uart_break_ctl(struct uart_port *port, int break_state) {} 108 | 109 | static int matrixio_uart_startup(struct uart_port *port) 110 | { 111 | int ret; 112 | 113 | spin_lock(&conf_lock); 114 | 115 | matrixio_reg_write(matrixio, MATRIXIO_UART_BASE + 0x102, 1); 116 | matrixio_reg_write(matrixio, MATRIXIO_UART_BASE + 0x102, 0); 117 | 118 | spin_unlock(&conf_lock); 119 | 120 | workqueue = create_singlethread_workqueue("matrixio_uart"); 121 | 122 | if (!workqueue) { 123 | dev_err(port->dev, "cannot create workqueue"); 124 | return -EBUSY; 125 | } 126 | 127 | INIT_WORK(&work, matrixio_uart_work); 128 | 129 | ret = request_irq(irq, uart_rxint, 0, driver_name, matrixio); 130 | 131 | if (ret) { 132 | dev_err(port->dev, "can't request irq %d\n", irq); 133 | destroy_workqueue(workqueue); 134 | return -EBUSY; 135 | } 136 | 137 | dev_info(port->dev, "MATRIX Creator TTY has been loaded (IRQ=%d,%d)", 138 | irq, ret); 139 | 140 | return 0; 141 | } 142 | 143 | static void matrixio_uart_shutdown(struct uart_port *port) 144 | { 145 | flush_workqueue(workqueue); 146 | destroy_workqueue(workqueue); 147 | cancel_work_sync(&work); 148 | free_irq(irq, matrixio); 149 | } 150 | 151 | static void matrixio_uart_set_termios(struct uart_port *port, 152 | struct ktermios *termios, 153 | struct ktermios *old) 154 | { 155 | } 156 | 157 | static const char *matrixio_uart_type(struct uart_port *port) 158 | { 159 | return "matrixio-uart"; 160 | } 161 | 162 | static int matrixio_uart_request_port(struct uart_port *port) { return 0; } 163 | 164 | static void matrixio_uart_config_port(struct uart_port *port, int flags) {} 165 | 166 | static void matrixio_uart_release_port(struct uart_port *port) {} 167 | 168 | static int matrixio_uart_verify_port(struct uart_port *port, 169 | struct serial_struct *ser) 170 | { 171 | return 0; 172 | } 173 | 174 | static struct uart_ops matrixio_uart_ops = { 175 | .tx_empty = matrixio_uart_tx_empty, 176 | .set_mctrl = matrixio_uart_set_mctrl, 177 | .get_mctrl = matrixio_uart_get_mctrl, 178 | .stop_tx = matrixio_uart_stop_tx, 179 | .start_tx = matrixio_uart_start_tx, 180 | .stop_rx = matrixio_uart_stop_rx, 181 | .enable_ms = matrixio_uart_enable_ms, 182 | .break_ctl = matrixio_uart_break_ctl, 183 | .startup = matrixio_uart_startup, 184 | .shutdown = matrixio_uart_shutdown, 185 | .set_termios = matrixio_uart_set_termios, 186 | .type = matrixio_uart_type, 187 | .release_port = matrixio_uart_release_port, 188 | .request_port = matrixio_uart_request_port, 189 | .config_port = matrixio_uart_config_port, 190 | .verify_port = matrixio_uart_verify_port, 191 | }; 192 | 193 | static struct uart_driver matrixio_uart_driver = { 194 | .owner = THIS_MODULE, 195 | .driver_name = driver_name, 196 | .dev_name = tty_dev_name, 197 | .major = TTY_MAJOR, 198 | .minor = 209, 199 | .nr = 1, 200 | }; 201 | 202 | static int matrixio_uart_probe(struct platform_device *pdev) 203 | { 204 | int ret; 205 | struct device *dev = &pdev->dev; 206 | struct device_node *np = dev->of_node; 207 | matrixio = dev_get_drvdata(pdev->dev.parent); 208 | 209 | if (np) 210 | dev_dbg(dev, "get of data\n"); 211 | 212 | ret = uart_register_driver(&matrixio_uart_driver); 213 | 214 | if (ret != 0) { 215 | dev_err(&pdev->dev, "Failed to register MATRIXIO UART: %d\n", 216 | ret); 217 | return ret; 218 | } 219 | 220 | spin_lock_init(&conf_lock); 221 | irq = irq_of_parse_and_map(np, 0); 222 | 223 | spin_lock_init(&port.lock); 224 | port.irq = irq; 225 | port.fifosize = 16; 226 | port.line = 0; 227 | port.ops = &matrixio_uart_ops; 228 | port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; 229 | port.dev = &pdev->dev; 230 | port.type = PORT_MAX3100; 231 | port.line = 0; 232 | 233 | ret = uart_add_one_port(&matrixio_uart_driver, &port); 234 | 235 | if (ret != 0) { 236 | dev_err(matrixio->dev, "Failed to add port: %d\n", ret); 237 | return ret; 238 | } 239 | return ret; 240 | } 241 | 242 | static int matrixio_uart_remove(struct platform_device *pdev) 243 | { 244 | uart_remove_one_port(&matrixio_uart_driver, &port); 245 | port.dev = NULL; 246 | uart_unregister_driver(&matrixio_uart_driver); 247 | return 0; 248 | } 249 | 250 | static const struct of_device_id matrixio_uart_dt_ids[] = { 251 | {.compatible = "matrixio-uart", .data = (void *)0}, {}}; 252 | 253 | MODULE_DEVICE_TABLE(of, matrixio_uart_dt_ids); 254 | 255 | static struct platform_driver matrixio_uart_platform_driver = { 256 | .driver = 257 | { 258 | .name = "matrixio-uart", 259 | .of_match_table = of_match_ptr(matrixio_uart_dt_ids), 260 | .owner = THIS_MODULE, 261 | }, 262 | .probe = matrixio_uart_probe, 263 | .remove = matrixio_uart_remove, 264 | 265 | }; 266 | 267 | module_platform_driver(matrixio_uart_platform_driver); 268 | 269 | MODULE_LICENSE("GPL"); 270 | MODULE_AUTHOR("Andres Calderon "); 271 | MODULE_DESCRIPTION("MATRIXIO TTY module"); 272 | -------------------------------------------------------------------------------- /src/matrixio.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/ ; 2 | /plugin/ ; 3 | 4 | / { 5 | compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; 6 | 7 | fragment@0 { 8 | target = <&spidev0>; 9 | __overlay__ { status = "disabled"; }; 10 | }; 11 | 12 | fragment@1 { 13 | target = <&spi0_cs_pins>; 14 | frag1: __overlay__ { 15 | brcm,pins = <8 7>; 16 | }; 17 | }; 18 | 19 | fragment@2 { 20 | target = <&spi0>; 21 | frag2: __overlay__ { 22 | #address-cells = <1>; 23 | #size-cells = <0>; 24 | status = "okay"; 25 | cs-gpios = <&gpio 8 1>, <&gpio 7 1>; 26 | 27 | matrixio_core_0: matrixio_core@0 { 28 | compatible = "matrixio-core"; 29 | spi-max-frequency = <15000000>; 30 | reg = <0>; 31 | }; 32 | }; 33 | }; 34 | 35 | fragment@3 { 36 | target = <&gpio>; 37 | __overlay__ { 38 | matrixio_pins: matrixio_pins { 39 | brcm,pins = <5 6>; 40 | brcm,function = <0 0>; 41 | }; 42 | }; 43 | }; 44 | 45 | 46 | fragment@4 { 47 | target = <&matrixio_core_0>; 48 | __overlay__ { 49 | matrixio-everloop@0 { 50 | compatible = "matrixio-everloop"; 51 | 52 | }; 53 | }; 54 | }; 55 | 56 | fragment@5 { 57 | target = <&matrixio_core_0>; 58 | __overlay__ { 59 | matrixio-uart@0 { 60 | compatible = "matrixio-uart"; 61 | interrupts = <5 1>; /* low-to-high edge triggered */ 62 | interrupt-parent = <&gpio>; 63 | status = "okay"; 64 | }; 65 | }; 66 | }; 67 | 68 | fragment@6 { 69 | target = <&matrixio_core_0>; 70 | __overlay__ { 71 | matrixio-gpio@0 { 72 | compatible = "matrixio-gpio"; 73 | status = "okay"; 74 | }; 75 | }; 76 | }; 77 | 78 | fragment@7 { 79 | target = <&matrixio_core_0>; 80 | __overlay__ { 81 | matrixio-pwm@0 { 82 | compatible = "matrixio-pwm"; 83 | status = "okay"; 84 | }; 85 | }; 86 | }; 87 | 88 | fragment@8 { 89 | target = <&matrixio_core_0>; 90 | __overlay__ { 91 | matrixio-env@0 { 92 | compatible = "matrixio-env"; 93 | status = "okay"; 94 | }; 95 | }; 96 | }; 97 | fragment@9 { 98 | target = <&matrixio_core_0>; 99 | __overlay__ { 100 | matrixio-imu@0 { 101 | compatible = "matrixio-imu"; 102 | status = "okay"; 103 | }; 104 | }; 105 | }; 106 | 107 | fragment@10 { 108 | target = <&matrixio_core_0>; 109 | __overlay__ { 110 | matrix-mic@0 { 111 | compatible = "matrixio-mic"; 112 | interrupts = <6 0x3>; /* both edges triggered */ 113 | interrupt-parent = <&gpio>; 114 | }; 115 | }; 116 | }; 117 | fragment@11 { 118 | target = <&matrixio_core_0>; 119 | __overlay__ { 120 | matrix-playback@0 { 121 | compatible = "matrixio-playback"; 122 | }; 123 | }; 124 | }; 125 | fragment@12 { 126 | target = <&matrixio_core_0>; 127 | __overlay__ { 128 | matrix-codec@0 { 129 | compatible = "matrixio-codec"; 130 | }; 131 | }; 132 | }; 133 | 134 | fragment@13 { 135 | target = <&matrixio_core_0>; 136 | __overlay__ { 137 | matrixio-regmap@0 { 138 | compatible = "matrixio-regmap"; 139 | 140 | }; 141 | }; 142 | }; 143 | 144 | __overrides__ { 145 | cs0_pin = <&frag1>,"brcm,pins:0", 146 | <&frag2>,"cs-gpios:4"; 147 | cs1_pin = <&frag1>,"brcm,pins:4", 148 | <&frag2>,"cs-gpios:16"; 149 | }; 150 | }; 151 | 152 | --------------------------------------------------------------------------------