openwrt/target/linux/brcm2708/patches-4.14/950-0138-Updates-for-Pisoun...

423 lines
11 KiB
Diff

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");