OpenWrt – Rev 2

Subversion Repositories:
Rev:
From 5f3557a2e88b324e026d44f8fb7eb3ea37bba16b Mon Sep 17 00:00:00 2001
From: Giedrius Trainavicius <giedrius@blokas.io>
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 <giedrius@blokas.io>
---
 .../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 <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/kfifo.h>
+#include <linux/jiffies.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -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 <giedrius@blokas.io>");
-MODULE_DESCRIPTION("ASoC Driver for pisound, http://blokas.io/pisound");
+MODULE_DESCRIPTION("ASoC Driver for Pisound, https://blokas.io/pisound");
 MODULE_LICENSE("GPL v2");