]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
pisound improvements:
authorGiedrius Trainavicius <giedrius@blokas.io>
Thu, 5 Jan 2017 00:38:16 +0000 (02:38 +0200)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 19 Sep 2017 10:07:48 +0000 (12:07 +0200)
* 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

index 4b8545487d06e4ea70073a5d063fb2310b3b94d0..ba70734b89e61a11201657406223f0b37d54f74a 100644 (file)
@@ -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 work_struct *work)
                        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 work_struct *work)
                } 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 *dev)
        }
 
        /* 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_of_match[] = {
        {},
 };
 
+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 constraints_rates = {
        .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_substream *substream)
        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_soc_card *card)
 {
        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
 };