From 5f3557a2e88b324e026d44f8fb7eb3ea37bba16b Mon Sep 17 00:00:00 2001 From: Giedrius Trainavicius Date: Tue, 25 Oct 2016 01:47:20 +0300 Subject: [PATCH 138/454] Updates for Pisound module code: * Merged 'Fix a warning in DEBUG builds' (1c8b82b). * Updating some strings and copyright information. * Fix for handling high load of MIDI input and output. * Use dual rate oversampling ratio for 96kHz instead of single rate one. Signed-off-by: Giedrius Trainavicius --- .../arm/boot/dts/overlays/pisound-overlay.dts | 4 +- sound/soc/bcm/pisound.c | 209 ++++++++++++------ 2 files changed, 146 insertions(+), 67 deletions(-) --- a/arch/arm/boot/dts/overlays/pisound-overlay.dts +++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts @@ -1,6 +1,6 @@ /* - * pisound Linux kernel module. - * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound + * Pisound Linux kernel module. + * Copyright (C) 2016-2017 Vilniaus Blokas UAB, https://blokas.io/pisound * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License --- a/sound/soc/bcm/pisound.c +++ b/sound/soc/bcm/pisound.c @@ -1,6 +1,6 @@ /* - * pisound Linux kernel module. - * Copyright (C) 2016 Vilniaus Blokas UAB, http://blokas.io/pisound + * Pisound Linux kernel module. + * Copyright (C) 2016-2017 Vilniaus Blokas UAB, https://blokas.io/pisound * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,8 @@ static int pisnd_spi_init(struct device *dev); static void pisnd_spi_uninit(void); -static void pisnd_spi_send(uint8_t val); +static void pisnd_spi_flush(void); +static void pisnd_spi_start(void); static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length); typedef void (*pisnd_spi_recv_cb)(void *data); @@ -56,7 +58,7 @@ static void pisnd_midi_uninit(void); #define PISOUND_LOG_PREFIX "pisound: " -#ifdef DEBUG +#ifdef PISOUND_DEBUG # define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__) #else # define printd(...) do {} while (0) @@ -65,13 +67,18 @@ static void pisnd_midi_uninit(void); #define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__) #define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__) +static struct snd_rawmidi *g_rmidi; +static struct snd_rawmidi_substream *g_midi_output_substream; + static int pisnd_output_open(struct snd_rawmidi_substream *substream) { + g_midi_output_substream = substream; return 0; } static int pisnd_output_close(struct snd_rawmidi_substream *substream) { + g_midi_output_substream = NULL; return 0; } @@ -80,26 +87,20 @@ static void pisnd_output_trigger( int up ) { - uint8_t data; + if (substream != g_midi_output_substream) { + printe("MIDI output trigger called for an unexpected stream!"); + return; + } if (!up) return; - while (snd_rawmidi_transmit_peek(substream, &data, 1)) { - pisnd_spi_send(data); - snd_rawmidi_transmit_ack(substream, 1); - } + pisnd_spi_start(); } static void pisnd_output_drain(struct snd_rawmidi_substream *substream) { - uint8_t data; - - while (snd_rawmidi_transmit_peek(substream, &data, 1)) { - pisnd_spi_send(data); - - snd_rawmidi_transmit_ack(substream, 1); - } + pisnd_spi_flush(); } static int pisnd_input_open(struct snd_rawmidi_substream *substream) @@ -120,7 +121,7 @@ static void pisnd_midi_recv_callback(voi while ((n = pisnd_spi_recv(data, sizeof(data)))) { int res = snd_rawmidi_receive(substream, data, n); (void)res; - printd("midi recv 0x%02x, res = %d\n", data, res); + printd("midi recv %u bytes, res = %d\n", n, res); } } @@ -134,8 +135,6 @@ static void pisnd_input_trigger(struct s } } -static struct snd_rawmidi *g_rmidi; - static struct snd_rawmidi_ops pisnd_output_ops = { .open = pisnd_output_open, .close = pisnd_output_close, @@ -168,7 +167,11 @@ static struct snd_rawmidi_global_ops pis static int pisnd_midi_init(struct snd_card *card) { - int err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi); + int err; + + g_midi_output_substream = NULL; + + err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi); if (err < 0) { printe("snd_rawmidi_new failed: %d\n", err); @@ -209,7 +212,7 @@ static void pisnd_midi_uninit(void) static void *g_recvData; static pisnd_spi_recv_cb g_recvCallback; -#define FIFO_SIZE 512 +#define FIFO_SIZE 4096 static char g_serial_num[11]; static char g_id[25]; @@ -231,6 +234,7 @@ static struct work_struct pisnd_work_pro static void pisnd_work_handler(struct work_struct *work); +static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len); static uint16_t spi_transfer16(uint16_t val); static int pisnd_init_workqueues(void) @@ -285,9 +289,6 @@ static unsigned long spilockflags; static uint16_t spi_transfer16(uint16_t val) { - int err; - struct spi_transfer transfer; - struct spi_message msg; uint8_t txbuf[2]; uint8_t rxbuf[2]; @@ -296,19 +297,38 @@ static uint16_t spi_transfer16(uint16_t return 0; } + txbuf[0] = val >> 8; + txbuf[1] = val & 0xff; + + spi_transfer(txbuf, rxbuf, sizeof(txbuf)); + + printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]); + + return (rxbuf[0] << 8) | rxbuf[1]; +} + +static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len) +{ + int err; + struct spi_transfer transfer; + struct spi_message msg; + + memset(rxbuf, 0, sizeof(txbuf)); + + if (!pisnd_spi_device) { + printe("pisnd_spi_device null, returning\n"); + return; + } + spi_message_init(&msg); memset(&transfer, 0, sizeof(transfer)); - memset(&rxbuf, 0, sizeof(rxbuf)); - txbuf[0] = val >> 8; - txbuf[1] = val & 0xff; - - transfer.tx_buf = &txbuf; - transfer.rx_buf = &rxbuf; - transfer.len = sizeof(txbuf); - transfer.speed_hz = 125000; - transfer.delay_usecs = 100; + transfer.tx_buf = txbuf; + transfer.rx_buf = rxbuf; + transfer.len = len; + transfer.speed_hz = 100000; + transfer.delay_usecs = 10; spi_message_add_tail(&transfer, &msg); spin_lock_irqsave(&spilock, spilockflags); @@ -317,13 +337,10 @@ static uint16_t spi_transfer16(uint16_t if (err < 0) { printe("spi_sync error %d\n", err); - return 0; + return; } - printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]); printd("hasMore %d\n", pisnd_spi_has_more()); - - return (rxbuf[0] << 8) | rxbuf[1]; } static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead) @@ -335,7 +352,7 @@ static int spi_read_bytes(char *dst, siz memset(dst, 0, length); *bytesRead = 0; - rx = spi_transfer16(0); + rx = spi_transfer16(0); if (!(rx >> 8)) return -EINVAL; @@ -388,35 +405,90 @@ static struct spi_device *pisnd_spi_find static void pisnd_work_handler(struct work_struct *work) { - uint16_t rx; - uint16_t tx; + enum { TRANSFER_SIZE = 4 }; + enum { PISOUND_OUTPUT_BUFFER_SIZE = 128 }; + enum { MIDI_BYTES_PER_SECOND = 3125 }; + int out_buffer_used = 0; + unsigned long now; uint8_t val; + uint8_t txbuf[TRANSFER_SIZE]; + uint8_t rxbuf[TRANSFER_SIZE]; + uint8_t midibuf[TRANSFER_SIZE]; + int i, n; + bool had_data; + + unsigned long last_transfer_at = jiffies; if (work == &pisnd_work_process) { if (pisnd_spi_device == NULL) return; do { - val = 0; - tx = 0; + if (g_midi_output_substream && + kfifo_avail(&spi_fifo_out) >= sizeof(midibuf)) { - if (g_ledFlashDurationChanged) { - tx = 0xf000 | g_ledFlashDuration; - g_ledFlashDuration = 0; - g_ledFlashDurationChanged = false; - } else if (kfifo_get(&spi_fifo_out, &val)) { - tx = 0x0f00 | val; + n = snd_rawmidi_transmit_peek( + g_midi_output_substream, + midibuf, sizeof(midibuf) + ); + + if (n > 0) { + for (i = 0; i < n; ++i) + kfifo_put( + &spi_fifo_out, + midibuf[i] + ); + snd_rawmidi_transmit_ack( + g_midi_output_substream, + i + ); + } } - rx = spi_transfer16(tx); + had_data = false; + memset(txbuf, 0, sizeof(txbuf)); + for (i = 0; i < sizeof(txbuf) && + out_buffer_used < PISOUND_OUTPUT_BUFFER_SIZE; + i += 2) { + + val = 0; + + if (g_ledFlashDurationChanged) { + txbuf[i+0] = 0xf0; + txbuf[i+1] = g_ledFlashDuration; + g_ledFlashDuration = 0; + g_ledFlashDurationChanged = false; + } else if (kfifo_get(&spi_fifo_out, &val)) { + txbuf[i+0] = 0x0f; + txbuf[i+1] = val; + ++out_buffer_used; + } + } - if (rx & 0xff00) { - kfifo_put(&spi_fifo_in, rx & 0xff); - if (kfifo_len(&spi_fifo_in) > 16 - && g_recvCallback) - g_recvCallback(g_recvData); + spi_transfer(txbuf, rxbuf, sizeof(txbuf)); + /* Estimate the Pisound's MIDI output buffer usage, so + * that we don't overflow it. Space in the buffer should + * be becoming available at the UART MIDI byte transfer + * rate. + */ + now = jiffies; + out_buffer_used -= + (MIDI_BYTES_PER_SECOND / HZ) / + (now - last_transfer_at); + if (out_buffer_used < 0) + out_buffer_used = 0; + last_transfer_at = now; + + for (i = 0; i < sizeof(rxbuf); i += 2) { + if (rxbuf[i]) { + kfifo_put(&spi_fifo_in, rxbuf[i+1]); + if (kfifo_len(&spi_fifo_in) > 16 && + g_recvCallback) + g_recvCallback(g_recvData); + had_data = true; + } } - } while (rx != 0 + } while (had_data || !kfifo_is_empty(&spi_fifo_out) || pisnd_spi_has_more() || g_ledFlashDurationChanged @@ -492,7 +564,7 @@ static int spi_read_info(void) if (!(tmp >> 8)) return -EINVAL; - count = tmp & 0xff; + count = tmp & 0xff; for (i = 0; i < count; ++i) { memset(buffer, 0, sizeof(buffer)); @@ -628,10 +700,17 @@ static void pisnd_spi_flash_leds(uint8_t pisnd_schedule_process(TASK_PROCESS); } -static void pisnd_spi_send(uint8_t val) +static void pisnd_spi_flush(void) +{ + while (!kfifo_is_empty(&spi_fifo_out)) { + pisnd_spi_start(); + flush_workqueue(pisnd_workqueue); + } +} + +static void pisnd_spi_start(void) { - kfifo_put(&spi_fifo_out, val); - printd("schedule from spi_send\n"); + printd("schedule from spi_start\n"); pisnd_schedule_process(TASK_PROCESS); } @@ -765,7 +844,7 @@ static int pisnd_hw_params( struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - /* pisound runs on fixed 32 clock counts per channel, + /* Pisound runs on fixed 32 clock counts per channel, * as generated by the master ADC. */ snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); @@ -786,8 +865,8 @@ static int pisnd_hw_params( break; case 96000: gpiod_set_value(osr0, true); - gpiod_set_value(osr1, true); - gpiod_set_value(osr2, false); + gpiod_set_value(osr1, false); + gpiod_set_value(osr2, true); break; case 192000: gpiod_set_value(osr0, true); @@ -1030,7 +1109,7 @@ static int pisnd_probe(struct platform_d return ret; } - printi("Detected pisound card:\n"); + printi("Detected Pisound card:\n"); printi("\tSerial: %s\n", pisnd_spi_get_serial()); printi("\tVersion: %s\n", pisnd_spi_get_version()); printi("\tId: %s\n", pisnd_spi_get_id()); @@ -1119,5 +1198,5 @@ static struct platform_driver pisnd_driv module_platform_driver(pisnd_driver); MODULE_AUTHOR("Giedrius Trainavicius "); -MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound"); +MODULE_DESCRIPTION("ASoC Driver for Pisound, https://blokas.io/pisound"); MODULE_LICENSE("GPL v2");