brcm2708: add kernel 4.14 support
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.14 / 950-0082-Support-for-Blokas-Labs-pisound-board.patch
1 From 16123264d8fb86c7c9efaed230a4b0028c3be800 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 082/454] Support for Blokas Labs pisound board
5
6 Pisound dynamic overlay (#1760)
7
8 Restructuring pisound-overlay.dts, so it can be loaded and unloaded dynamically using dtoverlay.
9
10 Print a logline when the kernel module is removed.
11
12 pisound improvements:
13
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
19 in volumealsa widget.
20
21 Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
22 ---
23 .../devicetree/bindings/vendor-prefixes.txt | 1 +
24 sound/soc/bcm/Kconfig | 6 +
25 sound/soc/bcm/Makefile | 2 +
26 sound/soc/bcm/pisound.c | 1123 +++++++++++++++++
27 4 files changed, 1132 insertions(+)
28 create mode 100644 sound/soc/bcm/pisound.c
29
30 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
31 +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
32 @@ -49,6 +49,7 @@ axentia Axentia Technologies AB
33 axis Axis Communications AB
34 bananapi BIPAI KEJI LIMITED
35 bhf Beckhoff Automation GmbH & Co. KG
36 +blokaslabs Vilniaus Blokas UAB
37 boe BOE Technology Group Co., Ltd.
38 bosch Bosch Sensortec GmbH
39 boundary Boundary Devices Inc.
40 --- a/sound/soc/bcm/Kconfig
41 +++ b/sound/soc/bcm/Kconfig
42 @@ -145,3 +145,9 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC
43 select SND_SOC_PCM512x_I2C
44 help
45 Say Y or M if you want to add support for Allo Boss DAC.
46 +
47 +config SND_PISOUND
48 + tristate "Support for Blokas Labs pisound"
49 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
50 + help
51 + Say Y or M if you want to add support for Blokas Labs pisound.
52 --- a/sound/soc/bcm/Makefile
53 +++ b/sound/soc/bcm/Makefile
54 @@ -27,6 +27,7 @@ snd-soc-dionaudio-loco-objs := dionaudio
55 snd-soc-allo-boss-dac-objs := allo-boss-dac.o
56 snd-soc-allo-piano-dac-objs := allo-piano-dac.o
57 snd-soc-allo-piano-dac-plus-objs := allo-piano-dac-plus.o
58 +snd-soc-pisound-objs := pisound.o
59
60 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
61 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
62 @@ -46,3 +47,4 @@ obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_L
63 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC) += snd-soc-allo-boss-dac.o
64 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
65 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS) += snd-soc-allo-piano-dac-plus.o
66 +obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o
67 --- /dev/null
68 +++ b/sound/soc/bcm/pisound.c
69 @@ -0,0 +1,1123 @@
70 +/*
71 + * pisound Linux kernel module.
72 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
73 + *
74 + * This program is free software; you can redistribute it and/or
75 + * modify it under the terms of the GNU General Public License
76 + * as published by the Free Software Foundation; version 2 of the
77 + * License.
78 + *
79 + * This program is distributed in the hope that it will be useful,
80 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
81 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82 + * GNU General Public License for more details.
83 + *
84 + * You should have received a copy of the GNU General Public License
85 + * along with this program; if not, write to the Free Software
86 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
87 + * MA 02110-1301, USA.
88 + */
89 +
90 +#include <linux/init.h>
91 +#include <linux/module.h>
92 +#include <linux/platform_device.h>
93 +#include <linux/gpio.h>
94 +#include <linux/kobject.h>
95 +#include <linux/sysfs.h>
96 +#include <linux/delay.h>
97 +#include <linux/spi/spi.h>
98 +#include <linux/interrupt.h>
99 +#include <linux/kfifo.h>
100 +
101 +#include <sound/core.h>
102 +#include <sound/pcm.h>
103 +#include <sound/pcm_params.h>
104 +#include <sound/soc.h>
105 +#include <sound/jack.h>
106 +#include <sound/rawmidi.h>
107 +#include <sound/asequencer.h>
108 +#include <sound/control.h>
109 +
110 +static int pisnd_spi_init(struct device *dev);
111 +static void pisnd_spi_uninit(void);
112 +
113 +static void pisnd_spi_send(uint8_t val);
114 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
115 +
116 +typedef void (*pisnd_spi_recv_cb)(void *data);
117 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
118 +
119 +static const char *pisnd_spi_get_serial(void);
120 +static const char *pisnd_spi_get_id(void);
121 +static const char *pisnd_spi_get_version(void);
122 +
123 +static int pisnd_midi_init(struct snd_card *card);
124 +static void pisnd_midi_uninit(void);
125 +
126 +#define PISOUND_LOG_PREFIX "pisound: "
127 +
128 +#ifdef DEBUG
129 +# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
130 +#else
131 +# define printd(...) do {} while (0)
132 +#endif
133 +
134 +#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
135 +#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
136 +
137 +static int pisnd_output_open(struct snd_rawmidi_substream *substream)
138 +{
139 + return 0;
140 +}
141 +
142 +static int pisnd_output_close(struct snd_rawmidi_substream *substream)
143 +{
144 + return 0;
145 +}
146 +
147 +static void pisnd_output_trigger(
148 + struct snd_rawmidi_substream *substream,
149 + int up
150 + )
151 +{
152 + uint8_t data;
153 +
154 + if (!up)
155 + return;
156 +
157 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
158 + pisnd_spi_send(data);
159 + snd_rawmidi_transmit_ack(substream, 1);
160 + }
161 +}
162 +
163 +static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
164 +{
165 + uint8_t data;
166 +
167 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
168 + pisnd_spi_send(data);
169 +
170 + snd_rawmidi_transmit_ack(substream, 1);
171 + }
172 +}
173 +
174 +static int pisnd_input_open(struct snd_rawmidi_substream *substream)
175 +{
176 + return 0;
177 +}
178 +
179 +static int pisnd_input_close(struct snd_rawmidi_substream *substream)
180 +{
181 + return 0;
182 +}
183 +
184 +static void pisnd_midi_recv_callback(void *substream)
185 +{
186 + uint8_t data[128];
187 + uint8_t n = 0;
188 +
189 + while ((n = pisnd_spi_recv(data, sizeof(data)))) {
190 + int res = snd_rawmidi_receive(substream, data, n);
191 + (void)res;
192 + printd("midi recv 0x%02x, res = %d\n", data, res);
193 + }
194 +}
195 +
196 +static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
197 +{
198 + if (up) {
199 + pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
200 + pisnd_midi_recv_callback(substream);
201 + } else {
202 + pisnd_spi_set_callback(NULL, NULL);
203 + }
204 +}
205 +
206 +static struct snd_rawmidi *g_rmidi;
207 +
208 +static struct snd_rawmidi_ops pisnd_output_ops = {
209 + .open = pisnd_output_open,
210 + .close = pisnd_output_close,
211 + .trigger = pisnd_output_trigger,
212 + .drain = pisnd_output_drain,
213 +};
214 +
215 +static struct snd_rawmidi_ops pisnd_input_ops = {
216 + .open = pisnd_input_open,
217 + .close = pisnd_input_close,
218 + .trigger = pisnd_input_trigger,
219 +};
220 +
221 +static void pisnd_get_port_info(
222 + struct snd_rawmidi *rmidi,
223 + int number,
224 + struct snd_seq_port_info *seq_port_info
225 + )
226 +{
227 + seq_port_info->type =
228 + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
229 + SNDRV_SEQ_PORT_TYPE_HARDWARE |
230 + SNDRV_SEQ_PORT_TYPE_PORT;
231 + seq_port_info->midi_voices = 0;
232 +}
233 +
234 +static struct snd_rawmidi_global_ops pisnd_global_ops = {
235 + .get_port_info = pisnd_get_port_info,
236 +};
237 +
238 +static int pisnd_midi_init(struct snd_card *card)
239 +{
240 + int err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
241 +
242 + if (err < 0) {
243 + printe("snd_rawmidi_new failed: %d\n", err);
244 + return err;
245 + }
246 +
247 + strcpy(g_rmidi->name, "pisound MIDI ");
248 + strcat(g_rmidi->name, pisnd_spi_get_serial());
249 +
250 + g_rmidi->info_flags =
251 + SNDRV_RAWMIDI_INFO_OUTPUT |
252 + SNDRV_RAWMIDI_INFO_INPUT |
253 + SNDRV_RAWMIDI_INFO_DUPLEX;
254 +
255 + g_rmidi->ops = &pisnd_global_ops;
256 +
257 + g_rmidi->private_data = (void *)0;
258 +
259 + snd_rawmidi_set_ops(
260 + g_rmidi,
261 + SNDRV_RAWMIDI_STREAM_OUTPUT,
262 + &pisnd_output_ops
263 + );
264 +
265 + snd_rawmidi_set_ops(
266 + g_rmidi,
267 + SNDRV_RAWMIDI_STREAM_INPUT,
268 + &pisnd_input_ops
269 + );
270 +
271 + return 0;
272 +}
273 +
274 +static void pisnd_midi_uninit(void)
275 +{
276 +}
277 +
278 +static void *g_recvData;
279 +static pisnd_spi_recv_cb g_recvCallback;
280 +
281 +#define FIFO_SIZE 512
282 +
283 +static char g_serial_num[11];
284 +static char g_id[25];
285 +static char g_version[5];
286 +
287 +static uint8_t g_ledFlashDuration;
288 +static bool g_ledFlashDurationChanged;
289 +
290 +DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
291 +DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
292 +
293 +static struct gpio_desc *data_available;
294 +static struct gpio_desc *spi_reset;
295 +
296 +static struct spi_device *pisnd_spi_device;
297 +
298 +static struct workqueue_struct *pisnd_workqueue;
299 +static struct work_struct pisnd_work_process;
300 +
301 +static void pisnd_work_handler(struct work_struct *work);
302 +
303 +static uint16_t spi_transfer16(uint16_t val);
304 +
305 +static int pisnd_init_workqueues(void)
306 +{
307 + pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
308 + INIT_WORK(&pisnd_work_process, pisnd_work_handler);
309 +
310 + return 0;
311 +}
312 +
313 +static void pisnd_uninit_workqueues(void)
314 +{
315 + flush_workqueue(pisnd_workqueue);
316 + destroy_workqueue(pisnd_workqueue);
317 +
318 + pisnd_workqueue = NULL;
319 +}
320 +
321 +static bool pisnd_spi_has_more(void)
322 +{
323 + return gpiod_get_value(data_available);
324 +}
325 +
326 +enum task_e {
327 + TASK_PROCESS = 0,
328 +};
329 +
330 +static void pisnd_schedule_process(enum task_e task)
331 +{
332 + if (pisnd_spi_device != NULL &&
333 + pisnd_workqueue != NULL &&
334 + !work_pending(&pisnd_work_process)
335 + ) {
336 + printd("schedule: has more = %d\n", pisnd_spi_has_more());
337 + if (task == TASK_PROCESS)
338 + queue_work(pisnd_workqueue, &pisnd_work_process);
339 + }
340 +}
341 +
342 +static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
343 +{
344 + if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
345 + printd("schedule from irq\n");
346 + pisnd_schedule_process(TASK_PROCESS);
347 + }
348 +
349 + return IRQ_HANDLED;
350 +}
351 +
352 +static DEFINE_SPINLOCK(spilock);
353 +static unsigned long spilockflags;
354 +
355 +static uint16_t spi_transfer16(uint16_t val)
356 +{
357 + int err;
358 + struct spi_transfer transfer;
359 + struct spi_message msg;
360 + uint8_t txbuf[2];
361 + uint8_t rxbuf[2];
362 +
363 + if (!pisnd_spi_device) {
364 + printe("pisnd_spi_device null, returning\n");
365 + return 0;
366 + }
367 +
368 + spi_message_init(&msg);
369 +
370 + memset(&transfer, 0, sizeof(transfer));
371 + memset(&rxbuf, 0, sizeof(rxbuf));
372 +
373 + txbuf[0] = val >> 8;
374 + txbuf[1] = val & 0xff;
375 +
376 + transfer.tx_buf = &txbuf;
377 + transfer.rx_buf = &rxbuf;
378 + transfer.len = sizeof(txbuf);
379 + transfer.speed_hz = 125000;
380 + transfer.delay_usecs = 100;
381 + spi_message_add_tail(&transfer, &msg);
382 +
383 + spin_lock_irqsave(&spilock, spilockflags);
384 + err = spi_sync(pisnd_spi_device, &msg);
385 + spin_unlock_irqrestore(&spilock, spilockflags);
386 +
387 + if (err < 0) {
388 + printe("spi_sync error %d\n", err);
389 + return 0;
390 + }
391 +
392 + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
393 + printd("hasMore %d\n", pisnd_spi_has_more());
394 +
395 + return (rxbuf[0] << 8) | rxbuf[1];
396 +}
397 +
398 +static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
399 +{
400 + uint16_t rx;
401 + uint8_t size;
402 + uint8_t i;
403 +
404 + memset(dst, 0, length);
405 + *bytesRead = 0;
406 +
407 + rx = spi_transfer16(0);
408 + if (!(rx >> 8))
409 + return -EINVAL;
410 +
411 + size = rx & 0xff;
412 +
413 + if (size > length)
414 + return -EINVAL;
415 +
416 + for (i = 0; i < size; ++i) {
417 + rx = spi_transfer16(0);
418 + if (!(rx >> 8))
419 + return -EINVAL;
420 +
421 + dst[i] = rx & 0xff;
422 + }
423 +
424 + *bytesRead = i;
425 +
426 + return 0;
427 +}
428 +
429 +static int spi_device_match(struct device *dev, void *data)
430 +{
431 + struct spi_device *spi = container_of(dev, struct spi_device, dev);
432 +
433 + printd(" %s %s %dkHz %d bits mode=0x%02X\n",
434 + spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
435 + spi->bits_per_word, spi->mode);
436 +
437 + if (strcmp("pisound-spi", spi->modalias) == 0) {
438 + printi("\tFound!\n");
439 + return 1;
440 + }
441 +
442 + printe("\tNot found!\n");
443 + return 0;
444 +}
445 +
446 +static struct spi_device *pisnd_spi_find_device(void)
447 +{
448 + struct device *dev;
449 +
450 + printi("Searching for spi device...\n");
451 + dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
452 + if (dev != NULL)
453 + return container_of(dev, struct spi_device, dev);
454 + else
455 + return NULL;
456 +}
457 +
458 +static void pisnd_work_handler(struct work_struct *work)
459 +{
460 + uint16_t rx;
461 + uint16_t tx;
462 + uint8_t val;
463 +
464 + if (work == &pisnd_work_process) {
465 + if (pisnd_spi_device == NULL)
466 + return;
467 +
468 + do {
469 + val = 0;
470 + tx = 0;
471 +
472 + if (g_ledFlashDurationChanged) {
473 + tx = 0xf000 | g_ledFlashDuration;
474 + g_ledFlashDuration = 0;
475 + g_ledFlashDurationChanged = false;
476 + } else if (kfifo_get(&spi_fifo_out, &val)) {
477 + tx = 0x0f00 | val;
478 + }
479 +
480 + rx = spi_transfer16(tx);
481 +
482 + if (rx & 0xff00) {
483 + kfifo_put(&spi_fifo_in, rx & 0xff);
484 + if (kfifo_len(&spi_fifo_in) > 16
485 + && g_recvCallback)
486 + g_recvCallback(g_recvData);
487 + }
488 + } while (rx != 0
489 + || !kfifo_is_empty(&spi_fifo_out)
490 + || pisnd_spi_has_more()
491 + || g_ledFlashDurationChanged
492 + );
493 +
494 + if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
495 + g_recvCallback(g_recvData);
496 + }
497 +}
498 +
499 +static int pisnd_spi_gpio_init(struct device *dev)
500 +{
501 + spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
502 + data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
503 +
504 + gpiod_direction_output(spi_reset, 1);
505 + gpiod_direction_input(data_available);
506 +
507 + /* Reset the slave. */
508 + gpiod_set_value(spi_reset, false);
509 + mdelay(1);
510 + gpiod_set_value(spi_reset, true);
511 +
512 + /* Give time for spi slave to start. */
513 + mdelay(64);
514 +
515 + return 0;
516 +}
517 +
518 +static void pisnd_spi_gpio_uninit(void)
519 +{
520 + gpiod_set_value(spi_reset, false);
521 + gpiod_put(spi_reset);
522 + spi_reset = NULL;
523 +
524 + gpiod_put(data_available);
525 + data_available = NULL;
526 +}
527 +
528 +static int pisnd_spi_gpio_irq_init(struct device *dev)
529 +{
530 + return request_irq(
531 + gpiod_to_irq(data_available),
532 + data_available_interrupt_handler,
533 + IRQF_TIMER | IRQF_TRIGGER_RISING,
534 + "data_available_int",
535 + NULL
536 + );
537 +}
538 +
539 +static void pisnd_spi_gpio_irq_uninit(void)
540 +{
541 + free_irq(gpiod_to_irq(data_available), NULL);
542 +}
543 +
544 +static int spi_read_info(void)
545 +{
546 + uint16_t tmp;
547 + uint8_t count;
548 + uint8_t n;
549 + uint8_t i;
550 + uint8_t j;
551 + char buffer[257];
552 + int ret;
553 + char *p;
554 +
555 + memset(g_serial_num, 0, sizeof(g_serial_num));
556 + memset(g_version, 0, sizeof(g_version));
557 + memset(g_id, 0, sizeof(g_id));
558 +
559 + tmp = spi_transfer16(0);
560 +
561 + if (!(tmp >> 8))
562 + return -EINVAL;
563 +
564 + count = tmp & 0xff;
565 +
566 + for (i = 0; i < count; ++i) {
567 + memset(buffer, 0, sizeof(buffer));
568 + ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
569 +
570 + if (ret < 0)
571 + return ret;
572 +
573 + switch (i) {
574 + case 0:
575 + if (n != 2)
576 + return -EINVAL;
577 +
578 + snprintf(
579 + g_version,
580 + sizeof(g_version),
581 + "%x.%02x",
582 + buffer[0],
583 + buffer[1]
584 + );
585 + break;
586 + case 1:
587 + if (n >= sizeof(g_serial_num))
588 + return -EINVAL;
589 +
590 + memcpy(g_serial_num, buffer, sizeof(g_serial_num));
591 + break;
592 + case 2:
593 + {
594 + if (n >= sizeof(g_id))
595 + return -EINVAL;
596 +
597 + p = g_id;
598 + for (j = 0; j < n; ++j)
599 + p += sprintf(p, "%02x", buffer[j]);
600 + }
601 + break;
602 + default:
603 + break;
604 + }
605 + }
606 +
607 + return 0;
608 +}
609 +
610 +static int pisnd_spi_init(struct device *dev)
611 +{
612 + int ret;
613 + struct spi_device *spi;
614 +
615 + memset(g_serial_num, 0, sizeof(g_serial_num));
616 + memset(g_id, 0, sizeof(g_id));
617 + memset(g_version, 0, sizeof(g_version));
618 +
619 + spi = pisnd_spi_find_device();
620 +
621 + if (spi != NULL) {
622 + printd("initializing spi!\n");
623 + pisnd_spi_device = spi;
624 + ret = spi_setup(pisnd_spi_device);
625 + } else {
626 + printe("SPI device not found, deferring!\n");
627 + return -EPROBE_DEFER;
628 + }
629 +
630 + ret = pisnd_spi_gpio_init(dev);
631 +
632 + if (ret < 0) {
633 + printe("SPI GPIO init failed: %d\n", ret);
634 + spi_dev_put(pisnd_spi_device);
635 + pisnd_spi_device = NULL;
636 + pisnd_spi_gpio_uninit();
637 + return ret;
638 + }
639 +
640 + ret = spi_read_info();
641 +
642 + if (ret < 0) {
643 + printe("Reading card info failed: %d\n", ret);
644 + spi_dev_put(pisnd_spi_device);
645 + pisnd_spi_device = NULL;
646 + pisnd_spi_gpio_uninit();
647 + return ret;
648 + }
649 +
650 + /* Flash the LEDs. */
651 + spi_transfer16(0xf008);
652 +
653 + ret = pisnd_spi_gpio_irq_init(dev);
654 + if (ret < 0) {
655 + printe("SPI irq request failed: %d\n", ret);
656 + spi_dev_put(pisnd_spi_device);
657 + pisnd_spi_device = NULL;
658 + pisnd_spi_gpio_irq_uninit();
659 + pisnd_spi_gpio_uninit();
660 + }
661 +
662 + ret = pisnd_init_workqueues();
663 + if (ret != 0) {
664 + printe("Workqueue initialization failed: %d\n", ret);
665 + spi_dev_put(pisnd_spi_device);
666 + pisnd_spi_device = NULL;
667 + pisnd_spi_gpio_irq_uninit();
668 + pisnd_spi_gpio_uninit();
669 + pisnd_uninit_workqueues();
670 + return ret;
671 + }
672 +
673 + if (pisnd_spi_has_more()) {
674 + printd("data is available, scheduling from init\n");
675 + pisnd_schedule_process(TASK_PROCESS);
676 + }
677 +
678 + return 0;
679 +}
680 +
681 +static void pisnd_spi_uninit(void)
682 +{
683 + pisnd_uninit_workqueues();
684 +
685 + spi_dev_put(pisnd_spi_device);
686 + pisnd_spi_device = NULL;
687 +
688 + pisnd_spi_gpio_irq_uninit();
689 + pisnd_spi_gpio_uninit();
690 +}
691 +
692 +static void pisnd_spi_flash_leds(uint8_t duration)
693 +{
694 + g_ledFlashDuration = duration;
695 + g_ledFlashDurationChanged = true;
696 + printd("schedule from spi_flash_leds\n");
697 + pisnd_schedule_process(TASK_PROCESS);
698 +}
699 +
700 +static void pisnd_spi_send(uint8_t val)
701 +{
702 + kfifo_put(&spi_fifo_out, val);
703 + printd("schedule from spi_send\n");
704 + pisnd_schedule_process(TASK_PROCESS);
705 +}
706 +
707 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
708 +{
709 + return kfifo_out(&spi_fifo_in, buffer, length);
710 +}
711 +
712 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
713 +{
714 + g_recvData = data;
715 + g_recvCallback = cb;
716 +}
717 +
718 +static const char *pisnd_spi_get_serial(void)
719 +{
720 + if (strlen(g_serial_num))
721 + return g_serial_num;
722 +
723 + return "";
724 +}
725 +
726 +static const char *pisnd_spi_get_id(void)
727 +{
728 + if (strlen(g_id))
729 + return g_id;
730 +
731 + return "";
732 +}
733 +
734 +static const char *pisnd_spi_get_version(void)
735 +{
736 + if (strlen(g_version))
737 + return g_version;
738 +
739 + return "";
740 +}
741 +
742 +static const struct of_device_id pisound_of_match[] = {
743 + { .compatible = "blokaslabs,pisound", },
744 + { .compatible = "blokaslabs,pisound-spi", },
745 + {},
746 +};
747 +
748 +enum {
749 + SWITCH = 0,
750 + VOLUME = 1,
751 +};
752 +
753 +static int pisnd_ctl_info(struct snd_kcontrol *kcontrol,
754 + struct snd_ctl_elem_info *uinfo)
755 +{
756 + if (kcontrol->private_value == SWITCH) {
757 + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
758 + uinfo->count = 1;
759 + uinfo->value.integer.min = 0;
760 + uinfo->value.integer.max = 1;
761 + return 0;
762 + } else if (kcontrol->private_value == VOLUME) {
763 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
764 + uinfo->count = 1;
765 + uinfo->value.integer.min = 0;
766 + uinfo->value.integer.max = 100;
767 + return 0;
768 + }
769 + return -EINVAL;
770 +}
771 +
772 +static int pisnd_ctl_get(struct snd_kcontrol *kcontrol,
773 + struct snd_ctl_elem_value *ucontrol)
774 +{
775 + if (kcontrol->private_value == SWITCH) {
776 + ucontrol->value.integer.value[0] = 1;
777 + return 0;
778 + } else if (kcontrol->private_value == VOLUME) {
779 + ucontrol->value.integer.value[0] = 100;
780 + return 0;
781 + }
782 +
783 + return -EINVAL;
784 +}
785 +
786 +static struct snd_kcontrol_new pisnd_ctl[] = {
787 + {
788 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
789 + .name = "PCM Playback Switch",
790 + .index = 0,
791 + .private_value = SWITCH,
792 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
793 + .info = pisnd_ctl_info,
794 + .get = pisnd_ctl_get,
795 + },
796 + {
797 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
798 + .name = "PCM Playback Volume",
799 + .index = 0,
800 + .private_value = VOLUME,
801 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
802 + .info = pisnd_ctl_info,
803 + .get = pisnd_ctl_get,
804 + },
805 +};
806 +
807 +static int pisnd_ctl_init(struct snd_card *card)
808 +{
809 + int err, i;
810 +
811 + for (i = 0; i < ARRAY_SIZE(pisnd_ctl); ++i) {
812 + err = snd_ctl_add(card, snd_ctl_new1(&pisnd_ctl[i], NULL));
813 + if (err < 0)
814 + return err;
815 + }
816 +
817 + return 0;
818 +}
819 +
820 +static int pisnd_ctl_uninit(void)
821 +{
822 + return 0;
823 +}
824 +
825 +static struct gpio_desc *osr0, *osr1, *osr2;
826 +static struct gpio_desc *reset;
827 +static struct gpio_desc *button;
828 +
829 +static int pisnd_hw_params(
830 + struct snd_pcm_substream *substream,
831 + struct snd_pcm_hw_params *params
832 + )
833 +{
834 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
835 + struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
836 +
837 + /* pisound runs on fixed 32 clock counts per channel,
838 + * as generated by the master ADC.
839 + */
840 + snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
841 +
842 + printd("rate = %d\n", params_rate(params));
843 + printd("ch = %d\n", params_channels(params));
844 + printd("bits = %u\n",
845 + snd_pcm_format_physical_width(params_format(params)));
846 + printd("format = %d\n", params_format(params));
847 +
848 + gpiod_set_value(reset, false);
849 +
850 + switch (params_rate(params)) {
851 + case 48000:
852 + gpiod_set_value(osr0, true);
853 + gpiod_set_value(osr1, false);
854 + gpiod_set_value(osr2, false);
855 + break;
856 + case 96000:
857 + gpiod_set_value(osr0, true);
858 + gpiod_set_value(osr1, true);
859 + gpiod_set_value(osr2, false);
860 + break;
861 + case 192000:
862 + gpiod_set_value(osr0, true);
863 + gpiod_set_value(osr1, true);
864 + gpiod_set_value(osr2, true);
865 + break;
866 + default:
867 + printe("Unsupported rate %u!\n", params_rate(params));
868 + return -EINVAL;
869 + }
870 +
871 + gpiod_set_value(reset, true);
872 +
873 + return 0;
874 +}
875 +
876 +static unsigned int rates[3] = {
877 + 48000, 96000, 192000
878 +};
879 +
880 +static struct snd_pcm_hw_constraint_list constraints_rates = {
881 + .count = ARRAY_SIZE(rates),
882 + .list = rates,
883 + .mask = 0,
884 +};
885 +
886 +static int pisnd_startup(struct snd_pcm_substream *substream)
887 +{
888 + int err = snd_pcm_hw_constraint_list(
889 + substream->runtime,
890 + 0,
891 + SNDRV_PCM_HW_PARAM_RATE,
892 + &constraints_rates
893 + );
894 +
895 + if (err < 0)
896 + return err;
897 +
898 + err = snd_pcm_hw_constraint_single(
899 + substream->runtime,
900 + SNDRV_PCM_HW_PARAM_CHANNELS,
901 + 2
902 + );
903 +
904 + if (err < 0)
905 + return err;
906 +
907 + err = snd_pcm_hw_constraint_mask64(
908 + substream->runtime,
909 + SNDRV_PCM_HW_PARAM_FORMAT,
910 + SNDRV_PCM_FMTBIT_S16_LE |
911 + SNDRV_PCM_FMTBIT_S24_LE |
912 + SNDRV_PCM_FMTBIT_S32_LE
913 + );
914 +
915 + if (err < 0)
916 + return err;
917 +
918 + return 0;
919 +}
920 +
921 +static struct snd_soc_ops pisnd_ops = {
922 + .startup = pisnd_startup,
923 + .hw_params = pisnd_hw_params,
924 +};
925 +
926 +static struct snd_soc_dai_link pisnd_dai[] = {
927 + {
928 + .name = "pisound",
929 + .stream_name = "pisound",
930 + .cpu_dai_name = "bcm2708-i2s.0",
931 + .codec_dai_name = "snd-soc-dummy-dai",
932 + .platform_name = "bcm2708-i2s.0",
933 + .codec_name = "snd-soc-dummy",
934 + .dai_fmt =
935 + SND_SOC_DAIFMT_I2S |
936 + SND_SOC_DAIFMT_NB_NF |
937 + SND_SOC_DAIFMT_CBM_CFM,
938 + .ops = &pisnd_ops,
939 + },
940 +};
941 +
942 +static int pisnd_card_probe(struct snd_soc_card *card)
943 +{
944 + int err = pisnd_midi_init(card->snd_card);
945 +
946 + if (err < 0) {
947 + printe("pisnd_midi_init failed: %d\n", err);
948 + return err;
949 + }
950 +
951 + err = pisnd_ctl_init(card->snd_card);
952 + if (err < 0) {
953 + printe("pisnd_ctl_init failed: %d\n", err);
954 + return err;
955 + }
956 +
957 + return 0;
958 +}
959 +
960 +static int pisnd_card_remove(struct snd_soc_card *card)
961 +{
962 + pisnd_ctl_uninit();
963 + pisnd_midi_uninit();
964 + return 0;
965 +}
966 +
967 +static struct snd_soc_card pisnd_card = {
968 + .name = "pisound",
969 + .owner = THIS_MODULE,
970 + .dai_link = pisnd_dai,
971 + .num_links = ARRAY_SIZE(pisnd_dai),
972 + .probe = pisnd_card_probe,
973 + .remove = pisnd_card_remove,
974 +};
975 +
976 +static int pisnd_init_gpio(struct device *dev)
977 +{
978 + osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
979 + osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
980 + osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
981 +
982 + reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
983 +
984 + button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
985 +
986 + gpiod_direction_output(osr0, 1);
987 + gpiod_direction_output(osr1, 1);
988 + gpiod_direction_output(osr2, 1);
989 + gpiod_direction_output(reset, 1);
990 +
991 + gpiod_set_value(reset, false);
992 + gpiod_set_value(osr0, true);
993 + gpiod_set_value(osr1, false);
994 + gpiod_set_value(osr2, false);
995 + gpiod_set_value(reset, true);
996 +
997 + gpiod_export(button, false);
998 +
999 + return 0;
1000 +}
1001 +
1002 +static int pisnd_uninit_gpio(void)
1003 +{
1004 + int i;
1005 +
1006 + struct gpio_desc **gpios[] = {
1007 + &osr0, &osr1, &osr2, &reset, &button,
1008 + };
1009 +
1010 + gpiod_unexport(button);
1011 +
1012 + for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
1013 + if (*gpios[i] == NULL) {
1014 + printd("weird, GPIO[%d] is NULL already\n", i);
1015 + continue;
1016 + }
1017 +
1018 + gpiod_put(*gpios[i]);
1019 + *gpios[i] = NULL;
1020 + }
1021 +
1022 + return 0;
1023 +}
1024 +
1025 +static struct kobject *pisnd_kobj;
1026 +
1027 +static ssize_t pisnd_serial_show(
1028 + struct kobject *kobj,
1029 + struct kobj_attribute *attr,
1030 + char *buf
1031 + )
1032 +{
1033 + return sprintf(buf, "%s\n", pisnd_spi_get_serial());
1034 +}
1035 +
1036 +static ssize_t pisnd_id_show(
1037 + struct kobject *kobj,
1038 + struct kobj_attribute *attr,
1039 + char *buf
1040 + )
1041 +{
1042 + return sprintf(buf, "%s\n", pisnd_spi_get_id());
1043 +}
1044 +
1045 +static ssize_t pisnd_version_show(
1046 + struct kobject *kobj,
1047 + struct kobj_attribute *attr,
1048 + char *buf
1049 + )
1050 +{
1051 + return sprintf(buf, "%s\n", pisnd_spi_get_version());
1052 +}
1053 +
1054 +static ssize_t pisnd_led_store(
1055 + struct kobject *kobj,
1056 + struct kobj_attribute *attr,
1057 + const char *buf,
1058 + size_t length
1059 + )
1060 +{
1061 + uint32_t timeout;
1062 + int err;
1063 +
1064 + err = kstrtou32(buf, 10, &timeout);
1065 +
1066 + if (err == 0 && timeout <= 255)
1067 + pisnd_spi_flash_leds(timeout);
1068 +
1069 + return length;
1070 +}
1071 +
1072 +static struct kobj_attribute pisnd_serial_attribute =
1073 + __ATTR(serial, 0444, pisnd_serial_show, NULL);
1074 +static struct kobj_attribute pisnd_id_attribute =
1075 + __ATTR(id, 0444, pisnd_id_show, NULL);
1076 +static struct kobj_attribute pisnd_version_attribute =
1077 + __ATTR(version, 0444, pisnd_version_show, NULL);
1078 +static struct kobj_attribute pisnd_led_attribute =
1079 + __ATTR(led, 0644, NULL, pisnd_led_store);
1080 +
1081 +static struct attribute *attrs[] = {
1082 + &pisnd_serial_attribute.attr,
1083 + &pisnd_id_attribute.attr,
1084 + &pisnd_version_attribute.attr,
1085 + &pisnd_led_attribute.attr,
1086 + NULL
1087 +};
1088 +
1089 +static struct attribute_group attr_group = { .attrs = attrs };
1090 +
1091 +static int pisnd_probe(struct platform_device *pdev)
1092 +{
1093 + int ret = 0;
1094 + int i;
1095 +
1096 + ret = pisnd_spi_init(&pdev->dev);
1097 + if (ret < 0) {
1098 + printe("pisnd_spi_init failed: %d\n", ret);
1099 + return ret;
1100 + }
1101 +
1102 + printi("Detected pisound card:\n");
1103 + printi("\tSerial: %s\n", pisnd_spi_get_serial());
1104 + printi("\tVersion: %s\n", pisnd_spi_get_version());
1105 + printi("\tId: %s\n", pisnd_spi_get_id());
1106 +
1107 + pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
1108 + if (!pisnd_kobj) {
1109 + pisnd_spi_uninit();
1110 + return -ENOMEM;
1111 + }
1112 +
1113 + ret = sysfs_create_group(pisnd_kobj, &attr_group);
1114 + if (ret < 0) {
1115 + pisnd_spi_uninit();
1116 + kobject_put(pisnd_kobj);
1117 + return -ENOMEM;
1118 + }
1119 +
1120 + pisnd_init_gpio(&pdev->dev);
1121 + pisnd_card.dev = &pdev->dev;
1122 +
1123 + if (pdev->dev.of_node) {
1124 + struct device_node *i2s_node;
1125 +
1126 + i2s_node = of_parse_phandle(
1127 + pdev->dev.of_node,
1128 + "i2s-controller",
1129 + 0
1130 + );
1131 +
1132 + for (i = 0; i < pisnd_card.num_links; ++i) {
1133 + struct snd_soc_dai_link *dai = &pisnd_dai[i];
1134 +
1135 + if (i2s_node) {
1136 + dai->cpu_dai_name = NULL;
1137 + dai->cpu_of_node = i2s_node;
1138 + dai->platform_name = NULL;
1139 + dai->platform_of_node = i2s_node;
1140 + dai->stream_name = pisnd_spi_get_serial();
1141 + }
1142 + }
1143 + }
1144 +
1145 + ret = snd_soc_register_card(&pisnd_card);
1146 +
1147 + if (ret < 0) {
1148 + if (ret != -EPROBE_DEFER)
1149 + printe("snd_soc_register_card() failed: %d\n", ret);
1150 + pisnd_uninit_gpio();
1151 + kobject_put(pisnd_kobj);
1152 + pisnd_spi_uninit();
1153 + }
1154 +
1155 + return ret;
1156 +}
1157 +
1158 +static int pisnd_remove(struct platform_device *pdev)
1159 +{
1160 + printi("Unloading.\n");
1161 +
1162 + if (pisnd_kobj) {
1163 + kobject_put(pisnd_kobj);
1164 + pisnd_kobj = NULL;
1165 + }
1166 +
1167 + pisnd_spi_uninit();
1168 +
1169 + /* Turn off */
1170 + gpiod_set_value(reset, false);
1171 + pisnd_uninit_gpio();
1172 +
1173 + return snd_soc_unregister_card(&pisnd_card);
1174 +}
1175 +
1176 +MODULE_DEVICE_TABLE(of, pisound_of_match);
1177 +
1178 +static struct platform_driver pisnd_driver = {
1179 + .driver = {
1180 + .name = "snd-rpi-pisound",
1181 + .owner = THIS_MODULE,
1182 + .of_match_table = pisound_of_match,
1183 + },
1184 + .probe = pisnd_probe,
1185 + .remove = pisnd_remove,
1186 +};
1187 +
1188 +module_platform_driver(pisnd_driver);
1189 +
1190 +MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
1191 +MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound");
1192 +MODULE_LICENSE("GPL v2");