1 From 1c20ff1871ce0edc2d4c71a1eb4591cb4e8912e9 Mon Sep 17 00:00:00 2001
2 From: gtrainavicius <gtrainavicius@users.noreply.github.com>
3 Date: Sun, 23 Oct 2016 12:06:53 +0300
4 Subject: [PATCH] Support for Blokas Labs pisound board
7 .../devicetree/bindings/vendor-prefixes.txt | 1 +
8 arch/arm/boot/dts/overlays/Makefile | 1 +
9 arch/arm/boot/dts/overlays/README | 6 +
10 arch/arm/boot/dts/overlays/pisound-overlay.dts | 114 +++
11 arch/arm/configs/bcm2709_defconfig | 1 +
12 arch/arm/configs/bcmrpi_defconfig | 1 +
13 sound/soc/bcm/Kconfig | 6 +
14 sound/soc/bcm/Makefile | 2 +
15 sound/soc/bcm/pisound.c | 987 +++++++++++++++++++++
16 9 files changed, 1119 insertions(+)
17 create mode 100644 arch/arm/boot/dts/overlays/pisound-overlay.dts
18 create mode 100644 sound/soc/bcm/pisound.c
20 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
21 +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
22 @@ -33,6 +33,7 @@ auo AU Optronics Corporation
23 avago Avago Technologies
24 avic Shanghai AVIC Optoelectronics Co., Ltd.
25 axis Axis Communications AB
26 +blokaslabs Vilniaus Blokas UAB
27 bosch Bosch Sensortec GmbH
28 boundary Boundary Devices Inc.
29 brcm Broadcom Corporation
30 --- a/arch/arm/boot/dts/overlays/Makefile
31 +++ b/arch/arm/boot/dts/overlays/Makefile
32 @@ -62,6 +62,7 @@ dtbo-$(RPI_DT_OVERLAYS) += pi3-disable-b
33 dtbo-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt.dtbo
34 dtbo-$(RPI_DT_OVERLAYS) += piscreen.dtbo
35 dtbo-$(RPI_DT_OVERLAYS) += piscreen2r.dtbo
36 +dtbo-$(RPI_DT_OVERLAYS) += pisound.dtbo
37 dtbo-$(RPI_DT_OVERLAYS) += pitft22.dtbo
38 dtbo-$(RPI_DT_OVERLAYS) += pitft28-capacitive.dtbo
39 dtbo-$(RPI_DT_OVERLAYS) += pitft28-resistive.dtbo
40 --- a/arch/arm/boot/dts/overlays/README
41 +++ b/arch/arm/boot/dts/overlays/README
42 @@ -808,6 +808,12 @@ Params: speed Display
43 xohms Touchpanel sensitivity (X-plate resistance)
47 +Info: Configures the Blokas Labs pisound card
48 +Load: dtoverlay=pisound
53 Info: Adafruit PiTFT 2.2" screen
54 Load: dtoverlay=pitft22,<param>=<val>
56 +++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts
59 + * pisound Linux kernel module.
60 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
62 + * This program is free software; you can redistribute it and/or
63 + * modify it under the terms of the GNU General Public License
64 + * as published by the Free Software Foundation; version 2 of the
67 + * This program is distributed in the hope that it will be useful,
68 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
69 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
70 + * GNU General Public License for more details.
72 + * You should have received a copy of the GNU General Public License
73 + * along with this program; if not, write to the Free Software
74 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
80 +#include <dt-bindings/gpio/gpio.h>
83 + compatible = "brcm,bcm2708";
88 + compatible = "blokaslabs,pisound";
89 + i2s-controller = <&i2s>;
92 + pinctrl-0 = <&pisound_button_pins>;
95 + <&gpio 13 GPIO_ACTIVE_HIGH>,
96 + <&gpio 26 GPIO_ACTIVE_HIGH>,
97 + <&gpio 16 GPIO_ACTIVE_HIGH>;
100 + <&gpio 12 GPIO_ACTIVE_HIGH>,
101 + <&gpio 24 GPIO_ACTIVE_HIGH>;
103 + data_available-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
105 + button-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
112 + pinctrl-names = "default";
113 + pinctrl-0 = <&pisound_button_pins>;
115 + pisound_button_pins: pisound_button_pins {
117 + brcm,function = <0>; // Input
118 + brcm,pull = <2>; // Pull-Up
134 + #sound-dai-cells = <0>;
135 + compatible = "ti,pcm5102a";
147 + status = "disabled";
159 + #address-cells = <1>;
162 + pisound_spi: pisound_spi@0{
163 + compatible = "blokaslabs,pisound-spi";
165 + pinctrl-names = "default";
166 + pinctrl-0 = <&spi0_pins>;
167 + spi-max-frequency = <1000000>;
172 --- a/arch/arm/configs/bcm2709_defconfig
173 +++ b/arch/arm/configs/bcm2709_defconfig
174 @@ -880,6 +880,7 @@ CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
175 CONFIG_SND_DIGIDAC1_SOUNDCARD=m
176 CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
177 CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
178 +CONFIG_SND_PISOUND=m
179 CONFIG_SND_SOC_ADAU1701=m
180 CONFIG_SND_SOC_WM8804_I2C=m
181 CONFIG_SND_SIMPLE_CARD=m
182 --- a/arch/arm/configs/bcmrpi_defconfig
183 +++ b/arch/arm/configs/bcmrpi_defconfig
184 @@ -872,6 +872,7 @@ CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
185 CONFIG_SND_DIGIDAC1_SOUNDCARD=m
186 CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
187 CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
188 +CONFIG_SND_PISOUND=m
189 CONFIG_SND_SOC_ADAU1701=m
190 CONFIG_SND_SOC_WM8804_I2C=m
191 CONFIG_SND_SIMPLE_CARD=m
192 --- a/sound/soc/bcm/Kconfig
193 +++ b/sound/soc/bcm/Kconfig
194 @@ -121,3 +121,9 @@ config SND_BCM2708_SOC_ALLO_PIANO_DAC
195 select SND_SOC_PCM512x_I2C
197 Say Y or M if you want to add support for Allo Piano DAC.
200 + tristate "Support for Blokas Labs pisound"
201 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
203 + Say Y or M if you want to add support for Blokas Labs pisound.
204 --- a/sound/soc/bcm/Makefile
205 +++ b/sound/soc/bcm/Makefile
206 @@ -20,6 +20,7 @@ snd-soc-audioinjector-pi-soundcard-objs
207 snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o
208 snd-soc-dionaudio-loco-objs := dionaudio_loco.o
209 snd-soc-allo-piano-dac-objs := allo-piano-dac.o
210 +snd-soc-pisound-objs := pisound.o
212 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
213 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
214 @@ -37,3 +38,4 @@ obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDC
215 obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o
216 obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o
217 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
218 +obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o
220 +++ b/sound/soc/bcm/pisound.c
223 + * pisound Linux kernel module.
224 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
226 + * This program is free software; you can redistribute it and/or
227 + * modify it under the terms of the GNU General Public License
228 + * as published by the Free Software Foundation; version 2 of the
231 + * This program is distributed in the hope that it will be useful,
232 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
233 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
234 + * GNU General Public License for more details.
236 + * You should have received a copy of the GNU General Public License
237 + * along with this program; if not, write to the Free Software
238 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
239 + * MA 02110-1301, USA.
242 +#include <linux/init.h>
243 +#include <linux/module.h>
244 +#include <linux/platform_device.h>
245 +#include <linux/gpio.h>
246 +#include <linux/kobject.h>
247 +#include <linux/sysfs.h>
248 +#include <linux/delay.h>
249 +#include <linux/spi/spi.h>
250 +#include <linux/interrupt.h>
251 +#include <linux/kfifo.h>
253 +#include <sound/core.h>
254 +#include <sound/pcm.h>
255 +#include <sound/pcm_params.h>
256 +#include <sound/soc.h>
257 +#include <sound/jack.h>
258 +#include <sound/rawmidi.h>
259 +#include <sound/asequencer.h>
261 +static int pisnd_spi_init(struct device *dev);
262 +static void pisnd_spi_uninit(void);
264 +static void pisnd_spi_send(uint8_t val);
265 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
267 +typedef void (*pisnd_spi_recv_cb)(void *data);
268 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
270 +static const char *pisnd_spi_get_serial(void);
271 +static const char *pisnd_spi_get_id(void);
272 +static const char *pisnd_spi_get_version(void);
274 +static int pisnd_midi_init(struct snd_card *card);
275 +static void pisnd_midi_uninit(void);
277 +#define PISOUND_LOG_PREFIX "pisound: "
280 +# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
282 +# define printd(...) do {} while (0)
285 +#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
286 +#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
288 +static int pisnd_output_open(struct snd_rawmidi_substream *substream)
293 +static int pisnd_output_close(struct snd_rawmidi_substream *substream)
298 +static void pisnd_output_trigger(
299 + struct snd_rawmidi_substream *substream,
308 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
309 + pisnd_spi_send(data);
310 + snd_rawmidi_transmit_ack(substream, 1);
314 +static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
318 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
319 + pisnd_spi_send(data);
321 + snd_rawmidi_transmit_ack(substream, 1);
325 +static int pisnd_input_open(struct snd_rawmidi_substream *substream)
330 +static int pisnd_input_close(struct snd_rawmidi_substream *substream)
335 +static void pisnd_midi_recv_callback(void *substream)
340 + while ((n = pisnd_spi_recv(data, sizeof(data)))) {
341 + int res = snd_rawmidi_receive(substream, data, n);
343 + printd("midi recv 0x%02x, res = %d\n", data, res);
347 +static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
350 + pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
351 + pisnd_midi_recv_callback(substream);
353 + pisnd_spi_set_callback(NULL, NULL);
357 +static struct snd_rawmidi *g_rmidi;
359 +static struct snd_rawmidi_ops pisnd_output_ops = {
360 + .open = pisnd_output_open,
361 + .close = pisnd_output_close,
362 + .trigger = pisnd_output_trigger,
363 + .drain = pisnd_output_drain,
366 +static struct snd_rawmidi_ops pisnd_input_ops = {
367 + .open = pisnd_input_open,
368 + .close = pisnd_input_close,
369 + .trigger = pisnd_input_trigger,
372 +static void pisnd_get_port_info(
373 + struct snd_rawmidi *rmidi,
375 + struct snd_seq_port_info *seq_port_info
378 + seq_port_info->type =
379 + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
380 + SNDRV_SEQ_PORT_TYPE_HARDWARE |
381 + SNDRV_SEQ_PORT_TYPE_PORT;
382 + seq_port_info->midi_voices = 0;
385 +static struct snd_rawmidi_global_ops pisnd_global_ops = {
386 + .get_port_info = pisnd_get_port_info,
389 +static int pisnd_midi_init(struct snd_card *card)
391 + int err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
394 + printe("snd_rawmidi_new failed: %d\n", err);
398 + strcpy(g_rmidi->name, "pisound MIDI ");
399 + strcat(g_rmidi->name, pisnd_spi_get_serial());
401 + g_rmidi->info_flags =
402 + SNDRV_RAWMIDI_INFO_OUTPUT |
403 + SNDRV_RAWMIDI_INFO_INPUT |
404 + SNDRV_RAWMIDI_INFO_DUPLEX;
406 + g_rmidi->ops = &pisnd_global_ops;
408 + g_rmidi->private_data = (void *)0;
410 + snd_rawmidi_set_ops(
412 + SNDRV_RAWMIDI_STREAM_OUTPUT,
416 + snd_rawmidi_set_ops(
418 + SNDRV_RAWMIDI_STREAM_INPUT,
425 +static void pisnd_midi_uninit(void)
429 +static void *g_recvData;
430 +static pisnd_spi_recv_cb g_recvCallback;
432 +#define FIFO_SIZE 512
434 +static char g_serial_num[11];
435 +static char g_id[25];
436 +static char g_version[5];
438 +DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
439 +DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
441 +static struct gpio_desc *data_available;
442 +static struct gpio_desc *spi_reset;
444 +static struct spi_device *pisnd_spi_device;
446 +static struct workqueue_struct *pisnd_workqueue;
447 +static struct work_struct pisnd_work_process;
449 +static void pisnd_work_handler(struct work_struct *work);
451 +static uint16_t spi_transfer16(uint16_t val);
453 +static int pisnd_init_workqueues(void)
455 + pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
456 + INIT_WORK(&pisnd_work_process, pisnd_work_handler);
461 +static void pisnd_uninit_workqueues(void)
463 + flush_workqueue(pisnd_workqueue);
464 + destroy_workqueue(pisnd_workqueue);
466 + pisnd_workqueue = NULL;
469 +static bool pisnd_spi_has_more(void)
471 + return gpiod_get_value(data_available);
478 +static void pisnd_schedule_process(enum task_e task)
480 + if (pisnd_spi_device != NULL &&
481 + pisnd_workqueue != NULL &&
482 + !work_pending(&pisnd_work_process)
484 + printd("schedule: has more = %d\n", pisnd_spi_has_more());
485 + if (task == TASK_PROCESS)
486 + queue_work(pisnd_workqueue, &pisnd_work_process);
490 +static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
492 + if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
493 + printd("schedule from irq\n");
494 + pisnd_schedule_process(TASK_PROCESS);
497 + return IRQ_HANDLED;
500 +static DEFINE_SPINLOCK(spilock);
501 +static unsigned long spilockflags;
503 +static uint16_t spi_transfer16(uint16_t val)
506 + struct spi_transfer transfer;
507 + struct spi_message msg;
511 + if (!pisnd_spi_device) {
512 + printe("pisnd_spi_device null, returning\n");
516 + spi_message_init(&msg);
518 + memset(&transfer, 0, sizeof(transfer));
519 + memset(&rxbuf, 0, sizeof(rxbuf));
521 + txbuf[0] = val >> 8;
522 + txbuf[1] = val & 0xff;
524 + transfer.tx_buf = &txbuf;
525 + transfer.rx_buf = &rxbuf;
526 + transfer.len = sizeof(txbuf);
527 + transfer.speed_hz = 125000;
528 + transfer.delay_usecs = 100;
529 + spi_message_add_tail(&transfer, &msg);
531 + spin_lock_irqsave(&spilock, spilockflags);
532 + err = spi_sync(pisnd_spi_device, &msg);
533 + spin_unlock_irqrestore(&spilock, spilockflags);
536 + printe("spi_sync error %d\n", err);
540 + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
541 + printd("hasMore %d\n", pisnd_spi_has_more());
543 + return (rxbuf[0] << 8) | rxbuf[1];
546 +static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
552 + memset(dst, 0, length);
555 + rx = spi_transfer16(0);
564 + for (i = 0; i < size; ++i) {
565 + rx = spi_transfer16(0);
569 + dst[i] = rx & 0xff;
577 +static int spi_device_match(struct device *dev, void *data)
579 + struct spi_device *spi = container_of(dev, struct spi_device, dev);
581 + printd(" %s %s %dkHz %d bits mode=0x%02X\n",
582 + spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
583 + spi->bits_per_word, spi->mode);
585 + if (strcmp("pisound-spi", spi->modalias) == 0) {
586 + printi("\tFound!\n");
590 + printe("\tNot found!\n");
594 +static struct spi_device *pisnd_spi_find_device(void)
596 + struct device *dev;
598 + printi("Searching for spi device...\n");
599 + dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
601 + return container_of(dev, struct spi_device, dev);
606 +static void pisnd_work_handler(struct work_struct *work)
612 + if (work == &pisnd_work_process) {
613 + if (pisnd_spi_device == NULL)
620 + if (kfifo_get(&spi_fifo_out, &val))
623 + rx = spi_transfer16(tx);
626 + kfifo_put(&spi_fifo_in, rx & 0xff);
627 + if (kfifo_len(&spi_fifo_in) > 16
629 + g_recvCallback(g_recvData);
632 + || !kfifo_is_empty(&spi_fifo_out)
633 + || pisnd_spi_has_more()
636 + if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
637 + g_recvCallback(g_recvData);
641 +static int pisnd_spi_gpio_init(struct device *dev)
643 + spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
644 + data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
646 + gpiod_direction_output(spi_reset, 1);
647 + gpiod_direction_input(data_available);
649 + /* Reset the slave. */
650 + gpiod_set_value(spi_reset, false);
652 + gpiod_set_value(spi_reset, true);
654 + /* Give time for spi slave to start. */
660 +static void pisnd_spi_gpio_uninit(void)
662 + gpiod_set_value(spi_reset, false);
663 + gpiod_put(spi_reset);
666 + gpiod_put(data_available);
667 + data_available = NULL;
670 +static int pisnd_spi_gpio_irq_init(struct device *dev)
672 + return request_irq(
673 + gpiod_to_irq(data_available),
674 + data_available_interrupt_handler,
675 + IRQF_TIMER | IRQF_TRIGGER_RISING,
676 + "data_available_int",
681 +static void pisnd_spi_gpio_irq_uninit(void)
683 + free_irq(gpiod_to_irq(data_available), NULL);
686 +static int spi_read_info(void)
697 + memset(g_serial_num, 0, sizeof(g_serial_num));
698 + memset(g_version, 0, sizeof(g_version));
699 + memset(g_id, 0, sizeof(g_id));
701 + tmp = spi_transfer16(0);
706 + count = tmp & 0xff;
708 + for (i = 0; i < count; ++i) {
709 + memset(buffer, 0, sizeof(buffer));
710 + ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
729 + if (n >= sizeof(g_serial_num))
732 + memcpy(g_serial_num, buffer, sizeof(g_serial_num));
736 + if (n >= sizeof(g_id))
740 + for (j = 0; j < n; ++j)
741 + p += sprintf(p, "%02x", buffer[j]);
752 +static int pisnd_spi_init(struct device *dev)
755 + struct spi_device *spi;
757 + memset(g_serial_num, 0, sizeof(g_serial_num));
758 + memset(g_id, 0, sizeof(g_id));
759 + memset(g_version, 0, sizeof(g_version));
761 + spi = pisnd_spi_find_device();
764 + printd("initializing spi!\n");
765 + pisnd_spi_device = spi;
766 + ret = spi_setup(pisnd_spi_device);
768 + printe("SPI device not found, deferring!\n");
769 + return -EPROBE_DEFER;
772 + ret = pisnd_spi_gpio_init(dev);
775 + printe("SPI GPIO init failed: %d\n", ret);
776 + spi_dev_put(pisnd_spi_device);
777 + pisnd_spi_device = NULL;
778 + pisnd_spi_gpio_uninit();
782 + ret = spi_read_info();
785 + printe("Reading card info failed: %d\n", ret);
786 + spi_dev_put(pisnd_spi_device);
787 + pisnd_spi_device = NULL;
788 + pisnd_spi_gpio_uninit();
792 + /* Flash the LEDs. */
793 + spi_transfer16(0xf000);
795 + ret = pisnd_spi_gpio_irq_init(dev);
797 + printe("SPI irq request failed: %d\n", ret);
798 + spi_dev_put(pisnd_spi_device);
799 + pisnd_spi_device = NULL;
800 + pisnd_spi_gpio_irq_uninit();
801 + pisnd_spi_gpio_uninit();
804 + ret = pisnd_init_workqueues();
806 + printe("Workqueue initialization failed: %d\n", ret);
807 + spi_dev_put(pisnd_spi_device);
808 + pisnd_spi_device = NULL;
809 + pisnd_spi_gpio_irq_uninit();
810 + pisnd_spi_gpio_uninit();
811 + pisnd_uninit_workqueues();
815 + if (pisnd_spi_has_more()) {
816 + printd("data is available, scheduling from init\n");
817 + pisnd_schedule_process(TASK_PROCESS);
823 +static void pisnd_spi_uninit(void)
825 + pisnd_uninit_workqueues();
827 + spi_dev_put(pisnd_spi_device);
828 + pisnd_spi_device = NULL;
830 + pisnd_spi_gpio_irq_uninit();
831 + pisnd_spi_gpio_uninit();
834 +static void pisnd_spi_send(uint8_t val)
836 + kfifo_put(&spi_fifo_out, val);
837 + printd("schedule from spi_send\n");
838 + pisnd_schedule_process(TASK_PROCESS);
841 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
843 + return kfifo_out(&spi_fifo_in, buffer, length);
846 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
849 + g_recvCallback = cb;
852 +static const char *pisnd_spi_get_serial(void)
854 + if (strlen(g_serial_num))
855 + return g_serial_num;
860 +static const char *pisnd_spi_get_id(void)
868 +static const char *pisnd_spi_get_version(void)
870 + if (strlen(g_version))
876 +static const struct of_device_id pisound_of_match[] = {
877 + { .compatible = "blokaslabs,pisound", },
878 + { .compatible = "blokaslabs,pisound-spi", },
882 +static struct gpio_desc *osr0, *osr1, *osr2;
883 +static struct gpio_desc *reset;
884 +static struct gpio_desc *button;
886 +static int pisnd_hw_params(
887 + struct snd_pcm_substream *substream,
888 + struct snd_pcm_hw_params *params
891 + printd("rate = %d\n", params_rate(params));
892 + printd("ch = %d\n", params_channels(params));
893 + printd("bits = %u\n",
894 + snd_pcm_format_physical_width(params_format(params)));
895 + printd("format = %d\n", params_format(params));
897 + gpiod_set_value(reset, false);
899 + switch (params_rate(params)) {
901 + gpiod_set_value(osr0, true);
902 + gpiod_set_value(osr1, false);
903 + gpiod_set_value(osr2, false);
906 + gpiod_set_value(osr0, true);
907 + gpiod_set_value(osr1, true);
908 + gpiod_set_value(osr2, false);
911 + gpiod_set_value(osr0, true);
912 + gpiod_set_value(osr1, true);
913 + gpiod_set_value(osr2, true);
916 + printe("Unsupported rate %u!\n", params_rate(params));
920 + gpiod_set_value(reset, true);
925 +static unsigned int rates[3] = {
926 + 48000, 96000, 192000
929 +static struct snd_pcm_hw_constraint_list constraints_rates = {
930 + .count = ARRAY_SIZE(rates),
935 +static unsigned int sample_bits[] = {
939 +static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
940 + .count = ARRAY_SIZE(sample_bits),
941 + .list = sample_bits,
945 +static int pisnd_startup(struct snd_pcm_substream *substream)
947 + int err = snd_pcm_hw_constraint_list(
948 + substream->runtime,
950 + SNDRV_PCM_HW_PARAM_RATE,
957 + err = snd_pcm_hw_constraint_list(
958 + substream->runtime,
960 + SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
961 + &constraints_sample_bits
970 +static struct snd_soc_ops pisnd_ops = {
971 + .startup = pisnd_startup,
972 + .hw_params = pisnd_hw_params,
975 +static struct snd_soc_dai_link pisnd_dai[] = {
978 + .stream_name = "pisound",
979 + .cpu_dai_name = "bcm2708-i2s.0",
980 + .codec_dai_name = "snd-soc-dummy-dai",
981 + .platform_name = "bcm2708-i2s.0",
982 + .codec_name = "snd-soc-dummy",
984 + SND_SOC_DAIFMT_I2S |
985 + SND_SOC_DAIFMT_NB_NF |
986 + SND_SOC_DAIFMT_CBM_CFM,
991 +static int pisnd_card_probe(struct snd_soc_card *card)
993 + int err = pisnd_midi_init(card->snd_card);
996 + printe("pisnd_midi_init failed: %d\n", err);
1001 +static int pisnd_card_remove(struct snd_soc_card *card)
1003 + pisnd_midi_uninit();
1007 +static struct snd_soc_card pisnd_card = {
1008 + .name = "pisound",
1009 + .owner = THIS_MODULE,
1010 + .dai_link = pisnd_dai,
1011 + .num_links = ARRAY_SIZE(pisnd_dai),
1012 + .probe = pisnd_card_probe,
1013 + .remove = pisnd_card_remove,
1016 +static int pisnd_init_gpio(struct device *dev)
1018 + osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
1019 + osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
1020 + osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
1022 + reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
1024 + button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
1026 + gpiod_direction_output(osr0, 1);
1027 + gpiod_direction_output(osr1, 1);
1028 + gpiod_direction_output(osr2, 1);
1029 + gpiod_direction_output(reset, 1);
1031 + gpiod_set_value(reset, false);
1032 + gpiod_set_value(osr0, true);
1033 + gpiod_set_value(osr1, false);
1034 + gpiod_set_value(osr2, false);
1035 + gpiod_set_value(reset, true);
1037 + gpiod_export(button, false);
1042 +static int pisnd_uninit_gpio(void)
1046 + struct gpio_desc **gpios[] = {
1047 + &osr0, &osr1, &osr2, &reset, &button,
1050 + gpiod_unexport(button);
1052 + for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
1053 + if (*gpios[i] == NULL) {
1054 + printd("weird, GPIO[%d] is NULL already\n", i);
1058 + gpiod_put(*gpios[i]);
1065 +static struct kobject *pisnd_kobj;
1067 +static ssize_t pisnd_serial_show(
1068 + struct kobject *kobj,
1069 + struct kobj_attribute *attr,
1073 + return sprintf(buf, "%s\n", pisnd_spi_get_serial());
1076 +static ssize_t pisnd_id_show(
1077 + struct kobject *kobj,
1078 + struct kobj_attribute *attr,
1082 + return sprintf(buf, "%s\n", pisnd_spi_get_id());
1085 +static ssize_t pisnd_version_show(
1086 + struct kobject *kobj,
1087 + struct kobj_attribute *attr,
1091 + return sprintf(buf, "%s\n", pisnd_spi_get_version());
1094 +static struct kobj_attribute pisnd_serial_attribute =
1095 + __ATTR(serial, 0644, pisnd_serial_show, NULL);
1096 +static struct kobj_attribute pisnd_id_attribute =
1097 + __ATTR(id, 0644, pisnd_id_show, NULL);
1098 +static struct kobj_attribute pisnd_version_attribute =
1099 + __ATTR(version, 0644, pisnd_version_show, NULL);
1101 +static struct attribute *attrs[] = {
1102 + &pisnd_serial_attribute.attr,
1103 + &pisnd_id_attribute.attr,
1104 + &pisnd_version_attribute.attr,
1108 +static struct attribute_group attr_group = { .attrs = attrs };
1110 +static int pisnd_probe(struct platform_device *pdev)
1115 + ret = pisnd_spi_init(&pdev->dev);
1117 + printe("pisnd_spi_init failed: %d\n", ret);
1121 + printi("Detected pisound card:\n");
1122 + printi("\tSerial: %s\n", pisnd_spi_get_serial());
1123 + printi("\tVersion: %s\n", pisnd_spi_get_version());
1124 + printi("\tId: %s\n", pisnd_spi_get_id());
1126 + pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
1127 + if (!pisnd_kobj) {
1128 + pisnd_spi_uninit();
1132 + ret = sysfs_create_group(pisnd_kobj, &attr_group);
1134 + pisnd_spi_uninit();
1135 + kobject_put(pisnd_kobj);
1139 + pisnd_init_gpio(&pdev->dev);
1140 + pisnd_card.dev = &pdev->dev;
1142 + if (pdev->dev.of_node) {
1143 + struct device_node *i2s_node;
1145 + i2s_node = of_parse_phandle(
1146 + pdev->dev.of_node,
1151 + for (i = 0; i < pisnd_card.num_links; ++i) {
1152 + struct snd_soc_dai_link *dai = &pisnd_dai[i];
1155 + dai->cpu_dai_name = NULL;
1156 + dai->cpu_of_node = i2s_node;
1157 + dai->platform_name = NULL;
1158 + dai->platform_of_node = i2s_node;
1159 + dai->stream_name = pisnd_spi_get_serial();
1164 + ret = snd_soc_register_card(&pisnd_card);
1167 + printe("snd_soc_register_card() failed: %d\n", ret);
1168 + pisnd_uninit_gpio();
1169 + kobject_put(pisnd_kobj);
1170 + pisnd_spi_uninit();
1176 +static int pisnd_remove(struct platform_device *pdev)
1179 + kobject_put(pisnd_kobj);
1180 + pisnd_kobj = NULL;
1183 + pisnd_spi_uninit();
1186 + gpiod_set_value(reset, false);
1187 + pisnd_uninit_gpio();
1189 + return snd_soc_unregister_card(&pisnd_card);
1192 +MODULE_DEVICE_TABLE(of, pisound_of_match);
1194 +static struct platform_driver pisnd_driver = {
1196 + .name = "snd-rpi-pisound",
1197 + .owner = THIS_MODULE,
1198 + .of_match_table = pisound_of_match,
1200 + .probe = pisnd_probe,
1201 + .remove = pisnd_remove,
1204 +module_platform_driver(pisnd_driver);
1206 +MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
1207 +MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound");
1208 +MODULE_LICENSE("GPL v2");