mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			430 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 8532ef4bc7d64096b539444d0b51855fca051efa Mon Sep 17 00:00:00 2001
 | |
| From: Giedrius Trainavicius <giedrius@blokas.io>
 | |
| Date: Tue, 25 Oct 2016 01:47:20 +0300
 | |
| Subject: [PATCH 155/277] 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>
 | |
| ---
 | |
|  arch/arm/boot/dts/overlays/pisound-overlay.dts |   4 +-
 | |
|  sound/soc/bcm/pisound.c                        | 209 +++++++++++++++++--------
 | |
|  2 files changed, 146 insertions(+), 67 deletions(-)
 | |
| 
 | |
| diff --git a/arch/arm/boot/dts/overlays/pisound-overlay.dts b/arch/arm/boot/dts/overlays/pisound-overlay.dts
 | |
| index 5197e656a3d7..0893717af4e0 100644
 | |
| --- 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
 | |
| diff --git a/sound/soc/bcm/pisound.c b/sound/soc/bcm/pisound.c
 | |
| index 06ff1e53dc9d..09739d51b70b 100644
 | |
| --- 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(void *substream)
 | |
|  	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 snd_rawmidi_substream *substream, int up)
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| -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 pisnd_global_ops = {
 | |
|  
 | |
|  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_process;
 | |
|  
 | |
|  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 val)
 | |
|  		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 val)
 | |
|  
 | |
|  	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, size_t length, uint8_t *bytesRead)
 | |
|  	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_device(void)
 | |
|  
 | |
|  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_ledFlashDurationChanged) {
 | |
| -				tx = 0xf000 | g_ledFlashDuration;
 | |
| -				g_ledFlashDuration = 0;
 | |
| -				g_ledFlashDurationChanged = false;
 | |
| -			} else if (kfifo_get(&spi_fifo_out, &val)) {
 | |
| -				tx = 0x0f00 | val;
 | |
| +			if (g_midi_output_substream &&
 | |
| +				kfifo_avail(&spi_fifo_out) >= sizeof(midibuf)) {
 | |
| +
 | |
| +				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 duration)
 | |
|  	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_device *pdev)
 | |
|  		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_driver = {
 | |
|  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");
 | |
| -- 
 | |
| 2.16.1
 | |
| 
 |