ALSA: firewire-motu: handle transactions specific for MOTU FireWire models
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Wed, 22 Mar 2017 12:30:19 +0000 (21:30 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 28 Mar 2017 10:33:32 +0000 (12:33 +0200)
All models of MOTU FireWire series can be controlled by write transaction
to addresses in a range from 0x'ffff'f0000'0b00 to 0x'ffff'f000'0cff.

The models support asynchronous notification. This notification has 32 bit
field data, and is transferred when status of clock changes. Meaning of
the value is not enough clear yet.

Drivers can register its address to receive the notification. Write
transaction to 0x'ffff'f000'0b04 registers higher 16 bits of the address.
Write transaction to 0x'ffff'f0000'0b08 registers the rest of bits. The
address includes node ID, thus it should be registered every time of bus
reset.

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

index 37391f5c623da344eb47ba30b221ec2e0864a401..03b07694df667ef60043d2573da5c07d594ef900 100644 (file)
@@ -1,2 +1,2 @@
-snd-firewire-motu-objs := motu.o amdtp-motu.o
+snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-transaction.c b/sound/firewire/motu/motu-transaction.c
new file mode 100644 (file)
index 0000000..416dd98
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * motu-transaction.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+
+#include "motu.h"
+
+#define SND_MOTU_ADDR_BASE     0xfffff0000000ULL
+#define ASYNC_ADDR_HI  0x0b04
+#define ASYNC_ADDR_LO  0x0b08
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+                             size_t size)
+{
+       int tcode;
+
+       if (size % sizeof(__be32) > 0 || size <= 0)
+               return -EINVAL;
+       if (size == sizeof(__be32))
+               tcode = TCODE_READ_QUADLET_REQUEST;
+       else
+               tcode = TCODE_READ_BLOCK_REQUEST;
+
+       return snd_fw_transaction(motu->unit, tcode,
+                                 SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+                              size_t size)
+{
+       int tcode;
+
+       if (size % sizeof(__be32) > 0 || size <= 0)
+               return -EINVAL;
+       if (size == sizeof(__be32))
+               tcode = TCODE_WRITE_QUADLET_REQUEST;
+       else
+               tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+       return snd_fw_transaction(motu->unit, tcode,
+                                 SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_motu_transaction_reregister(struct snd_motu *motu)
+{
+       struct fw_device *device = fw_parent_device(motu->unit);
+       __be32 data;
+       int err;
+
+       if (motu->async_handler.callback_data == NULL)
+               return -EINVAL;
+
+       /* Register messaging address. Block transaction is not allowed. */
+       data = cpu_to_be32((device->card->node_id << 16) |
+                          (motu->async_handler.offset >> 32));
+       err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
+                                        sizeof(data));
+       if (err < 0)
+               return err;
+
+       data = cpu_to_be32(motu->async_handler.offset);
+       return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
+                                         sizeof(data));
+}
+
+int snd_motu_transaction_register(struct snd_motu *motu)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       int err;
+
+       /* Perhaps, 4 byte messages are transferred. */
+       motu->async_handler.length = 4;
+       motu->async_handler.address_callback = handle_message;
+       motu->async_handler.callback_data = motu;
+
+       err = fw_core_add_address_handler(&motu->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_motu_transaction_reregister(motu);
+       if (err < 0) {
+               fw_core_remove_address_handler(&motu->async_handler);
+               motu->async_handler.address_callback = NULL;
+       }
+
+       return err;
+}
+
+void snd_motu_transaction_unregister(struct snd_motu *motu)
+{
+       __be32 data;
+
+       if (motu->async_handler.address_callback != NULL)
+               fw_core_remove_address_handler(&motu->async_handler);
+       motu->async_handler.address_callback = NULL;
+
+       /* Unregister the address. */
+       data = cpu_to_be32(0x00000000);
+       snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
+       snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
+}
index 1e6fc74a64581857b75538b6e351cae970b98522..db6014c2f16d1af340686091f50548473399389b 100644 (file)
@@ -54,6 +54,8 @@ static void name_card(struct snd_motu *motu)
 
 static void motu_free(struct snd_motu *motu)
 {
+       snd_motu_transaction_unregister(motu);
+
        fw_unit_put(motu->unit);
 
        mutex_destroy(&motu->mutex);
@@ -86,6 +88,10 @@ static void do_registration(struct work_struct *work)
 
        name_card(motu);
 
+       err = snd_motu_transaction_register(motu);
+       if (err < 0)
+               goto error;
+
        err = snd_card_register(motu->card);
        if (err < 0)
                goto error;
@@ -100,6 +106,7 @@ static void do_registration(struct work_struct *work)
 
        return;
 error:
+       snd_motu_transaction_unregister(motu);
        snd_card_free(motu->card);
        dev_info(&motu->unit->device,
                 "Sound card registration failed: %d\n", err);
@@ -155,6 +162,9 @@ static void motu_bus_update(struct fw_unit *unit)
        /* Postpone a workqueue for deferred registration. */
        if (!motu->registered)
                snd_fw_schedule_registration(unit, &motu->dwork);
+
+       /* The handler address register becomes initialized. */
+       snd_motu_transaction_reregister(motu);
 }
 
 #define SND_MOTU_DEV_ENTRY(model, data)                        \
index cd1b3dd3e3712d6b2a0d4e22cd6ab93785015b8c..ed1d779c0dcc2185ee2facd88d06c697f01b6dfe 100644 (file)
@@ -48,6 +48,10 @@ struct snd_motu {
        struct snd_motu_packet_format rx_packet_formats;
        struct amdtp_stream tx_stream;
        struct amdtp_stream rx_stream;
+
+       /* For notification. */
+       struct fw_address_handler async_handler;
+       u32 msg;
 };
 
 enum snd_motu_spec_flags {
@@ -106,4 +110,12 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
                              struct snd_motu_packet_format *formats);
 int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
                                      struct snd_pcm_runtime *runtime);
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+                             size_t size);
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+                              size_t size);
+int snd_motu_transaction_register(struct snd_motu *motu);
+int snd_motu_transaction_reregister(struct snd_motu *motu);
+void snd_motu_transaction_unregister(struct snd_motu *motu);
 #endif