ALSA: dice: use extended protocol to detect available stream formats
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Wed, 2 May 2018 10:16:45 +0000 (19:16 +0900)
committerTakashi Iwai <tiwai@suse.de>
Wed, 2 May 2018 14:02:00 +0000 (16:02 +0200)
TC Applied Technologies (TCAT) have added extension to DICE protocol. This
protocol extension is called as Extended Application Protocol, a.k.a. EAP.

In this protocol extension, units get additional 9 address spaces. One of
it is for current configuration. In this address space, a pair of router
and stream formats are exposed per mode of three sampling transmission
frequencies.

This commit adds support the protocol extension for address space of the
current configuration to generate cache of stream formats.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/dice/Makefile
sound/firewire/dice/dice-extension.c [new file with mode: 0644]
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h

index 5cfe618f45ef332f69e61f839ceb35fa01d33c40..7b7997a5754c3dc6c4e3fe6f895428bb274fd3d9 100644 (file)
@@ -1,4 +1,4 @@
 snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
                 dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
-                dice-alesis.o
+                dice-alesis.o dice-extension.o
 obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c
new file mode 100644 (file)
index 0000000..a63fcbc
--- /dev/null
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-extension.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+/* For TCD2210/2220, TCAT defines extension of application protocol. */
+
+#define DICE_EXT_APP_SPACE             0xffffe0200000uLL
+
+#define DICE_EXT_APP_CAPS_OFFSET       0x00
+#define DICE_EXT_APP_CAPS_SIZE         0x04
+#define DICE_EXT_APP_CMD_OFFSET                0x08
+#define DICE_EXT_APP_CMD_SIZE          0x0c
+#define DICE_EXT_APP_MIXER_OFFSET      0x10
+#define DICE_EXT_APP_MIXER_SIZE                0x14
+#define DICE_EXT_APP_PEAK_OFFSET       0x18
+#define DICE_EXT_APP_PEAK_SIZE         0x1c
+#define DICE_EXT_APP_ROUTER_OFFSET     0x20
+#define DICE_EXT_APP_ROUTER_SIZE       0x24
+#define DICE_EXT_APP_STREAM_OFFSET     0x28
+#define DICE_EXT_APP_STREAM_SIZE       0x2c
+#define DICE_EXT_APP_CURRENT_OFFSET    0x30
+#define DICE_EXT_APP_CURRENT_SIZE      0x34
+#define DICE_EXT_APP_STANDALONE_OFFSET 0x38
+#define DICE_EXT_APP_STANDALONE_SIZE   0x3c
+#define DICE_EXT_APP_APPLICATION_OFFSET        0x40
+#define DICE_EXT_APP_APPLICATION_SIZE  0x44
+
+#define EXT_APP_STREAM_TX_NUMBER       0x0000
+#define EXT_APP_STREAM_RX_NUMBER       0x0004
+#define EXT_APP_STREAM_ENTRIES         0x0008
+#define EXT_APP_STREAM_ENTRY_SIZE      0x010c
+#define  EXT_APP_NUMBER_AUDIO          0x0000
+#define  EXT_APP_NUMBER_MIDI           0x0004
+#define  EXT_APP_NAMES                 0x0008
+#define   EXT_APP_NAMES_SIZE           256
+#define  EXT_APP_AC3                   0x0108
+
+#define EXT_APP_CONFIG_LOW_ROUTER      0x0000
+#define EXT_APP_CONFIG_LOW_STREAM      0x1000
+#define EXT_APP_CONFIG_MIDDLE_ROUTER   0x2000
+#define EXT_APP_CONFIG_MIDDLE_STREAM   0x3000
+#define EXT_APP_CONFIG_HIGH_ROUTER     0x4000
+#define EXT_APP_CONFIG_HIGH_STREAM     0x5000
+
+static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
+                                  u32 offset, void *buf, size_t len)
+{
+       return snd_fw_transaction(dice->unit,
+                                 len == 4 ? TCODE_READ_QUADLET_REQUEST :
+                                            TCODE_READ_BLOCK_REQUEST,
+                                 section_addr + offset, buf, len, 0);
+}
+
+static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
+                              u32 base_offset, unsigned int stream_count,
+                              unsigned int mode,
+                              unsigned int pcm_channels[MAX_STREAMS][3],
+                              unsigned int midi_ports[MAX_STREAMS])
+{
+       u32 entry_offset;
+       __be32 reg[2];
+       int err;
+       int i;
+
+       for (i = 0; i < stream_count; ++i) {
+               entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
+               err = read_transaction(dice, section_addr,
+                                   entry_offset + EXT_APP_NUMBER_AUDIO,
+                                   reg, sizeof(reg));
+               if (err < 0)
+                       return err;
+               pcm_channels[i][mode] = be32_to_cpu(reg[0]);
+               midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
+       }
+
+       return 0;
+}
+
+static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
+{
+       u32 base_offset;
+       __be32 reg[2];
+       unsigned int stream_count;
+       int mode;
+       int err = 0;
+
+       for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
+               unsigned int cap;
+
+               /*
+                * Some models report stream formats at highest mode, however
+                * they don't support the mode. Check clock capabilities.
+                */
+               if (mode == 2) {
+                       cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
+               } else if (mode == 1) {
+                       cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
+               } else {
+                       cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
+                             CLOCK_CAP_RATE_48000;
+               }
+               if (!(cap & dice->clock_caps))
+                       continue;
+
+               base_offset = 0x2000 * mode + 0x1000;
+
+               err = read_transaction(dice, section_addr,
+                                      base_offset + EXT_APP_STREAM_TX_NUMBER,
+                                      &reg, sizeof(reg));
+               if (err < 0)
+                       break;
+
+               base_offset += EXT_APP_STREAM_ENTRIES;
+               stream_count = be32_to_cpu(reg[0]);
+               err = read_stream_entries(dice, section_addr, base_offset,
+                                         stream_count, mode,
+                                         dice->tx_pcm_chs,
+                                         dice->tx_midi_ports);
+               if (err < 0)
+                       break;
+
+               base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
+               stream_count = be32_to_cpu(reg[1]);
+               err = read_stream_entries(dice, section_addr, base_offset,
+                                         stream_count,
+                                         mode, dice->rx_pcm_chs,
+                                         dice->rx_midi_ports);
+               if (err < 0)
+                       break;
+       }
+
+       return err;
+}
+
+int snd_dice_detect_extension_formats(struct snd_dice *dice)
+{
+       __be32 *pointers;
+       unsigned int i;
+       u64 section_addr;
+       int err;
+
+       pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
+       if (pointers == NULL)
+               return -ENOMEM;
+
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                DICE_EXT_APP_SPACE, pointers,
+                                9 * sizeof(__be32) * 2, 0);
+       if (err < 0)
+               goto end;
+
+       /* Check two of them for offset have the same value or not. */
+       for (i = 0; i < 9; ++i) {
+               int j;
+
+               for (j = i + 1; j < 9; ++j) {
+                       if (pointers[i * 2] == pointers[j * 2])
+                               goto end;
+               }
+       }
+
+       section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
+       err = detect_stream_formats(dice, section_addr);
+end:
+       kfree(pointers);
+       return err;
+}
index 6c859d2b9084a177178fb4bfa99afc8088d33930..d3fb460bb86c5e561aa808a07abd34241b184626 100644 (file)
@@ -521,6 +521,11 @@ int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
        int i;
        int err;
 
+       /* If extended protocol is available, detect detail spec. */
+       err = snd_dice_detect_extension_formats(dice);
+       if (err >= 0)
+               return err;
+
        /*
         * Available stream format is restricted at current mode of sampling
         * clock.
index cbd1a07e70b9cd6f99c215f08f6e95d10a44b24e..6d55a62ec89ead6259c54304fedd8736d6f212c8 100644 (file)
@@ -16,6 +16,7 @@ MODULE_LICENSE("GPL v2");
 #define OUI_FOCUSRITE          0x00130e
 #define OUI_TCELECTRONIC       0x000166
 #define OUI_ALESIS             0x000595
+#define OUI_MAUDIO             0x000d6c
 
 #define DICE_CATEGORY_ID       0x04
 #define WEISS_CATEGORY_ID      0x00
@@ -330,12 +331,21 @@ static void dice_bus_reset(struct fw_unit *unit)
 #define DICE_INTERFACE 0x000001
 
 static const struct ieee1394_device_id dice_id_table[] = {
-       /* M-Audio Profire 610/2626 has a different value in version field. */
+       /* M-Audio Profire 2626 has a different value in version field. */
        {
                .match_flags    = IEEE1394_MATCH_VENDOR_ID |
-                                 IEEE1394_MATCH_SPECIFIER_ID,
-               .vendor_id      = 0x000d6c,
-               .specifier_id   = 0x000d6c,
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = OUI_MAUDIO,
+               .model_id       = 0x000010,
+               .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
+       },
+       /* M-Audio Profire 610 has a different value in version field. */
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = OUI_MAUDIO,
+               .model_id       = 0x000011,
+               .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
        },
        /* TC Electronic Konnekt 24D. */
        {
index 6be1bcf00116477824c5ecf41652260d7a864342..4465a5925641f9e98a0ab60e1e8b211ca04f1be7 100644 (file)
@@ -227,5 +227,6 @@ int snd_dice_create_midi(struct snd_dice *dice);
 
 int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
 int snd_dice_detect_alesis_formats(struct snd_dice *dice);
+int snd_dice_detect_extension_formats(struct snd_dice *dice);
 
 #endif