diff options
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.patch | 1752 |
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"); |