You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
422 lines
11 KiB
422 lines
11 KiB
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");
|
|
|