From 8ed8e29b19dba95a7a6c2364c2bb32d926601713 Mon Sep 17 00:00:00 2001 From: Giedrius Trainavicius 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 --- 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 #include #include +#include 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 };