aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch1752
1 files changed, 0 insertions, 1752 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch b/target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch
deleted file mode 100644
index 88346102b7..0000000000
--- a/target/linux/bcm27xx/patches-5.4/950-0071-Support-for-Blokas-Labs-pisound-board.patch
+++ /dev/null
@@ -1,1752 +0,0 @@
-From ed992c4a8392b757e54b60bf2390015b72e3e947 Mon Sep 17 00:00:00 2001
-From: gtrainavicius <gtrainavicius@users.noreply.github.com>
-Date: Sun, 23 Oct 2016 12:06:53 +0300
-Subject: [PATCH] Support for Blokas Labs pisound board
-
-Pisound dynamic overlay (#1760)
-
-Restructuring pisound-overlay.dts, so it can be loaded and unloaded dynamically using dtoverlay.
-
-Print a logline when the kernel module is removed.
-
-pisound improvements:
-
-* Added a writable sysfs object to enable scripts / user space software
-to blink MIDI activity LEDs for variable duration.
-* Improved hw_param constraints setting.
-* Added compatibility with S16_LE sample format.
-* Exposed some simple placeholder volume controls, so the card appears
-in volumealsa widget.
-
-Add missing SND_PISOUND selects dependency to SND_RAWMIDI
-
-Without it the Pisound module fails to compile.
-See https://github.com/raspberrypi/linux/issues/2366
-
-Updates for Pisound module code:
-
- * Merged 'Fix a warning in DEBUG builds' (1c8b82b).
- * Updating some strings and copyright information.
- * Fix for handling high load of MIDI input and output.
- * Use dual rate oversampling ratio for 96kHz instead of single
- rate one.
-
-Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
-
-Fixing memset call in pisound.c
-
-Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
-
-Fix for Pisound's MIDI Input getting blocked for a while in rare cases.
-
-There was a possible race condition which could lead to Input's FIFO queue
-to be underflown, causing high amount of processing in the worker thread for
-some period of time.
-
-Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
-
-Fix for Pisound kernel module in Real Time kernel configuration.
-
-When handler of data_available interrupt is fired, queue_work ends up
-getting called and it can block on a spin lock which is not allowed in
-interrupt context. The fix was to run the handler from a thread context
-instead.
-
-Pisound: Remove spinlock usage around spi_sync
-
-ASoC: pisound: use modern dai_link style
-
-Signed-off-by: Hui Wang <hui.wang@canonical.com>
-
-ASoC: pisound: fix the parameter for spi_device_match
-
-Signed-off-by: Hui Wang <hui.wang@canonical.com>
----
- .../devicetree/bindings/vendor-prefixes.txt | 463 +++++++
- .../devicetree/bindings/vendor-prefixes.yaml | 2 +
- sound/soc/bcm/pisound.c | 1201 +++++++++++++++++
- 3 files changed, 1666 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/vendor-prefixes.txt
- create mode 100644 sound/soc/bcm/pisound.c
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
-@@ -0,0 +1,463 @@
-+Device tree binding vendor prefix registry. Keep list in alphabetical order.
-+
-+This isn't an exhaustive list, but you should add new prefixes to it before
-+using them to avoid name-space collisions.
-+
-+abilis Abilis Systems
-+abracon Abracon Corporation
-+actions Actions Semiconductor Co., Ltd.
-+active-semi Active-Semi International Inc
-+ad Avionic Design GmbH
-+adafruit Adafruit Industries, LLC
-+adapteva Adapteva, Inc.
-+adaptrum Adaptrum, Inc.
-+adh AD Holdings Plc.
-+adi Analog Devices, Inc.
-+advantech Advantech Corporation
-+aeroflexgaisler Aeroflex Gaisler AB
-+al Annapurna Labs
-+allo Allo.com
-+allwinner Allwinner Technology Co., Ltd.
-+alphascale AlphaScale Integrated Circuits Systems, Inc.
-+altr Altera Corp.
-+amarula Amarula Solutions
-+amazon Amazon.com, Inc.
-+amcc Applied Micro Circuits Corporation (APM, formally AMCC)
-+amd Advanced Micro Devices (AMD), Inc.
-+amediatech Shenzhen Amediatech Technology Co., Ltd
-+amlogic Amlogic, Inc.
-+ampire Ampire Co., Ltd.
-+ams AMS AG
-+amstaos AMS-Taos Inc.
-+analogix Analogix Semiconductor, Inc.
-+andestech Andes Technology Corporation
-+apm Applied Micro Circuits Corporation (APM)
-+aptina Aptina Imaging
-+arasan Arasan Chip Systems
-+archermind ArcherMind Technology (Nanjing) Co., Ltd.
-+arctic Arctic Sand
-+aries Aries Embedded GmbH
-+arm ARM Ltd.
-+armadeus ARMadeus Systems SARL
-+arrow Arrow Electronics
-+artesyn Artesyn Embedded Technologies Inc.
-+asahi-kasei Asahi Kasei Corp.
-+aspeed ASPEED Technology Inc.
-+asus AsusTek Computer Inc.
-+atlas Atlas Scientific LLC
-+atmel Atmel Corporation
-+auo AU Optronics Corporation
-+auvidea Auvidea GmbH
-+avago Avago Technologies
-+avia avia semiconductor
-+avic Shanghai AVIC Optoelectronics Co., Ltd.
-+avnet Avnet, Inc.
-+axentia Axentia Technologies AB
-+axis Axis Communications AB
-+bananapi BIPAI KEJI LIMITED
-+bhf Beckhoff Automation GmbH & Co. KG
-+bitmain Bitmain Technologies
-+blokaslabs Vilniaus Blokas UAB
-+boe BOE Technology Group Co., Ltd.
-+bosch Bosch Sensortec GmbH
-+boundary Boundary Devices Inc.
-+brcm Broadcom Corporation
-+buffalo Buffalo, Inc.
-+bticino Bticino International
-+calxeda Calxeda
-+capella Capella Microsystems, Inc
-+cascoda Cascoda, Ltd.
-+catalyst Catalyst Semiconductor, Inc.
-+cavium Cavium, Inc.
-+cdns Cadence Design Systems Inc.
-+cdtech CDTech(H.K.) Electronics Limited
-+ceva Ceva, Inc.
-+chipidea Chipidea, Inc
-+chipone ChipOne
-+chipspark ChipSPARK
-+chrp Common Hardware Reference Platform
-+chunghwa Chunghwa Picture Tubes Ltd.
-+ciaa Computadora Industrial Abierta Argentina
-+cirrus Cirrus Logic, Inc.
-+cloudengines Cloud Engines, Inc.
-+cnm Chips&Media, Inc.
-+cnxt Conexant Systems, Inc.
-+compulab CompuLab Ltd.
-+cortina Cortina Systems, Inc.
-+cosmic Cosmic Circuits
-+crane Crane Connectivity Solutions
-+creative Creative Technology Ltd
-+crystalfontz Crystalfontz America, Inc.
-+csky Hangzhou C-SKY Microsystems Co., Ltd
-+cubietech Cubietech, Ltd.
-+cypress Cypress Semiconductor Corporation
-+cznic CZ.NIC, z.s.p.o.
-+dallas Maxim Integrated Products (formerly Dallas Semiconductor)
-+dataimage DataImage, Inc.
-+davicom DAVICOM Semiconductor, Inc.
-+delta Delta Electronics, Inc.
-+denx Denx Software Engineering
-+devantech Devantech, Ltd.
-+dh DH electronics GmbH
-+digi Digi International Inc.
-+digilent Diglent, Inc.
-+dioo Dioo Microcircuit Co., Ltd
-+dlc DLC Display Co., Ltd.
-+dlg Dialog Semiconductor
-+dlink D-Link Corporation
-+dmo Data Modul AG
-+domintech Domintech Co., Ltd.
-+dongwoon Dongwoon Anatech
-+dptechnics DPTechnics
-+dragino Dragino Technology Co., Limited
-+ea Embedded Artists AB
-+ebs-systart EBS-SYSTART GmbH
-+ebv EBV Elektronik
-+eckelmann Eckelmann AG
-+edt Emerging Display Technologies
-+eeti eGalax_eMPIA Technology Inc
-+elan Elan Microelectronic Corp.
-+elgin Elgin S/A.
-+embest Shenzhen Embest Technology Co., Ltd.
-+emlid Emlid, Ltd.
-+emmicro EM Microelectronic
-+emtrion emtrion GmbH
-+endless Endless Mobile, Inc.
-+energymicro Silicon Laboratories (formerly Energy Micro AS)
-+engicam Engicam S.r.l.
-+epcos EPCOS AG
-+epfl Ecole Polytechnique Fédérale de Lausanne
-+epson Seiko Epson Corp.
-+est ESTeem Wireless Modems
-+ettus NI Ettus Research
-+eukrea Eukréa Electromatique
-+everest Everest Semiconductor Co. Ltd.
-+everspin Everspin Technologies, Inc.
-+exar Exar Corporation
-+excito Excito
-+ezchip EZchip Semiconductor
-+facebook Facebook
-+fairphone Fairphone B.V.
-+faraday Faraday Technology Corporation
-+fastrax Fastrax Oy
-+fcs Fairchild Semiconductor
-+feiyang Shenzhen Fly Young Technology Co.,LTD.
-+firefly Firefly
-+focaltech FocalTech Systems Co.,Ltd
-+friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
-+fsl Freescale Semiconductor
-+fujitsu Fujitsu Ltd.
-+gateworks Gateworks Corporation
-+gcw Game Consoles Worldwide
-+ge General Electric Company
-+geekbuying GeekBuying
-+gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
-+GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
-+geniatech Geniatech, Inc.
-+giantec Giantec Semiconductor, Inc.
-+giantplus Giantplus Technology Co., Ltd.
-+globalscale Globalscale Technologies, Inc.
-+globaltop GlobalTop Technology, Inc.
-+gmt Global Mixed-mode Technology, Inc.
-+goodix Shenzhen Huiding Technology Co., Ltd.
-+google Google, Inc.
-+grinn Grinn
-+grmn Garmin Limited
-+gumstix Gumstix, Inc.
-+gw Gateworks Corporation
-+hannstar HannStar Display Corporation
-+haoyu Haoyu Microelectronic Co. Ltd.
-+hardkernel Hardkernel Co., Ltd
-+hideep HiDeep Inc.
-+himax Himax Technologies, Inc.
-+hisilicon Hisilicon Limited.
-+hit Hitachi Ltd.
-+hitex Hitex Development Tools
-+holt Holt Integrated Circuits, Inc.
-+honeywell Honeywell
-+hp Hewlett Packard
-+holtek Holtek Semiconductor, Inc.
-+hwacom HwaCom Systems Inc.
-+i2se I2SE GmbH
-+ibm International Business Machines (IBM)
-+icplus IC Plus Corp.
-+idt Integrated Device Technologies, Inc.
-+ifi Ingenieurburo Fur Ic-Technologie (I/F/I)
-+ilitek ILI Technology Corporation (ILITEK)
-+img Imagination Technologies Ltd.
-+infineon Infineon Technologies
-+inforce Inforce Computing
-+ingenic Ingenic Semiconductor
-+innolux Innolux Corporation
-+inside-secure INSIDE Secure
-+intel Intel Corporation
-+intercontrol Inter Control Group
-+invensense InvenSense Inc.
-+inversepath Inverse Path
-+iom Iomega Corporation
-+isee ISEE 2007 S.L.
-+isil Intersil
-+issi Integrated Silicon Solutions Inc.
-+itead ITEAD Intelligent Systems Co.Ltd
-+iwave iWave Systems Technologies Pvt. Ltd.
-+jdi Japan Display Inc.
-+jedec JEDEC Solid State Technology Association
-+jianda Jiandangjing Technology Co., Ltd.
-+karo Ka-Ro electronics GmbH
-+keithkoep Keith & Koep GmbH
-+keymile Keymile GmbH
-+khadas Khadas
-+kiebackpeter Kieback & Peter GmbH
-+kinetic Kinetic Technologies
-+kingdisplay King & Display Technology Co., Ltd.
-+kingnovel Kingnovel Technology Co., Ltd.
-+koe Kaohsiung Opto-Electronics Inc.
-+kosagi Sutajio Ko-Usagi PTE Ltd.
-+kyo Kyocera Corporation
-+lacie LaCie
-+laird Laird PLC
-+lantiq Lantiq Semiconductor
-+lattice Lattice Semiconductor
-+lego LEGO Systems A/S
-+lemaker Shenzhen LeMaker Technology Co., Ltd.
-+lenovo Lenovo Group Ltd.
-+lg LG Corporation
-+libretech Shenzhen Libre Technology Co., Ltd
-+licheepi Lichee Pi
-+linaro Linaro Limited
-+linksys Belkin International, Inc. (Linksys)
-+linux Linux-specific binding
-+linx Linx Technologies
-+lltc Linear Technology Corporation
-+logicpd Logic PD, Inc.
-+lsi LSI Corp. (LSI Logic)
-+lwn Liebherr-Werk Nenzing GmbH
-+macnica Macnica Americas
-+marvell Marvell Technology Group Ltd.
-+maxim Maxim Integrated Products
-+mbvl Mobiveil Inc.
-+mcube mCube
-+meas Measurement Specialties
-+mediatek MediaTek Inc.
-+megachips MegaChips
-+mele Shenzhen MeLE Digital Technology Ltd.
-+melexis Melexis N.V.
-+melfas MELFAS Inc.
-+mellanox Mellanox Technologies
-+memsic MEMSIC Inc.
-+merrii Merrii Technology Co., Ltd.
-+micrel Micrel Inc.
-+microchip Microchip Technology Inc.
-+microcrystal Micro Crystal AG
-+micron Micron Technology Inc.
-+mikroe MikroElektronika d.o.o.
-+minix MINIX Technology Ltd.
-+miramems MiraMEMS Sensing Technology Co., Ltd.
-+mitsubishi Mitsubishi Electric Corporation
-+mosaixtech Mosaix Technologies, Inc.
-+motorola Motorola, Inc.
-+moxa Moxa Inc.
-+mpl MPL AG
-+mqmaker mqmaker Inc.
-+mscc Microsemi Corporation
-+msi Micro-Star International Co. Ltd.
-+mti Imagination Technologies Ltd. (formerly MIPS Technologies Inc.)
-+multi-inno Multi-Inno Technology Co.,Ltd
-+mundoreader Mundo Reader S.L.
-+murata Murata Manufacturing Co., Ltd.
-+mxicy Macronix International Co., Ltd.
-+myir MYIR Tech Limited
-+national National Semiconductor
-+nec NEC LCD Technologies, Ltd.
-+neonode Neonode Inc.
-+netgear NETGEAR
-+netlogic Broadcom Corporation (formerly NetLogic Microsystems)
-+netron-dy Netron DY
-+netxeon Shenzhen Netxeon Technology CO., LTD
-+nexbox Nexbox
-+nextthing Next Thing Co.
-+newhaven Newhaven Display International
-+ni National Instruments
-+nintendo Nintendo
-+nlt NLT Technologies, Ltd.
-+nokia Nokia
-+nordic Nordic Semiconductor
-+novtech NovTech, Inc.
-+nutsboard NutsBoard
-+nuvoton Nuvoton Technology Corporation
-+nvd New Vision Display
-+nvidia NVIDIA
-+nxp NXP Semiconductors
-+okaya Okaya Electric America, Inc.
-+oki Oki Electric Industry Co., Ltd.
-+olimex OLIMEX Ltd.
-+olpc One Laptop Per Child
-+onion Onion Corporation
-+onnn ON Semiconductor Corp.
-+ontat On Tat Industrial Company
-+opalkelly Opal Kelly Incorporated
-+opencores OpenCores.org
-+openrisc OpenRISC.io
-+option Option NV
-+oranth Shenzhen Oranth Technology Co., Ltd.
-+ORCL Oracle Corporation
-+orisetech Orise Technology
-+ortustech Ortus Technology Co., Ltd.
-+ovti OmniVision Technologies
-+oxsemi Oxford Semiconductor, Ltd.
-+panasonic Panasonic Corporation
-+parade Parade Technologies Inc.
-+pda Precision Design Associates, Inc.
-+pericom Pericom Technology Inc.
-+pervasive Pervasive Displays, Inc.
-+phicomm PHICOMM Co., Ltd.
-+phytec PHYTEC Messtechnik GmbH
-+picochip Picochip Ltd
-+pine64 Pine64
-+pixcir PIXCIR MICROELECTRONICS Co., Ltd
-+plantower Plantower Co., Ltd
-+plathome Plat'Home Co., Ltd.
-+plda PLDA
-+plx Broadcom Corporation (formerly PLX Technology)
-+pni PNI Sensor Corporation
-+portwell Portwell Inc.
-+poslab Poslab Technology Co., Ltd.
-+powervr PowerVR (deprecated, use img)
-+probox2 PROBOX2 (by W2COMP Co., Ltd.)
-+pulsedlight PulsedLight, Inc
-+qca Qualcomm Atheros, Inc.
-+qcom Qualcomm Technologies, Inc
-+qemu QEMU, a generic and open source machine emulator and virtualizer
-+qi Qi Hardware
-+qiaodian QiaoDian XianShi Corporation
-+qnap QNAP Systems, Inc.
-+radxa Radxa
-+raidsonic RaidSonic Technology GmbH
-+ralink Mediatek/Ralink Technology Corp.
-+ramtron Ramtron International
-+raspberrypi Raspberry Pi Foundation
-+raydium Raydium Semiconductor Corp.
-+rda Unisoc Communications, Inc.
-+realtek Realtek Semiconductor Corp.
-+renesas Renesas Electronics Corporation
-+richtek Richtek Technology Corporation
-+ricoh Ricoh Co. Ltd.
-+rikomagic Rikomagic Tech Corp. Ltd
-+riscv RISC-V Foundation
-+rockchip Fuzhou Rockchip Electronics Co., Ltd
-+rohm ROHM Semiconductor Co., Ltd
-+roofull Shenzhen Roofull Technology Co, Ltd
-+samsung Samsung Semiconductor
-+samtec Samtec/Softing company
-+sancloud Sancloud Ltd
-+sandisk Sandisk Corporation
-+sbs Smart Battery System
-+schindler Schindler
-+seagate Seagate Technology PLC
-+semtech Semtech Corporation
-+sensirion Sensirion AG
-+sff Small Form Factor Committee
-+sgd Solomon Goldentek Display Corporation
-+sgx SGX Sensortech
-+sharp Sharp Corporation
-+shimafuji Shimafuji Electric, Inc.
-+si-en Si-En Technology Ltd.
-+sifive SiFive, Inc.
-+sigma Sigma Designs, Inc.
-+sii Seiko Instruments, Inc.
-+sil Silicon Image
-+silabs Silicon Laboratories
-+silead Silead Inc.
-+silergy Silergy Corp.
-+siliconmitus Silicon Mitus, Inc.
-+simtek
-+sirf SiRF Technology, Inc.
-+sis Silicon Integrated Systems Corp.
-+sitronix Sitronix Technology Corporation
-+skyworks Skyworks Solutions, Inc.
-+smsc Standard Microsystems Corporation
-+snps Synopsys, Inc.
-+socionext Socionext Inc.
-+solidrun SolidRun
-+solomon Solomon Systech Limited
-+sony Sony Corporation
-+spansion Spansion Inc.
-+sprd Spreadtrum Communications Inc.
-+sst Silicon Storage Technology, Inc.
-+st STMicroelectronics
-+starry Starry Electronic Technology (ShenZhen) Co., LTD
-+startek Startek
-+ste ST-Ericsson
-+stericsson ST-Ericsson
-+summit Summit microelectronics
-+sunchip Shenzhen Sunchip Technology Co., Ltd
-+SUNW Sun Microsystems, Inc
-+swir Sierra Wireless
-+syna Synaptics Inc.
-+synology Synology, Inc.
-+tbs TBS Technologies
-+tbs-biometrics Touchless Biometric Systems AG
-+tcg Trusted Computing Group
-+tcl Toby Churchill Ltd.
-+technexion TechNexion
-+technologic Technologic Systems
-+tempo Tempo Semiconductor
-+techstar Shenzhen Techstar Electronics Co., Ltd.
-+terasic Terasic Inc.
-+thine THine Electronics, Inc.
-+ti Texas Instruments
-+tianma Tianma Micro-electronics Co., Ltd.
-+tlm Trusted Logic Mobility
-+tmt Tecon Microprocessor Technologies, LLC.
-+topeet Topeet
-+toradex Toradex AG
-+toshiba Toshiba Corporation
-+toumaz Toumaz
-+tpk TPK U.S.A. LLC
-+tplink TP-LINK Technologies Co., Ltd.
-+tpo TPO
-+tronfy Tronfy
-+tronsmart Tronsmart
-+truly Truly Semiconductors Limited
-+tsd Theobroma Systems Design und Consulting GmbH
-+tyan Tyan Computer Corporation
-+u-blox u-blox
-+ucrobotics uCRobotics
-+ubnt Ubiquiti Networks
-+udoo Udoo
-+uniwest United Western Technologies Corp (UniWest)
-+upisemi uPI Semiconductor Corp.
-+urt United Radiant Technology Corporation
-+usi Universal Scientific Industrial Co., Ltd.
-+v3 V3 Semiconductor
-+vamrs Vamrs Ltd.
-+variscite Variscite Ltd.
-+via VIA Technologies, Inc.
-+virtio Virtual I/O Device Specification, developed by the OASIS consortium
-+vishay Vishay Intertechnology, Inc
-+vitesse Vitesse Semiconductor Corporation
-+vivante Vivante Corporation
-+vocore VoCore Studio
-+voipac Voipac Technologies s.r.o.
-+vot Vision Optical Technology Co., Ltd.
-+wd Western Digital Corp.
-+wetek WeTek Electronics, limited.
-+wexler Wexler
-+whwave Shenzhen whwave Electronics, Inc.
-+wi2wi Wi2Wi, Inc.
-+winbond Winbond Electronics corp.
-+winstar Winstar Display Corp.
-+wlf Wolfson Microelectronics
-+wm Wondermedia Technologies, Inc.
-+x-powers X-Powers
-+xes Extreme Engineering Solutions (X-ES)
-+xillybus Xillybus Ltd.
-+xlnx Xilinx
-+xunlong Shenzhen Xunlong Software CO.,Limited
-+ysoft Y Soft Corporation a.s.
-+zarlink Zarlink Semiconductor
-+zeitec ZEITEC Semiconductor Co., LTD.
-+zidoo Shenzhen Zidoo Technology Co., Ltd.
-+zii Zodiac Inflight Innovations
-+zte ZTE Corp.
-+zyxel ZyXEL Communications Corp.
---- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
-+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
-@@ -143,6 +143,8 @@ patternProperties:
- description: Beckhoff Automation GmbH & Co. KG
- "^bitmain,.*":
- description: Bitmain Technologies
-+ "^blokaslabs,.*":
-+ description: Vilniaus Blokas UAB
- "^boe,.*":
- description: BOE Technology Group Co., Ltd.
- "^bosch,.*":
---- /dev/null
-+++ b/sound/soc/bcm/pisound.c
-@@ -0,0 +1,1201 @@
-+/*
-+ * Pisound Linux kernel module.
-+ * Copyright (C) 2016-2019 Vilniaus Blokas UAB, https://blokas.io/pisound
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; version 2 of the
-+ * License.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-+ * MA 02110-1301, USA.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/gpio.h>
-+#include <linux/kobject.h>
-+#include <linux/sysfs.h>
-+#include <linux/delay.h>
-+#include <linux/spi/spi.h>
-+#include <linux/interrupt.h>
-+#include <linux/kfifo.h>
-+#include <linux/jiffies.h>
-+
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/jack.h>
-+#include <sound/rawmidi.h>
-+#include <sound/asequencer.h>
-+#include <sound/control.h>
-+
-+static int pisnd_spi_init(struct device *dev);
-+static void pisnd_spi_uninit(void);
-+
-+static void pisnd_spi_flush(void);
-+static void pisnd_spi_start(void);
-+static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
-+
-+typedef void (*pisnd_spi_recv_cb)(void *data);
-+static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
-+
-+static const char *pisnd_spi_get_serial(void);
-+static const char *pisnd_spi_get_id(void);
-+static const char *pisnd_spi_get_version(void);
-+
-+static int pisnd_midi_init(struct snd_card *card);
-+static void pisnd_midi_uninit(void);
-+
-+enum task_e {
-+ TASK_PROCESS = 0,
-+};
-+
-+static void pisnd_schedule_process(enum task_e task);
-+
-+#define PISOUND_LOG_PREFIX "pisound: "
-+
-+#ifdef PISOUND_DEBUG
-+# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
-+#else
-+# define printd(...) do {} while (0)
-+#endif
-+
-+#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
-+#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
-+
-+static struct snd_rawmidi *g_rmidi;
-+static struct snd_rawmidi_substream *g_midi_output_substream;
-+
-+static int pisnd_output_open(struct snd_rawmidi_substream *substream)
-+{
-+ g_midi_output_substream = substream;
-+ return 0;
-+}
-+
-+static int pisnd_output_close(struct snd_rawmidi_substream *substream)
-+{
-+ g_midi_output_substream = NULL;
-+ return 0;
-+}
-+
-+static void pisnd_output_trigger(
-+ struct snd_rawmidi_substream *substream,
-+ int up
-+ )
-+{
-+ if (substream != g_midi_output_substream) {
-+ printe("MIDI output trigger called for an unexpected stream!");
-+ return;
-+ }
-+
-+ if (!up)
-+ return;
-+
-+ pisnd_spi_start();
-+}
-+
-+static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
-+{
-+ pisnd_spi_flush();
-+}
-+
-+static int pisnd_input_open(struct snd_rawmidi_substream *substream)
-+{
-+ return 0;
-+}
-+
-+static int pisnd_input_close(struct snd_rawmidi_substream *substream)
-+{
-+ return 0;
-+}
-+
-+static void pisnd_midi_recv_callback(void *substream)
-+{
-+ uint8_t data[128];
-+ uint8_t n = 0;
-+
-+ while ((n = pisnd_spi_recv(data, sizeof(data)))) {
-+ int res = snd_rawmidi_receive(substream, data, n);
-+ (void)res;
-+ printd("midi recv %u bytes, res = %d\n", n, res);
-+ }
-+}
-+
-+static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
-+{
-+ if (up) {
-+ pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
-+ pisnd_schedule_process(TASK_PROCESS);
-+ } else {
-+ pisnd_spi_set_callback(NULL, NULL);
-+ }
-+}
-+
-+static struct snd_rawmidi_ops pisnd_output_ops = {
-+ .open = pisnd_output_open,
-+ .close = pisnd_output_close,
-+ .trigger = pisnd_output_trigger,
-+ .drain = pisnd_output_drain,
-+};
-+
-+static struct snd_rawmidi_ops pisnd_input_ops = {
-+ .open = pisnd_input_open,
-+ .close = pisnd_input_close,
-+ .trigger = pisnd_input_trigger,
-+};
-+
-+static void pisnd_get_port_info(
-+ struct snd_rawmidi *rmidi,
-+ int number,
-+ struct snd_seq_port_info *seq_port_info
-+ )
-+{
-+ seq_port_info->type =
-+ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
-+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
-+ SNDRV_SEQ_PORT_TYPE_PORT;
-+ seq_port_info->midi_voices = 0;
-+}
-+
-+static struct snd_rawmidi_global_ops pisnd_global_ops = {
-+ .get_port_info = pisnd_get_port_info,
-+};
-+
-+static int pisnd_midi_init(struct snd_card *card)
-+{
-+ int err;
-+
-+ g_midi_output_substream = NULL;
-+
-+ err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
-+
-+ if (err < 0) {
-+ printe("snd_rawmidi_new failed: %d\n", err);
-+ return err;
-+ }
-+
-+ strcpy(g_rmidi->name, "pisound MIDI ");
-+ strcat(g_rmidi->name, pisnd_spi_get_serial());
-+
-+ g_rmidi->info_flags =
-+ SNDRV_RAWMIDI_INFO_OUTPUT |
-+ SNDRV_RAWMIDI_INFO_INPUT |
-+ SNDRV_RAWMIDI_INFO_DUPLEX;
-+
-+ g_rmidi->ops = &pisnd_global_ops;
-+
-+ g_rmidi->private_data = (void *)0;
-+
-+ snd_rawmidi_set_ops(
-+ g_rmidi,
-+ SNDRV_RAWMIDI_STREAM_OUTPUT,
-+ &pisnd_output_ops
-+ );
-+
-+ snd_rawmidi_set_ops(
-+ g_rmidi,
-+ SNDRV_RAWMIDI_STREAM_INPUT,
-+ &pisnd_input_ops
-+ );
-+
-+ return 0;
-+}
-+
-+static void pisnd_midi_uninit(void)
-+{
-+}
-+
-+static void *g_recvData;
-+static pisnd_spi_recv_cb g_recvCallback;
-+
-+#define FIFO_SIZE 4096
-+
-+static char g_serial_num[11];
-+static char g_id[25];
-+static char g_version[5];
-+
-+static uint8_t g_ledFlashDuration;
-+static bool g_ledFlashDurationChanged;
-+
-+DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
-+DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
-+
-+static struct gpio_desc *data_available;
-+static struct gpio_desc *spi_reset;
-+
-+static struct spi_device *pisnd_spi_device;
-+
-+static struct workqueue_struct *pisnd_workqueue;
-+static struct work_struct pisnd_work_process;
-+
-+static void pisnd_work_handler(struct work_struct *work);
-+
-+static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len);
-+static uint16_t spi_transfer16(uint16_t val);
-+
-+static int pisnd_init_workqueues(void)
-+{
-+ pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
-+ INIT_WORK(&pisnd_work_process, pisnd_work_handler);
-+
-+ return 0;
-+}
-+
-+static void pisnd_uninit_workqueues(void)
-+{
-+ flush_workqueue(pisnd_workqueue);
-+ destroy_workqueue(pisnd_workqueue);
-+
-+ pisnd_workqueue = NULL;
-+}
-+
-+static bool pisnd_spi_has_more(void)
-+{
-+ return gpiod_get_value(data_available);
-+}
-+
-+static void pisnd_schedule_process(enum task_e task)
-+{
-+ if (pisnd_spi_device != NULL &&
-+ pisnd_workqueue != NULL &&
-+ !work_pending(&pisnd_work_process)
-+ ) {
-+ printd("schedule: has more = %d\n", pisnd_spi_has_more());
-+ if (task == TASK_PROCESS)
-+ queue_work(pisnd_workqueue, &pisnd_work_process);
-+ }
-+}
-+
-+static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
-+{
-+ if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
-+ printd("schedule from irq\n");
-+ pisnd_schedule_process(TASK_PROCESS);
-+ }
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static uint16_t spi_transfer16(uint16_t val)
-+{
-+ uint8_t txbuf[2];
-+ uint8_t rxbuf[2];
-+
-+ if (!pisnd_spi_device) {
-+ printe("pisnd_spi_device null, returning\n");
-+ return 0;
-+ }
-+
-+ txbuf[0] = val >> 8;
-+ txbuf[1] = val & 0xff;
-+
-+ spi_transfer(txbuf, rxbuf, sizeof(txbuf));
-+
-+ printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
-+
-+ return (rxbuf[0] << 8) | rxbuf[1];
-+}
-+
-+static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len)
-+{
-+ int err;
-+ struct spi_transfer transfer;
-+ struct spi_message msg;
-+
-+ memset(rxbuf, 0, len);
-+
-+ if (!pisnd_spi_device) {
-+ printe("pisnd_spi_device null, returning\n");
-+ return;
-+ }
-+
-+ spi_message_init(&msg);
-+
-+ memset(&transfer, 0, sizeof(transfer));
-+
-+ transfer.tx_buf = txbuf;
-+ transfer.rx_buf = rxbuf;
-+ transfer.len = len;
-+ transfer.speed_hz = 100000;
-+ transfer.delay_usecs = 10;
-+ spi_message_add_tail(&transfer, &msg);
-+
-+ err = spi_sync(pisnd_spi_device, &msg);
-+
-+ if (err < 0) {
-+ printe("spi_sync error %d\n", err);
-+ return;
-+ }
-+
-+ printd("hasMore %d\n", pisnd_spi_has_more());
-+}
-+
-+static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
-+{
-+ uint16_t rx;
-+ uint8_t size;
-+ uint8_t i;
-+
-+ memset(dst, 0, length);
-+ *bytesRead = 0;
-+
-+ rx = spi_transfer16(0);
-+ if (!(rx >> 8))
-+ return -EINVAL;
-+
-+ size = rx & 0xff;
-+
-+ if (size > length)
-+ return -EINVAL;
-+
-+ for (i = 0; i < size; ++i) {
-+ rx = spi_transfer16(0);
-+ if (!(rx >> 8))
-+ return -EINVAL;
-+
-+ dst[i] = rx & 0xff;
-+ }
-+
-+ *bytesRead = i;
-+
-+ return 0;
-+}
-+
-+static int spi_device_match(struct device *dev, const void *data)
-+{
-+ struct spi_device *spi = container_of(dev, struct spi_device, dev);
-+
-+ printd(" %s %s %dkHz %d bits mode=0x%02X\n",
-+ spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
-+ spi->bits_per_word, spi->mode);
-+
-+ if (strcmp("pisound-spi", spi->modalias) == 0) {
-+ printi("\tFound!\n");
-+ return 1;
-+ }
-+
-+ printe("\tNot found!\n");
-+ return 0;
-+}
-+
-+static struct spi_device *pisnd_spi_find_device(void)
-+{
-+ struct device *dev;
-+
-+ printi("Searching for spi device...\n");
-+ dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
-+ if (dev != NULL)
-+ return container_of(dev, struct spi_device, dev);
-+ else
-+ return NULL;
-+}
-+
-+static void pisnd_work_handler(struct work_struct *work)
-+{
-+ enum { TRANSFER_SIZE = 4 };
-+ enum { PISOUND_OUTPUT_BUFFER_SIZE = 128 };
-+ enum { MIDI_BYTES_PER_SECOND = 3125 };
-+ int out_buffer_used = 0;
-+ unsigned long now;
-+ uint8_t val;
-+ uint8_t txbuf[TRANSFER_SIZE];
-+ uint8_t rxbuf[TRANSFER_SIZE];
-+ uint8_t midibuf[TRANSFER_SIZE];
-+ int i, n;
-+ bool had_data;
-+
-+ unsigned long last_transfer_at = jiffies;
-+
-+ if (work == &pisnd_work_process) {
-+ if (pisnd_spi_device == NULL)
-+ return;
-+
-+ do {
-+ if (g_midi_output_substream &&
-+ kfifo_avail(&spi_fifo_out) >= sizeof(midibuf)) {
-+
-+ n = snd_rawmidi_transmit_peek(
-+ g_midi_output_substream,
-+ midibuf, sizeof(midibuf)
-+ );
-+
-+ if (n > 0) {
-+ for (i = 0; i < n; ++i)
-+ kfifo_put(
-+ &spi_fifo_out,
-+ midibuf[i]
-+ );
-+ snd_rawmidi_transmit_ack(
-+ g_midi_output_substream,
-+ i
-+ );
-+ }
-+ }
-+
-+ had_data = false;
-+ memset(txbuf, 0, sizeof(txbuf));
-+ for (i = 0; i < sizeof(txbuf) &&
-+ out_buffer_used < PISOUND_OUTPUT_BUFFER_SIZE;
-+ i += 2) {
-+
-+ val = 0;
-+
-+ if (g_ledFlashDurationChanged) {
-+ txbuf[i+0] = 0xf0;
-+ txbuf[i+1] = g_ledFlashDuration;
-+ g_ledFlashDuration = 0;
-+ g_ledFlashDurationChanged = false;
-+ } else if (kfifo_get(&spi_fifo_out, &val)) {
-+ txbuf[i+0] = 0x0f;
-+ txbuf[i+1] = val;
-+ ++out_buffer_used;
-+ }
-+ }
-+
-+ spi_transfer(txbuf, rxbuf, sizeof(txbuf));
-+ /* Estimate the Pisound's MIDI output buffer usage, so
-+ * that we don't overflow it. Space in the buffer should
-+ * be becoming available at the UART MIDI byte transfer
-+ * rate.
-+ */
-+ now = jiffies;
-+ out_buffer_used -=
-+ (MIDI_BYTES_PER_SECOND / HZ) /
-+ (now - last_transfer_at);
-+ if (out_buffer_used < 0)
-+ out_buffer_used = 0;
-+ last_transfer_at = now;
-+
-+ for (i = 0; i < sizeof(rxbuf); i += 2) {
-+ if (rxbuf[i]) {
-+ kfifo_put(&spi_fifo_in, rxbuf[i+1]);
-+ if (kfifo_len(&spi_fifo_in) > 16 &&
-+ g_recvCallback)
-+ g_recvCallback(g_recvData);
-+ had_data = true;
-+ }
-+ }
-+ } while (had_data
-+ || !kfifo_is_empty(&spi_fifo_out)
-+ || pisnd_spi_has_more()
-+ || g_ledFlashDurationChanged
-+ );
-+
-+ if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
-+ g_recvCallback(g_recvData);
-+ }
-+}
-+
-+static int pisnd_spi_gpio_init(struct device *dev)
-+{
-+ spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
-+ data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
-+
-+ gpiod_direction_output(spi_reset, 1);
-+ gpiod_direction_input(data_available);
-+
-+ /* Reset the slave. */
-+ gpiod_set_value(spi_reset, false);
-+ mdelay(1);
-+ gpiod_set_value(spi_reset, true);
-+
-+ /* Give time for spi slave to start. */
-+ mdelay(64);
-+
-+ return 0;
-+}
-+
-+static void pisnd_spi_gpio_uninit(void)
-+{
-+ gpiod_set_value(spi_reset, false);
-+ gpiod_put(spi_reset);
-+ spi_reset = NULL;
-+
-+ gpiod_put(data_available);
-+ data_available = NULL;
-+}
-+
-+static int pisnd_spi_gpio_irq_init(struct device *dev)
-+{
-+ return request_threaded_irq(
-+ gpiod_to_irq(data_available), NULL,
-+ data_available_interrupt_handler,
-+ IRQF_TIMER | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-+ "data_available_int",
-+ NULL
-+ );
-+}
-+
-+static void pisnd_spi_gpio_irq_uninit(void)
-+{
-+ free_irq(gpiod_to_irq(data_available), NULL);
-+}
-+
-+static int spi_read_info(void)
-+{
-+ uint16_t tmp;
-+ uint8_t count;
-+ uint8_t n;
-+ uint8_t i;
-+ uint8_t j;
-+ char buffer[257];
-+ int ret;
-+ char *p;
-+
-+ memset(g_serial_num, 0, sizeof(g_serial_num));
-+ memset(g_version, 0, sizeof(g_version));
-+ memset(g_id, 0, sizeof(g_id));
-+
-+ tmp = spi_transfer16(0);
-+
-+ if (!(tmp >> 8))
-+ return -EINVAL;
-+
-+ count = tmp & 0xff;
-+
-+ for (i = 0; i < count; ++i) {
-+ memset(buffer, 0, sizeof(buffer));
-+ ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
-+
-+ if (ret < 0)
-+ return ret;
-+
-+ switch (i) {
-+ case 0:
-+ if (n != 2)
-+ return -EINVAL;
-+
-+ snprintf(
-+ g_version,
-+ sizeof(g_version),
-+ "%x.%02x",
-+ buffer[0],
-+ buffer[1]
-+ );
-+ break;
-+ case 1:
-+ if (n >= sizeof(g_serial_num))
-+ return -EINVAL;
-+
-+ memcpy(g_serial_num, buffer, sizeof(g_serial_num));
-+ break;
-+ case 2:
-+ {
-+ if (n >= sizeof(g_id))
-+ return -EINVAL;
-+
-+ p = g_id;
-+ for (j = 0; j < n; ++j)
-+ p += sprintf(p, "%02x", buffer[j]);
-+ }
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int pisnd_spi_init(struct device *dev)
-+{
-+ int ret;
-+ struct spi_device *spi;
-+
-+ memset(g_serial_num, 0, sizeof(g_serial_num));
-+ memset(g_id, 0, sizeof(g_id));
-+ memset(g_version, 0, sizeof(g_version));
-+
-+ spi = pisnd_spi_find_device();
-+
-+ if (spi != NULL) {
-+ printd("initializing spi!\n");
-+ pisnd_spi_device = spi;
-+ ret = spi_setup(pisnd_spi_device);
-+ } else {
-+ printe("SPI device not found, deferring!\n");
-+ return -EPROBE_DEFER;
-+ }
-+
-+ ret = pisnd_spi_gpio_init(dev);
-+
-+ if (ret < 0) {
-+ printe("SPI GPIO init failed: %d\n", ret);
-+ spi_dev_put(pisnd_spi_device);
-+ pisnd_spi_device = NULL;
-+ pisnd_spi_gpio_uninit();
-+ return ret;
-+ }
-+
-+ ret = spi_read_info();
-+
-+ if (ret < 0) {
-+ printe("Reading card info failed: %d\n", ret);
-+ spi_dev_put(pisnd_spi_device);
-+ pisnd_spi_device = NULL;
-+ pisnd_spi_gpio_uninit();
-+ return ret;
-+ }
-+
-+ /* Flash the LEDs. */
-+ spi_transfer16(0xf008);
-+
-+ ret = pisnd_spi_gpio_irq_init(dev);
-+ if (ret < 0) {
-+ printe("SPI irq request failed: %d\n", ret);
-+ spi_dev_put(pisnd_spi_device);
-+ pisnd_spi_device = NULL;
-+ pisnd_spi_gpio_irq_uninit();
-+ pisnd_spi_gpio_uninit();
-+ }
-+
-+ ret = pisnd_init_workqueues();
-+ if (ret != 0) {
-+ printe("Workqueue initialization failed: %d\n", ret);
-+ spi_dev_put(pisnd_spi_device);
-+ pisnd_spi_device = NULL;
-+ pisnd_spi_gpio_irq_uninit();
-+ pisnd_spi_gpio_uninit();
-+ pisnd_uninit_workqueues();
-+ return ret;
-+ }
-+
-+ if (pisnd_spi_has_more()) {
-+ printd("data is available, scheduling from init\n");
-+ pisnd_schedule_process(TASK_PROCESS);
-+ }
-+
-+ return 0;
-+}
-+
-+static void pisnd_spi_uninit(void)
-+{
-+ pisnd_uninit_workqueues();
-+
-+ spi_dev_put(pisnd_spi_device);
-+ pisnd_spi_device = NULL;
-+
-+ pisnd_spi_gpio_irq_uninit();
-+ pisnd_spi_gpio_uninit();
-+}
-+
-+static void pisnd_spi_flash_leds(uint8_t duration)
-+{
-+ g_ledFlashDuration = duration;
-+ g_ledFlashDurationChanged = true;
-+ printd("schedule from spi_flash_leds\n");
-+ pisnd_schedule_process(TASK_PROCESS);
-+}
-+
-+static void pisnd_spi_flush(void)
-+{
-+ while (!kfifo_is_empty(&spi_fifo_out)) {
-+ pisnd_spi_start();
-+ flush_workqueue(pisnd_workqueue);
-+ }
-+}
-+
-+static void pisnd_spi_start(void)
-+{
-+ printd("schedule from spi_start\n");
-+ pisnd_schedule_process(TASK_PROCESS);
-+}
-+
-+static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
-+{
-+ return kfifo_out(&spi_fifo_in, buffer, length);
-+}
-+
-+static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
-+{
-+ g_recvData = data;
-+ g_recvCallback = cb;
-+}
-+
-+static const char *pisnd_spi_get_serial(void)
-+{
-+ if (strlen(g_serial_num))
-+ return g_serial_num;
-+
-+ return "";
-+}
-+
-+static const char *pisnd_spi_get_id(void)
-+{
-+ if (strlen(g_id))
-+ return g_id;
-+
-+ return "";
-+}
-+
-+static const char *pisnd_spi_get_version(void)
-+{
-+ if (strlen(g_version))
-+ return g_version;
-+
-+ return "";
-+}
-+
-+static const struct of_device_id pisound_of_match[] = {
-+ { .compatible = "blokaslabs,pisound", },
-+ { .compatible = "blokaslabs,pisound-spi", },
-+ {},
-+};
-+
-+enum {
-+ SWITCH = 0,
-+ VOLUME = 1,
-+};
-+
-+static int pisnd_ctl_info(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_info *uinfo)
-+{
-+ if (kcontrol->private_value == SWITCH) {
-+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-+ uinfo->count = 1;
-+ uinfo->value.integer.min = 0;
-+ uinfo->value.integer.max = 1;
-+ return 0;
-+ } else if (kcontrol->private_value == VOLUME) {
-+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-+ uinfo->count = 1;
-+ uinfo->value.integer.min = 0;
-+ uinfo->value.integer.max = 100;
-+ return 0;
-+ }
-+ return -EINVAL;
-+}
-+
-+static int pisnd_ctl_get(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ if (kcontrol->private_value == SWITCH) {
-+ ucontrol->value.integer.value[0] = 1;
-+ return 0;
-+ } else if (kcontrol->private_value == VOLUME) {
-+ ucontrol->value.integer.value[0] = 100;
-+ return 0;
-+ }
-+
-+ return -EINVAL;
-+}
-+
-+static struct snd_kcontrol_new pisnd_ctl[] = {
-+ {
-+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-+ .name = "PCM Playback Switch",
-+ .index = 0,
-+ .private_value = SWITCH,
-+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
-+ .info = pisnd_ctl_info,
-+ .get = pisnd_ctl_get,
-+ },
-+ {
-+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-+ .name = "PCM Playback Volume",
-+ .index = 0,
-+ .private_value = VOLUME,
-+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
-+ .info = pisnd_ctl_info,
-+ .get = pisnd_ctl_get,
-+ },
-+};
-+
-+static int pisnd_ctl_init(struct snd_card *card)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(pisnd_ctl); ++i) {
-+ err = snd_ctl_add(card, snd_ctl_new1(&pisnd_ctl[i], NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+static int pisnd_ctl_uninit(void)
-+{
-+ return 0;
-+}
-+
-+static struct gpio_desc *osr0, *osr1, *osr2;
-+static struct gpio_desc *reset;
-+static struct gpio_desc *button;
-+
-+static int pisnd_hw_params(
-+ struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params
-+ )
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-+
-+ /* Pisound runs on fixed 32 clock counts per channel,
-+ * as generated by the master ADC.
-+ */
-+ snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
-+
-+ printd("rate = %d\n", params_rate(params));
-+ printd("ch = %d\n", params_channels(params));
-+ printd("bits = %u\n",
-+ snd_pcm_format_physical_width(params_format(params)));
-+ printd("format = %d\n", params_format(params));
-+
-+ gpiod_set_value(reset, false);
-+
-+ switch (params_rate(params)) {
-+ case 48000:
-+ gpiod_set_value(osr0, true);
-+ gpiod_set_value(osr1, false);
-+ gpiod_set_value(osr2, false);
-+ break;
-+ case 96000:
-+ gpiod_set_value(osr0, true);
-+ gpiod_set_value(osr1, false);
-+ gpiod_set_value(osr2, true);
-+ break;
-+ case 192000:
-+ gpiod_set_value(osr0, true);
-+ gpiod_set_value(osr1, true);
-+ gpiod_set_value(osr2, true);
-+ break;
-+ default:
-+ printe("Unsupported rate %u!\n", params_rate(params));
-+ return -EINVAL;
-+ }
-+
-+ gpiod_set_value(reset, true);
-+
-+ return 0;
-+}
-+
-+static unsigned int rates[3] = {
-+ 48000, 96000, 192000
-+};
-+
-+static struct snd_pcm_hw_constraint_list constraints_rates = {
-+ .count = ARRAY_SIZE(rates),
-+ .list = rates,
-+ .mask = 0,
-+};
-+
-+static int pisnd_startup(struct snd_pcm_substream *substream)
-+{
-+ int err = snd_pcm_hw_constraint_list(
-+ substream->runtime,
-+ 0,
-+ SNDRV_PCM_HW_PARAM_RATE,
-+ &constraints_rates
-+ );
-+
-+ if (err < 0)
-+ return err;
-+
-+ err = snd_pcm_hw_constraint_single(
-+ substream->runtime,
-+ SNDRV_PCM_HW_PARAM_CHANNELS,
-+ 2
-+ );
-+
-+ if (err < 0)
-+ return err;
-+
-+ err = snd_pcm_hw_constraint_mask64(
-+ substream->runtime,
-+ SNDRV_PCM_HW_PARAM_FORMAT,
-+ SNDRV_PCM_FMTBIT_S16_LE |
-+ SNDRV_PCM_FMTBIT_S24_LE |
-+ SNDRV_PCM_FMTBIT_S32_LE
-+ );
-+
-+ if (err < 0)
-+ return err;
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_ops pisnd_ops = {
-+ .startup = pisnd_startup,
-+ .hw_params = pisnd_hw_params,
-+};
-+
-+SND_SOC_DAILINK_DEFS(pisnd,
-+ DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
-+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
-+ DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
-+
-+static struct snd_soc_dai_link pisnd_dai[] = {
-+ {
-+ .name = "pisound",
-+ .stream_name = "pisound",
-+ .dai_fmt =
-+ SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF |
-+ SND_SOC_DAIFMT_CBM_CFM,
-+ .ops = &pisnd_ops,
-+ SND_SOC_DAILINK_REG(pisnd),
-+ },
-+};
-+
-+static int pisnd_card_probe(struct snd_soc_card *card)
-+{
-+ int err = pisnd_midi_init(card->snd_card);
-+
-+ if (err < 0) {
-+ printe("pisnd_midi_init failed: %d\n", err);
-+ return err;
-+ }
-+
-+ err = pisnd_ctl_init(card->snd_card);
-+ if (err < 0) {
-+ printe("pisnd_ctl_init failed: %d\n", err);
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+static int pisnd_card_remove(struct snd_soc_card *card)
-+{
-+ pisnd_ctl_uninit();
-+ pisnd_midi_uninit();
-+ return 0;
-+}
-+
-+static struct snd_soc_card pisnd_card = {
-+ .name = "pisound",
-+ .owner = THIS_MODULE,
-+ .dai_link = pisnd_dai,
-+ .num_links = ARRAY_SIZE(pisnd_dai),
-+ .probe = pisnd_card_probe,
-+ .remove = pisnd_card_remove,
-+};
-+
-+static int pisnd_init_gpio(struct device *dev)
-+{
-+ osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
-+ osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
-+ osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
-+
-+ reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
-+
-+ button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
-+
-+ gpiod_direction_output(osr0, 1);
-+ gpiod_direction_output(osr1, 1);
-+ gpiod_direction_output(osr2, 1);
-+ gpiod_direction_output(reset, 1);
-+
-+ gpiod_set_value(reset, false);
-+ gpiod_set_value(osr0, true);
-+ gpiod_set_value(osr1, false);
-+ gpiod_set_value(osr2, false);
-+ gpiod_set_value(reset, true);
-+
-+ gpiod_export(button, false);
-+
-+ return 0;
-+}
-+
-+static int pisnd_uninit_gpio(void)
-+{
-+ int i;
-+
-+ struct gpio_desc **gpios[] = {
-+ &osr0, &osr1, &osr2, &reset, &button,
-+ };
-+
-+ gpiod_unexport(button);
-+
-+ for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
-+ if (*gpios[i] == NULL) {
-+ printd("weird, GPIO[%d] is NULL already\n", i);
-+ continue;
-+ }
-+
-+ gpiod_put(*gpios[i]);
-+ *gpios[i] = NULL;
-+ }
-+
-+ return 0;
-+}
-+
-+static struct kobject *pisnd_kobj;
-+
-+static ssize_t pisnd_serial_show(
-+ struct kobject *kobj,
-+ struct kobj_attribute *attr,
-+ char *buf
-+ )
-+{
-+ return sprintf(buf, "%s\n", pisnd_spi_get_serial());
-+}
-+
-+static ssize_t pisnd_id_show(
-+ struct kobject *kobj,
-+ struct kobj_attribute *attr,
-+ char *buf
-+ )
-+{
-+ return sprintf(buf, "%s\n", pisnd_spi_get_id());
-+}
-+
-+static ssize_t pisnd_version_show(
-+ struct kobject *kobj,
-+ struct kobj_attribute *attr,
-+ char *buf
-+ )
-+{
-+ return sprintf(buf, "%s\n", pisnd_spi_get_version());
-+}
-+
-+static ssize_t pisnd_led_store(
-+ struct kobject *kobj,
-+ struct kobj_attribute *attr,
-+ const char *buf,
-+ size_t length
-+ )
-+{
-+ uint32_t timeout;
-+ int err;
-+
-+ err = kstrtou32(buf, 10, &timeout);
-+
-+ if (err == 0 && timeout <= 255)
-+ pisnd_spi_flash_leds(timeout);
-+
-+ return length;
-+}
-+
-+static struct kobj_attribute pisnd_serial_attribute =
-+ __ATTR(serial, 0444, pisnd_serial_show, NULL);
-+static struct kobj_attribute pisnd_id_attribute =
-+ __ATTR(id, 0444, pisnd_id_show, NULL);
-+static struct kobj_attribute pisnd_version_attribute =
-+ __ATTR(version, 0444, pisnd_version_show, NULL);
-+static struct kobj_attribute pisnd_led_attribute =
-+ __ATTR(led, 0644, NULL, pisnd_led_store);
-+
-+static struct attribute *attrs[] = {
-+ &pisnd_serial_attribute.attr,
-+ &pisnd_id_attribute.attr,
-+ &pisnd_version_attribute.attr,
-+ &pisnd_led_attribute.attr,
-+ NULL
-+};
-+
-+static struct attribute_group attr_group = { .attrs = attrs };
-+
-+static int pisnd_probe(struct platform_device *pdev)
-+{
-+ int ret = 0;
-+ int i;
-+
-+ ret = pisnd_spi_init(&pdev->dev);
-+ if (ret < 0) {
-+ printe("pisnd_spi_init failed: %d\n", ret);
-+ return ret;
-+ }
-+
-+ printi("Detected Pisound card:\n");
-+ printi("\tSerial: %s\n", pisnd_spi_get_serial());
-+ printi("\tVersion: %s\n", pisnd_spi_get_version());
-+ printi("\tId: %s\n", pisnd_spi_get_id());
-+
-+ pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
-+ if (!pisnd_kobj) {
-+ pisnd_spi_uninit();
-+ return -ENOMEM;
-+ }
-+
-+ ret = sysfs_create_group(pisnd_kobj, &attr_group);
-+ if (ret < 0) {
-+ pisnd_spi_uninit();
-+ kobject_put(pisnd_kobj);
-+ return -ENOMEM;
-+ }
-+
-+ pisnd_init_gpio(&pdev->dev);
-+ pisnd_card.dev = &pdev->dev;
-+
-+ if (pdev->dev.of_node) {
-+ struct device_node *i2s_node;
-+
-+ i2s_node = of_parse_phandle(
-+ pdev->dev.of_node,
-+ "i2s-controller",
-+ 0
-+ );
-+
-+ for (i = 0; i < pisnd_card.num_links; ++i) {
-+ struct snd_soc_dai_link *dai = &pisnd_dai[i];
-+
-+ if (i2s_node) {
-+ dai->cpus->dai_name = NULL;
-+ dai->cpus->of_node = i2s_node;
-+ dai->platforms->name = NULL;
-+ dai->platforms->of_node = i2s_node;
-+ dai->stream_name = pisnd_spi_get_serial();
-+ }
-+ }
-+ }
-+
-+ ret = snd_soc_register_card(&pisnd_card);
-+
-+ if (ret < 0) {
-+ if (ret != -EPROBE_DEFER)
-+ printe("snd_soc_register_card() failed: %d\n", ret);
-+ pisnd_uninit_gpio();
-+ kobject_put(pisnd_kobj);
-+ pisnd_spi_uninit();
-+ }
-+
-+ return ret;
-+}
-+
-+static int pisnd_remove(struct platform_device *pdev)
-+{
-+ printi("Unloading.\n");
-+
-+ if (pisnd_kobj) {
-+ kobject_put(pisnd_kobj);
-+ pisnd_kobj = NULL;
-+ }
-+
-+ pisnd_spi_uninit();
-+
-+ /* Turn off */
-+ gpiod_set_value(reset, false);
-+ pisnd_uninit_gpio();
-+
-+ return snd_soc_unregister_card(&pisnd_card);
-+}
-+
-+MODULE_DEVICE_TABLE(of, pisound_of_match);
-+
-+static struct platform_driver pisnd_driver = {
-+ .driver = {
-+ .name = "snd-rpi-pisound",
-+ .owner = THIS_MODULE,
-+ .of_match_table = pisound_of_match,
-+ },
-+ .probe = pisnd_probe,
-+ .remove = pisnd_remove,
-+};
-+
-+module_platform_driver(pisnd_driver);
-+
-+MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
-+MODULE_DESCRIPTION("ASoC Driver for Pisound, https://blokas.io/pisound");
-+MODULE_LICENSE("GPL v2");