1 From 7d8efd6f3198fd3dafe5a74003616a2aea5dc6c5 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
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.
12 .../devicetree/bindings/vendor-prefixes.txt | 1 +
13 arch/arm/boot/dts/overlays/pisound-overlay.dts | 94 +-
14 sound/soc/bcm/Kconfig | 6 +
15 sound/soc/bcm/Makefile | 2 +
16 sound/soc/bcm/pisound.c | 989 +++++++++++++++++++++
17 5 files changed, 1048 insertions(+), 44 deletions(-)
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 @@ -41,6 +41,7 @@ avago Avago Technologies
23 avia avia semiconductor
24 avic Shanghai AVIC Optoelectronics Co., Ltd.
25 axis Axis Communications AB
26 +blokaslabs Vilniaus Blokas UAB
27 boe BOE Technology Group Co., Ltd.
28 bosch Bosch Sensortec GmbH
29 boundary Boundary Devices Inc.
30 --- a/arch/arm/boot/dts/overlays/pisound-overlay.dts
31 +++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts
33 compatible = "brcm,bcm2708";
43 + target = <&spidev0>;
45 + status = "disabled";
50 + target = <&spidev1>;
59 + #address-cells = <1>;
62 + pisound_spi: pisound_spi@0{
63 + compatible = "blokaslabs,pisound-spi";
65 + pinctrl-names = "default";
66 + pinctrl-0 = <&spi0_pins>;
67 + spi-max-frequency = <1000000>;
76 + #sound-dai-cells = <0>;
77 + compatible = "ti,pcm5102a";
86 compatible = "blokaslabs,pisound";
95 pinctrl-names = "default";
112 - #sound-dai-cells = <0>;
113 - compatible = "ti,pcm5102a";
125 - status = "disabled";
137 - #address-cells = <1>;
140 - pisound_spi: pisound_spi@0{
141 - compatible = "blokaslabs,pisound-spi";
143 - pinctrl-names = "default";
144 - pinctrl-0 = <&spi0_pins>;
145 - spi-max-frequency = <1000000>;
150 --- a/sound/soc/bcm/Kconfig
151 +++ b/sound/soc/bcm/Kconfig
152 @@ -130,3 +130,9 @@ config SND_BCM2708_SOC_ALLO_PIANO_DAC
153 select SND_SOC_PCM512x_I2C
155 Say Y or M if you want to add support for Allo Piano DAC.
158 + tristate "Support for Blokas Labs pisound"
159 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
161 + Say Y or M if you want to add support for Blokas Labs pisound.
162 --- a/sound/soc/bcm/Makefile
163 +++ b/sound/soc/bcm/Makefile
164 @@ -25,6 +25,7 @@ snd-soc-audioinjector-pi-soundcard-objs
165 snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o
166 snd-soc-dionaudio-loco-objs := dionaudio_loco.o
167 snd-soc-allo-piano-dac-objs := allo-piano-dac.o
168 +snd-soc-pisound-objs := pisound.o
170 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
171 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
172 @@ -42,3 +43,4 @@ obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDC
173 obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o
174 obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o
175 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
176 +obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o
178 +++ b/sound/soc/bcm/pisound.c
181 + * pisound Linux kernel module.
182 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
184 + * This program is free software; you can redistribute it and/or
185 + * modify it under the terms of the GNU General Public License
186 + * as published by the Free Software Foundation; version 2 of the
189 + * This program is distributed in the hope that it will be useful,
190 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
191 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
192 + * GNU General Public License for more details.
194 + * You should have received a copy of the GNU General Public License
195 + * along with this program; if not, write to the Free Software
196 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
197 + * MA 02110-1301, USA.
200 +#include <linux/init.h>
201 +#include <linux/module.h>
202 +#include <linux/platform_device.h>
203 +#include <linux/gpio.h>
204 +#include <linux/kobject.h>
205 +#include <linux/sysfs.h>
206 +#include <linux/delay.h>
207 +#include <linux/spi/spi.h>
208 +#include <linux/interrupt.h>
209 +#include <linux/kfifo.h>
211 +#include <sound/core.h>
212 +#include <sound/pcm.h>
213 +#include <sound/pcm_params.h>
214 +#include <sound/soc.h>
215 +#include <sound/jack.h>
216 +#include <sound/rawmidi.h>
217 +#include <sound/asequencer.h>
219 +static int pisnd_spi_init(struct device *dev);
220 +static void pisnd_spi_uninit(void);
222 +static void pisnd_spi_send(uint8_t val);
223 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
225 +typedef void (*pisnd_spi_recv_cb)(void *data);
226 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
228 +static const char *pisnd_spi_get_serial(void);
229 +static const char *pisnd_spi_get_id(void);
230 +static const char *pisnd_spi_get_version(void);
232 +static int pisnd_midi_init(struct snd_card *card);
233 +static void pisnd_midi_uninit(void);
235 +#define PISOUND_LOG_PREFIX "pisound: "
238 +# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
240 +# define printd(...) do {} while (0)
243 +#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
244 +#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
246 +static int pisnd_output_open(struct snd_rawmidi_substream *substream)
251 +static int pisnd_output_close(struct snd_rawmidi_substream *substream)
256 +static void pisnd_output_trigger(
257 + struct snd_rawmidi_substream *substream,
266 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
267 + pisnd_spi_send(data);
268 + snd_rawmidi_transmit_ack(substream, 1);
272 +static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
276 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
277 + pisnd_spi_send(data);
279 + snd_rawmidi_transmit_ack(substream, 1);
283 +static int pisnd_input_open(struct snd_rawmidi_substream *substream)
288 +static int pisnd_input_close(struct snd_rawmidi_substream *substream)
293 +static void pisnd_midi_recv_callback(void *substream)
298 + while ((n = pisnd_spi_recv(data, sizeof(data)))) {
299 + int res = snd_rawmidi_receive(substream, data, n);
301 + printd("midi recv 0x%02x, res = %d\n", data, res);
305 +static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
308 + pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
309 + pisnd_midi_recv_callback(substream);
311 + pisnd_spi_set_callback(NULL, NULL);
315 +static struct snd_rawmidi *g_rmidi;
317 +static struct snd_rawmidi_ops pisnd_output_ops = {
318 + .open = pisnd_output_open,
319 + .close = pisnd_output_close,
320 + .trigger = pisnd_output_trigger,
321 + .drain = pisnd_output_drain,
324 +static struct snd_rawmidi_ops pisnd_input_ops = {
325 + .open = pisnd_input_open,
326 + .close = pisnd_input_close,
327 + .trigger = pisnd_input_trigger,
330 +static void pisnd_get_port_info(
331 + struct snd_rawmidi *rmidi,
333 + struct snd_seq_port_info *seq_port_info
336 + seq_port_info->type =
337 + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
338 + SNDRV_SEQ_PORT_TYPE_HARDWARE |
339 + SNDRV_SEQ_PORT_TYPE_PORT;
340 + seq_port_info->midi_voices = 0;
343 +static struct snd_rawmidi_global_ops pisnd_global_ops = {
344 + .get_port_info = pisnd_get_port_info,
347 +static int pisnd_midi_init(struct snd_card *card)
349 + int err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
352 + printe("snd_rawmidi_new failed: %d\n", err);
356 + strcpy(g_rmidi->name, "pisound MIDI ");
357 + strcat(g_rmidi->name, pisnd_spi_get_serial());
359 + g_rmidi->info_flags =
360 + SNDRV_RAWMIDI_INFO_OUTPUT |
361 + SNDRV_RAWMIDI_INFO_INPUT |
362 + SNDRV_RAWMIDI_INFO_DUPLEX;
364 + g_rmidi->ops = &pisnd_global_ops;
366 + g_rmidi->private_data = (void *)0;
368 + snd_rawmidi_set_ops(
370 + SNDRV_RAWMIDI_STREAM_OUTPUT,
374 + snd_rawmidi_set_ops(
376 + SNDRV_RAWMIDI_STREAM_INPUT,
383 +static void pisnd_midi_uninit(void)
387 +static void *g_recvData;
388 +static pisnd_spi_recv_cb g_recvCallback;
390 +#define FIFO_SIZE 512
392 +static char g_serial_num[11];
393 +static char g_id[25];
394 +static char g_version[5];
396 +DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
397 +DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
399 +static struct gpio_desc *data_available;
400 +static struct gpio_desc *spi_reset;
402 +static struct spi_device *pisnd_spi_device;
404 +static struct workqueue_struct *pisnd_workqueue;
405 +static struct work_struct pisnd_work_process;
407 +static void pisnd_work_handler(struct work_struct *work);
409 +static uint16_t spi_transfer16(uint16_t val);
411 +static int pisnd_init_workqueues(void)
413 + pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
414 + INIT_WORK(&pisnd_work_process, pisnd_work_handler);
419 +static void pisnd_uninit_workqueues(void)
421 + flush_workqueue(pisnd_workqueue);
422 + destroy_workqueue(pisnd_workqueue);
424 + pisnd_workqueue = NULL;
427 +static bool pisnd_spi_has_more(void)
429 + return gpiod_get_value(data_available);
436 +static void pisnd_schedule_process(enum task_e task)
438 + if (pisnd_spi_device != NULL &&
439 + pisnd_workqueue != NULL &&
440 + !work_pending(&pisnd_work_process)
442 + printd("schedule: has more = %d\n", pisnd_spi_has_more());
443 + if (task == TASK_PROCESS)
444 + queue_work(pisnd_workqueue, &pisnd_work_process);
448 +static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
450 + if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
451 + printd("schedule from irq\n");
452 + pisnd_schedule_process(TASK_PROCESS);
455 + return IRQ_HANDLED;
458 +static DEFINE_SPINLOCK(spilock);
459 +static unsigned long spilockflags;
461 +static uint16_t spi_transfer16(uint16_t val)
464 + struct spi_transfer transfer;
465 + struct spi_message msg;
469 + if (!pisnd_spi_device) {
470 + printe("pisnd_spi_device null, returning\n");
474 + spi_message_init(&msg);
476 + memset(&transfer, 0, sizeof(transfer));
477 + memset(&rxbuf, 0, sizeof(rxbuf));
479 + txbuf[0] = val >> 8;
480 + txbuf[1] = val & 0xff;
482 + transfer.tx_buf = &txbuf;
483 + transfer.rx_buf = &rxbuf;
484 + transfer.len = sizeof(txbuf);
485 + transfer.speed_hz = 125000;
486 + transfer.delay_usecs = 100;
487 + spi_message_add_tail(&transfer, &msg);
489 + spin_lock_irqsave(&spilock, spilockflags);
490 + err = spi_sync(pisnd_spi_device, &msg);
491 + spin_unlock_irqrestore(&spilock, spilockflags);
494 + printe("spi_sync error %d\n", err);
498 + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
499 + printd("hasMore %d\n", pisnd_spi_has_more());
501 + return (rxbuf[0] << 8) | rxbuf[1];
504 +static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
510 + memset(dst, 0, length);
513 + rx = spi_transfer16(0);
522 + for (i = 0; i < size; ++i) {
523 + rx = spi_transfer16(0);
527 + dst[i] = rx & 0xff;
535 +static int spi_device_match(struct device *dev, void *data)
537 + struct spi_device *spi = container_of(dev, struct spi_device, dev);
539 + printd(" %s %s %dkHz %d bits mode=0x%02X\n",
540 + spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
541 + spi->bits_per_word, spi->mode);
543 + if (strcmp("pisound-spi", spi->modalias) == 0) {
544 + printi("\tFound!\n");
548 + printe("\tNot found!\n");
552 +static struct spi_device *pisnd_spi_find_device(void)
554 + struct device *dev;
556 + printi("Searching for spi device...\n");
557 + dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
559 + return container_of(dev, struct spi_device, dev);
564 +static void pisnd_work_handler(struct work_struct *work)
570 + if (work == &pisnd_work_process) {
571 + if (pisnd_spi_device == NULL)
578 + if (kfifo_get(&spi_fifo_out, &val))
581 + rx = spi_transfer16(tx);
584 + kfifo_put(&spi_fifo_in, rx & 0xff);
585 + if (kfifo_len(&spi_fifo_in) > 16
587 + g_recvCallback(g_recvData);
590 + || !kfifo_is_empty(&spi_fifo_out)
591 + || pisnd_spi_has_more()
594 + if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
595 + g_recvCallback(g_recvData);
599 +static int pisnd_spi_gpio_init(struct device *dev)
601 + spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
602 + data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
604 + gpiod_direction_output(spi_reset, 1);
605 + gpiod_direction_input(data_available);
607 + /* Reset the slave. */
608 + gpiod_set_value(spi_reset, false);
610 + gpiod_set_value(spi_reset, true);
612 + /* Give time for spi slave to start. */
618 +static void pisnd_spi_gpio_uninit(void)
620 + gpiod_set_value(spi_reset, false);
621 + gpiod_put(spi_reset);
624 + gpiod_put(data_available);
625 + data_available = NULL;
628 +static int pisnd_spi_gpio_irq_init(struct device *dev)
630 + return request_irq(
631 + gpiod_to_irq(data_available),
632 + data_available_interrupt_handler,
633 + IRQF_TIMER | IRQF_TRIGGER_RISING,
634 + "data_available_int",
639 +static void pisnd_spi_gpio_irq_uninit(void)
641 + free_irq(gpiod_to_irq(data_available), NULL);
644 +static int spi_read_info(void)
655 + memset(g_serial_num, 0, sizeof(g_serial_num));
656 + memset(g_version, 0, sizeof(g_version));
657 + memset(g_id, 0, sizeof(g_id));
659 + tmp = spi_transfer16(0);
664 + count = tmp & 0xff;
666 + for (i = 0; i < count; ++i) {
667 + memset(buffer, 0, sizeof(buffer));
668 + ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
687 + if (n >= sizeof(g_serial_num))
690 + memcpy(g_serial_num, buffer, sizeof(g_serial_num));
694 + if (n >= sizeof(g_id))
698 + for (j = 0; j < n; ++j)
699 + p += sprintf(p, "%02x", buffer[j]);
710 +static int pisnd_spi_init(struct device *dev)
713 + struct spi_device *spi;
715 + memset(g_serial_num, 0, sizeof(g_serial_num));
716 + memset(g_id, 0, sizeof(g_id));
717 + memset(g_version, 0, sizeof(g_version));
719 + spi = pisnd_spi_find_device();
722 + printd("initializing spi!\n");
723 + pisnd_spi_device = spi;
724 + ret = spi_setup(pisnd_spi_device);
726 + printe("SPI device not found, deferring!\n");
727 + return -EPROBE_DEFER;
730 + ret = pisnd_spi_gpio_init(dev);
733 + printe("SPI GPIO init failed: %d\n", ret);
734 + spi_dev_put(pisnd_spi_device);
735 + pisnd_spi_device = NULL;
736 + pisnd_spi_gpio_uninit();
740 + ret = spi_read_info();
743 + printe("Reading card info failed: %d\n", ret);
744 + spi_dev_put(pisnd_spi_device);
745 + pisnd_spi_device = NULL;
746 + pisnd_spi_gpio_uninit();
750 + /* Flash the LEDs. */
751 + spi_transfer16(0xf000);
753 + ret = pisnd_spi_gpio_irq_init(dev);
755 + printe("SPI irq request failed: %d\n", ret);
756 + spi_dev_put(pisnd_spi_device);
757 + pisnd_spi_device = NULL;
758 + pisnd_spi_gpio_irq_uninit();
759 + pisnd_spi_gpio_uninit();
762 + ret = pisnd_init_workqueues();
764 + printe("Workqueue initialization failed: %d\n", ret);
765 + spi_dev_put(pisnd_spi_device);
766 + pisnd_spi_device = NULL;
767 + pisnd_spi_gpio_irq_uninit();
768 + pisnd_spi_gpio_uninit();
769 + pisnd_uninit_workqueues();
773 + if (pisnd_spi_has_more()) {
774 + printd("data is available, scheduling from init\n");
775 + pisnd_schedule_process(TASK_PROCESS);
781 +static void pisnd_spi_uninit(void)
783 + pisnd_uninit_workqueues();
785 + spi_dev_put(pisnd_spi_device);
786 + pisnd_spi_device = NULL;
788 + pisnd_spi_gpio_irq_uninit();
789 + pisnd_spi_gpio_uninit();
792 +static void pisnd_spi_send(uint8_t val)
794 + kfifo_put(&spi_fifo_out, val);
795 + printd("schedule from spi_send\n");
796 + pisnd_schedule_process(TASK_PROCESS);
799 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
801 + return kfifo_out(&spi_fifo_in, buffer, length);
804 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
807 + g_recvCallback = cb;
810 +static const char *pisnd_spi_get_serial(void)
812 + if (strlen(g_serial_num))
813 + return g_serial_num;
818 +static const char *pisnd_spi_get_id(void)
826 +static const char *pisnd_spi_get_version(void)
828 + if (strlen(g_version))
834 +static const struct of_device_id pisound_of_match[] = {
835 + { .compatible = "blokaslabs,pisound", },
836 + { .compatible = "blokaslabs,pisound-spi", },
840 +static struct gpio_desc *osr0, *osr1, *osr2;
841 +static struct gpio_desc *reset;
842 +static struct gpio_desc *button;
844 +static int pisnd_hw_params(
845 + struct snd_pcm_substream *substream,
846 + struct snd_pcm_hw_params *params
849 + printd("rate = %d\n", params_rate(params));
850 + printd("ch = %d\n", params_channels(params));
851 + printd("bits = %u\n",
852 + snd_pcm_format_physical_width(params_format(params)));
853 + printd("format = %d\n", params_format(params));
855 + gpiod_set_value(reset, false);
857 + switch (params_rate(params)) {
859 + gpiod_set_value(osr0, true);
860 + gpiod_set_value(osr1, false);
861 + gpiod_set_value(osr2, false);
864 + gpiod_set_value(osr0, true);
865 + gpiod_set_value(osr1, true);
866 + gpiod_set_value(osr2, false);
869 + gpiod_set_value(osr0, true);
870 + gpiod_set_value(osr1, true);
871 + gpiod_set_value(osr2, true);
874 + printe("Unsupported rate %u!\n", params_rate(params));
878 + gpiod_set_value(reset, true);
883 +static unsigned int rates[3] = {
884 + 48000, 96000, 192000
887 +static struct snd_pcm_hw_constraint_list constraints_rates = {
888 + .count = ARRAY_SIZE(rates),
893 +static unsigned int sample_bits[] = {
897 +static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
898 + .count = ARRAY_SIZE(sample_bits),
899 + .list = sample_bits,
903 +static int pisnd_startup(struct snd_pcm_substream *substream)
905 + int err = snd_pcm_hw_constraint_list(
906 + substream->runtime,
908 + SNDRV_PCM_HW_PARAM_RATE,
915 + err = snd_pcm_hw_constraint_list(
916 + substream->runtime,
918 + SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
919 + &constraints_sample_bits
928 +static struct snd_soc_ops pisnd_ops = {
929 + .startup = pisnd_startup,
930 + .hw_params = pisnd_hw_params,
933 +static struct snd_soc_dai_link pisnd_dai[] = {
936 + .stream_name = "pisound",
937 + .cpu_dai_name = "bcm2708-i2s.0",
938 + .codec_dai_name = "snd-soc-dummy-dai",
939 + .platform_name = "bcm2708-i2s.0",
940 + .codec_name = "snd-soc-dummy",
942 + SND_SOC_DAIFMT_I2S |
943 + SND_SOC_DAIFMT_NB_NF |
944 + SND_SOC_DAIFMT_CBM_CFM,
949 +static int pisnd_card_probe(struct snd_soc_card *card)
951 + int err = pisnd_midi_init(card->snd_card);
954 + printe("pisnd_midi_init failed: %d\n", err);
959 +static int pisnd_card_remove(struct snd_soc_card *card)
961 + pisnd_midi_uninit();
965 +static struct snd_soc_card pisnd_card = {
967 + .owner = THIS_MODULE,
968 + .dai_link = pisnd_dai,
969 + .num_links = ARRAY_SIZE(pisnd_dai),
970 + .probe = pisnd_card_probe,
971 + .remove = pisnd_card_remove,
974 +static int pisnd_init_gpio(struct device *dev)
976 + osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
977 + osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
978 + osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
980 + reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
982 + button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
984 + gpiod_direction_output(osr0, 1);
985 + gpiod_direction_output(osr1, 1);
986 + gpiod_direction_output(osr2, 1);
987 + gpiod_direction_output(reset, 1);
989 + gpiod_set_value(reset, false);
990 + gpiod_set_value(osr0, true);
991 + gpiod_set_value(osr1, false);
992 + gpiod_set_value(osr2, false);
993 + gpiod_set_value(reset, true);
995 + gpiod_export(button, false);
1000 +static int pisnd_uninit_gpio(void)
1004 + struct gpio_desc **gpios[] = {
1005 + &osr0, &osr1, &osr2, &reset, &button,
1008 + gpiod_unexport(button);
1010 + for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
1011 + if (*gpios[i] == NULL) {
1012 + printd("weird, GPIO[%d] is NULL already\n", i);
1016 + gpiod_put(*gpios[i]);
1023 +static struct kobject *pisnd_kobj;
1025 +static ssize_t pisnd_serial_show(
1026 + struct kobject *kobj,
1027 + struct kobj_attribute *attr,
1031 + return sprintf(buf, "%s\n", pisnd_spi_get_serial());
1034 +static ssize_t pisnd_id_show(
1035 + struct kobject *kobj,
1036 + struct kobj_attribute *attr,
1040 + return sprintf(buf, "%s\n", pisnd_spi_get_id());
1043 +static ssize_t pisnd_version_show(
1044 + struct kobject *kobj,
1045 + struct kobj_attribute *attr,
1049 + return sprintf(buf, "%s\n", pisnd_spi_get_version());
1052 +static struct kobj_attribute pisnd_serial_attribute =
1053 + __ATTR(serial, 0644, pisnd_serial_show, NULL);
1054 +static struct kobj_attribute pisnd_id_attribute =
1055 + __ATTR(id, 0644, pisnd_id_show, NULL);
1056 +static struct kobj_attribute pisnd_version_attribute =
1057 + __ATTR(version, 0644, pisnd_version_show, NULL);
1059 +static struct attribute *attrs[] = {
1060 + &pisnd_serial_attribute.attr,
1061 + &pisnd_id_attribute.attr,
1062 + &pisnd_version_attribute.attr,
1066 +static struct attribute_group attr_group = { .attrs = attrs };
1068 +static int pisnd_probe(struct platform_device *pdev)
1073 + ret = pisnd_spi_init(&pdev->dev);
1075 + printe("pisnd_spi_init failed: %d\n", ret);
1079 + printi("Detected pisound card:\n");
1080 + printi("\tSerial: %s\n", pisnd_spi_get_serial());
1081 + printi("\tVersion: %s\n", pisnd_spi_get_version());
1082 + printi("\tId: %s\n", pisnd_spi_get_id());
1084 + pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
1085 + if (!pisnd_kobj) {
1086 + pisnd_spi_uninit();
1090 + ret = sysfs_create_group(pisnd_kobj, &attr_group);
1092 + pisnd_spi_uninit();
1093 + kobject_put(pisnd_kobj);
1097 + pisnd_init_gpio(&pdev->dev);
1098 + pisnd_card.dev = &pdev->dev;
1100 + if (pdev->dev.of_node) {
1101 + struct device_node *i2s_node;
1103 + i2s_node = of_parse_phandle(
1104 + pdev->dev.of_node,
1109 + for (i = 0; i < pisnd_card.num_links; ++i) {
1110 + struct snd_soc_dai_link *dai = &pisnd_dai[i];
1113 + dai->cpu_dai_name = NULL;
1114 + dai->cpu_of_node = i2s_node;
1115 + dai->platform_name = NULL;
1116 + dai->platform_of_node = i2s_node;
1117 + dai->stream_name = pisnd_spi_get_serial();
1122 + ret = snd_soc_register_card(&pisnd_card);
1125 + printe("snd_soc_register_card() failed: %d\n", ret);
1126 + pisnd_uninit_gpio();
1127 + kobject_put(pisnd_kobj);
1128 + pisnd_spi_uninit();
1134 +static int pisnd_remove(struct platform_device *pdev)
1136 + printi("Unloading.\n");
1139 + kobject_put(pisnd_kobj);
1140 + pisnd_kobj = NULL;
1143 + pisnd_spi_uninit();
1146 + gpiod_set_value(reset, false);
1147 + pisnd_uninit_gpio();
1149 + return snd_soc_unregister_card(&pisnd_card);
1152 +MODULE_DEVICE_TABLE(of, pisound_of_match);
1154 +static struct platform_driver pisnd_driver = {
1156 + .name = "snd-rpi-pisound",
1157 + .owner = THIS_MODULE,
1158 + .of_match_table = pisound_of_match,
1160 + .probe = pisnd_probe,
1161 + .remove = pisnd_remove,
1164 +module_platform_driver(pisnd_driver);
1166 +MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
1167 +MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound");
1168 +MODULE_LICENSE("GPL v2");