2017-03-04 18:37:50 +00:00
|
|
|
From 8ed8e29b19dba95a7a6c2364c2bb32d926601713 Mon Sep 17 00:00:00 2001
|
2017-02-07 20:07:54 +00:00
|
|
|
From: Giedrius Trainavicius <giedrius@blokas.io>
|
|
|
|
Date: Thu, 5 Jan 2017 02:38:16 +0200
|
|
|
|
Subject: [PATCH] pisound improvements:
|
|
|
|
|
|
|
|
* Added a writable sysfs object to enable scripts / user space software
|
|
|
|
to blink MIDI activity LEDs for variable duration.
|
|
|
|
* Improved hw_param constraints setting.
|
|
|
|
* Added compatibility with S16_LE sample format.
|
|
|
|
* Exposed some simple placeholder volume controls, so the card appears
|
|
|
|
in volumealsa widget.
|
|
|
|
|
|
|
|
Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
|
|
|
|
---
|
|
|
|
sound/soc/bcm/pisound.c | 175 ++++++++++++++++++++++++++++++++++++++++++------
|
|
|
|
1 file changed, 154 insertions(+), 21 deletions(-)
|
|
|
|
|
|
|
|
--- a/sound/soc/bcm/pisound.c
|
|
|
|
+++ b/sound/soc/bcm/pisound.c
|
|
|
|
@@ -36,6 +36,7 @@
|
|
|
|
#include <sound/jack.h>
|
|
|
|
#include <sound/rawmidi.h>
|
|
|
|
#include <sound/asequencer.h>
|
|
|
|
+#include <sound/control.h>
|
|
|
|
|
|
|
|
static int pisnd_spi_init(struct device *dev);
|
|
|
|
static void pisnd_spi_uninit(void);
|
|
|
|
@@ -214,6 +215,9 @@ static char g_serial_num[11];
|
|
|
|
static char g_id[25];
|
|
|
|
static char g_version[5];
|
|
|
|
|
|
|
|
+static uint8_t g_ledFlashDuration;
|
|
|
|
+static bool g_ledFlashDurationChanged;
|
|
|
|
+
|
|
|
|
DEFINE_KFIFO(spi_fifo_in, uint8_t, FIFO_SIZE);
|
|
|
|
DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
|
|
|
|
|
|
|
|
@@ -396,8 +400,13 @@ static void pisnd_work_handler(struct wo
|
|
|
|
val = 0;
|
|
|
|
tx = 0;
|
|
|
|
|
|
|
|
- if (kfifo_get(&spi_fifo_out, &val))
|
|
|
|
+ if (g_ledFlashDurationChanged) {
|
|
|
|
+ tx = 0xf000 | g_ledFlashDuration;
|
|
|
|
+ g_ledFlashDuration = 0;
|
|
|
|
+ g_ledFlashDurationChanged = false;
|
|
|
|
+ } else if (kfifo_get(&spi_fifo_out, &val)) {
|
|
|
|
tx = 0x0f00 | val;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
rx = spi_transfer16(tx);
|
|
|
|
|
|
|
|
@@ -410,6 +419,7 @@ static void pisnd_work_handler(struct wo
|
|
|
|
} while (rx != 0
|
|
|
|
|| !kfifo_is_empty(&spi_fifo_out)
|
|
|
|
|| pisnd_spi_has_more()
|
|
|
|
+ || g_ledFlashDurationChanged
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
|
|
|
|
@@ -569,7 +579,7 @@ static int pisnd_spi_init(struct device
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flash the LEDs. */
|
|
|
|
- spi_transfer16(0xf000);
|
|
|
|
+ spi_transfer16(0xf008);
|
|
|
|
|
|
|
|
ret = pisnd_spi_gpio_irq_init(dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
@@ -610,6 +620,14 @@ static void pisnd_spi_uninit(void)
|
|
|
|
pisnd_spi_gpio_uninit();
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void pisnd_spi_flash_leds(uint8_t duration)
|
|
|
|
+{
|
|
|
|
+ g_ledFlashDuration = duration;
|
|
|
|
+ g_ledFlashDurationChanged = true;
|
|
|
|
+ printd("schedule from spi_flash_leds\n");
|
|
|
|
+ pisnd_schedule_process(TASK_PROCESS);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void pisnd_spi_send(uint8_t val)
|
|
|
|
{
|
|
|
|
kfifo_put(&spi_fifo_out, val);
|
|
|
|
@@ -658,6 +676,83 @@ static const struct of_device_id pisound
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
+enum {
|
|
|
|
+ SWITCH = 0,
|
|
|
|
+ VOLUME = 1,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int pisnd_ctl_info(struct snd_kcontrol *kcontrol,
|
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
|
+{
|
|
|
|
+ if (kcontrol->private_value == SWITCH) {
|
|
|
|
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
|
|
+ uinfo->count = 1;
|
|
|
|
+ uinfo->value.integer.min = 0;
|
|
|
|
+ uinfo->value.integer.max = 1;
|
|
|
|
+ return 0;
|
|
|
|
+ } else if (kcontrol->private_value == VOLUME) {
|
|
|
|
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
+ uinfo->count = 1;
|
|
|
|
+ uinfo->value.integer.min = 0;
|
|
|
|
+ uinfo->value.integer.max = 100;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ return -EINVAL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pisnd_ctl_get(struct snd_kcontrol *kcontrol,
|
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
|
+{
|
|
|
|
+ if (kcontrol->private_value == SWITCH) {
|
|
|
|
+ ucontrol->value.integer.value[0] = 1;
|
|
|
|
+ return 0;
|
|
|
|
+ } else if (kcontrol->private_value == VOLUME) {
|
|
|
|
+ ucontrol->value.integer.value[0] = 100;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -EINVAL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct snd_kcontrol_new pisnd_ctl[] = {
|
|
|
|
+ {
|
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
+ .name = "PCM Playback Switch",
|
|
|
|
+ .index = 0,
|
|
|
|
+ .private_value = SWITCH,
|
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
|
+ .info = pisnd_ctl_info,
|
|
|
|
+ .get = pisnd_ctl_get,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
+ .name = "PCM Playback Volume",
|
|
|
|
+ .index = 0,
|
|
|
|
+ .private_value = VOLUME,
|
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
|
+ .info = pisnd_ctl_info,
|
|
|
|
+ .get = pisnd_ctl_get,
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int pisnd_ctl_init(struct snd_card *card)
|
|
|
|
+{
|
|
|
|
+ int err, i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(pisnd_ctl); ++i) {
|
|
|
|
+ err = snd_ctl_add(card, snd_ctl_new1(&pisnd_ctl[i], NULL));
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pisnd_ctl_uninit(void)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static struct gpio_desc *osr0, *osr1, *osr2;
|
|
|
|
static struct gpio_desc *reset;
|
|
|
|
static struct gpio_desc *button;
|
|
|
|
@@ -667,6 +762,14 @@ static int pisnd_hw_params(
|
|
|
|
struct snd_pcm_hw_params *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,
|
|
|
|
+ * as generated by the master ADC.
|
|
|
|
+ */
|
|
|
|
+ snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
|
|
|
|
+
|
|
|
|
printd("rate = %d\n", params_rate(params));
|
|
|
|
printd("ch = %d\n", params_channels(params));
|
|
|
|
printd("bits = %u\n",
|
|
|
|
@@ -711,16 +814,6 @@ static struct snd_pcm_hw_constraint_list
|
|
|
|
.mask = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
-static unsigned int sample_bits[] = {
|
|
|
|
- 24, 32
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
|
|
|
|
- .count = ARRAY_SIZE(sample_bits),
|
|
|
|
- .list = sample_bits,
|
|
|
|
- .mask = 0,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static int pisnd_startup(struct snd_pcm_substream *substream)
|
|
|
|
{
|
|
|
|
int err = snd_pcm_hw_constraint_list(
|
|
|
|
@@ -733,11 +826,21 @@ static int pisnd_startup(struct snd_pcm_
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
- err = snd_pcm_hw_constraint_list(
|
|
|
|
+ err = snd_pcm_hw_constraint_single(
|
|
|
|
substream->runtime,
|
|
|
|
- 0,
|
|
|
|
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
|
|
|
- &constraints_sample_bits
|
|
|
|
+ SNDRV_PCM_HW_PARAM_CHANNELS,
|
|
|
|
+ 2
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ err = snd_pcm_hw_constraint_mask64(
|
|
|
|
+ substream->runtime,
|
|
|
|
+ SNDRV_PCM_HW_PARAM_FORMAT,
|
|
|
|
+ SNDRV_PCM_FMTBIT_S16_LE |
|
|
|
|
+ SNDRV_PCM_FMTBIT_S24_LE |
|
|
|
|
+ SNDRV_PCM_FMTBIT_S32_LE
|
|
|
|
);
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
@@ -771,14 +874,23 @@ static int pisnd_card_probe(struct snd_s
|
|
|
|
{
|
|
|
|
int err = pisnd_midi_init(card->snd_card);
|
|
|
|
|
|
|
|
- if (err < 0)
|
|
|
|
+ if (err < 0) {
|
|
|
|
printe("pisnd_midi_init failed: %d\n", err);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return err;
|
|
|
|
+ err = pisnd_ctl_init(card->snd_card);
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ printe("pisnd_ctl_init failed: %d\n", err);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pisnd_card_remove(struct snd_soc_card *card)
|
|
|
|
{
|
|
|
|
+ pisnd_ctl_uninit();
|
|
|
|
pisnd_midi_uninit();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
@@ -870,17 +982,38 @@ static ssize_t pisnd_version_show(
|
|
|
|
return sprintf(buf, "%s\n", pisnd_spi_get_version());
|
|
|
|
}
|
|
|
|
|
|
|
|
+static ssize_t pisnd_led_store(
|
|
|
|
+ struct kobject *kobj,
|
|
|
|
+ struct kobj_attribute *attr,
|
|
|
|
+ const char *buf,
|
|
|
|
+ size_t length
|
|
|
|
+ )
|
|
|
|
+{
|
|
|
|
+ uint32_t timeout;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ err = kstrtou32(buf, 10, &timeout);
|
|
|
|
+
|
|
|
|
+ if (err == 0 && timeout <= 255)
|
|
|
|
+ pisnd_spi_flash_leds(timeout);
|
|
|
|
+
|
|
|
|
+ return length;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static struct kobj_attribute pisnd_serial_attribute =
|
|
|
|
- __ATTR(serial, 0644, pisnd_serial_show, NULL);
|
|
|
|
+ __ATTR(serial, 0444, pisnd_serial_show, NULL);
|
|
|
|
static struct kobj_attribute pisnd_id_attribute =
|
|
|
|
- __ATTR(id, 0644, pisnd_id_show, NULL);
|
|
|
|
+ __ATTR(id, 0444, pisnd_id_show, NULL);
|
|
|
|
static struct kobj_attribute pisnd_version_attribute =
|
|
|
|
- __ATTR(version, 0644, pisnd_version_show, NULL);
|
|
|
|
+ __ATTR(version, 0444, pisnd_version_show, NULL);
|
|
|
|
+static struct kobj_attribute pisnd_led_attribute =
|
|
|
|
+ __ATTR(led, 0644, NULL, pisnd_led_store);
|
|
|
|
|
|
|
|
static struct attribute *attrs[] = {
|
|
|
|
&pisnd_serial_attribute.attr,
|
|
|
|
&pisnd_id_attribute.attr,
|
|
|
|
&pisnd_version_attribute.attr,
|
|
|
|
+ &pisnd_led_attribute.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|