1 From 225c4a483b34eb88cab2d617a961fef9cb5a876e 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 078/725] Support for Blokas Labs pisound board
6 Pisound dynamic overlay (#1760)
8 Restructuring pisound-overlay.dts, so it can be loaded and unloaded dynamically using dtoverlay.
10 Print a logline when the kernel module is removed.
14 * Added a writable sysfs object to enable scripts / user space software
15 to blink MIDI activity LEDs for variable duration.
16 * Improved hw_param constraints setting.
17 * Added compatibility with S16_LE sample format.
18 * Exposed some simple placeholder volume controls, so the card appears
21 Add missing SND_PISOUND selects dependency to SND_RAWMIDI
23 Without it the Pisound module fails to compile.
24 See https://github.com/raspberrypi/linux/issues/2366
26 Updates for Pisound module code:
28 * Merged 'Fix a warning in DEBUG builds' (1c8b82b).
29 * Updating some strings and copyright information.
30 * Fix for handling high load of MIDI input and output.
31 * Use dual rate oversampling ratio for 96kHz instead of single
34 Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
36 Fixing memset call in pisound.c
38 Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
40 Fix for Pisound's MIDI Input getting blocked for a while in rare cases.
42 There was a possible race condition which could lead to Input's FIFO queue
43 to be underflown, causing high amount of processing in the worker thread for
46 Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
48 .../devicetree/bindings/vendor-prefixes.txt | 1 +
49 sound/soc/bcm/pisound.c | 1204 +++++++++++++++++
50 2 files changed, 1205 insertions(+)
51 create mode 100644 sound/soc/bcm/pisound.c
53 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
54 +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
55 @@ -56,6 +56,7 @@ axis Axis Communications AB
56 bananapi BIPAI KEJI LIMITED
57 bhf Beckhoff Automation GmbH & Co. KG
58 bitmain Bitmain Technologies
59 +blokaslabs Vilniaus Blokas UAB
60 boe BOE Technology Group Co., Ltd.
61 bosch Bosch Sensortec GmbH
62 boundary Boundary Devices Inc.
64 +++ b/sound/soc/bcm/pisound.c
67 + * Pisound Linux kernel module.
68 + * Copyright (C) 2016-2017 Vilniaus Blokas UAB, https://blokas.io/pisound
70 + * This program is free software; you can redistribute it and/or
71 + * modify it under the terms of the GNU General Public License
72 + * as published by the Free Software Foundation; version 2 of the
75 + * This program is distributed in the hope that it will be useful,
76 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
77 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
78 + * GNU General Public License for more details.
80 + * You should have received a copy of the GNU General Public License
81 + * along with this program; if not, write to the Free Software
82 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
83 + * MA 02110-1301, USA.
86 +#include <linux/init.h>
87 +#include <linux/module.h>
88 +#include <linux/platform_device.h>
89 +#include <linux/gpio.h>
90 +#include <linux/kobject.h>
91 +#include <linux/sysfs.h>
92 +#include <linux/delay.h>
93 +#include <linux/spi/spi.h>
94 +#include <linux/interrupt.h>
95 +#include <linux/kfifo.h>
96 +#include <linux/jiffies.h>
98 +#include <sound/core.h>
99 +#include <sound/pcm.h>
100 +#include <sound/pcm_params.h>
101 +#include <sound/soc.h>
102 +#include <sound/jack.h>
103 +#include <sound/rawmidi.h>
104 +#include <sound/asequencer.h>
105 +#include <sound/control.h>
107 +static int pisnd_spi_init(struct device *dev);
108 +static void pisnd_spi_uninit(void);
110 +static void pisnd_spi_flush(void);
111 +static void pisnd_spi_start(void);
112 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
114 +typedef void (*pisnd_spi_recv_cb)(void *data);
115 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
117 +static const char *pisnd_spi_get_serial(void);
118 +static const char *pisnd_spi_get_id(void);
119 +static const char *pisnd_spi_get_version(void);
121 +static int pisnd_midi_init(struct snd_card *card);
122 +static void pisnd_midi_uninit(void);
128 +static void pisnd_schedule_process(enum task_e task);
130 +#define PISOUND_LOG_PREFIX "pisound: "
132 +#ifdef PISOUND_DEBUG
133 +# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
135 +# define printd(...) do {} while (0)
138 +#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
139 +#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
141 +static struct snd_rawmidi *g_rmidi;
142 +static struct snd_rawmidi_substream *g_midi_output_substream;
144 +static int pisnd_output_open(struct snd_rawmidi_substream *substream)
146 + g_midi_output_substream = substream;
150 +static int pisnd_output_close(struct snd_rawmidi_substream *substream)
152 + g_midi_output_substream = NULL;
156 +static void pisnd_output_trigger(
157 + struct snd_rawmidi_substream *substream,
161 + if (substream != g_midi_output_substream) {
162 + printe("MIDI output trigger called for an unexpected stream!");
172 +static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
177 +static int pisnd_input_open(struct snd_rawmidi_substream *substream)
182 +static int pisnd_input_close(struct snd_rawmidi_substream *substream)
187 +static void pisnd_midi_recv_callback(void *substream)
192 + while ((n = pisnd_spi_recv(data, sizeof(data)))) {
193 + int res = snd_rawmidi_receive(substream, data, n);
195 + printd("midi recv %u bytes, res = %d\n", n, res);
199 +static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
202 + pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
203 + pisnd_schedule_process(TASK_PROCESS);
205 + pisnd_spi_set_callback(NULL, NULL);
209 +static struct snd_rawmidi_ops pisnd_output_ops = {
210 + .open = pisnd_output_open,
211 + .close = pisnd_output_close,
212 + .trigger = pisnd_output_trigger,
213 + .drain = pisnd_output_drain,
216 +static struct snd_rawmidi_ops pisnd_input_ops = {
217 + .open = pisnd_input_open,
218 + .close = pisnd_input_close,
219 + .trigger = pisnd_input_trigger,
222 +static void pisnd_get_port_info(
223 + struct snd_rawmidi *rmidi,
225 + struct snd_seq_port_info *seq_port_info
228 + seq_port_info->type =
229 + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
230 + SNDRV_SEQ_PORT_TYPE_HARDWARE |
231 + SNDRV_SEQ_PORT_TYPE_PORT;
232 + seq_port_info->midi_voices = 0;
235 +static struct snd_rawmidi_global_ops pisnd_global_ops = {
236 + .get_port_info = pisnd_get_port_info,
239 +static int pisnd_midi_init(struct snd_card *card)
243 + g_midi_output_substream = NULL;
245 + err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
248 + printe("snd_rawmidi_new failed: %d\n", err);
252 + strcpy(g_rmidi->name, "pisound MIDI ");
253 + strcat(g_rmidi->name, pisnd_spi_get_serial());
255 + g_rmidi->info_flags =
256 + SNDRV_RAWMIDI_INFO_OUTPUT |
257 + SNDRV_RAWMIDI_INFO_INPUT |
258 + SNDRV_RAWMIDI_INFO_DUPLEX;
260 + g_rmidi->ops = &pisnd_global_ops;
262 + g_rmidi->private_data = (void *)0;
264 + snd_rawmidi_set_ops(
266 + SNDRV_RAWMIDI_STREAM_OUTPUT,
270 + snd_rawmidi_set_ops(
272 + SNDRV_RAWMIDI_STREAM_INPUT,
279 +static void pisnd_midi_uninit(void)
283 +static void *g_recvData;
284 +static pisnd_spi_recv_cb g_recvCallback;
286 +#define FIFO_SIZE 4096
288 +static char g_serial_num[11];
289 +static char g_id[25];
290 +static char g_version[5];
292 +static uint8_t g_ledFlashDuration;
293 +static bool g_ledFlashDurationChanged;
295 +DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
296 +DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
298 +static struct gpio_desc *data_available;
299 +static struct gpio_desc *spi_reset;
301 +static struct spi_device *pisnd_spi_device;
303 +static struct workqueue_struct *pisnd_workqueue;
304 +static struct work_struct pisnd_work_process;
306 +static void pisnd_work_handler(struct work_struct *work);
308 +static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len);
309 +static uint16_t spi_transfer16(uint16_t val);
311 +static int pisnd_init_workqueues(void)
313 + pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
314 + INIT_WORK(&pisnd_work_process, pisnd_work_handler);
319 +static void pisnd_uninit_workqueues(void)
321 + flush_workqueue(pisnd_workqueue);
322 + destroy_workqueue(pisnd_workqueue);
324 + pisnd_workqueue = NULL;
327 +static bool pisnd_spi_has_more(void)
329 + return gpiod_get_value(data_available);
332 +static void pisnd_schedule_process(enum task_e task)
334 + if (pisnd_spi_device != NULL &&
335 + pisnd_workqueue != NULL &&
336 + !work_pending(&pisnd_work_process)
338 + printd("schedule: has more = %d\n", pisnd_spi_has_more());
339 + if (task == TASK_PROCESS)
340 + queue_work(pisnd_workqueue, &pisnd_work_process);
344 +static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
346 + if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
347 + printd("schedule from irq\n");
348 + pisnd_schedule_process(TASK_PROCESS);
351 + return IRQ_HANDLED;
354 +static DEFINE_SPINLOCK(spilock);
355 +static unsigned long spilockflags;
357 +static uint16_t spi_transfer16(uint16_t val)
362 + if (!pisnd_spi_device) {
363 + printe("pisnd_spi_device null, returning\n");
367 + txbuf[0] = val >> 8;
368 + txbuf[1] = val & 0xff;
370 + spi_transfer(txbuf, rxbuf, sizeof(txbuf));
372 + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
374 + return (rxbuf[0] << 8) | rxbuf[1];
377 +static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len)
380 + struct spi_transfer transfer;
381 + struct spi_message msg;
383 + memset(rxbuf, 0, len);
385 + if (!pisnd_spi_device) {
386 + printe("pisnd_spi_device null, returning\n");
390 + spi_message_init(&msg);
392 + memset(&transfer, 0, sizeof(transfer));
394 + transfer.tx_buf = txbuf;
395 + transfer.rx_buf = rxbuf;
396 + transfer.len = len;
397 + transfer.speed_hz = 100000;
398 + transfer.delay_usecs = 10;
399 + spi_message_add_tail(&transfer, &msg);
401 + spin_lock_irqsave(&spilock, spilockflags);
402 + err = spi_sync(pisnd_spi_device, &msg);
403 + spin_unlock_irqrestore(&spilock, spilockflags);
406 + printe("spi_sync error %d\n", err);
410 + printd("hasMore %d\n", pisnd_spi_has_more());
413 +static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
419 + memset(dst, 0, length);
422 + rx = spi_transfer16(0);
431 + for (i = 0; i < size; ++i) {
432 + rx = spi_transfer16(0);
436 + dst[i] = rx & 0xff;
444 +static int spi_device_match(struct device *dev, void *data)
446 + struct spi_device *spi = container_of(dev, struct spi_device, dev);
448 + printd(" %s %s %dkHz %d bits mode=0x%02X\n",
449 + spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
450 + spi->bits_per_word, spi->mode);
452 + if (strcmp("pisound-spi", spi->modalias) == 0) {
453 + printi("\tFound!\n");
457 + printe("\tNot found!\n");
461 +static struct spi_device *pisnd_spi_find_device(void)
463 + struct device *dev;
465 + printi("Searching for spi device...\n");
466 + dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
468 + return container_of(dev, struct spi_device, dev);
473 +static void pisnd_work_handler(struct work_struct *work)
475 + enum { TRANSFER_SIZE = 4 };
476 + enum { PISOUND_OUTPUT_BUFFER_SIZE = 128 };
477 + enum { MIDI_BYTES_PER_SECOND = 3125 };
478 + int out_buffer_used = 0;
481 + uint8_t txbuf[TRANSFER_SIZE];
482 + uint8_t rxbuf[TRANSFER_SIZE];
483 + uint8_t midibuf[TRANSFER_SIZE];
487 + unsigned long last_transfer_at = jiffies;
489 + if (work == &pisnd_work_process) {
490 + if (pisnd_spi_device == NULL)
494 + if (g_midi_output_substream &&
495 + kfifo_avail(&spi_fifo_out) >= sizeof(midibuf)) {
497 + n = snd_rawmidi_transmit_peek(
498 + g_midi_output_substream,
499 + midibuf, sizeof(midibuf)
503 + for (i = 0; i < n; ++i)
508 + snd_rawmidi_transmit_ack(
509 + g_midi_output_substream,
516 + memset(txbuf, 0, sizeof(txbuf));
517 + for (i = 0; i < sizeof(txbuf) &&
518 + out_buffer_used < PISOUND_OUTPUT_BUFFER_SIZE;
523 + if (g_ledFlashDurationChanged) {
525 + txbuf[i+1] = g_ledFlashDuration;
526 + g_ledFlashDuration = 0;
527 + g_ledFlashDurationChanged = false;
528 + } else if (kfifo_get(&spi_fifo_out, &val)) {
535 + spi_transfer(txbuf, rxbuf, sizeof(txbuf));
536 + /* Estimate the Pisound's MIDI output buffer usage, so
537 + * that we don't overflow it. Space in the buffer should
538 + * be becoming available at the UART MIDI byte transfer
543 + (MIDI_BYTES_PER_SECOND / HZ) /
544 + (now - last_transfer_at);
545 + if (out_buffer_used < 0)
546 + out_buffer_used = 0;
547 + last_transfer_at = now;
549 + for (i = 0; i < sizeof(rxbuf); i += 2) {
551 + kfifo_put(&spi_fifo_in, rxbuf[i+1]);
552 + if (kfifo_len(&spi_fifo_in) > 16 &&
554 + g_recvCallback(g_recvData);
559 + || !kfifo_is_empty(&spi_fifo_out)
560 + || pisnd_spi_has_more()
561 + || g_ledFlashDurationChanged
564 + if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
565 + g_recvCallback(g_recvData);
569 +static int pisnd_spi_gpio_init(struct device *dev)
571 + spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
572 + data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
574 + gpiod_direction_output(spi_reset, 1);
575 + gpiod_direction_input(data_available);
577 + /* Reset the slave. */
578 + gpiod_set_value(spi_reset, false);
580 + gpiod_set_value(spi_reset, true);
582 + /* Give time for spi slave to start. */
588 +static void pisnd_spi_gpio_uninit(void)
590 + gpiod_set_value(spi_reset, false);
591 + gpiod_put(spi_reset);
594 + gpiod_put(data_available);
595 + data_available = NULL;
598 +static int pisnd_spi_gpio_irq_init(struct device *dev)
600 + return request_irq(
601 + gpiod_to_irq(data_available),
602 + data_available_interrupt_handler,
603 + IRQF_TIMER | IRQF_TRIGGER_RISING,
604 + "data_available_int",
609 +static void pisnd_spi_gpio_irq_uninit(void)
611 + free_irq(gpiod_to_irq(data_available), NULL);
614 +static int spi_read_info(void)
625 + memset(g_serial_num, 0, sizeof(g_serial_num));
626 + memset(g_version, 0, sizeof(g_version));
627 + memset(g_id, 0, sizeof(g_id));
629 + tmp = spi_transfer16(0);
634 + count = tmp & 0xff;
636 + for (i = 0; i < count; ++i) {
637 + memset(buffer, 0, sizeof(buffer));
638 + ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
657 + if (n >= sizeof(g_serial_num))
660 + memcpy(g_serial_num, buffer, sizeof(g_serial_num));
664 + if (n >= sizeof(g_id))
668 + for (j = 0; j < n; ++j)
669 + p += sprintf(p, "%02x", buffer[j]);
680 +static int pisnd_spi_init(struct device *dev)
683 + struct spi_device *spi;
685 + memset(g_serial_num, 0, sizeof(g_serial_num));
686 + memset(g_id, 0, sizeof(g_id));
687 + memset(g_version, 0, sizeof(g_version));
689 + spi = pisnd_spi_find_device();
692 + printd("initializing spi!\n");
693 + pisnd_spi_device = spi;
694 + ret = spi_setup(pisnd_spi_device);
696 + printe("SPI device not found, deferring!\n");
697 + return -EPROBE_DEFER;
700 + ret = pisnd_spi_gpio_init(dev);
703 + printe("SPI GPIO init failed: %d\n", ret);
704 + spi_dev_put(pisnd_spi_device);
705 + pisnd_spi_device = NULL;
706 + pisnd_spi_gpio_uninit();
710 + ret = spi_read_info();
713 + printe("Reading card info failed: %d\n", ret);
714 + spi_dev_put(pisnd_spi_device);
715 + pisnd_spi_device = NULL;
716 + pisnd_spi_gpio_uninit();
720 + /* Flash the LEDs. */
721 + spi_transfer16(0xf008);
723 + ret = pisnd_spi_gpio_irq_init(dev);
725 + printe("SPI irq request failed: %d\n", ret);
726 + spi_dev_put(pisnd_spi_device);
727 + pisnd_spi_device = NULL;
728 + pisnd_spi_gpio_irq_uninit();
729 + pisnd_spi_gpio_uninit();
732 + ret = pisnd_init_workqueues();
734 + printe("Workqueue initialization failed: %d\n", ret);
735 + spi_dev_put(pisnd_spi_device);
736 + pisnd_spi_device = NULL;
737 + pisnd_spi_gpio_irq_uninit();
738 + pisnd_spi_gpio_uninit();
739 + pisnd_uninit_workqueues();
743 + if (pisnd_spi_has_more()) {
744 + printd("data is available, scheduling from init\n");
745 + pisnd_schedule_process(TASK_PROCESS);
751 +static void pisnd_spi_uninit(void)
753 + pisnd_uninit_workqueues();
755 + spi_dev_put(pisnd_spi_device);
756 + pisnd_spi_device = NULL;
758 + pisnd_spi_gpio_irq_uninit();
759 + pisnd_spi_gpio_uninit();
762 +static void pisnd_spi_flash_leds(uint8_t duration)
764 + g_ledFlashDuration = duration;
765 + g_ledFlashDurationChanged = true;
766 + printd("schedule from spi_flash_leds\n");
767 + pisnd_schedule_process(TASK_PROCESS);
770 +static void pisnd_spi_flush(void)
772 + while (!kfifo_is_empty(&spi_fifo_out)) {
774 + flush_workqueue(pisnd_workqueue);
778 +static void pisnd_spi_start(void)
780 + printd("schedule from spi_start\n");
781 + pisnd_schedule_process(TASK_PROCESS);
784 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
786 + return kfifo_out(&spi_fifo_in, buffer, length);
789 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
792 + g_recvCallback = cb;
795 +static const char *pisnd_spi_get_serial(void)
797 + if (strlen(g_serial_num))
798 + return g_serial_num;
803 +static const char *pisnd_spi_get_id(void)
811 +static const char *pisnd_spi_get_version(void)
813 + if (strlen(g_version))
819 +static const struct of_device_id pisound_of_match[] = {
820 + { .compatible = "blokaslabs,pisound", },
821 + { .compatible = "blokaslabs,pisound-spi", },
830 +static int pisnd_ctl_info(struct snd_kcontrol *kcontrol,
831 + struct snd_ctl_elem_info *uinfo)
833 + if (kcontrol->private_value == SWITCH) {
834 + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
836 + uinfo->value.integer.min = 0;
837 + uinfo->value.integer.max = 1;
839 + } else if (kcontrol->private_value == VOLUME) {
840 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
842 + uinfo->value.integer.min = 0;
843 + uinfo->value.integer.max = 100;
849 +static int pisnd_ctl_get(struct snd_kcontrol *kcontrol,
850 + struct snd_ctl_elem_value *ucontrol)
852 + if (kcontrol->private_value == SWITCH) {
853 + ucontrol->value.integer.value[0] = 1;
855 + } else if (kcontrol->private_value == VOLUME) {
856 + ucontrol->value.integer.value[0] = 100;
863 +static struct snd_kcontrol_new pisnd_ctl[] = {
865 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
866 + .name = "PCM Playback Switch",
868 + .private_value = SWITCH,
869 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
870 + .info = pisnd_ctl_info,
871 + .get = pisnd_ctl_get,
874 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
875 + .name = "PCM Playback Volume",
877 + .private_value = VOLUME,
878 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
879 + .info = pisnd_ctl_info,
880 + .get = pisnd_ctl_get,
884 +static int pisnd_ctl_init(struct snd_card *card)
888 + for (i = 0; i < ARRAY_SIZE(pisnd_ctl); ++i) {
889 + err = snd_ctl_add(card, snd_ctl_new1(&pisnd_ctl[i], NULL));
897 +static int pisnd_ctl_uninit(void)
902 +static struct gpio_desc *osr0, *osr1, *osr2;
903 +static struct gpio_desc *reset;
904 +static struct gpio_desc *button;
906 +static int pisnd_hw_params(
907 + struct snd_pcm_substream *substream,
908 + struct snd_pcm_hw_params *params
911 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
912 + struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
914 + /* Pisound runs on fixed 32 clock counts per channel,
915 + * as generated by the master ADC.
917 + snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
919 + printd("rate = %d\n", params_rate(params));
920 + printd("ch = %d\n", params_channels(params));
921 + printd("bits = %u\n",
922 + snd_pcm_format_physical_width(params_format(params)));
923 + printd("format = %d\n", params_format(params));
925 + gpiod_set_value(reset, false);
927 + switch (params_rate(params)) {
929 + gpiod_set_value(osr0, true);
930 + gpiod_set_value(osr1, false);
931 + gpiod_set_value(osr2, false);
934 + gpiod_set_value(osr0, true);
935 + gpiod_set_value(osr1, false);
936 + gpiod_set_value(osr2, true);
939 + gpiod_set_value(osr0, true);
940 + gpiod_set_value(osr1, true);
941 + gpiod_set_value(osr2, true);
944 + printe("Unsupported rate %u!\n", params_rate(params));
948 + gpiod_set_value(reset, true);
953 +static unsigned int rates[3] = {
954 + 48000, 96000, 192000
957 +static struct snd_pcm_hw_constraint_list constraints_rates = {
958 + .count = ARRAY_SIZE(rates),
963 +static int pisnd_startup(struct snd_pcm_substream *substream)
965 + int err = snd_pcm_hw_constraint_list(
966 + substream->runtime,
968 + SNDRV_PCM_HW_PARAM_RATE,
975 + err = snd_pcm_hw_constraint_single(
976 + substream->runtime,
977 + SNDRV_PCM_HW_PARAM_CHANNELS,
984 + err = snd_pcm_hw_constraint_mask64(
985 + substream->runtime,
986 + SNDRV_PCM_HW_PARAM_FORMAT,
987 + SNDRV_PCM_FMTBIT_S16_LE |
988 + SNDRV_PCM_FMTBIT_S24_LE |
989 + SNDRV_PCM_FMTBIT_S32_LE
998 +static struct snd_soc_ops pisnd_ops = {
999 + .startup = pisnd_startup,
1000 + .hw_params = pisnd_hw_params,
1003 +static struct snd_soc_dai_link pisnd_dai[] = {
1005 + .name = "pisound",
1006 + .stream_name = "pisound",
1007 + .cpu_dai_name = "bcm2708-i2s.0",
1008 + .codec_dai_name = "snd-soc-dummy-dai",
1009 + .platform_name = "bcm2708-i2s.0",
1010 + .codec_name = "snd-soc-dummy",
1012 + SND_SOC_DAIFMT_I2S |
1013 + SND_SOC_DAIFMT_NB_NF |
1014 + SND_SOC_DAIFMT_CBM_CFM,
1015 + .ops = &pisnd_ops,
1019 +static int pisnd_card_probe(struct snd_soc_card *card)
1021 + int err = pisnd_midi_init(card->snd_card);
1024 + printe("pisnd_midi_init failed: %d\n", err);
1028 + err = pisnd_ctl_init(card->snd_card);
1030 + printe("pisnd_ctl_init failed: %d\n", err);
1037 +static int pisnd_card_remove(struct snd_soc_card *card)
1039 + pisnd_ctl_uninit();
1040 + pisnd_midi_uninit();
1044 +static struct snd_soc_card pisnd_card = {
1045 + .name = "pisound",
1046 + .owner = THIS_MODULE,
1047 + .dai_link = pisnd_dai,
1048 + .num_links = ARRAY_SIZE(pisnd_dai),
1049 + .probe = pisnd_card_probe,
1050 + .remove = pisnd_card_remove,
1053 +static int pisnd_init_gpio(struct device *dev)
1055 + osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
1056 + osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
1057 + osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
1059 + reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
1061 + button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
1063 + gpiod_direction_output(osr0, 1);
1064 + gpiod_direction_output(osr1, 1);
1065 + gpiod_direction_output(osr2, 1);
1066 + gpiod_direction_output(reset, 1);
1068 + gpiod_set_value(reset, false);
1069 + gpiod_set_value(osr0, true);
1070 + gpiod_set_value(osr1, false);
1071 + gpiod_set_value(osr2, false);
1072 + gpiod_set_value(reset, true);
1074 + gpiod_export(button, false);
1079 +static int pisnd_uninit_gpio(void)
1083 + struct gpio_desc **gpios[] = {
1084 + &osr0, &osr1, &osr2, &reset, &button,
1087 + gpiod_unexport(button);
1089 + for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
1090 + if (*gpios[i] == NULL) {
1091 + printd("weird, GPIO[%d] is NULL already\n", i);
1095 + gpiod_put(*gpios[i]);
1102 +static struct kobject *pisnd_kobj;
1104 +static ssize_t pisnd_serial_show(
1105 + struct kobject *kobj,
1106 + struct kobj_attribute *attr,
1110 + return sprintf(buf, "%s\n", pisnd_spi_get_serial());
1113 +static ssize_t pisnd_id_show(
1114 + struct kobject *kobj,
1115 + struct kobj_attribute *attr,
1119 + return sprintf(buf, "%s\n", pisnd_spi_get_id());
1122 +static ssize_t pisnd_version_show(
1123 + struct kobject *kobj,
1124 + struct kobj_attribute *attr,
1128 + return sprintf(buf, "%s\n", pisnd_spi_get_version());
1131 +static ssize_t pisnd_led_store(
1132 + struct kobject *kobj,
1133 + struct kobj_attribute *attr,
1141 + err = kstrtou32(buf, 10, &timeout);
1143 + if (err == 0 && timeout <= 255)
1144 + pisnd_spi_flash_leds(timeout);
1149 +static struct kobj_attribute pisnd_serial_attribute =
1150 + __ATTR(serial, 0444, pisnd_serial_show, NULL);
1151 +static struct kobj_attribute pisnd_id_attribute =
1152 + __ATTR(id, 0444, pisnd_id_show, NULL);
1153 +static struct kobj_attribute pisnd_version_attribute =
1154 + __ATTR(version, 0444, pisnd_version_show, NULL);
1155 +static struct kobj_attribute pisnd_led_attribute =
1156 + __ATTR(led, 0644, NULL, pisnd_led_store);
1158 +static struct attribute *attrs[] = {
1159 + &pisnd_serial_attribute.attr,
1160 + &pisnd_id_attribute.attr,
1161 + &pisnd_version_attribute.attr,
1162 + &pisnd_led_attribute.attr,
1166 +static struct attribute_group attr_group = { .attrs = attrs };
1168 +static int pisnd_probe(struct platform_device *pdev)
1173 + ret = pisnd_spi_init(&pdev->dev);
1175 + printe("pisnd_spi_init failed: %d\n", ret);
1179 + printi("Detected Pisound card:\n");
1180 + printi("\tSerial: %s\n", pisnd_spi_get_serial());
1181 + printi("\tVersion: %s\n", pisnd_spi_get_version());
1182 + printi("\tId: %s\n", pisnd_spi_get_id());
1184 + pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
1185 + if (!pisnd_kobj) {
1186 + pisnd_spi_uninit();
1190 + ret = sysfs_create_group(pisnd_kobj, &attr_group);
1192 + pisnd_spi_uninit();
1193 + kobject_put(pisnd_kobj);
1197 + pisnd_init_gpio(&pdev->dev);
1198 + pisnd_card.dev = &pdev->dev;
1200 + if (pdev->dev.of_node) {
1201 + struct device_node *i2s_node;
1203 + i2s_node = of_parse_phandle(
1204 + pdev->dev.of_node,
1209 + for (i = 0; i < pisnd_card.num_links; ++i) {
1210 + struct snd_soc_dai_link *dai = &pisnd_dai[i];
1213 + dai->cpu_dai_name = NULL;
1214 + dai->cpu_of_node = i2s_node;
1215 + dai->platform_name = NULL;
1216 + dai->platform_of_node = i2s_node;
1217 + dai->stream_name = pisnd_spi_get_serial();
1222 + ret = snd_soc_register_card(&pisnd_card);
1225 + if (ret != -EPROBE_DEFER)
1226 + printe("snd_soc_register_card() failed: %d\n", ret);
1227 + pisnd_uninit_gpio();
1228 + kobject_put(pisnd_kobj);
1229 + pisnd_spi_uninit();
1235 +static int pisnd_remove(struct platform_device *pdev)
1237 + printi("Unloading.\n");
1240 + kobject_put(pisnd_kobj);
1241 + pisnd_kobj = NULL;
1244 + pisnd_spi_uninit();
1247 + gpiod_set_value(reset, false);
1248 + pisnd_uninit_gpio();
1250 + return snd_soc_unregister_card(&pisnd_card);
1253 +MODULE_DEVICE_TABLE(of, pisound_of_match);
1255 +static struct platform_driver pisnd_driver = {
1257 + .name = "snd-rpi-pisound",
1258 + .owner = THIS_MODULE,
1259 + .of_match_table = pisound_of_match,
1261 + .probe = pisnd_probe,
1262 + .remove = pisnd_remove,
1265 +module_platform_driver(pisnd_driver);
1267 +MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
1268 +MODULE_DESCRIPTION("ASoC Driver for Pisound, https://blokas.io/pisound");
1269 +MODULE_LICENSE("GPL v2");