44335f1fd245ffab8391f0c13eefaf814ba7cb01
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.4 / 0527-Support-for-Blokas-Labs-pisound-board.patch
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
5
6 ---
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
19
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)
44
45
46 +Name: pisound
47 +Info: Configures the Blokas Labs pisound card
48 +Load: dtoverlay=pisound
49 +Params: <None>
50 +
51 +
52 Name: pitft22
53 Info: Adafruit PiTFT 2.2" screen
54 Load: dtoverlay=pitft22,<param>=<val>
55 --- /dev/null
56 +++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts
57 @@ -0,0 +1,114 @@
58 +/*
59 + * pisound Linux kernel module.
60 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
61 + *
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
65 + * License.
66 + *
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.
71 + *
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.
75 + */
76 +
77 +/dts-v1/;
78 +/plugin/;
79 +
80 +#include <dt-bindings/gpio/gpio.h>
81 +
82 +/ {
83 + compatible = "brcm,bcm2708";
84 +
85 + fragment@0 {
86 + target = <&sound>;
87 + __overlay__ {
88 + compatible = "blokaslabs,pisound";
89 + i2s-controller = <&i2s>;
90 + status = "okay";
91 +
92 + pinctrl-0 = <&pisound_button_pins>;
93 +
94 + osr-gpios =
95 + <&gpio 13 GPIO_ACTIVE_HIGH>,
96 + <&gpio 26 GPIO_ACTIVE_HIGH>,
97 + <&gpio 16 GPIO_ACTIVE_HIGH>;
98 +
99 + reset-gpios =
100 + <&gpio 12 GPIO_ACTIVE_HIGH>,
101 + <&gpio 24 GPIO_ACTIVE_HIGH>;
102 +
103 + data_available-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
104 +
105 + button-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
106 + };
107 + };
108 +
109 + fragment@1 {
110 + target = <&gpio>;
111 + __overlay__ {
112 + pinctrl-names = "default";
113 + pinctrl-0 = <&pisound_button_pins>;
114 +
115 + pisound_button_pins: pisound_button_pins {
116 + brcm,pins = <17>;
117 + brcm,function = <0>; // Input
118 + brcm,pull = <2>; // Pull-Up
119 + };
120 + };
121 + };
122 +
123 + fragment@2 {
124 + target = <&i2s>;
125 + __overlay__ {
126 + status = "okay";
127 + };
128 + };
129 +
130 + fragment@3 {
131 + target-path = "/";
132 + __overlay__ {
133 + pcm5102a-codec {
134 + #sound-dai-cells = <0>;
135 + compatible = "ti,pcm5102a";
136 + status = "okay";
137 + };
138 + };
139 + };
140 +
141 + fragment@4 {
142 + target = <&spi0>;
143 + __overlay__ {
144 + status = "okay";
145 +
146 + spidev@0{
147 + status = "disabled";
148 + };
149 +
150 + spidev@1{
151 + status = "okay";
152 + };
153 + };
154 + };
155 +
156 + fragment@5 {
157 + target = <&spi0>;
158 + __overlay__ {
159 + #address-cells = <1>;
160 + #size-cells = <0>;
161 +
162 + pisound_spi: pisound_spi@0{
163 + compatible = "blokaslabs,pisound-spi";
164 + reg = <0>;
165 + pinctrl-names = "default";
166 + pinctrl-0 = <&spi0_pins>;
167 + spi-max-frequency = <1000000>;
168 + };
169 + };
170 + };
171 +};
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
196 help
197 Say Y or M if you want to add support for Allo Piano DAC.
198 +
199 +config SND_PISOUND
200 + tristate "Support for Blokas Labs pisound"
201 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
202 + help
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
211
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
219 --- /dev/null
220 +++ b/sound/soc/bcm/pisound.c
221 @@ -0,0 +1,987 @@
222 +/*
223 + * pisound Linux kernel module.
224 + * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound
225 + *
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
229 + * License.
230 + *
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.
235 + *
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.
240 + */
241 +
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>
252 +
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>
260 +
261 +static int pisnd_spi_init(struct device *dev);
262 +static void pisnd_spi_uninit(void);
263 +
264 +static void pisnd_spi_send(uint8_t val);
265 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
266 +
267 +typedef void (*pisnd_spi_recv_cb)(void *data);
268 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
269 +
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);
273 +
274 +static int pisnd_midi_init(struct snd_card *card);
275 +static void pisnd_midi_uninit(void);
276 +
277 +#define PISOUND_LOG_PREFIX "pisound: "
278 +
279 +#ifdef DEBUG
280 +# define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
281 +#else
282 +# define printd(...) do {} while (0)
283 +#endif
284 +
285 +#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
286 +#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
287 +
288 +static int pisnd_output_open(struct snd_rawmidi_substream *substream)
289 +{
290 + return 0;
291 +}
292 +
293 +static int pisnd_output_close(struct snd_rawmidi_substream *substream)
294 +{
295 + return 0;
296 +}
297 +
298 +static void pisnd_output_trigger(
299 + struct snd_rawmidi_substream *substream,
300 + int up
301 + )
302 +{
303 + uint8_t data;
304 +
305 + if (!up)
306 + return;
307 +
308 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
309 + pisnd_spi_send(data);
310 + snd_rawmidi_transmit_ack(substream, 1);
311 + }
312 +}
313 +
314 +static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
315 +{
316 + uint8_t data;
317 +
318 + while (snd_rawmidi_transmit_peek(substream, &data, 1)) {
319 + pisnd_spi_send(data);
320 +
321 + snd_rawmidi_transmit_ack(substream, 1);
322 + }
323 +}
324 +
325 +static int pisnd_input_open(struct snd_rawmidi_substream *substream)
326 +{
327 + return 0;
328 +}
329 +
330 +static int pisnd_input_close(struct snd_rawmidi_substream *substream)
331 +{
332 + return 0;
333 +}
334 +
335 +static void pisnd_midi_recv_callback(void *substream)
336 +{
337 + uint8_t data[128];
338 + uint8_t n = 0;
339 +
340 + while ((n = pisnd_spi_recv(data, sizeof(data)))) {
341 + int res = snd_rawmidi_receive(substream, data, n);
342 + (void)res;
343 + printd("midi recv 0x%02x, res = %d\n", data, res);
344 + }
345 +}
346 +
347 +static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
348 +{
349 + if (up) {
350 + pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
351 + pisnd_midi_recv_callback(substream);
352 + } else {
353 + pisnd_spi_set_callback(NULL, NULL);
354 + }
355 +}
356 +
357 +static struct snd_rawmidi *g_rmidi;
358 +
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,
364 +};
365 +
366 +static struct snd_rawmidi_ops pisnd_input_ops = {
367 + .open = pisnd_input_open,
368 + .close = pisnd_input_close,
369 + .trigger = pisnd_input_trigger,
370 +};
371 +
372 +static void pisnd_get_port_info(
373 + struct snd_rawmidi *rmidi,
374 + int number,
375 + struct snd_seq_port_info *seq_port_info
376 + )
377 +{
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;
383 +}
384 +
385 +static struct snd_rawmidi_global_ops pisnd_global_ops = {
386 + .get_port_info = pisnd_get_port_info,
387 +};
388 +
389 +static int pisnd_midi_init(struct snd_card *card)
390 +{
391 + int err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
392 +
393 + if (err < 0) {
394 + printe("snd_rawmidi_new failed: %d\n", err);
395 + return err;
396 + }
397 +
398 + strcpy(g_rmidi->name, "pisound MIDI ");
399 + strcat(g_rmidi->name, pisnd_spi_get_serial());
400 +
401 + g_rmidi->info_flags =
402 + SNDRV_RAWMIDI_INFO_OUTPUT |
403 + SNDRV_RAWMIDI_INFO_INPUT |
404 + SNDRV_RAWMIDI_INFO_DUPLEX;
405 +
406 + g_rmidi->ops = &pisnd_global_ops;
407 +
408 + g_rmidi->private_data = (void *)0;
409 +
410 + snd_rawmidi_set_ops(
411 + g_rmidi,
412 + SNDRV_RAWMIDI_STREAM_OUTPUT,
413 + &pisnd_output_ops
414 + );
415 +
416 + snd_rawmidi_set_ops(
417 + g_rmidi,
418 + SNDRV_RAWMIDI_STREAM_INPUT,
419 + &pisnd_input_ops
420 + );
421 +
422 + return 0;
423 +}
424 +
425 +static void pisnd_midi_uninit(void)
426 +{
427 +}
428 +
429 +static void *g_recvData;
430 +static pisnd_spi_recv_cb g_recvCallback;
431 +
432 +#define FIFO_SIZE 512
433 +
434 +static char g_serial_num[11];
435 +static char g_id[25];
436 +static char g_version[5];
437 +
438 +DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
439 +DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
440 +
441 +static struct gpio_desc *data_available;
442 +static struct gpio_desc *spi_reset;
443 +
444 +static struct spi_device *pisnd_spi_device;
445 +
446 +static struct workqueue_struct *pisnd_workqueue;
447 +static struct work_struct pisnd_work_process;
448 +
449 +static void pisnd_work_handler(struct work_struct *work);
450 +
451 +static uint16_t spi_transfer16(uint16_t val);
452 +
453 +static int pisnd_init_workqueues(void)
454 +{
455 + pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
456 + INIT_WORK(&pisnd_work_process, pisnd_work_handler);
457 +
458 + return 0;
459 +}
460 +
461 +static void pisnd_uninit_workqueues(void)
462 +{
463 + flush_workqueue(pisnd_workqueue);
464 + destroy_workqueue(pisnd_workqueue);
465 +
466 + pisnd_workqueue = NULL;
467 +}
468 +
469 +static bool pisnd_spi_has_more(void)
470 +{
471 + return gpiod_get_value(data_available);
472 +}
473 +
474 +enum task_e {
475 + TASK_PROCESS = 0,
476 +};
477 +
478 +static void pisnd_schedule_process(enum task_e task)
479 +{
480 + if (pisnd_spi_device != NULL &&
481 + pisnd_workqueue != NULL &&
482 + !work_pending(&pisnd_work_process)
483 + ) {
484 + printd("schedule: has more = %d\n", pisnd_spi_has_more());
485 + if (task == TASK_PROCESS)
486 + queue_work(pisnd_workqueue, &pisnd_work_process);
487 + }
488 +}
489 +
490 +static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
491 +{
492 + if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
493 + printd("schedule from irq\n");
494 + pisnd_schedule_process(TASK_PROCESS);
495 + }
496 +
497 + return IRQ_HANDLED;
498 +}
499 +
500 +static DEFINE_SPINLOCK(spilock);
501 +static unsigned long spilockflags;
502 +
503 +static uint16_t spi_transfer16(uint16_t val)
504 +{
505 + int err;
506 + struct spi_transfer transfer;
507 + struct spi_message msg;
508 + uint8_t txbuf[2];
509 + uint8_t rxbuf[2];
510 +
511 + if (!pisnd_spi_device) {
512 + printe("pisnd_spi_device null, returning\n");
513 + return 0;
514 + }
515 +
516 + spi_message_init(&msg);
517 +
518 + memset(&transfer, 0, sizeof(transfer));
519 + memset(&rxbuf, 0, sizeof(rxbuf));
520 +
521 + txbuf[0] = val >> 8;
522 + txbuf[1] = val & 0xff;
523 +
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);
530 +
531 + spin_lock_irqsave(&spilock, spilockflags);
532 + err = spi_sync(pisnd_spi_device, &msg);
533 + spin_unlock_irqrestore(&spilock, spilockflags);
534 +
535 + if (err < 0) {
536 + printe("spi_sync error %d\n", err);
537 + return 0;
538 + }
539 +
540 + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
541 + printd("hasMore %d\n", pisnd_spi_has_more());
542 +
543 + return (rxbuf[0] << 8) | rxbuf[1];
544 +}
545 +
546 +static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
547 +{
548 + uint16_t rx;
549 + uint8_t size;
550 + uint8_t i;
551 +
552 + memset(dst, 0, length);
553 + *bytesRead = 0;
554 +
555 + rx = spi_transfer16(0);
556 + if (!(rx >> 8))
557 + return -EINVAL;
558 +
559 + size = rx & 0xff;
560 +
561 + if (size > length)
562 + return -EINVAL;
563 +
564 + for (i = 0; i < size; ++i) {
565 + rx = spi_transfer16(0);
566 + if (!(rx >> 8))
567 + return -EINVAL;
568 +
569 + dst[i] = rx & 0xff;
570 + }
571 +
572 + *bytesRead = i;
573 +
574 + return 0;
575 +}
576 +
577 +static int spi_device_match(struct device *dev, void *data)
578 +{
579 + struct spi_device *spi = container_of(dev, struct spi_device, dev);
580 +
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);
584 +
585 + if (strcmp("pisound-spi", spi->modalias) == 0) {
586 + printi("\tFound!\n");
587 + return 1;
588 + }
589 +
590 + printe("\tNot found!\n");
591 + return 0;
592 +}
593 +
594 +static struct spi_device *pisnd_spi_find_device(void)
595 +{
596 + struct device *dev;
597 +
598 + printi("Searching for spi device...\n");
599 + dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
600 + if (dev != NULL)
601 + return container_of(dev, struct spi_device, dev);
602 + else
603 + return NULL;
604 +}
605 +
606 +static void pisnd_work_handler(struct work_struct *work)
607 +{
608 + uint16_t rx;
609 + uint16_t tx;
610 + uint8_t val;
611 +
612 + if (work == &pisnd_work_process) {
613 + if (pisnd_spi_device == NULL)
614 + return;
615 +
616 + do {
617 + val = 0;
618 + tx = 0;
619 +
620 + if (kfifo_get(&spi_fifo_out, &val))
621 + tx = 0x0f00 | val;
622 +
623 + rx = spi_transfer16(tx);
624 +
625 + if (rx & 0xff00) {
626 + kfifo_put(&spi_fifo_in, rx & 0xff);
627 + if (kfifo_len(&spi_fifo_in) > 16
628 + && g_recvCallback)
629 + g_recvCallback(g_recvData);
630 + }
631 + } while (rx != 0
632 + || !kfifo_is_empty(&spi_fifo_out)
633 + || pisnd_spi_has_more()
634 + );
635 +
636 + if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
637 + g_recvCallback(g_recvData);
638 + }
639 +}
640 +
641 +static int pisnd_spi_gpio_init(struct device *dev)
642 +{
643 + spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
644 + data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
645 +
646 + gpiod_direction_output(spi_reset, 1);
647 + gpiod_direction_input(data_available);
648 +
649 + /* Reset the slave. */
650 + gpiod_set_value(spi_reset, false);
651 + mdelay(1);
652 + gpiod_set_value(spi_reset, true);
653 +
654 + /* Give time for spi slave to start. */
655 + mdelay(64);
656 +
657 + return 0;
658 +}
659 +
660 +static void pisnd_spi_gpio_uninit(void)
661 +{
662 + gpiod_set_value(spi_reset, false);
663 + gpiod_put(spi_reset);
664 + spi_reset = NULL;
665 +
666 + gpiod_put(data_available);
667 + data_available = NULL;
668 +}
669 +
670 +static int pisnd_spi_gpio_irq_init(struct device *dev)
671 +{
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",
677 + NULL
678 + );
679 +}
680 +
681 +static void pisnd_spi_gpio_irq_uninit(void)
682 +{
683 + free_irq(gpiod_to_irq(data_available), NULL);
684 +}
685 +
686 +static int spi_read_info(void)
687 +{
688 + uint16_t tmp;
689 + uint8_t count;
690 + uint8_t n;
691 + uint8_t i;
692 + uint8_t j;
693 + char buffer[257];
694 + int ret;
695 + char *p;
696 +
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));
700 +
701 + tmp = spi_transfer16(0);
702 +
703 + if (!(tmp >> 8))
704 + return -EINVAL;
705 +
706 + count = tmp & 0xff;
707 +
708 + for (i = 0; i < count; ++i) {
709 + memset(buffer, 0, sizeof(buffer));
710 + ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
711 +
712 + if (ret < 0)
713 + return ret;
714 +
715 + switch (i) {
716 + case 0:
717 + if (n != 2)
718 + return -EINVAL;
719 +
720 + snprintf(
721 + g_version,
722 + sizeof(g_version),
723 + "%x.%02x",
724 + buffer[0],
725 + buffer[1]
726 + );
727 + break;
728 + case 1:
729 + if (n >= sizeof(g_serial_num))
730 + return -EINVAL;
731 +
732 + memcpy(g_serial_num, buffer, sizeof(g_serial_num));
733 + break;
734 + case 2:
735 + {
736 + if (n >= sizeof(g_id))
737 + return -EINVAL;
738 +
739 + p = g_id;
740 + for (j = 0; j < n; ++j)
741 + p += sprintf(p, "%02x", buffer[j]);
742 + }
743 + break;
744 + default:
745 + break;
746 + }
747 + }
748 +
749 + return 0;
750 +}
751 +
752 +static int pisnd_spi_init(struct device *dev)
753 +{
754 + int ret;
755 + struct spi_device *spi;
756 +
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));
760 +
761 + spi = pisnd_spi_find_device();
762 +
763 + if (spi != NULL) {
764 + printd("initializing spi!\n");
765 + pisnd_spi_device = spi;
766 + ret = spi_setup(pisnd_spi_device);
767 + } else {
768 + printe("SPI device not found, deferring!\n");
769 + return -EPROBE_DEFER;
770 + }
771 +
772 + ret = pisnd_spi_gpio_init(dev);
773 +
774 + if (ret < 0) {
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();
779 + return ret;
780 + }
781 +
782 + ret = spi_read_info();
783 +
784 + if (ret < 0) {
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();
789 + return ret;
790 + }
791 +
792 + /* Flash the LEDs. */
793 + spi_transfer16(0xf000);
794 +
795 + ret = pisnd_spi_gpio_irq_init(dev);
796 + if (ret < 0) {
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();
802 + }
803 +
804 + ret = pisnd_init_workqueues();
805 + if (ret != 0) {
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();
812 + return ret;
813 + }
814 +
815 + if (pisnd_spi_has_more()) {
816 + printd("data is available, scheduling from init\n");
817 + pisnd_schedule_process(TASK_PROCESS);
818 + }
819 +
820 + return 0;
821 +}
822 +
823 +static void pisnd_spi_uninit(void)
824 +{
825 + pisnd_uninit_workqueues();
826 +
827 + spi_dev_put(pisnd_spi_device);
828 + pisnd_spi_device = NULL;
829 +
830 + pisnd_spi_gpio_irq_uninit();
831 + pisnd_spi_gpio_uninit();
832 +}
833 +
834 +static void pisnd_spi_send(uint8_t val)
835 +{
836 + kfifo_put(&spi_fifo_out, val);
837 + printd("schedule from spi_send\n");
838 + pisnd_schedule_process(TASK_PROCESS);
839 +}
840 +
841 +static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
842 +{
843 + return kfifo_out(&spi_fifo_in, buffer, length);
844 +}
845 +
846 +static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
847 +{
848 + g_recvData = data;
849 + g_recvCallback = cb;
850 +}
851 +
852 +static const char *pisnd_spi_get_serial(void)
853 +{
854 + if (strlen(g_serial_num))
855 + return g_serial_num;
856 +
857 + return "";
858 +}
859 +
860 +static const char *pisnd_spi_get_id(void)
861 +{
862 + if (strlen(g_id))
863 + return g_id;
864 +
865 + return "";
866 +}
867 +
868 +static const char *pisnd_spi_get_version(void)
869 +{
870 + if (strlen(g_version))
871 + return g_version;
872 +
873 + return "";
874 +}
875 +
876 +static const struct of_device_id pisound_of_match[] = {
877 + { .compatible = "blokaslabs,pisound", },
878 + { .compatible = "blokaslabs,pisound-spi", },
879 + {},
880 +};
881 +
882 +static struct gpio_desc *osr0, *osr1, *osr2;
883 +static struct gpio_desc *reset;
884 +static struct gpio_desc *button;
885 +
886 +static int pisnd_hw_params(
887 + struct snd_pcm_substream *substream,
888 + struct snd_pcm_hw_params *params
889 + )
890 +{
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));
896 +
897 + gpiod_set_value(reset, false);
898 +
899 + switch (params_rate(params)) {
900 + case 48000:
901 + gpiod_set_value(osr0, true);
902 + gpiod_set_value(osr1, false);
903 + gpiod_set_value(osr2, false);
904 + break;
905 + case 96000:
906 + gpiod_set_value(osr0, true);
907 + gpiod_set_value(osr1, true);
908 + gpiod_set_value(osr2, false);
909 + break;
910 + case 192000:
911 + gpiod_set_value(osr0, true);
912 + gpiod_set_value(osr1, true);
913 + gpiod_set_value(osr2, true);
914 + break;
915 + default:
916 + printe("Unsupported rate %u!\n", params_rate(params));
917 + return -EINVAL;
918 + }
919 +
920 + gpiod_set_value(reset, true);
921 +
922 + return 0;
923 +}
924 +
925 +static unsigned int rates[3] = {
926 + 48000, 96000, 192000
927 +};
928 +
929 +static struct snd_pcm_hw_constraint_list constraints_rates = {
930 + .count = ARRAY_SIZE(rates),
931 + .list = rates,
932 + .mask = 0,
933 +};
934 +
935 +static unsigned int sample_bits[] = {
936 + 24, 32
937 +};
938 +
939 +static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
940 + .count = ARRAY_SIZE(sample_bits),
941 + .list = sample_bits,
942 + .mask = 0,
943 +};
944 +
945 +static int pisnd_startup(struct snd_pcm_substream *substream)
946 +{
947 + int err = snd_pcm_hw_constraint_list(
948 + substream->runtime,
949 + 0,
950 + SNDRV_PCM_HW_PARAM_RATE,
951 + &constraints_rates
952 + );
953 +
954 + if (err < 0)
955 + return err;
956 +
957 + err = snd_pcm_hw_constraint_list(
958 + substream->runtime,
959 + 0,
960 + SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
961 + &constraints_sample_bits
962 + );
963 +
964 + if (err < 0)
965 + return err;
966 +
967 + return 0;
968 +}
969 +
970 +static struct snd_soc_ops pisnd_ops = {
971 + .startup = pisnd_startup,
972 + .hw_params = pisnd_hw_params,
973 +};
974 +
975 +static struct snd_soc_dai_link pisnd_dai[] = {
976 + {
977 + .name = "pisound",
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",
983 + .dai_fmt =
984 + SND_SOC_DAIFMT_I2S |
985 + SND_SOC_DAIFMT_NB_NF |
986 + SND_SOC_DAIFMT_CBM_CFM,
987 + .ops = &pisnd_ops,
988 + },
989 +};
990 +
991 +static int pisnd_card_probe(struct snd_soc_card *card)
992 +{
993 + int err = pisnd_midi_init(card->snd_card);
994 +
995 + if (err < 0)
996 + printe("pisnd_midi_init failed: %d\n", err);
997 +
998 + return err;
999 +}
1000 +
1001 +static int pisnd_card_remove(struct snd_soc_card *card)
1002 +{
1003 + pisnd_midi_uninit();
1004 + return 0;
1005 +}
1006 +
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,
1014 +};
1015 +
1016 +static int pisnd_init_gpio(struct device *dev)
1017 +{
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);
1021 +
1022 + reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
1023 +
1024 + button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
1025 +
1026 + gpiod_direction_output(osr0, 1);
1027 + gpiod_direction_output(osr1, 1);
1028 + gpiod_direction_output(osr2, 1);
1029 + gpiod_direction_output(reset, 1);
1030 +
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);
1036 +
1037 + gpiod_export(button, false);
1038 +
1039 + return 0;
1040 +}
1041 +
1042 +static int pisnd_uninit_gpio(void)
1043 +{
1044 + int i;
1045 +
1046 + struct gpio_desc **gpios[] = {
1047 + &osr0, &osr1, &osr2, &reset, &button,
1048 + };
1049 +
1050 + gpiod_unexport(button);
1051 +
1052 + for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
1053 + if (*gpios[i] == NULL) {
1054 + printd("weird, GPIO[%d] is NULL already\n", i);
1055 + continue;
1056 + }
1057 +
1058 + gpiod_put(*gpios[i]);
1059 + *gpios[i] = NULL;
1060 + }
1061 +
1062 + return 0;
1063 +}
1064 +
1065 +static struct kobject *pisnd_kobj;
1066 +
1067 +static ssize_t pisnd_serial_show(
1068 + struct kobject *kobj,
1069 + struct kobj_attribute *attr,
1070 + char *buf
1071 + )
1072 +{
1073 + return sprintf(buf, "%s\n", pisnd_spi_get_serial());
1074 +}
1075 +
1076 +static ssize_t pisnd_id_show(
1077 + struct kobject *kobj,
1078 + struct kobj_attribute *attr,
1079 + char *buf
1080 + )
1081 +{
1082 + return sprintf(buf, "%s\n", pisnd_spi_get_id());
1083 +}
1084 +
1085 +static ssize_t pisnd_version_show(
1086 + struct kobject *kobj,
1087 + struct kobj_attribute *attr,
1088 + char *buf
1089 + )
1090 +{
1091 + return sprintf(buf, "%s\n", pisnd_spi_get_version());
1092 +}
1093 +
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);
1100 +
1101 +static struct attribute *attrs[] = {
1102 + &pisnd_serial_attribute.attr,
1103 + &pisnd_id_attribute.attr,
1104 + &pisnd_version_attribute.attr,
1105 + NULL
1106 +};
1107 +
1108 +static struct attribute_group attr_group = { .attrs = attrs };
1109 +
1110 +static int pisnd_probe(struct platform_device *pdev)
1111 +{
1112 + int ret = 0;
1113 + int i;
1114 +
1115 + ret = pisnd_spi_init(&pdev->dev);
1116 + if (ret < 0) {
1117 + printe("pisnd_spi_init failed: %d\n", ret);
1118 + return ret;
1119 + }
1120 +
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());
1125 +
1126 + pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
1127 + if (!pisnd_kobj) {
1128 + pisnd_spi_uninit();
1129 + return -ENOMEM;
1130 + }
1131 +
1132 + ret = sysfs_create_group(pisnd_kobj, &attr_group);
1133 + if (ret < 0) {
1134 + pisnd_spi_uninit();
1135 + kobject_put(pisnd_kobj);
1136 + return -ENOMEM;
1137 + }
1138 +
1139 + pisnd_init_gpio(&pdev->dev);
1140 + pisnd_card.dev = &pdev->dev;
1141 +
1142 + if (pdev->dev.of_node) {
1143 + struct device_node *i2s_node;
1144 +
1145 + i2s_node = of_parse_phandle(
1146 + pdev->dev.of_node,
1147 + "i2s-controller",
1148 + 0
1149 + );
1150 +
1151 + for (i = 0; i < pisnd_card.num_links; ++i) {
1152 + struct snd_soc_dai_link *dai = &pisnd_dai[i];
1153 +
1154 + if (i2s_node) {
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();
1160 + }
1161 + }
1162 + }
1163 +
1164 + ret = snd_soc_register_card(&pisnd_card);
1165 +
1166 + if (ret < 0) {
1167 + printe("snd_soc_register_card() failed: %d\n", ret);
1168 + pisnd_uninit_gpio();
1169 + kobject_put(pisnd_kobj);
1170 + pisnd_spi_uninit();
1171 + }
1172 +
1173 + return ret;
1174 +}
1175 +
1176 +static int pisnd_remove(struct platform_device *pdev)
1177 +{
1178 + if (pisnd_kobj) {
1179 + kobject_put(pisnd_kobj);
1180 + pisnd_kobj = NULL;
1181 + }
1182 +
1183 + pisnd_spi_uninit();
1184 +
1185 + /* Turn off */
1186 + gpiod_set_value(reset, false);
1187 + pisnd_uninit_gpio();
1188 +
1189 + return snd_soc_unregister_card(&pisnd_card);
1190 +}
1191 +
1192 +MODULE_DEVICE_TABLE(of, pisound_of_match);
1193 +
1194 +static struct platform_driver pisnd_driver = {
1195 + .driver = {
1196 + .name = "snd-rpi-pisound",
1197 + .owner = THIS_MODULE,
1198 + .of_match_table = pisound_of_match,
1199 + },
1200 + .probe = pisnd_probe,
1201 + .remove = pisnd_remove,
1202 +};
1203 +
1204 +module_platform_driver(pisnd_driver);
1205 +
1206 +MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
1207 +MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound");
1208 +MODULE_LICENSE("GPL v2");