All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series
@ 2017-01-29  3:53 Takashi Sakamoto
  2017-01-29  3:53 ` [PATCH 01/19] firewire-motu: add skeleton for Mark of the unicorn (MOTU) " Takashi Sakamoto
                   ` (20 more replies)
  0 siblings, 21 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:53 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Hi,

This patchset updates a part of my previous RFC, just for MOTU FireWire series.

[RFC][PATCH 00/37] ALSA: firewire: support AMDTP variants
http://mailman.alsa-project.org/pipermail/alsa-devel/2015-July/094789.html

This patchset adds support for a part of MOTU FireWire series with their
functionality of packet streaming. Below models are newly supported:
 - 828
 - 828mk2
 - 828mk3 (FireWire/Hybrid)

However, this module cannot handle 828 correctly to generate sound. The reason
is not clear yet. On the other hand, 828mk2 and 828mk3 can be handled most
properly via ALSA PCM/MIDI/HwDep interfaces.

Currently, I have a plan to post this patchset to merge into Linux 4.12.
Corresponding merge window will be estimated to open this April. If you're
willing to test this module, please report the result till then.

For testers, I prepared for backport modules in my repository. Please follow
instructions in README.
https://github.com/takaswie/snd-firewire-improve/tree/topic/motu

As user land tools, I added some stuffs to libhinawa and hinawa-utils. Please
refer to 'topic/motu' branch of these repositories. You can use
'hinawa-motu-common-cui' to configure some streaming-related features of the
above units:
 * https://github.com/takaswie/libhinawa/tree/topic/motu
 * https://github.com/takaswie/hinawa-utils/tree/topic/motu

Takashi Sakamoto (19):
  firewire-motu: add skeleton for Mark of the unicorn (MOTU) FireWire
    series
  firewire-motu: postpone sound card registration
  firewire-motu: add a structure for model-dependent parameters.
  firewire-motu: add an abstraction layer for three types of protocols
  firewire-lib: record cycle count for the first packet
  firewire-lib: add support for source packet header field in CIP header
  firewire-lib: enable CIP_DBC_IS_END_EVENT for both directions of
    stream
  firewire-motu: add MOTU specific protocol layer
  firewire-motu: handle transactions specific for MOTU FireWire models
  firewire-motu: add stream management functionality
  firewire-motu: add proc node to show current statuc of clock and
    packet formats
  firewire-motu: add PCM functionality
  firewire-motu: add MIDI functionality
  firewire-motu: add hwdep interface
  firewire-motu: enable to read transaction cache via hwdep interface
  firewire-motu: add support for MOTU 828 as a model with protocol
    version 1
  firewire-motu: add support for MOTU 828mk2 as a model with protocol
    version 2
  firewire-lib: add a quirk of packet without valid EOH in CIP format
  firewire-motu: add support for MOTU 828mk3 (FireWire/Hybrid) as a
    model with protocol version 3

 include/uapi/sound/asound.h            |   3 +-
 include/uapi/sound/firewire.h          |  10 +-
 sound/firewire/Kconfig                 |  13 ++
 sound/firewire/Makefile                |   1 +
 sound/firewire/amdtp-stream.c          |  36 ++-
 sound/firewire/amdtp-stream.h          |   9 +-
 sound/firewire/motu/Makefile           |   5 +
 sound/firewire/motu/amdtp-motu.c       | 388 ++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu-hwdep.c       | 198 ++++++++++++++++
 sound/firewire/motu/motu-midi.c        | 169 ++++++++++++++
 sound/firewire/motu/motu-pcm.c         | 398 +++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu-proc.c        | 118 ++++++++++
 sound/firewire/motu/motu-protocol-v1.c | 204 +++++++++++++++++
 sound/firewire/motu/motu-protocol-v2.c | 237 ++++++++++++++++++++
 sound/firewire/motu/motu-protocol-v3.c | 312 ++++++++++++++++++++++++++
 sound/firewire/motu/motu-stream.c      | 381 +++++++++++++++++++++++++++++++
 sound/firewire/motu/motu-transaction.c | 137 ++++++++++++
 sound/firewire/motu/motu.c             | 273 ++++++++++++++++++++++
 sound/firewire/motu/motu.h             | 161 +++++++++++++
 19 files changed, 3042 insertions(+), 11 deletions(-)
 create mode 100644 sound/firewire/motu/Makefile
 create mode 100644 sound/firewire/motu/amdtp-motu.c
 create mode 100644 sound/firewire/motu/motu-hwdep.c
 create mode 100644 sound/firewire/motu/motu-midi.c
 create mode 100644 sound/firewire/motu/motu-pcm.c
 create mode 100644 sound/firewire/motu/motu-proc.c
 create mode 100644 sound/firewire/motu/motu-protocol-v1.c
 create mode 100644 sound/firewire/motu/motu-protocol-v2.c
 create mode 100644 sound/firewire/motu/motu-protocol-v3.c
 create mode 100644 sound/firewire/motu/motu-stream.c
 create mode 100644 sound/firewire/motu/motu-transaction.c
 create mode 100644 sound/firewire/motu/motu.c
 create mode 100644 sound/firewire/motu/motu.h

-- 
2.9.3

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 01/19] firewire-motu: add skeleton for Mark of the unicorn (MOTU) FireWire series
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
@ 2017-01-29  3:53 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 02/19] firewire-motu: postpone sound card registration Takashi Sakamoto
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:53 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds an new driver for MOTU FireWire series. In this commit,
this driver just creates/removes card instance according to bus event.
More functionalities will be added in following commits.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig       |  10 ++++
 sound/firewire/Makefile      |   1 +
 sound/firewire/motu/Makefile |   2 +
 sound/firewire/motu/motu.c   | 135 +++++++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.h   |  29 ++++++++++
 5 files changed, 177 insertions(+)
 create mode 100644 sound/firewire/motu/Makefile
 create mode 100644 sound/firewire/motu/motu.c
 create mode 100644 sound/firewire/motu/motu.h

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 9f00696..11a3285 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -140,4 +140,14 @@ config SND_FIREWIRE_TASCAM
 	 To compile this driver as a module, choose M here: the module
 	 will be called snd-firewire-tascam.
 
+config SND_FIREWIRE_MOTU
+	tristate "Mark of the unicorn FireWire series support"
+	select SND_FIREWIRE_LIB
+	select SND_HWDEP
+	help
+	 Say Y here to enable support for FireWire devices which MOTU produced:
+
+	 To compile this driver as a module, choose M here: the module
+	 will be called snd-firewire-motu.
+
 endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 0ee1fb1..9388ded 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_SND_FIREWORKS) += fireworks/
 obj-$(CONFIG_SND_BEBOB) += bebob/
 obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
 obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += motu/
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
new file mode 100644
index 0000000..d7819d5
--- /dev/null
+++ b/sound/firewire/motu/Makefile
@@ -0,0 +1,2 @@
+snd-firewire-motu-objs := motu.o
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
new file mode 100644
index 0000000..82aae6d
--- /dev/null
+++ b/sound/firewire/motu/motu.c
@@ -0,0 +1,135 @@
+/*
+ * motu.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 OUI_MOTU	0x0001f2
+
+MODULE_DESCRIPTION("MOTU FireWire driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static void name_card(struct snd_motu *motu)
+{
+	struct fw_device *fw_dev = fw_parent_device(motu->unit);
+	struct fw_csr_iterator it;
+	int key, val;
+	u32 version = 0;
+
+	fw_csr_iterator_init(&it, motu->unit->directory);
+	while (fw_csr_iterator_next(&it, &key, &val)) {
+		switch (key) {
+		case CSR_VERSION:
+			version = val;
+			break;
+		}
+	}
+
+	strcpy(motu->card->driver, "FW-MOTU");
+	snprintf(motu->card->longname, sizeof(motu->card->longname),
+		 "MOTU (version:%d), GUID %08x%08x at %s, S%d",
+		 version,
+		 fw_dev->config_rom[3], fw_dev->config_rom[4],
+		 dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void motu_card_free(struct snd_card *card)
+{
+	struct snd_motu *motu = card->private_data;
+
+	fw_unit_put(motu->unit);
+
+	mutex_destroy(&motu->mutex);
+}
+
+static int motu_probe(struct fw_unit *unit,
+		      const struct ieee1394_device_id *entry)
+{
+	struct snd_card *card;
+	struct snd_motu *motu;
+	int err;
+
+	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+			   sizeof(*motu), &card);
+	if (err < 0)
+		return err;
+
+	motu = card->private_data;
+	motu->card = card;
+	motu->unit = fw_unit_get(unit);
+	card->private_free = motu_card_free;
+
+	mutex_init(&motu->mutex);
+
+	name_card(motu);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	dev_set_drvdata(&unit->device, motu);
+
+	return 0;
+error:
+	snd_card_free(card);
+	return err;
+}
+
+static void motu_remove(struct fw_unit *unit)
+{
+	struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+	/* No need to wait for releasing card object in this context. */
+	snd_card_free_when_closed(motu->card);
+}
+
+static void motu_bus_update(struct fw_unit *unit)
+{
+	return;
+}
+
+#define SND_MOTU_DEV_ENTRY(model, data)			\
+{							\
+	.match_flags	= IEEE1394_MATCH_VENDOR_ID |	\
+			  IEEE1394_MATCH_MODEL_ID |	\
+			  IEEE1394_MATCH_SPECIFIER_ID,	\
+	.vendor_id	= OUI_MOTU,			\
+	.model_id	= model,			\
+	.specifier_id	= OUI_MOTU,			\
+	.driver_data	= (kernel_ulong_t)data,		\
+}
+
+static const struct ieee1394_device_id motu_id_table[] = {
+	{ }
+};
+MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
+
+static struct fw_driver motu_driver = {
+	.driver   = {
+		.owner	= THIS_MODULE,
+		.name	= KBUILD_MODNAME,
+		.bus	= &fw_bus_type,
+	},
+	.probe    = motu_probe,
+	.update   = motu_bus_update,
+	.remove   = motu_remove,
+	.id_table = motu_id_table,
+};
+
+static int __init alsa_motu_init(void)
+{
+	return driver_register(&motu_driver.driver);
+}
+
+static void __exit alsa_motu_exit(void)
+{
+	driver_unregister(&motu_driver.driver);
+}
+
+module_init(alsa_motu_init);
+module_exit(alsa_motu_exit);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
new file mode 100644
index 0000000..f3d0b28
--- /dev/null
+++ b/sound/firewire/motu/motu.h
@@ -0,0 +1,29 @@
+/*
+ * motu.h - 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.
+ */
+
+#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
+#define SOUND_FIREWIRE_MOTU_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+
+struct snd_motu {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	struct mutex mutex;
+};
+
+#endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 02/19] firewire-motu: postpone sound card registration
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
  2017-01-29  3:53 ` [PATCH 01/19] firewire-motu: add skeleton for Mark of the unicorn (MOTU) " Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 03/19] firewire-motu: add a structure for model-dependent parameters Takashi Sakamoto
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Just after appearing on IEEE 1394 bus, this unit generates several bus
resets. This is due to loading firmware from on-board flash memory and
initialize hardware. It's better to postpone sound card registration.

This commit applies this idea.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/motu.c | 97 ++++++++++++++++++++++++++++++++++------------
 sound/firewire/motu/motu.h |  5 +++
 2 files changed, 78 insertions(+), 24 deletions(-)

diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 82aae6d..d6ce4f3 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -38,59 +38,108 @@ static void name_card(struct snd_motu *motu)
 		 dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
 }
 
-static void motu_card_free(struct snd_card *card)
+static void motu_free(struct snd_motu *motu)
 {
-	struct snd_motu *motu = card->private_data;
-
 	fw_unit_put(motu->unit);
 
 	mutex_destroy(&motu->mutex);
+	kfree(motu);
 }
 
-static int motu_probe(struct fw_unit *unit,
-		      const struct ieee1394_device_id *entry)
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
+static void motu_card_free(struct snd_card *card)
 {
-	struct snd_card *card;
-	struct snd_motu *motu;
-	int err;
+	motu_free(card->private_data);
+}
 
-	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
-			   sizeof(*motu), &card);
-	if (err < 0)
-		return err;
+static void do_registration(struct work_struct *work)
+{
+	struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
+	int err;
 
-	motu = card->private_data;
-	motu->card = card;
-	motu->unit = fw_unit_get(unit);
-	card->private_free = motu_card_free;
+	if (motu->registered)
+		return;
 
-	mutex_init(&motu->mutex);
+	err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
+			   &motu->card);
+	if (err < 0)
+		return;
 
 	name_card(motu);
 
-	err = snd_card_register(card);
+	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
 
+	/*
+	 * After registered, motu instance can be released corresponding to
+	 * releasing the sound card instance.
+	 */
+	motu->card->private_free = motu_card_free;
+	motu->card->private_data = motu;
+	motu->registered = true;
+
+	return;
+error:
+	snd_card_free(motu->card);
+	dev_info(&motu->unit->device,
+		 "Sound card registration failed: %d\n", err);
+}
+
+static int motu_probe(struct fw_unit *unit,
+		      const struct ieee1394_device_id *entry)
+{
+	struct snd_motu *motu;
+
+	/* Allocate this independently of sound card instance. */
+	motu = kzalloc(sizeof(struct snd_motu), GFP_KERNEL);
+	if (motu == NULL)
+		return -ENOMEM;
+
+	motu->unit = fw_unit_get(unit);
 	dev_set_drvdata(&unit->device, motu);
 
+	mutex_init(&motu->mutex);
+
+	/* Allocate and register this sound card later. */
+	INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
+	snd_fw_schedule_registration(unit, &motu->dwork);
+
 	return 0;
-error:
-	snd_card_free(card);
-	return err;
 }
 
 static void motu_remove(struct fw_unit *unit)
 {
 	struct snd_motu *motu = dev_get_drvdata(&unit->device);
 
-	/* No need to wait for releasing card object in this context. */
-	snd_card_free_when_closed(motu->card);
+	/*
+	 * Confirm to stop the work for registration before the sound card is
+	 * going to be released. The work is not scheduled again because bus
+	 * reset handler is not called anymore.
+	 */
+	cancel_delayed_work_sync(&motu->dwork);
+
+	if (motu->registered) {
+		/* No need to wait for releasing card object in this context. */
+		snd_card_free_when_closed(motu->card);
+	} else {
+		/* Don't forget this case. */
+		motu_free(motu);
+	}
 }
 
 static void motu_bus_update(struct fw_unit *unit)
 {
-	return;
+	struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+	/* Postpone a workqueue for deferred registration. */
+	if (!motu->registered)
+		snd_fw_schedule_registration(unit, &motu->dwork);
 }
 
 #define SND_MOTU_DEV_ENTRY(model, data)			\
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index f3d0b28..eb0ffd5 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -20,10 +20,15 @@
 #include <sound/control.h>
 #include <sound/core.h>
 
+#include "../lib.h"
+
 struct snd_motu {
 	struct snd_card *card;
 	struct fw_unit *unit;
 	struct mutex mutex;
+
+	bool registered;
+	struct delayed_work dwork;
 };
 
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 03/19] firewire-motu: add a structure for model-dependent parameters.
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
  2017-01-29  3:53 ` [PATCH 01/19] firewire-motu: add skeleton for Mark of the unicorn (MOTU) " Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 02/19] firewire-motu: postpone sound card registration Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 04/19] firewire-motu: add an abstraction layer for three types of protocols Takashi Sakamoto
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU FireWire series doesn't tell drivers their capabilities, thus
the drivers should have model-dependent parameters and apply it to
detected models.

This commit adds a structure to represent such parameters. Capabilities
are represented by enumeration except for the number of analog line
in/out. Identification name also be in the structure because the units has
no registers for this purpose.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/motu.c |  7 +++++--
 sound/firewire/motu/motu.h | 23 +++++++++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index d6ce4f3..e69aa7b 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -31,9 +31,11 @@ static void name_card(struct snd_motu *motu)
 	}
 
 	strcpy(motu->card->driver, "FW-MOTU");
+	strcpy(motu->card->shortname, motu->spec->name);
+	strcpy(motu->card->mixername, motu->spec->name);
 	snprintf(motu->card->longname, sizeof(motu->card->longname),
-		 "MOTU (version:%d), GUID %08x%08x at %s, S%d",
-		 version,
+		 "MOTU %s (version:%d), GUID %08x%08x at %s, S%d",
+		 motu->spec->name, version,
 		 fw_dev->config_rom[3], fw_dev->config_rom[4],
 		 dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
 }
@@ -101,6 +103,7 @@ static int motu_probe(struct fw_unit *unit,
 	if (motu == NULL)
 		return -ENOMEM;
 
+	motu->spec = (const struct snd_motu_spec *)entry->driver_data;
 	motu->unit = fw_unit_get(unit);
 	dev_set_drvdata(&unit->device, motu);
 
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index eb0ffd5..cb7324d 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -29,6 +29,29 @@ struct snd_motu {
 
 	bool registered;
 	struct delayed_work dwork;
+
+	/* Model dependent information. */
+	const struct snd_motu_spec *spec;
+};
+
+enum snd_motu_spec_flags {
+	SND_MOTU_SPEC_SUPPORT_CLOCK_X2	= 0x0001,
+	SND_MOTU_SPEC_SUPPORT_CLOCK_X4	= 0x0002,
+	SND_MOTU_SPEC_TX_MICINST_CHUNK	= 0x0004,
+	SND_MOTU_SPEC_TX_RETURN_CHUNK	= 0x0008,
+	SND_MOTU_SPEC_TX_REVERB_CHUNK	= 0x0010,
+	SND_MOTU_SPEC_TX_AESEBU_CHUNK	= 0x0020,
+	SND_MOTU_SPEC_HAS_OPT_IFACE_A	= 0x0040,
+	SND_MOTU_SPEC_HAS_OPT_IFACE_B	= 0x0080,
+	SND_MOTU_SPEC_HAS_MIDI		= 0x0100,
+};
+
+struct snd_motu_spec {
+	const char *const name;
+	enum snd_motu_spec_flags flags;
+
+	unsigned char analog_in_ports;
+	unsigned char analog_out_ports;
 };
 
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 04/19] firewire-motu: add an abstraction layer for three types of protocols
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (2 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 03/19] firewire-motu: add a structure for model-dependent parameters Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 05/19] firewire-lib: record cycle count for the first packet Takashi Sakamoto
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In an aspect of used protocols to communicate, models of MOTU FireWire
units are categorized to three generations.

This commit adds an abstraction layer of the protocols for features
related to packet streaming functionality. This layer includes 5
operations.

When configuring packet streaming functionality with sampling rate and
sampling transmission frequency, .get_clock_rate and .set_clock_rate are
called with proper arguments. MOTU FireWire series supports up to 192.0kHz.

When checking current source of sampling clock (not clock for packetization
layer), .get_clock_source is used. Enumeration is added to represent the
sources supported by this series. This operation can be used to expose
available sampling rate to user space applications when the unit is
configured to use input signals as source of clock.

In the protocols, the path between packet handling layer and digital signal
processing layer can be controlled. This looks a functionality to 'mute'
the unit. For this feature, .switch_fetching_mode is added. This can be
used to suppress noises every time packet streaming starts/stops.

In a point of the size of data blocks in a certain sampling transmission
frequency, the most units accept several modes. This is due to usage of
optical interfaces. The size differs depending on which modes are
configured to the interfaces; None, S/PDIF and ADAT. Additionally, format
of packet is different depending on protocols. To cache current size of
data blocks and its format, .cache_packet_formats is added. This is used
by PCM functionality, packet streaming functionality and data block
processing layer.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/motu.c | 12 ++++++++++++
 sound/firewire/motu/motu.h | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index e69aa7b..1e6fc74 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -14,6 +14,18 @@ MODULE_DESCRIPTION("MOTU FireWire driver");
 MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
 MODULE_LICENSE("GPL v2");
 
+const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
+	/* mode 0 */
+	[0] =  44100,
+	[1] =  48000,
+	/* mode 1 */
+	[2] =  88200,
+	[3] =  96000,
+	/* mode 2 */
+	[4] = 176400,
+	[5] = 192000,
+};
+
 static void name_card(struct snd_motu *motu)
 {
 	struct fw_device *fw_dev = fw_parent_device(motu->unit);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index cb7324d..cb6b573 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -22,6 +22,16 @@
 
 #include "../lib.h"
 
+struct snd_motu_packet_format {
+	unsigned char midi_flag_offset;
+	unsigned char midi_byte_offset;
+	unsigned char pcm_byte_offset;
+
+	unsigned char msg_chunks;
+	unsigned char fixed_part_pcm_chunks[3];
+	unsigned char differed_part_pcm_chunks[3];
+};
+
 struct snd_motu {
 	struct snd_card *card;
 	struct fw_unit *unit;
@@ -32,6 +42,10 @@ struct snd_motu {
 
 	/* Model dependent information. */
 	const struct snd_motu_spec *spec;
+
+	/* For packet streaming */
+	struct snd_motu_packet_format tx_packet_formats;
+	struct snd_motu_packet_format rx_packet_formats;
 };
 
 enum snd_motu_spec_flags {
@@ -46,12 +60,41 @@ enum snd_motu_spec_flags {
 	SND_MOTU_SPEC_HAS_MIDI		= 0x0100,
 };
 
+#define SND_MOTU_CLOCK_RATE_COUNT	6
+extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];
+
+enum snd_motu_clock_source {
+	SND_MOTU_CLOCK_SOURCE_INTERNAL,
+	SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
+	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
+	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
+	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
+	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
+	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
+	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
+	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
+	SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
+	SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
+	SND_MOTU_CLOCK_SOURCE_UNKNOWN,
+};
+
+struct snd_motu_protocol {
+	int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
+	int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
+	int (*get_clock_source)(struct snd_motu *motu,
+				enum snd_motu_clock_source *source);
+	int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
+	int (*cache_packet_formats)(struct snd_motu *motu);
+};
+
 struct snd_motu_spec {
 	const char *const name;
 	enum snd_motu_spec_flags flags;
 
 	unsigned char analog_in_ports;
 	unsigned char analog_out_ports;
+
+	const struct snd_motu_protocol *const protocol;
 };
 
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 05/19] firewire-lib: record cycle count for the first packet
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (3 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 04/19] firewire-motu: add an abstraction layer for three types of protocols Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 06/19] firewire-lib: add support for source packet header field in CIP header Takashi Sakamoto
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Currently, packet streaming layer passes generated SYT value to data block
processing layer. However, this is not enough in a case that the data block
processing layer generates time stamps by its own ways.

For out packet stream, the packet streaming layer guarantees 8,000 times
calls of data block processing layers per sec. Therefore, when cycle count
of the first packet is recorded, data block processing layers can calculate
own time stamps with the recorded value.

For the reason, this commit allows packet streaming layer to record the
first cycle count. Each data block processing layer can read the count by
accessing a member of structure for packet streaming layer.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp-stream.c | 15 +++++++++++++--
 sound/firewire/amdtp-stream.h |  1 +
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 00060c4..371cf97 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -671,6 +671,8 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
 					void *header, void *private_data)
 {
 	struct amdtp_stream *s = private_data;
+	u32 cycle;
+	unsigned int packets;
 
 	/*
 	 * For in-stream, first packet has come.
@@ -679,10 +681,19 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
 	s->callbacked = true;
 	wake_up(&s->callback_wait);
 
-	if (s->direction == AMDTP_IN_STREAM)
+	cycle = compute_cycle_count(tstamp);
+
+	if (s->direction == AMDTP_IN_STREAM) {
+		packets = header_length / IN_PACKET_HEADER_SIZE;
+		cycle = decrement_cycle_count(cycle, packets);
 		context->callback.sc = in_stream_callback;
-	else
+	} else {
+		packets = header_length / 4;
+		cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
 		context->callback.sc = out_stream_callback;
+	}
+
+	s->start_cycle = cycle;
 
 	context->callback.sc(context, tstamp, header_length, header, s);
 }
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index c1bc7fa..edf5646 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -130,6 +130,7 @@ struct amdtp_stream {
 	/* To wait for first packet. */
 	bool callbacked;
 	wait_queue_head_t callback_wait;
+	u32 start_cycle;
 
 	/* For backends to process data blocks. */
 	void *protocol;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 06/19] firewire-lib: add support for source packet header field in CIP header
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (4 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 05/19] firewire-lib: record cycle count for the first packet Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 07/19] firewire-lib: enable CIP_DBC_IS_END_EVENT for both directions of stream Takashi Sakamoto
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In IEC 61883-1, CIP headers have a SPH field. When a packet has 1 in the
SPH field, the packet has a source packet headers. A source packet header
consists of 32 bit field (= 1 quadlet) and it transfers time stamp, which
is the same value as the lower 25 bits of the IEEE 1394 CYCLE_TIMER
register and the rest is zero.

This commit just supports source packet header field because IEC 61883-1
includes ambiguity the position of this header and the number. Each
protocol layer can have actual implementation according its requirements.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp-stream.c | 8 ++++++--
 sound/firewire/amdtp-stream.h | 1 +
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 371cf97..65c5ed7 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -37,6 +37,8 @@
 #define CIP_SID_MASK		0x3f000000
 #define CIP_DBS_MASK		0x00ff0000
 #define CIP_DBS_SHIFT		16
+#define CIP_SPH_MASK		0x00000400
+#define CIP_SPH_SHIFT		10
 #define CIP_DBC_MASK		0x000000ff
 #define CIP_FMT_SHIFT		24
 #define CIP_FMT_MASK		0x3f000000
@@ -426,6 +428,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
 
 	buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
 				(s->data_block_quadlets << CIP_DBS_SHIFT) |
+				((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
 				s->data_block_counter);
 	buffer[1] = cpu_to_be32(CIP_EOH |
 				((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
@@ -454,7 +457,7 @@ static int handle_in_packet(struct amdtp_stream *s,
 {
 	__be32 *buffer;
 	u32 cip_header[2];
-	unsigned int fmt, fdf, syt;
+	unsigned int sph, fmt, fdf, syt;
 	unsigned int data_block_quadlets, data_block_counter, dbc_interval;
 	unsigned int data_blocks;
 	struct snd_pcm_substream *pcm;
@@ -482,8 +485,9 @@ static int handle_in_packet(struct amdtp_stream *s,
 	}
 
 	/* Check valid protocol or not. */
+	sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
 	fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
-	if (fmt != s->fmt) {
+	if (sph != s->sph || fmt != s->fmt) {
 		dev_info_ratelimited(&s->unit->device,
 				     "Detect unexpected protocol: %08x %08x\n",
 				     cip_header[0], cip_header[1]);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index edf5646..679053d 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -106,6 +106,7 @@ struct amdtp_stream {
 	unsigned int source_node_id_field;
 	unsigned int data_block_quadlets;
 	unsigned int data_block_counter;
+	unsigned int sph;
 	unsigned int fmt;
 	unsigned int fdf;
 	/* quirk: fixed interval of dbc between previos/current packets. */
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 07/19] firewire-lib: enable CIP_DBC_IS_END_EVENT for both directions of stream
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (5 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 06/19] firewire-lib: add support for source packet header field in CIP header Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 08/19] firewire-motu: add MOTU specific protocol layer Takashi Sakamoto
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Commit c8bdf49b9935("ALSA: fireworks/firewire-lib: Add a quirk for the
meaning of dbc") adds CIP_DBC_IS_END_EVENT flag just for tx packets.
However, MOTU FireWire series has this quirk for rx packets.

This commit allows both directions with the flag.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp-stream.c | 8 +++++++-
 sound/firewire/amdtp-stream.h | 4 ++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 65c5ed7..f9d12f4 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -426,6 +426,10 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
 	data_blocks = calculate_data_blocks(s, syt);
 	pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
 
+	if (s->flags & CIP_DBC_IS_END_EVENT)
+		s->data_block_counter =
+				(s->data_block_counter + data_blocks) & 0xff;
+
 	buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
 				(s->data_block_quadlets << CIP_DBS_SHIFT) |
 				((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
@@ -435,7 +439,9 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
 				((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
 				(syt & CIP_SYT_MASK));
 
-	s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+	if (!(s->flags & CIP_DBC_IS_END_EVENT))
+		s->data_block_counter =
+				(s->data_block_counter + data_blocks) & 0xff;
 	payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
 
 	trace_out_packet(s, cycle, buffer, payload_length, index);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 679053d..d2a3163 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -18,8 +18,8 @@
  *	SYT_INTERVAL samples, with these two types alternating so that
  *	the overall sample rate comes out right.
  * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
- * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
- *	corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_DBC_IS_END_EVENT: The value of dbc in an packet corresponds to the end
+ * of event in the packet. Out of IEC 61883.
  * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
  *	The value of data_block_quadlets is used instead of reported value.
  * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 08/19] firewire-motu: add MOTU specific protocol layer
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (6 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 07/19] firewire-lib: enable CIP_DBC_IS_END_EVENT for both directions of stream Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29 13:16   ` [FFADO-devel] " Jonathan Woithe
  2017-01-29  3:54 ` [PATCH 09/19] firewire-motu: handle transactions specific for MOTU FireWire models Takashi Sakamoto
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU FireWire series uses blocking transmission for AMDTP packet streaming.
They transmit/receive 8,000 packets per second, to handle the same number
of data blocks as current sampling transmission frequency. Thus,
IEC 61883-1/6 packet streaming engine of ALSA firewire stack can be
applied.

However, the sequence of packet and data blocks includes some quirks. This
is a sequence of CIP headers of packets received by 828mk2, at 44.1kHz of
sampling transmission frequency.

quads CIP1        CIP2
488   0x020F04E8  0x8222FFFF
  8   0x020F04F8  0x8222FFFF
488   0x020F0400  0x8222FFFF
488   0x020F0408  0x8222FFFF
  8   0x020F04E8  0x8222FFFF
488   0x020F04F0  0x8222FFFF
488   0x020F04F8  0x8222FFFF

The SID (source node ID), DBS (data block size), SPH (source packet header),
FMT (format ID), FDF (format dependent field) and SYT (time stamp) fields
are compliant to IEC 61883-1. Especially, FMT is 0x02, FDF is 0x22 and SYT
is 0xffff to define MOTU specific protocol. In an aspect of dbc field, the
value represents accumulated number of data blocks included the packet. This
is against IEC 61883-1, because according to the specification this value
should be the number of transferred data blocks. In the engine, this quirk
is represented by CIP_DBC_IS_END_EVENT.

Each data block includes SPH as its first quadlet field, to represent its
presentation time stamp. Actual value of SPH is compliant to IEC 61883-1;
lower 25 bits of 32 bits width consists of 13 bits cycle count and 12 bits
cycle offset.

The rest of each data block consists of 24 bit chunks. All of PCM samples,
MIDI messages, status and control messages are transferred by the chunks.
This is similar to '24-bit * 4 Audio Pack' in IEC 61883-6. The position of
each kind of data depends on generations of each model. The number of
whole chunks in a data block is a multiple of 4, to consists of
quadlet-aligned packets.

This commit adds data block processing layer specific for the MOTU
protocol. The remarkable point is the way to generate SPH header. Time
stamps for each data blocks are generated by below calculation:

 * Using pre-computed table for the number of ticks per event
  *  44,1kHz: (557 + 123/441)
  *  48.0kHz: (512 +   0/441)
  *  88.2kHz: (278 + 282/441)
  *  96.0kHz: (256 +   0/441)
  * 176.4kHz: (139 + 141/441)
  * 192.0kHz: (128 +   0/441)
 * Accumulate the ticks and set the value to SPH for every events.
 * This way makes sense only for blocking transmission because this mode
   transfers fixed number or none of events.

This calculation assumes that each data block has a PCM frame which is
sampled according to event timing clock. Current packet streaming layer
has the same assumption.

Although this sequence works fine for MOTU FireWire series at sampling
transmission frequency based on 48.0kHz, it is not enough at the frequency
based on 44.1kHz. The units generate choppy noise every a few seconds.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/Makefile     |   2 +-
 sound/firewire/motu/amdtp-motu.c | 292 +++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.h       |  13 +-
 3 files changed, 304 insertions(+), 3 deletions(-)
 create mode 100644 sound/firewire/motu/amdtp-motu.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index d7819d5..37391f5 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,2 +1,2 @@
-snd-firewire-motu-objs := motu.o
+snd-firewire-motu-objs := motu.o amdtp-motu.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
new file mode 100644
index 0000000..11e4412
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -0,0 +1,292 @@
+/*
+ * amdtp-motu.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 <linux/slab.h>
+#include <sound/pcm.h>
+#include "motu.h"
+
+#define CIP_FMT_MOTU		0x02
+#define MOTU_FDF_AM824		0x22
+
+struct amdtp_motu {
+	/* For timestamp processing.  */
+	unsigned int quotient_ticks_per_event;
+	unsigned int remainder_ticks_per_event;
+	unsigned int next_ticks;
+	unsigned int next_accumulated;
+	unsigned int next_cycles;
+	unsigned int next_seconds;
+
+	unsigned int pcm_chunks;
+	unsigned int pcm_byte_offset;
+};
+
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+			      struct snd_motu_packet_format *formats)
+{
+	static const struct {
+		unsigned int quotient_ticks_per_event;
+		unsigned int remainder_ticks_per_event;
+	} params[] = {
+		[CIP_SFC_44100]  = { 557, 123 },
+		[CIP_SFC_48000]  = { 512,   0 },
+		[CIP_SFC_88200]  = { 278, 282 },
+		[CIP_SFC_96000]  = { 256,   0 },
+		[CIP_SFC_176400] = { 139, 141 },
+		[CIP_SFC_192000] = { 128,   0 },
+	};
+	struct amdtp_motu *p = s->protocol;
+	unsigned int pcm_chunks, data_chunks, data_block_quadlets;
+	unsigned int delay;
+	unsigned int mode;
+	int i, err;
+
+	if (amdtp_stream_running(s))
+		return -EBUSY;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		if (snd_motu_clock_rates[i] == rate) {
+			mode = i >> 1;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(snd_motu_clock_rates))
+		return -EINVAL;
+
+	pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
+		     formats->differed_part_pcm_chunks[mode];
+	data_chunks = formats->msg_chunks + pcm_chunks;
+
+	/*
+	 * Each data block includes SPH in its head. Data chunks follow with
+	 * 3 byte alignment. Padding follows with zero to conform to quadlet
+	 * alignment.
+	 */
+	data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
+
+	err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
+	if (err < 0)
+		return err;
+
+	p->pcm_chunks = pcm_chunks;
+	p->pcm_byte_offset = formats->pcm_byte_offset;
+
+	/* IEEE 1394 bus requires. */
+	delay = 0x2e00;
+
+	/* For no-data or empty packets to adjust PCM sampling frequency. */
+	delay += 8000 * 3072 * s->syt_interval / rate;
+
+	p->next_seconds = 0;
+	p->next_cycles = delay / 3072;
+	p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
+	p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
+	p->next_ticks = delay % 3072;
+	p->next_accumulated = 0;
+
+	return 0;
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+			 struct snd_pcm_runtime *runtime,
+			 __be32 *buffer, unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	unsigned int channels, remaining_frames, i, c;
+	u8 *byte;
+	u32 *dst;
+
+	channels = p->pcm_chunks;
+	dst = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+	for (i = 0; i < data_blocks; ++i) {
+		byte = (u8 *)buffer + p->pcm_byte_offset;
+
+		for (c = 0; c < channels; ++c) {
+			*dst = (byte[0] << 24) | (byte[1] << 16) | byte[2];
+			byte += 3;
+			dst++;
+		}
+		buffer += s->data_block_quadlets;
+		if (--remaining_frames == 0)
+			dst = (void *)runtime->dma_area;
+	}
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+			  struct snd_pcm_runtime *runtime,
+			  __be32 *buffer, unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	unsigned int channels, remaining_frames, i, c;
+	u8 *byte;
+	const u32 *src;
+
+	channels = p->pcm_chunks;
+	src = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+	for (i = 0; i < data_blocks; ++i) {
+		byte = (u8 *)buffer + p->pcm_byte_offset;
+
+		for (c = 0; c < channels; ++c) {
+			byte[0] = (*src >> 24) & 0xff;
+			byte[1] = (*src >> 16) & 0xff;
+			byte[2] = (*src >>  8) & 0xff;
+			byte += 3;
+			src++;
+		}
+
+		buffer += s->data_block_quadlets;
+		if (--remaining_frames == 0)
+			src = (void *)runtime->dma_area;
+	}
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+			      unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	unsigned int channels, i, c;
+	u8 *byte;
+
+	channels = p->pcm_chunks;
+
+	for (i = 0; i < data_blocks; ++i) {
+		byte = (u8 *)buffer + p->pcm_byte_offset;
+
+		for (c = 0; c < channels; ++c) {
+			byte[0] = 0;
+			byte[1] = 0;
+			byte[2] = 0;
+			byte += 3;
+		}
+
+		buffer += s->data_block_quadlets;
+	}
+}
+
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+				      struct snd_pcm_runtime *runtime)
+{
+	int err;
+
+	/* TODO: how to set an constraint for exactly 24bit PCM sample? */
+	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	if (err < 0)
+		return err;
+
+	return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+				__be32 *buffer, unsigned int data_blocks,
+				unsigned int *syt)
+{
+	struct snd_pcm_substream *pcm;
+
+	pcm = ACCESS_ONCE(s->pcm);
+	if (data_blocks > 0 && pcm)
+		read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
+
+	return data_blocks;
+}
+
+static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
+{
+	p->next_accumulated += p->remainder_ticks_per_event;
+	if (p->next_accumulated >= 441) {
+		p->next_accumulated -= 441;
+		p->next_ticks++;
+	}
+
+	p->next_ticks += p->quotient_ticks_per_event;
+	if (p->next_ticks >= 3072) {
+		p->next_ticks -= 3072;
+		p->next_cycles++;
+	}
+
+	if (p->next_cycles >= 8000) {
+		p->next_cycles -= 8000;
+		p->next_seconds++;
+	}
+
+	if (p->next_seconds >= 128)
+		p->next_seconds -= 128;
+}
+
+static void write_sph(struct amdtp_stream *s, __be32 *buffer,
+		      unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	unsigned int next_cycles;
+	unsigned int i;
+	u32 sph;
+
+	for (i = 0; i < data_blocks; i++) {
+		next_cycles = (s->start_cycle + p->next_cycles) % 8000;
+		sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
+		*buffer = cpu_to_be32(sph);
+
+		compute_next_elapse_from_start(p);
+
+		buffer += s->data_block_quadlets;
+	}
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+				__be32 *buffer, unsigned int data_blocks,
+				unsigned int *syt)
+{
+	struct snd_pcm_substream *pcm;
+
+	/* Not used. */
+	*syt = 0xffff;
+
+	/* TODO: how to interact control messages between userspace? */
+
+	pcm = ACCESS_ONCE(s->pcm);
+	if (pcm)
+		write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
+	else
+		write_pcm_silence(s, buffer, data_blocks);
+
+	write_sph(s, buffer, data_blocks);
+
+	return data_blocks;
+}
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+		    enum amdtp_stream_direction dir,
+		    const struct snd_motu_protocol *const protocol)
+{
+	amdtp_stream_process_data_blocks_t process_data_blocks;
+	int fmt = CIP_FMT_MOTU;
+	int flags = CIP_BLOCKING;
+	int err;
+
+	if (dir == AMDTP_IN_STREAM) {
+		process_data_blocks = process_tx_data_blocks;
+	} else {
+		process_data_blocks = process_rx_data_blocks;
+		flags |= CIP_DBC_IS_END_EVENT;
+	}
+
+	err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
+				sizeof(struct amdtp_motu));
+	if (err < 0)
+		return err;
+
+	s->sph = 1;
+	s->fdf = MOTU_FDF_AM824;
+
+	return 0;
+}
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index cb6b573..cd1b3dd 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -19,12 +19,12 @@
 
 #include <sound/control.h>
 #include <sound/core.h>
+#include <sound/pcm.h>
 
 #include "../lib.h"
+#include "../amdtp-stream.h"
 
 struct snd_motu_packet_format {
-	unsigned char midi_flag_offset;
-	unsigned char midi_byte_offset;
 	unsigned char pcm_byte_offset;
 
 	unsigned char msg_chunks;
@@ -46,6 +46,8 @@ struct snd_motu {
 	/* For packet streaming */
 	struct snd_motu_packet_format tx_packet_formats;
 	struct snd_motu_packet_format rx_packet_formats;
+	struct amdtp_stream tx_stream;
+	struct amdtp_stream rx_stream;
 };
 
 enum snd_motu_spec_flags {
@@ -97,4 +99,11 @@ struct snd_motu_spec {
 	const struct snd_motu_protocol *const protocol;
 };
 
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+		    enum amdtp_stream_direction dir,
+		    const struct snd_motu_protocol *const protocol);
+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);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 09/19] firewire-motu: handle transactions specific for MOTU FireWire models
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (7 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 08/19] firewire-motu: add MOTU specific protocol layer Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 10/19] firewire-motu: add stream management functionality Takashi Sakamoto
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

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>
---
 sound/firewire/motu/Makefile           |   2 +-
 sound/firewire/motu/motu-transaction.c | 117 +++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c             |  10 +++
 sound/firewire/motu/motu.h             |  12 ++++
 4 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-transaction.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 37391f5..03b0769 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -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
index 0000000..416dd98
--- /dev/null
+++ b/sound/firewire/motu/motu-transaction.c
@@ -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));
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 1e6fc74..db6014c 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -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)			\
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index cd1b3dd..ed1d779 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -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
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 10/19] firewire-motu: add stream management functionality
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (8 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 09/19] firewire-motu: handle transactions specific for MOTU FireWire models Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 11/19] firewire-motu: add proc node to show current statuc of clock and packet formats Takashi Sakamoto
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a functionality to manage packet streaming.

The streaming is not controlled by CMP, thus against IEC 61883-1. Write
transaction to certain addresses start/stop packet streaming.

Transactions to 0x'ffff'f000'0b00 results in isochronous channel number for
both directions and starting/stopping transmission of packets. The
isochronous channel number is represented in 6 bit field, thus units can
identify the channels up to 64, as IEEE 1394 bus specification described.

Transactions to 0x'ffff'f000'0b10 results in packet format for both
directions and transmission speed. When each of data block includes fixed
part of data chunks only, corresponding flags stand.

When bus reset occurs, the units continue to transmit packets with
non-contiguous data block counter. This causes discontinuity detection in
packet streaming engine and ALSA PCM applications receives EPIPE from any
I/O operation. In this case, typical applications manage to recover
corresponding PCM substream. This behaviour is kicked much earlier than
callback of bus reset handler by Linux FireWire subsystem, therefore
status of packet streaming is not changed in the handler.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/Makefile      |   2 +-
 sound/firewire/motu/motu-stream.c | 340 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c        |   5 +
 sound/firewire/motu/motu.h        |  10 ++
 4 files changed, 356 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-stream.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 03b0769..504a4f9 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,2 +1,2 @@
-snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o
+snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
new file mode 100644
index 0000000..9aa698f
--- /dev/null
+++ b/sound/firewire/motu/motu-stream.c
@@ -0,0 +1,340 @@
+/*
+ * motu-stream.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	CALLBACK_TIMEOUT	200
+
+#define ISOC_COMM_CONTROL_OFFSET		0x0b00
+#define  ISOC_COMM_CONTROL_MASK			0xffff0000
+#define  CHANGE_RX_ISOC_COMM_STATE		0x80000000
+#define  RX_ISOC_COMM_IS_ACTIVATED		0x40000000
+#define  RX_ISOC_COMM_CHANNEL_MASK		0x3f000000
+#define  RX_ISOC_COMM_CHANNEL_SHIFT		24
+#define  CHANGE_TX_ISOC_COMM_STATE		0x00800000
+#define  TX_ISOC_COMM_IS_ACTIVATED		0x00400000
+#define  TX_ISOC_COMM_CHANNEL_MASK		0x003f0000
+#define  TX_ISOC_COMM_CHANNEL_SHIFT		16
+
+#define PACKET_FORMAT_OFFSET			0x0b10
+#define  TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS	0x00000080
+#define  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS	0x00000040
+#define  TX_PACKET_TRANSMISSION_SPEED_MASK	0x0000000f
+
+static int start_both_streams(struct snd_motu *motu, unsigned int rate)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	/* Set packet formation to our packet streaming engine. */
+	err = amdtp_motu_set_parameters(&motu->rx_stream, rate,
+					&motu->rx_packet_formats);
+	if (err < 0)
+		return err;
+
+	err = amdtp_motu_set_parameters(&motu->tx_stream, rate,
+					&motu->tx_packet_formats);
+	if (err < 0)
+		return err;
+
+
+	/* Get isochronous resources on the bus. */
+	err = fw_iso_resources_allocate(&motu->rx_resources,
+				amdtp_stream_get_max_payload(&motu->rx_stream),
+				fw_parent_device(motu->unit)->max_speed);
+	if (err < 0)
+		return err;
+
+	err = fw_iso_resources_allocate(&motu->tx_resources,
+				amdtp_stream_get_max_payload(&motu->tx_stream),
+				fw_parent_device(motu->unit)->max_speed);
+	if (err < 0)
+		return err;
+
+	/* Configure the unit to start isochronous communication. */
+	err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
+
+	data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
+		(motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
+		CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
+		(motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+static void stop_both_streams(struct snd_motu *motu)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = motu->spec->protocol->switch_fetching_mode(motu, false);
+	if (err < 0)
+		return;
+
+	err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return;
+	data = be32_to_cpu(reg);
+
+	data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
+	data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
+
+	reg = cpu_to_be32(data);
+	snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+				   sizeof(reg));
+
+	fw_iso_resources_free(&motu->tx_resources);
+	fw_iso_resources_free(&motu->rx_resources);
+}
+
+static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
+{
+	struct fw_iso_resources *resources;
+	int err;
+
+	if (stream == &motu->rx_stream)
+		resources = &motu->rx_resources;
+	else
+		resources = &motu->tx_resources;
+
+	err = amdtp_stream_start(stream, resources->channel,
+				 fw_parent_device(motu->unit)->max_speed);
+	if (err < 0)
+		return err;
+
+	if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
+		amdtp_stream_stop(stream);
+		fw_iso_resources_free(resources);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
+{
+	struct fw_iso_resources *resources;
+
+	if (stream == &motu->rx_stream)
+		resources = &motu->rx_resources;
+	else
+		resources = &motu->tx_resources;
+
+	amdtp_stream_stop(stream);
+	fw_iso_resources_free(resources);
+}
+
+static int ensure_packet_formats(struct snd_motu *motu)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
+		  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
+		  TX_PACKET_TRANSMISSION_SPEED_MASK);
+	if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
+		data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+	if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
+		data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+	data |= fw_parent_device(motu->unit)->max_speed;
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
+{
+	const struct snd_motu_protocol *protocol = motu->spec->protocol;
+	unsigned int curr_rate;
+	int err = 0;
+
+	if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
+		return 0;
+
+	/* Some packet queueing errors. */
+	if (amdtp_streaming_error(&motu->rx_stream) ||
+	    amdtp_streaming_error(&motu->tx_stream)) {
+		amdtp_stream_stop(&motu->rx_stream);
+		amdtp_stream_stop(&motu->tx_stream);
+		stop_both_streams(motu);
+	}
+
+	err = protocol->cache_packet_formats(motu);
+	if (err < 0)
+		return err;
+
+	/* Stop stream if rate is different. */
+	err = protocol->get_clock_rate(motu, &curr_rate);
+	if (err < 0) {
+		dev_err(&motu->unit->device,
+			"fail to get sampling rate: %d\n", err);
+		return err;
+	}
+	if (rate == 0)
+		rate = curr_rate;
+	if (rate != curr_rate) {
+		amdtp_stream_stop(&motu->rx_stream);
+		amdtp_stream_stop(&motu->tx_stream);
+		stop_both_streams(motu);
+	}
+
+	if (!amdtp_stream_running(&motu->rx_stream)) {
+		err = protocol->set_clock_rate(motu, rate);
+		if (err < 0) {
+			dev_err(&motu->unit->device,
+				"fail to set sampling rate: %d\n", err);
+			return err;
+		}
+
+		err = ensure_packet_formats(motu);
+		if (err < 0)
+			return err;
+
+		err = start_both_streams(motu, rate);
+		if (err < 0) {
+			dev_err(&motu->unit->device,
+				"fail to start isochronous comm: %d\n", err);
+			stop_both_streams(motu);
+			return err;
+		}
+
+		err = start_isoc_ctx(motu, &motu->rx_stream);
+		if (err < 0) {
+			dev_err(&motu->unit->device,
+				"fail to start IT context: %d\n", err);
+			stop_both_streams(motu);
+			return err;
+		}
+
+		err = protocol->switch_fetching_mode(motu, true);
+		if (err < 0) {
+			dev_err(&motu->unit->device,
+				"fail to enable frame fetching: %d\n", err);
+			stop_both_streams(motu);
+			return err;
+		}
+	}
+
+	if (!amdtp_stream_running(&motu->tx_stream) &&
+	    motu->capture_substreams > 0) {
+		err = start_isoc_ctx(motu, &motu->tx_stream);
+		if (err < 0) {
+			dev_err(&motu->unit->device,
+				"fail to start IR context: %d", err);
+			amdtp_stream_stop(&motu->rx_stream);
+			stop_both_streams(motu);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+void snd_motu_stream_stop_duplex(struct snd_motu *motu)
+{
+	if (motu->capture_substreams == 0) {
+		if (amdtp_stream_running(&motu->tx_stream))
+			stop_isoc_ctx(motu, &motu->tx_stream);
+
+		if (motu->playback_substreams == 0) {
+			if (amdtp_stream_running(&motu->rx_stream))
+				stop_isoc_ctx(motu, &motu->rx_stream);
+			stop_both_streams(motu);
+		}
+	}
+}
+
+static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
+{
+	int err;
+	struct amdtp_stream *stream;
+	struct fw_iso_resources *resources;
+
+	if (dir == AMDTP_IN_STREAM) {
+		stream = &motu->tx_stream;
+		resources = &motu->tx_resources;
+	} else {
+		stream = &motu->rx_stream;
+		resources = &motu->rx_resources;
+	}
+
+	err = fw_iso_resources_init(resources, motu->unit);
+	if (err < 0)
+		return err;
+
+	err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
+	if (err < 0) {
+		amdtp_stream_destroy(stream);
+		fw_iso_resources_destroy(resources);
+	}
+
+	return err;
+}
+
+static void destroy_stream(struct snd_motu *motu,
+			   enum amdtp_stream_direction dir)
+{
+	struct amdtp_stream *stream;
+	struct fw_iso_resources *resources;
+
+	if (dir == AMDTP_IN_STREAM) {
+		stream = &motu->tx_stream;
+		resources = &motu->tx_resources;
+	} else {
+		stream = &motu->rx_stream;
+		resources = &motu->rx_resources;
+	}
+
+	amdtp_stream_destroy(stream);
+	fw_iso_resources_free(resources);
+}
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu)
+{
+	int err;
+
+	err = init_stream(motu, AMDTP_IN_STREAM);
+	if (err < 0)
+		return err;
+
+	err = init_stream(motu, AMDTP_OUT_STREAM);
+	if (err < 0)
+		destroy_stream(motu, AMDTP_IN_STREAM);
+
+	return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
+{
+	destroy_stream(motu, AMDTP_IN_STREAM);
+	destroy_stream(motu, AMDTP_OUT_STREAM);
+
+	motu->playback_substreams = 0;
+	motu->capture_substreams = 0;
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index db6014c..9d52238 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -56,6 +56,7 @@ static void motu_free(struct snd_motu *motu)
 {
 	snd_motu_transaction_unregister(motu);
 
+	snd_motu_stream_destroy_duplex(motu);
 	fw_unit_put(motu->unit);
 
 	mutex_destroy(&motu->mutex);
@@ -92,6 +93,10 @@ static void do_registration(struct work_struct *work)
 	if (err < 0)
 		goto error;
 
+	err = snd_motu_stream_init_duplex(motu);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index ed1d779..90d2741 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -23,6 +23,7 @@
 
 #include "../lib.h"
 #include "../amdtp-stream.h"
+#include "../iso-resources.h"
 
 struct snd_motu_packet_format {
 	unsigned char pcm_byte_offset;
@@ -48,6 +49,10 @@ struct snd_motu {
 	struct snd_motu_packet_format rx_packet_formats;
 	struct amdtp_stream tx_stream;
 	struct amdtp_stream rx_stream;
+	struct fw_iso_resources tx_resources;
+	struct fw_iso_resources rx_resources;
+	unsigned int capture_substreams;
+	unsigned int playback_substreams;
 
 	/* For notification. */
 	struct fw_address_handler async_handler;
@@ -118,4 +123,9 @@ int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
 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);
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu);
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
+int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
+void snd_motu_stream_stop_duplex(struct snd_motu *motu);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 11/19] firewire-motu: add proc node to show current statuc of clock and packet formats
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (9 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 10/19] firewire-motu: add stream management functionality Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 12/19] firewire-motu: add PCM functionality Takashi Sakamoto
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a proc node for debugging purpose.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/Makefile    |   3 +-
 sound/firewire/motu/motu-proc.c | 118 ++++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c      |   2 +
 sound/firewire/motu/motu.h      |   3 +
 4 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-proc.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 504a4f9..0eccbe2 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,2 +1,3 @@
-snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o
+snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
+			  motu-proc.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
new file mode 100644
index 0000000..4edc064
--- /dev/null
+++ b/sound/firewire/motu/motu-proc.c
@@ -0,0 +1,118 @@
+/*
+ * motu-proc.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"
+
+static const char *const clock_names[] = {
+	[SND_MOTU_CLOCK_SOURCE_INTERNAL] = "Internal",
+	[SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB] = "ADAT on Dsub-9pin interface",
+	[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT] = "ADAT on optical interface",
+	[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A] = "ADAT on optical interface A",
+	[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B] = "ADAT on optical interface B",
+	[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
+	[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
+	[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
+	[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
+	[SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
+	[SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
+};
+
+static void proc_read_clock(struct snd_info_entry *entry,
+			    struct snd_info_buffer *buffer)
+{
+
+	struct snd_motu *motu = entry->private_data;
+	const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+	unsigned int rate;
+	enum snd_motu_clock_source source;
+
+	if (protocol->get_clock_rate(motu, &rate) < 0)
+		return;
+	if (protocol->get_clock_source(motu, &source) < 0)
+		return;
+
+	snd_iprintf(buffer, "Rate:\t%d\n", rate);
+	snd_iprintf(buffer, "Source:\t%s\n", clock_names[source]);
+}
+
+static void proc_read_format(struct snd_info_entry *entry,
+			     struct snd_info_buffer *buffer)
+{
+	struct snd_motu *motu = entry->private_data;
+	const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+	unsigned int mode;
+	struct snd_motu_packet_format *formats;
+	int i;
+
+	if (protocol->cache_packet_formats(motu) < 0)
+		return;
+
+	snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
+	for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+		mode = i >> 1;
+
+		formats = &motu->tx_packet_formats;
+		snd_iprintf(buffer,
+			    "%u:\t%u\t%u\t%u\n",
+			    snd_motu_clock_rates[i],
+			    formats->msg_chunks,
+			    formats->fixed_part_pcm_chunks[mode],
+			    formats->differed_part_pcm_chunks[mode]);
+	}
+
+	snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
+	for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+		mode = i >> 1;
+
+		formats = &motu->rx_packet_formats;
+		snd_iprintf(buffer,
+			    "%u:\t%u\t%u\t%u\n",
+			    snd_motu_clock_rates[i],
+			    formats->msg_chunks,
+			    formats->fixed_part_pcm_chunks[mode],
+			    formats->differed_part_pcm_chunks[mode]);
+	}
+}
+
+static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
+		     const char *name,
+		     void (*op)(struct snd_info_entry *e,
+				struct snd_info_buffer *b))
+{
+	struct snd_info_entry *entry;
+
+	entry = snd_info_create_card_entry(motu->card, name, root);
+	if (entry == NULL)
+		return;
+
+	snd_info_set_text_ops(entry, motu, op);
+	if (snd_info_register(entry) < 0)
+		snd_info_free_entry(entry);
+}
+
+void snd_motu_proc_init(struct snd_motu *motu)
+{
+	struct snd_info_entry *root;
+
+	/*
+	 * All nodes are automatically removed at snd_card_disconnect(),
+	 * by following to link list.
+	 */
+	root = snd_info_create_card_entry(motu->card, "firewire",
+					  motu->card->proc_root);
+	if (root == NULL)
+		return;
+	root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	if (snd_info_register(root) < 0) {
+		snd_info_free_entry(root);
+		return;
+	}
+
+	add_node(motu, root, "clock", proc_read_clock);
+	add_node(motu, root, "format", proc_read_format);
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 9d52238..cbf4ed0 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -97,6 +97,8 @@ static void do_registration(struct work_struct *work)
 	if (err < 0)
 		goto error;
 
+	snd_motu_proc_init(motu);
+
 	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 90d2741..4d079d6 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -20,6 +20,7 @@
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/info.h>
 
 #include "../lib.h"
 #include "../amdtp-stream.h"
@@ -128,4 +129,6 @@ int snd_motu_stream_init_duplex(struct snd_motu *motu);
 void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
 int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
 void snd_motu_stream_stop_duplex(struct snd_motu *motu);
+
+void snd_motu_proc_init(struct snd_motu *motu);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 12/19] firewire-motu: add PCM functionality
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (10 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 11/19] firewire-motu: add proc node to show current statuc of clock and packet formats Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 13/19] firewire-motu: add MIDI functionality Takashi Sakamoto
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds PCM functionality to transmit/receive PCM samples.

When one of PCM substreams are running or external clock source is
selected, current sampling rate is used. Else, the sampling rate is
changed as an userspace application requests.

Available number of samples in a frame of PCM substream is determined at
open(2) to corresponding PCM character device. Later, packet streaming
starts by ioctl(2) with SNDRV_PCM_IOCTL_PREPARE. In theory, between them,
applications can change state of the unit by any write transaction to
change the number. In this case, this driver may fail packet streaming due
to wrong data format.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/Makefile   |   2 +-
 sound/firewire/motu/motu-pcm.c | 386 +++++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c     |   4 +
 sound/firewire/motu/motu.h     |   2 +
 4 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-pcm.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 0eccbe2..508b689 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,3 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
-			  motu-proc.o
+			  motu-proc.o motu-pcm.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
new file mode 100644
index 0000000..a50bcd6
--- /dev/null
+++ b/sound/firewire/motu/motu-pcm.c
@@ -0,0 +1,386 @@
+/*
+ * motu-pcm.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 <sound/pcm_params.h>
+#include "motu.h"
+
+static int motu_rate_constraint(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_motu_packet_format *formats = rule->private;
+
+	const struct snd_interval *c =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *r =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval rates = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, pcm_channels, rate, mode;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		rate = snd_motu_clock_rates[i];
+		mode = i / 2;
+
+		pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+			       formats->differed_part_pcm_chunks[mode];
+		if (!snd_interval_test(c, pcm_channels))
+			continue;
+
+		rates.min = min(rates.min, rate);
+		rates.max = max(rates.max, rate);
+	}
+
+	return snd_interval_refine(r, &rates);
+}
+
+static int motu_channels_constraint(struct snd_pcm_hw_params *params,
+				    struct snd_pcm_hw_rule *rule)
+{
+	struct snd_motu_packet_format *formats = rule->private;
+
+	const struct snd_interval *r =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *c =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval channels = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, pcm_channels, rate, mode;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		rate = snd_motu_clock_rates[i];
+		mode = i / 2;
+
+		if (!snd_interval_test(r, rate))
+			continue;
+
+		pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+			       formats->differed_part_pcm_chunks[mode];
+		channels.min = min(channels.min, pcm_channels);
+		channels.max = max(channels.max, pcm_channels);
+	}
+
+	return snd_interval_refine(c, &channels);
+}
+
+static void limit_channels_and_rates(struct snd_motu *motu,
+				     struct snd_pcm_runtime *runtime,
+				     struct snd_motu_packet_format *formats)
+{
+	struct snd_pcm_hardware *hw = &runtime->hw;
+	unsigned int i, pcm_channels, rate, mode;
+
+	hw->channels_min = UINT_MAX;
+	hw->channels_max = 0;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		rate = snd_motu_clock_rates[i];
+		mode = i / 2;
+
+		pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+			       formats->differed_part_pcm_chunks[mode];
+		if (pcm_channels == 0)
+			continue;
+
+		hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+		hw->channels_min = min(hw->channels_min, pcm_channels);
+		hw->channels_max = max(hw->channels_max, pcm_channels);
+	}
+
+	snd_pcm_limit_hw_rates(runtime);
+}
+
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+	hw->periods_min = 2;			/* SNDRV_PCM_INFO_BATCH */
+	hw->periods_max = UINT_MAX;
+
+	hw->period_bytes_min = 4 * hw->channels_max;    /* byte for a frame */
+
+	/* Just to prevent from allocating much pages. */
+	hw->period_bytes_max = hw->period_bytes_min * 2048;
+	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int init_hw_info(struct snd_motu *motu,
+			struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_pcm_hardware *hw = &runtime->hw;
+	struct amdtp_stream *stream;
+	struct snd_motu_packet_format *formats;
+	int err;
+
+	hw->info = SNDRV_PCM_INFO_MMAP |
+		   SNDRV_PCM_INFO_MMAP_VALID |
+		   SNDRV_PCM_INFO_BATCH |
+		   SNDRV_PCM_INFO_INTERLEAVED |
+		   SNDRV_PCM_INFO_JOINT_DUPLEX |
+		   SNDRV_PCM_INFO_BLOCK_TRANSFER;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		hw->formats = SNDRV_PCM_FMTBIT_S32;
+		stream = &motu->tx_stream;
+		formats = &motu->tx_packet_formats;
+	} else {
+		hw->formats = SNDRV_PCM_FMTBIT_S32;
+		stream = &motu->rx_stream;
+		formats = &motu->rx_packet_formats;
+	}
+
+	limit_channels_and_rates(motu, runtime, formats);
+	limit_period_and_buffer(hw);
+
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  motu_rate_constraint, formats,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  motu_channels_constraint, formats,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+
+	return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+	const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+	enum snd_motu_clock_source src;
+	unsigned int rate;
+	int err;
+
+	mutex_lock(&motu->mutex);
+
+	err = protocol->cache_packet_formats(motu);
+	if (err < 0)
+		return err;
+
+	err = init_hw_info(motu, substream);
+	if (err < 0)
+		return err;
+
+	/*
+	 * When source of clock is not internal or any PCM streams are running,
+	 * available sampling rate is limited at current sampling rate.
+	 */
+	err = protocol->get_clock_source(motu, &src);
+	if (err < 0)
+		return err;
+	if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
+	    amdtp_stream_pcm_running(&motu->tx_stream) ||
+	    amdtp_stream_pcm_running(&motu->rx_stream)) {
+		err = protocol->get_clock_rate(motu, &rate);
+		if (err < 0)
+			return err;
+		substream->runtime->hw.rate_min = rate;
+		substream->runtime->hw.rate_max = rate;
+	}
+
+	snd_pcm_set_sync(substream);
+
+	mutex_unlock(&motu->mutex);
+
+	return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int capture_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_motu *motu = substream->private_data;
+	int err;
+
+	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+					       params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&motu->mutex);
+		motu->capture_substreams++;
+		mutex_unlock(&motu->mutex);
+	}
+
+	return 0;
+}
+static int playback_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_motu *motu = substream->private_data;
+	int err;
+
+	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+					       params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&motu->mutex);
+		motu->playback_substreams++;
+		mutex_unlock(&motu->mutex);
+	}
+
+	return 0;
+}
+
+static int capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	mutex_lock(&motu->mutex);
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		motu->capture_substreams--;
+
+	snd_motu_stream_stop_duplex(motu);
+
+	mutex_unlock(&motu->mutex);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	mutex_lock(&motu->mutex);
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		motu->playback_substreams--;
+
+	snd_motu_stream_stop_duplex(motu);
+
+	mutex_unlock(&motu->mutex);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+	int err;
+
+	mutex_lock(&motu->mutex);
+	err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+	mutex_unlock(&motu->mutex);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&motu->tx_stream);
+
+	return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+	int err;
+
+	mutex_lock(&motu->mutex);
+	err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+	mutex_unlock(&motu->mutex);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&motu->rx_stream);
+
+	return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	return amdtp_stream_pcm_pointer(&motu->tx_stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_motu *motu = substream->private_data;
+
+	return amdtp_stream_pcm_pointer(&motu->rx_stream);
+}
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu)
+{
+	static struct snd_pcm_ops capture_ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = capture_hw_params,
+		.hw_free   = capture_hw_free,
+		.prepare   = capture_prepare,
+		.trigger   = capture_trigger,
+		.pointer   = capture_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	static struct snd_pcm_ops playback_ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = playback_hw_params,
+		.hw_free   = playback_hw_free,
+		.prepare   = playback_prepare,
+		.trigger   = playback_trigger,
+		.pointer   = playback_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = motu;
+	strcpy(pcm->name, motu->card->shortname);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+
+	return 0;
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index cbf4ed0..801d6a7 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -99,6 +99,10 @@ static void do_registration(struct work_struct *work)
 
 	snd_motu_proc_init(motu);
 
+	err = snd_motu_create_pcm_devices(motu);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 4d079d6..afc6de6 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -131,4 +131,6 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
 void snd_motu_stream_stop_duplex(struct snd_motu *motu);
 
 void snd_motu_proc_init(struct snd_motu *motu);
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 13/19] firewire-motu: add MIDI functionality
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (11 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 12/19] firewire-motu: add PCM functionality Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 14/19] firewire-motu: add hwdep interface Takashi Sakamoto
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In MOTU FireWire series, MIDI messages are multiplexed to isochronous
packets as well as PCM frames, while the way is different from the one
in IEC 61883-6.

MIDI messages are put into a certain position in message chunks. One data
block can includes one byte of the MIDI messages. When data block includes
a MIDI byte, the block has a flag in a certain position in the message
chunks. These positions are unique depending on protocols.

Once a data block includes a MIDI byte, some following data blocks includes
no MIDI bytes. Next MIDI byte appears on a data block corresponding to
next cycle of physical MIDI bus. This seems to avoid buffer overflow caused
by bandwidth differences between IEEE 1394 bus and physical MIDI bus.

This commit adds MIDI functionality to transfer/receive MIDI messages.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/motu/Makefile      |   2 +-
 sound/firewire/motu/amdtp-motu.c  |  84 +++++++++++++++++++++
 sound/firewire/motu/motu-midi.c   | 153 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu-stream.c |   9 ++-
 sound/firewire/motu/motu.c        |   7 ++
 sound/firewire/motu/motu.h        |   9 +++
 6 files changed, 260 insertions(+), 4 deletions(-)
 create mode 100644 sound/firewire/motu/motu-midi.c

diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 508b689..a512c1e 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,3 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
-			  motu-proc.o motu-pcm.o
+			  motu-proc.o motu-pcm.o motu-midi.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 11e4412..0930cd8 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -13,6 +13,12 @@
 #define CIP_FMT_MOTU		0x02
 #define MOTU_FDF_AM824		0x22
 
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND	3093
+
 struct amdtp_motu {
 	/* For timestamp processing.  */
 	unsigned int quotient_ticks_per_event;
@@ -24,9 +30,18 @@ struct amdtp_motu {
 
 	unsigned int pcm_chunks;
 	unsigned int pcm_byte_offset;
+
+	struct snd_rawmidi_substream *midi;
+	unsigned int midi_ports;
+	unsigned int midi_flag_offset;
+	unsigned int midi_byte_offset;
+
+	int midi_db_count;
+	unsigned int midi_db_interval;
 };
 
 int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+			      unsigned int midi_ports,
 			      struct snd_motu_packet_format *formats)
 {
 	static const struct {
@@ -76,6 +91,13 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
 	p->pcm_chunks = pcm_chunks;
 	p->pcm_byte_offset = formats->pcm_byte_offset;
 
+	p->midi_ports = midi_ports;
+	p->midi_flag_offset = formats->midi_flag_offset;
+	p->midi_byte_offset = formats->midi_byte_offset;
+
+	p->midi_db_count = 0;
+	p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
+
 	/* IEEE 1394 bus requires. */
 	delay = 0x2e00;
 
@@ -187,12 +209,70 @@ int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
 	return amdtp_stream_add_pcm_hw_constraints(s, runtime);
 }
 
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+			     struct snd_rawmidi_substream *midi)
+{
+	struct amdtp_motu *p = s->protocol;
+
+	if (port < p->midi_ports)
+		WRITE_ONCE(p->midi, midi);
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+				unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
+	u8 *b;
+	int i;
+
+	for (i = 0; i < data_blocks; i++) {
+		b = (u8 *)buffer;
+
+		if (midi && p->midi_db_count == 0 &&
+		    snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
+			b[p->midi_flag_offset] = 0x01;
+		} else {
+			b[p->midi_byte_offset] = 0x00;
+			b[p->midi_flag_offset] = 0x00;
+		}
+
+		buffer += s->data_block_quadlets;
+
+		if (--p->midi_db_count < 0)
+			p->midi_db_count = p->midi_db_interval;
+	}
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+			       unsigned int data_blocks)
+{
+	struct amdtp_motu *p = s->protocol;
+	struct snd_rawmidi_substream *midi;
+	u8 *b;
+	int i;
+
+	for (i = 0; i < data_blocks; i++) {
+		b = (u8 *)buffer;
+		midi = READ_ONCE(p->midi);
+
+		if (midi && (b[p->midi_flag_offset] & 0x01))
+			snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
+
+		buffer += s->data_block_quadlets;
+	}
+}
+
 static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
 				__be32 *buffer, unsigned int data_blocks,
 				unsigned int *syt)
 {
+	struct amdtp_motu *p = s->protocol;
 	struct snd_pcm_substream *pcm;
 
+	if (p->midi_ports)
+		read_midi_messages(s, buffer, data_blocks);
+
 	pcm = ACCESS_ONCE(s->pcm);
 	if (data_blocks > 0 && pcm)
 		read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
@@ -246,6 +326,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
 				__be32 *buffer, unsigned int data_blocks,
 				unsigned int *syt)
 {
+	struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
 	struct snd_pcm_substream *pcm;
 
 	/* Not used. */
@@ -253,6 +334,9 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
 
 	/* TODO: how to interact control messages between userspace? */
 
+	if (p->midi_ports)
+		write_midi_messages(s, buffer, data_blocks);
+
 	pcm = ACCESS_ONCE(s->pcm);
 	if (pcm)
 		write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
new file mode 100644
index 0000000..f232f29
--- /dev/null
+++ b/sound/firewire/motu/motu-midi.c
@@ -0,0 +1,153 @@
+/*
+ * motu-midi.h - 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"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_motu *motu = substream->rmidi->private_data;
+	int err;
+
+	mutex_lock(&motu->mutex);
+
+	motu->capture_substreams++;
+	err = snd_motu_stream_start_duplex(motu, 0);
+
+	mutex_unlock(&motu->mutex);
+
+	return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_motu *motu = substream->rmidi->private_data;
+	int err;
+
+	mutex_lock(&motu->mutex);
+
+	motu->playback_substreams++;
+	err = snd_motu_stream_start_duplex(motu, 0);
+
+	mutex_unlock(&motu->mutex);
+
+	return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_motu *motu = substream->rmidi->private_data;
+
+	mutex_lock(&motu->mutex);
+
+	motu->capture_substreams--;
+	snd_motu_stream_stop_duplex(motu);
+
+	mutex_unlock(&motu->mutex);
+
+	return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_motu *motu = substream->rmidi->private_data;
+
+	mutex_lock(&motu->mutex);
+
+	motu->playback_substreams--;
+	snd_motu_stream_stop_duplex(motu);
+
+	mutex_unlock(&motu->mutex);
+
+	return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_motu *motu = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&motu->lock, flags);
+
+	if (up)
+		amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+					substrm);
+	else
+		amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+					NULL);
+
+	spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_motu *motu = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&motu->lock, flags);
+
+	if (up)
+		amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+					substrm);
+	else
+		amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+					NULL);
+
+	spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_motu *motu,
+				     struct snd_rawmidi_str *str)
+{
+	struct snd_rawmidi_substream *subs;
+
+	list_for_each_entry(subs, &str->substreams, list) {
+		snprintf(subs->name, sizeof(subs->name),
+			 "%s MIDI %d", motu->card->shortname, subs->number + 1);
+	}
+}
+
+int snd_motu_create_midi_devices(struct snd_motu *motu)
+{
+	static struct snd_rawmidi_ops capture_ops = {
+		.open		= midi_capture_open,
+		.close		= midi_capture_close,
+		.trigger	= midi_capture_trigger,
+	};
+	static struct snd_rawmidi_ops playback_ops = {
+		.open		= midi_playback_open,
+		.close		= midi_playback_close,
+		.trigger	= midi_playback_trigger,
+	};
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *str;
+	int err;
+
+	/* create midi ports */
+	err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
+	if (err < 0)
+		return err;
+
+	snprintf(rmidi->name, sizeof(rmidi->name),
+		 "%s MIDI", motu->card->shortname);
+	rmidi->private_data = motu;
+
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+			     SNDRV_RAWMIDI_INFO_OUTPUT |
+			     SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+			    &capture_ops);
+	str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+	set_midi_substream_names(motu, str);
+
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+			    &playback_ops);
+	str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+	set_midi_substream_names(motu, str);
+
+	return 0;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 9aa698f..911d348 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -28,22 +28,25 @@
 
 static int start_both_streams(struct snd_motu *motu, unsigned int rate)
 {
+	unsigned int midi_ports = 0;
 	__be32 reg;
 	u32 data;
 	int err;
 
+	if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI)
+		midi_ports = 1;
+
 	/* Set packet formation to our packet streaming engine. */
-	err = amdtp_motu_set_parameters(&motu->rx_stream, rate,
+	err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
 					&motu->rx_packet_formats);
 	if (err < 0)
 		return err;
 
-	err = amdtp_motu_set_parameters(&motu->tx_stream, rate,
+	err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
 					&motu->tx_packet_formats);
 	if (err < 0)
 		return err;
 
-
 	/* Get isochronous resources on the bus. */
 	err = fw_iso_resources_allocate(&motu->rx_resources,
 				amdtp_stream_get_max_payload(&motu->rx_stream),
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 801d6a7..d4da137 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -103,6 +103,12 @@ static void do_registration(struct work_struct *work)
 	if (err < 0)
 		goto error;
 
+	if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI) {
+		err = snd_motu_create_midi_devices(motu);
+		if (err < 0)
+			goto error;
+	}
+
 	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
@@ -138,6 +144,7 @@ static int motu_probe(struct fw_unit *unit,
 	dev_set_drvdata(&unit->device, motu);
 
 	mutex_init(&motu->mutex);
+	spin_lock_init(&motu->lock);
 
 	/* Allocate and register this sound card later. */
 	INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index afc6de6..338b351 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -21,12 +21,15 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
+#include <sound/rawmidi.h>
 
 #include "../lib.h"
 #include "../amdtp-stream.h"
 #include "../iso-resources.h"
 
 struct snd_motu_packet_format {
+	unsigned char midi_flag_offset;
+	unsigned char midi_byte_offset;
 	unsigned char pcm_byte_offset;
 
 	unsigned char msg_chunks;
@@ -38,6 +41,7 @@ struct snd_motu {
 	struct snd_card *card;
 	struct fw_unit *unit;
 	struct mutex mutex;
+	spinlock_t lock;
 
 	bool registered;
 	struct delayed_work dwork;
@@ -113,9 +117,12 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
 		    enum amdtp_stream_direction dir,
 		    const struct snd_motu_protocol *const protocol);
 int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+			      unsigned int midi_ports,
 			      struct snd_motu_packet_format *formats);
 int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
 				      struct snd_pcm_runtime *runtime);
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+			     struct snd_rawmidi_substream *midi);
 
 int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
 			      size_t size);
@@ -133,4 +140,6 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu);
 void snd_motu_proc_init(struct snd_motu *motu);
 
 int snd_motu_create_pcm_devices(struct snd_motu *motu);
+
+int snd_motu_create_midi_devices(struct snd_motu *motu);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 14/19] firewire-motu: add hwdep interface
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (12 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 13/19] firewire-motu: add MIDI functionality Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 15/19] firewire-motu: enable to read transaction cache via " Takashi Sakamoto
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds hwdep interface so as the other sound drivers for units
on IEEE 1394 bus have.

This interface is designed for mixer/control applications. By using this
interface, an application can get information about firewire node, can
lock/unlock kernel streaming and can get notification at starting/stopping
kernel streaming.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/asound.h       |   3 +-
 include/uapi/sound/firewire.h     |   3 +-
 sound/firewire/motu/Makefile      |   2 +-
 sound/firewire/motu/motu-hwdep.c  | 192 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu-midi.c   |  16 ++++
 sound/firewire/motu/motu-pcm.c    |  20 +++-
 sound/firewire/motu/motu-stream.c |  38 ++++++++
 sound/firewire/motu/motu.c        |   5 +
 sound/firewire/motu/motu.h        |  12 +++
 9 files changed, 284 insertions(+), 7 deletions(-)
 create mode 100644 sound/firewire/motu/motu-hwdep.c

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index be353a7..fd7b561 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -107,9 +107,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
 	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
 	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
+	SNDRV_HWDEP_IFACE_FW_MOTU,	/* MOTU FireWire series */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_MOTU
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index db79a12..59c6d81 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -65,7 +65,8 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_TYPE_OXFW	4
 #define SNDRV_FIREWIRE_TYPE_DIGI00X	5
 #define SNDRV_FIREWIRE_TYPE_TASCAM	6
-/* RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_MOTU	7
+/* RME... */
 
 struct snd_firewire_get_info {
 	unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index a512c1e..cc195d5 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,3 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
-			  motu-proc.o motu-pcm.o motu-midi.o
+			  motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
new file mode 100644
index 0000000..e795a52
--- /dev/null
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -0,0 +1,192 @@
+/*
+ * motu-hwdep.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.
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ *
+ */
+
+#include "motu.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+		       loff_t *offset)
+{
+	struct snd_motu *motu = hwdep->private_data;
+	DEFINE_WAIT(wait);
+	union snd_firewire_event event;
+
+	spin_lock_irq(&motu->lock);
+
+	while (!motu->dev_lock_changed) {
+		prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+		spin_unlock_irq(&motu->lock);
+		schedule();
+		finish_wait(&motu->hwdep_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		spin_lock_irq(&motu->lock);
+	}
+
+	memset(&event, 0, sizeof(event));
+	if (motu->dev_lock_changed) {
+		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+		event.lock_status.status = (motu->dev_lock_count > 0);
+		motu->dev_lock_changed = false;
+
+		count = min_t(long, count, sizeof(event.lock_status));
+	}
+
+	spin_unlock_irq(&motu->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+			       poll_table *wait)
+{
+	struct snd_motu *motu = hwdep->private_data;
+	unsigned int events;
+
+	poll_wait(file, &motu->hwdep_wait, wait);
+
+	spin_lock_irq(&motu->lock);
+	if (motu->dev_lock_changed)
+		events = POLLIN | POLLRDNORM;
+	else
+		events = 0;
+	spin_unlock_irq(&motu->lock);
+
+	return events | POLLOUT;
+}
+
+static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
+{
+	struct fw_device *dev = fw_parent_device(motu->unit);
+	struct snd_firewire_get_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.type = SNDRV_FIREWIRE_TYPE_MOTU;
+	info.card = dev->card->index;
+	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+	strlcpy(info.device_name, dev_name(&dev->device),
+		sizeof(info.device_name));
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int hwdep_lock(struct snd_motu *motu)
+{
+	int err;
+
+	spin_lock_irq(&motu->lock);
+
+	if (motu->dev_lock_count == 0) {
+		motu->dev_lock_count = -1;
+		err = 0;
+	} else {
+		err = -EBUSY;
+	}
+
+	spin_unlock_irq(&motu->lock);
+
+	return err;
+}
+
+static int hwdep_unlock(struct snd_motu *motu)
+{
+	int err;
+
+	spin_lock_irq(&motu->lock);
+
+	if (motu->dev_lock_count == -1) {
+		motu->dev_lock_count = 0;
+		err = 0;
+	} else {
+		err = -EBADFD;
+	}
+
+	spin_unlock_irq(&motu->lock);
+
+	return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct snd_motu *motu = hwdep->private_data;
+
+	spin_lock_irq(&motu->lock);
+	if (motu->dev_lock_count == -1)
+		motu->dev_lock_count = 0;
+	spin_unlock_irq(&motu->lock);
+
+	return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+	    unsigned int cmd, unsigned long arg)
+{
+	struct snd_motu *motu = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return hwdep_get_info(motu, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return hwdep_lock(motu);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return hwdep_unlock(motu);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+			      unsigned int cmd, unsigned long arg)
+{
+	return hwdep_ioctl(hwdep, file, cmd,
+			   (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu)
+{
+	static const struct snd_hwdep_ops ops = {
+		.read		= hwdep_read,
+		.release	= hwdep_release,
+		.poll		= hwdep_poll,
+		.ioctl		= hwdep_ioctl,
+		.ioctl_compat	= hwdep_compat_ioctl,
+	};
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
+	if (err < 0)
+		return err;
+
+	strcpy(hwdep->name, "MOTU");
+	hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
+	hwdep->ops = ops;
+	hwdep->private_data = motu;
+	hwdep->exclusive = true;
+
+	return 0;
+}
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
index f232f29..e3acfcc 100644
--- a/sound/firewire/motu/motu-midi.c
+++ b/sound/firewire/motu/motu-midi.c
@@ -12,6 +12,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
 	struct snd_motu *motu = substream->rmidi->private_data;
 	int err;
 
+	err = snd_motu_stream_lock_try(motu);
+	if (err < 0)
+		return err;
+
 	mutex_lock(&motu->mutex);
 
 	motu->capture_substreams++;
@@ -19,6 +23,9 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
 
 	mutex_unlock(&motu->mutex);
 
+	if (err < 0)
+		snd_motu_stream_lock_release(motu);
+
 	return err;
 }
 
@@ -27,6 +34,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
 	struct snd_motu *motu = substream->rmidi->private_data;
 	int err;
 
+	err = snd_motu_stream_lock_try(motu);
+	if (err < 0)
+		return err;
+
 	mutex_lock(&motu->mutex);
 
 	motu->playback_substreams++;
@@ -34,6 +45,9 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
 
 	mutex_unlock(&motu->mutex);
 
+	if (err < 0)
+		snd_motu_stream_lock_release(motu);
+
 	return err;
 }
 
@@ -48,6 +62,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 
 	mutex_unlock(&motu->mutex);
 
+	snd_motu_stream_lock_release(motu);
 	return 0;
 }
 
@@ -62,6 +77,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 
 	mutex_unlock(&motu->mutex);
 
+	snd_motu_stream_lock_release(motu);
 	return 0;
 }
 
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
index a50bcd6..94558f3 100644
--- a/sound/firewire/motu/motu-pcm.c
+++ b/sound/firewire/motu/motu-pcm.c
@@ -159,15 +159,19 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	unsigned int rate;
 	int err;
 
+	err = snd_motu_stream_lock_try(motu);
+	if (err < 0)
+		return err;
+
 	mutex_lock(&motu->mutex);
 
 	err = protocol->cache_packet_formats(motu);
 	if (err < 0)
-		return err;
+		goto err_locked;
 
 	err = init_hw_info(motu, substream);
 	if (err < 0)
-		return err;
+		goto err_locked;
 
 	/*
 	 * When source of clock is not internal or any PCM streams are running,
@@ -175,13 +179,13 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	 */
 	err = protocol->get_clock_source(motu, &src);
 	if (err < 0)
-		return err;
+		goto err_locked;
 	if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
 	    amdtp_stream_pcm_running(&motu->tx_stream) ||
 	    amdtp_stream_pcm_running(&motu->rx_stream)) {
 		err = protocol->get_clock_rate(motu, &rate);
 		if (err < 0)
-			return err;
+			goto err_locked;
 		substream->runtime->hw.rate_min = rate;
 		substream->runtime->hw.rate_max = rate;
 	}
@@ -191,10 +195,18 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	mutex_unlock(&motu->mutex);
 
 	return err;
+err_locked:
+	mutex_unlock(&motu->mutex);
+	snd_motu_stream_lock_release(motu);
+	return err;
 }
 
 static int pcm_close(struct snd_pcm_substream *substream)
 {
+	struct snd_motu *motu = substream->private_data;
+
+	snd_motu_stream_lock_release(motu);
+
 	return 0;
 }
 
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 911d348..bd45802 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -341,3 +341,41 @@ void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
 	motu->playback_substreams = 0;
 	motu->capture_substreams = 0;
 }
+
+static void motu_lock_changed(struct snd_motu *motu)
+{
+	motu->dev_lock_changed = true;
+	wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_stream_lock_try(struct snd_motu *motu)
+{
+	int err;
+
+	spin_lock_irq(&motu->lock);
+
+	if (motu->dev_lock_count < 0) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	if (motu->dev_lock_count++ == 0)
+		motu_lock_changed(motu);
+	err = 0;
+out:
+	spin_unlock_irq(&motu->lock);
+	return err;
+}
+
+void snd_motu_stream_lock_release(struct snd_motu *motu)
+{
+	spin_lock_irq(&motu->lock);
+
+	if (WARN_ON(motu->dev_lock_count <= 0))
+		goto out;
+
+	if (--motu->dev_lock_count == 0)
+		motu_lock_changed(motu);
+out:
+	spin_unlock_irq(&motu->lock);
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index d4da137..619554b 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -109,6 +109,10 @@ static void do_registration(struct work_struct *work)
 			goto error;
 	}
 
+	err = snd_motu_create_hwdep_device(motu);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(motu->card);
 	if (err < 0)
 		goto error;
@@ -145,6 +149,7 @@ static int motu_probe(struct fw_unit *unit,
 
 	mutex_init(&motu->mutex);
 	spin_lock_init(&motu->lock);
+	init_waitqueue_head(&motu->hwdep_wait);
 
 	/* Allocate and register this sound card later. */
 	INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 338b351..407ce09 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -16,12 +16,15 @@
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/compat.h>
 
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
 #include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
 
 #include "../lib.h"
 #include "../amdtp-stream.h"
@@ -62,6 +65,11 @@ struct snd_motu {
 	/* For notification. */
 	struct fw_address_handler async_handler;
 	u32 msg;
+
+	/* For uapi */
+	int dev_lock_count;
+	bool dev_lock_changed;
+	wait_queue_head_t hwdep_wait;
 };
 
 enum snd_motu_spec_flags {
@@ -136,10 +144,14 @@ int snd_motu_stream_init_duplex(struct snd_motu *motu);
 void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
 int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
 void snd_motu_stream_stop_duplex(struct snd_motu *motu);
+int snd_motu_stream_lock_try(struct snd_motu *motu);
+void snd_motu_stream_lock_release(struct snd_motu *motu);
 
 void snd_motu_proc_init(struct snd_motu *motu);
 
 int snd_motu_create_pcm_devices(struct snd_motu *motu);
 
 int snd_motu_create_midi_devices(struct snd_motu *motu);
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu);
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 15/19] firewire-motu: enable to read transaction cache via hwdep interface
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (13 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 14/19] firewire-motu: add hwdep interface Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 16/19] firewire-motu: add support for MOTU 828 as a model with protocol version 1 Takashi Sakamoto
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU FireWire series can transfer messages to registered address. These
messages are transferred for the status of internal clock synchronization
just after starting streams.

When the synchronization is stable, it's 0x01ffffff. Else, it's 0x05ffffff.

This commit adds a functionality to receive the message. Currently, the
received message are output to system logging.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/firewire.h          |  7 +++++++
 sound/firewire/motu/motu-hwdep.c       | 10 ++++++++--
 sound/firewire/motu/motu-transaction.c | 20 ++++++++++++++++++++
 3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 59c6d81..29afc5e 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -10,6 +10,7 @@
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION	0xd1ce004e
 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE	0x4e617475
 #define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE	0x746e736c
+#define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION	0x64776479
 
 struct snd_firewire_event_common {
 	unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -46,12 +47,18 @@ struct snd_firewire_event_digi00x_message {
 	__u32 message;	/* Digi00x-specific message */
 };
 
+struct snd_firewire_event_motu_notification {
+	unsigned int type;
+	__u32 message;	/* MOTU-specific bits. */
+};
+
 union snd_firewire_event {
 	struct snd_firewire_event_common            common;
 	struct snd_firewire_event_lock_status       lock_status;
 	struct snd_firewire_event_dice_notification dice_notification;
 	struct snd_firewire_event_efw_response      efw_response;
 	struct snd_firewire_event_digi00x_message   digi00x_message;
+	struct snd_firewire_event_motu_notification motu_notification;
 };
 
 
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
index e795a52..b87ccb6 100644
--- a/sound/firewire/motu/motu-hwdep.c
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -26,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 
 	spin_lock_irq(&motu->lock);
 
-	while (!motu->dev_lock_changed) {
+	while (!motu->dev_lock_changed && motu->msg == 0) {
 		prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 		spin_unlock_irq(&motu->lock);
 		schedule();
@@ -43,6 +43,12 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 		motu->dev_lock_changed = false;
 
 		count = min_t(long, count, sizeof(event.lock_status));
+	} else {
+		event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
+		event.motu_notification.message = motu->msg;
+		motu->msg = 0;
+
+		count = min_t(long, count, sizeof(event.motu_notification));
 	}
 
 	spin_unlock_irq(&motu->lock);
@@ -62,7 +68,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 	poll_wait(file, &motu->hwdep_wait, wait);
 
 	spin_lock_irq(&motu->lock);
-	if (motu->dev_lock_changed)
+	if (motu->dev_lock_changed || motu->msg)
 		events = POLLIN | POLLRDNORM;
 	else
 		events = 0;
diff --git a/sound/firewire/motu/motu-transaction.c b/sound/firewire/motu/motu-transaction.c
index 416dd98..7fc3009 100644
--- a/sound/firewire/motu/motu-transaction.c
+++ b/sound/firewire/motu/motu-transaction.c
@@ -50,7 +50,27 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
 			   int generation, unsigned long long offset,
 			   void *data, size_t length, void *callback_data)
 {
+	struct snd_motu *motu = callback_data;
+	__be32 *buf = (__be32 *)data;
+	unsigned long flags;
+
+	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+		fw_send_response(card, request, RCODE_COMPLETE);
+		return;
+	}
+
+	if (offset != motu->async_handler.offset || length != 4) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	spin_lock_irqsave(&motu->lock, flags);
+	motu->msg = be32_to_cpu(*buf);
+	spin_unlock_irqrestore(&motu->lock, flags);
+
 	fw_send_response(card, request, RCODE_COMPLETE);
+
+	wake_up(&motu->hwdep_wait);
 }
 
 int snd_motu_transaction_reregister(struct snd_motu *motu)
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 16/19] firewire-motu: add support for MOTU 828 as a model with protocol version 1
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (14 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 15/19] firewire-motu: enable to read transaction cache via " Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 17/19] firewire-motu: add support for MOTU 828mk2 as a model with protocol version 2 Takashi Sakamoto
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU 828 is a first model in this series, produced in 2001. This model
consists of three chips:
 * TI TSB41AB1 (Physical layer for IEEE 1394 bus)
 * PDI 1394L21BE (Link layer for IEEE 1394 bus and packet processing layer)
 * QuickLogic DA828FW (Data block processing layer and digital signal
   processing)

This commit adds a support for this model, with its unique protocol as
version 1. The features of this protocol are:

 * no MIDI support.
 * Rx packets have no data chunks for control and status messages.
 * Tx packets have data chunks for control and status messages in the end
   of each data block.
 * All of settings are represented in bit flag in one quadlet address
   (0x'ffff'f000'0b00).
 * When optical interface is configured as S/PDIF, signals of the interface
   is multiplexed for packets, instead of signals of coaxial interface.
   Thus, differed part of data chunks is not used.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                 |   1 +
 sound/firewire/motu/Makefile           |   3 +-
 sound/firewire/motu/motu-protocol-v1.c | 204 +++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c             |   9 ++
 sound/firewire/motu/motu.h             |   2 +
 5 files changed, 218 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-protocol-v1.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 11a3285..a031b9c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -146,6 +146,7 @@ config SND_FIREWIRE_MOTU
 	select SND_HWDEP
 	help
 	 Say Y here to enable support for FireWire devices which MOTU produced:
+	  * 828
 
 	 To compile this driver as a module, choose M here: the module
 	 will be called snd-firewire-motu.
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index cc195d5..15090be 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,4 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
-			  motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o
+			  motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
+			  motu-protocol-v1.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c
new file mode 100644
index 0000000..1087f46
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v1.c
@@ -0,0 +1,204 @@
+/*
+ * motu-protocol-v1.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 V1_CLOCK_STATUS_OFFSET			0x0b00
+#define  V1_OPT_IN_IFACE_IS_SPDIF		0x00008000
+#define  V1_OPT_OUT_IFACE_IS_SPDIF		0x00004000
+#define  V1_FETCH_PCM_FRAMES			0x00000080
+#define  V1_CLOCK_SRC_IS_NOT_FROM_ADAT_DSUB	0x00000020
+#define  V1_CLOCK_RATE_BASED_ON_48000		0x00000004
+#define  V1_CLOCK_SRC_SPDIF_ON_OPT_OR_COAX	0x00000002
+#define  V1_CLOCK_SRC_ADAT_ON_OPT_OR_DSUB	0x00000001
+
+static int v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+	__be32 reg;
+	u32 data;
+	int index, err;
+
+	err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	if (data & V1_CLOCK_RATE_BASED_ON_48000)
+		index = 1;
+	else
+		index = 0;
+
+	*rate = snd_motu_clock_rates[index];
+
+	return 0;
+}
+
+static int v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+	__be32 reg;
+	u32 data;
+	int i, err;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		if (snd_motu_clock_rates[i] == rate)
+			break;
+	}
+	if (i == ARRAY_SIZE(snd_motu_clock_rates))
+		return -EINVAL;
+
+	err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	data &= ~V1_FETCH_PCM_FRAMES;
+	if (rate == 48000)
+		data |= V1_CLOCK_RATE_BASED_ON_48000;
+	else
+		data &= ~V1_CLOCK_RATE_BASED_ON_48000;
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+static int v1_get_clock_source(struct snd_motu *motu,
+			       enum snd_motu_clock_source *src)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+
+	data = be32_to_cpu(reg);
+	if (data & V1_CLOCK_SRC_ADAT_ON_OPT_OR_DSUB) {
+		if (data & V1_CLOCK_SRC_IS_NOT_FROM_ADAT_DSUB)
+			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+		else
+			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+	} else if (data & V1_CLOCK_SRC_SPDIF_ON_OPT_OR_COAX) {
+		if (data & V1_OPT_IN_IFACE_IS_SPDIF)
+			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+		else
+			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+	} else {
+		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+	}
+
+	return 0;
+}
+
+static int v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	if (enable)
+		data |= V1_FETCH_PCM_FRAMES;
+	else
+		data &= ~V1_FETCH_PCM_FRAMES;
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+				 enum amdtp_stream_direction dir,
+				 enum snd_motu_spec_flags flags,
+				 unsigned char analog_ports)
+{
+	unsigned char pcm_chunks[3] = {0, 0, 0};
+	int i;
+
+	if (dir == AMDTP_IN_STREAM)
+		formats->msg_chunks = 2;
+	else
+		formats->msg_chunks = 0;
+
+	pcm_chunks[0] = analog_ports;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+		pcm_chunks[1] = analog_ports;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+		pcm_chunks[2] = analog_ports;
+
+	pcm_chunks[0] += 2;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+		pcm_chunks[1] += 2;
+
+	for (i = 0; i < 3; ++i)
+		formats->fixed_part_pcm_chunks[i] = pcm_chunks[i];
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+				    enum snd_motu_spec_flags flags,
+				    u32 opt_iface_mode_data,
+				    u32 opt_iface_mode_mask)
+{
+	unsigned char pcm_chunks[3] = {0, 0, 0};
+	int i;
+
+	/* Packet includes PCM frames from ADAT on optical interface. */
+	if (!(opt_iface_mode_data & opt_iface_mode_mask)) {
+		pcm_chunks[0] += 8;
+		if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+			pcm_chunks[1] += 4;
+	}
+
+	for (i = 0; i < 3; ++i)
+		formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
+}
+
+static int v1_cache_packet_formats(struct snd_motu *motu)
+{
+	__be32 reg;
+	u32 opt_iface_mode_data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	opt_iface_mode_data = be32_to_cpu(reg);
+
+	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+			     motu->spec->flags, motu->spec->analog_in_ports);
+	calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
+				opt_iface_mode_data, V1_OPT_IN_IFACE_IS_SPDIF);
+
+	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+			     motu->spec->flags, motu->spec->analog_out_ports);
+	calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
+				opt_iface_mode_data, V1_OPT_OUT_IFACE_IS_SPDIF);
+
+	motu->tx_packet_formats.pcm_byte_offset = 4;
+	motu->rx_packet_formats.pcm_byte_offset = 4;
+
+	return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v1 = {
+	.get_clock_rate		= v1_get_clock_rate,
+	.set_clock_rate		= v1_set_clock_rate,
+	.get_clock_source	= v1_get_clock_source,
+	.switch_fetching_mode	= v1_switch_fetching_mode,
+	.cache_packet_formats	= v1_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 619554b..e9705e3 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -190,6 +190,14 @@ static void motu_bus_update(struct fw_unit *unit)
 	snd_motu_transaction_reregister(motu);
 }
 
+static struct snd_motu_spec motu_828orig = {
+	.name = "828",
+	.protocol = &snd_motu_protocol_v1,
+
+	.analog_in_ports = 8,
+	.analog_out_ports = 8,
+};
+
 #define SND_MOTU_DEV_ENTRY(model, data)			\
 {							\
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID |	\
@@ -202,6 +210,7 @@ static void motu_bus_update(struct fw_unit *unit)
 }
 
 static const struct ieee1394_device_id motu_id_table[] = {
+	SND_MOTU_DEV_ENTRY(0x102802, &motu_828orig),
 	{ }
 };
 MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 407ce09..d091f68 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -121,6 +121,8 @@ struct snd_motu_spec {
 	const struct snd_motu_protocol *const protocol;
 };
 
+extern const struct snd_motu_protocol snd_motu_protocol_v1;
+
 int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
 		    enum amdtp_stream_direction dir,
 		    const struct snd_motu_protocol *const protocol);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 17/19] firewire-motu: add support for MOTU 828mk2 as a model with protocol version 2
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (15 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 16/19] firewire-motu: add support for MOTU 828 as a model with protocol version 1 Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 18/19] firewire-lib: add a quirk of packet without valid EOH in CIP format Takashi Sakamoto
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU 828mk2 is one of second generation in MOTU FireWire series, produced in
2003. This model consists of four chips:
 * TI TSB41AB2 (Physical layer for IEEE 1394 bus)
 * PDI 1394L40BE (Link layer for IEEE 1394 bus and packet processing layer)
 * ALTERA ACEX 1K EP1K30 Series FPGA (Data block processing layer)
 * TI TMS320VC5402 (Digital signal processing)

This commit adds a support for this model, with its unique protocol as
version 2. The feature of this protocol are:

 * Support data chunks for status and control messages for both
   directions.
 * Support a pair of MIDI input/output.
 * Support a data chunk for mic/instrument independent of analog line in.
 * Support a data chunk for return capture.
 * Support independent data chunks for S/PDIF of both optical/coaxial
   interfaces.
 * Support independent data chunks for each of main out and phone out.

Status of clock is configured by write transactions to 0x'ffff'f000'0b14.
Modes of optical interfaces are configured by write transactions to
0x'ffff'f000'0c04.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                 |   1 +
 sound/firewire/motu/Makefile           |   2 +-
 sound/firewire/motu/motu-protocol-v2.c | 237 +++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c             |  14 ++
 sound/firewire/motu/motu.h             |   1 +
 5 files changed, 254 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-protocol-v2.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index a031b9c..1053a1b 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -147,6 +147,7 @@ config SND_FIREWIRE_MOTU
 	help
 	 Say Y here to enable support for FireWire devices which MOTU produced:
 	  * 828
+	  * 828mk2
 
 	 To compile this driver as a module, choose M here: the module
 	 will be called snd-firewire-motu.
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 15090be..d3b9abc 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,4 +1,4 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
 			  motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
-			  motu-protocol-v1.o
+			  motu-protocol-v1.o motu-protocol-v2.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
new file mode 100644
index 0000000..05b5d287
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -0,0 +1,237 @@
+/*
+ * motu-protocol-v2.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 V2_CLOCK_STATUS_OFFSET			0x0b14
+#define  V2_CLOCK_RATE_MASK			0x00000038
+#define  V2_CLOCK_RATE_SHIFT			3
+#define  V2_CLOCK_SRC_MASK			0x00000007
+#define  V2_CLOCK_SRC_SHIFT			0
+
+#define V2_IN_OUT_CONF_OFFSET			0x0c04
+#define  V2_OPT_OUT_IFACE_MASK			0x00000c00
+#define  V2_OPT_OUT_IFACE_SHIFT			10
+#define  V2_OPT_IN_IFACE_MASK			0x00000300
+#define  V2_OPT_IN_IFACE_SHIFT			8
+#define  V2_OPT_IFACE_MODE_NONE			0
+#define  V2_OPT_IFACE_MODE_ADAT			1
+#define  V2_OPT_IFACE_MODE_SPDIF		2
+
+static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+	__be32 reg;
+	unsigned int index;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+
+	index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
+	if (index >= ARRAY_SIZE(snd_motu_clock_rates))
+		return -EIO;
+
+	*rate = snd_motu_clock_rates[index];
+
+	return 0;
+}
+
+static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+	__be32 reg;
+	u32 data;
+	int i;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		if (snd_motu_clock_rates[i] == rate)
+			break;
+	}
+	if (i == ARRAY_SIZE(snd_motu_clock_rates))
+		return -EINVAL;
+
+	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	data &= ~V2_CLOCK_RATE_MASK;
+	data |= i << V2_CLOCK_RATE_SHIFT;
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+static int v2_get_clock_source(struct snd_motu *motu,
+			       enum snd_motu_clock_source *src)
+{
+	__be32 reg;
+	unsigned int index;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+
+	index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
+	if (index > 5)
+		return -EIO;
+
+	/* To check the configuration of optical interface. */
+	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+
+	switch (index) {
+	case 0:
+		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+		break;
+	case 1:
+		if (be32_to_cpu(reg) & 0x00000200)
+			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+		else
+			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+		break;
+	case 2:
+		*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+		break;
+	case 4:
+		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+		break;
+	case 5:
+		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+	/* V2 protocol doesn't have this feature. */
+	return 0;
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+				 enum amdtp_stream_direction dir,
+				 enum snd_motu_spec_flags flags,
+				 unsigned char analog_ports)
+{
+	unsigned char pcm_chunks[3] = {0, 0, 0};
+
+	formats->msg_chunks = 2;
+
+	pcm_chunks[0] = analog_ports;
+	pcm_chunks[1] = analog_ports;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+		pcm_chunks[2] = analog_ports;
+
+	if (dir == AMDTP_IN_STREAM) {
+		if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
+			pcm_chunks[0] += 2;
+			pcm_chunks[1] += 2;
+		}
+		if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
+			pcm_chunks[0] += 2;
+			pcm_chunks[1] += 2;
+		}
+	} else {
+		/*
+		 * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
+		 */
+		pcm_chunks[0] += 4;
+		pcm_chunks[1] += 4;
+	}
+
+	/*
+	 * All of v2 models have a pair of coaxial interfaces for digital in/out
+	 * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
+	 * ports.
+	 */
+	pcm_chunks[0] += 2;
+	pcm_chunks[1] += 2;
+
+	/* This part should be multiples of 4. */
+	formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
+	formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+		formats->fixed_part_pcm_chunks[2] =
+					round_up(2 + pcm_chunks[2], 4) - 2;
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+				    enum snd_motu_spec_flags flags,
+				    u32 data, u32 mask, u32 shift)
+{
+	unsigned char pcm_chunks[3] = {0, 0};
+
+	/*
+	 * When optical interfaces are configured for S/PDIF (TOSLINK),
+	 * the above PCM frames come from them, instead of coaxial
+	 * interfaces.
+	 */
+	data = (data & mask) >> shift;
+	if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
+	    data == V2_OPT_IFACE_MODE_ADAT) {
+		pcm_chunks[0] += 8;
+		pcm_chunks[1] += 4;
+	}
+
+	/* At mode x4, no data chunks are supported in this part. */
+	formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
+	formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
+}
+
+static int v2_cache_packet_formats(struct snd_motu *motu)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+			     motu->spec->flags, motu->spec->analog_in_ports);
+	calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
+			data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
+
+	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+			     motu->spec->flags, motu->spec->analog_out_ports);
+	calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
+			data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
+
+	motu->tx_packet_formats.midi_flag_offset = 4;
+	motu->tx_packet_formats.midi_byte_offset = 6;
+	motu->tx_packet_formats.pcm_byte_offset = 10;
+
+	motu->rx_packet_formats.midi_flag_offset = 4;
+	motu->rx_packet_formats.midi_byte_offset = 6;
+	motu->rx_packet_formats.pcm_byte_offset = 10;
+
+	return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v2 = {
+	.get_clock_rate		= v2_get_clock_rate,
+	.set_clock_rate		= v2_set_clock_rate,
+	.get_clock_source	= v2_get_clock_source,
+	.switch_fetching_mode	= v2_switch_fetching_mode,
+	.cache_packet_formats	= v2_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index e9705e3..d8dada1 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -198,6 +198,19 @@ static struct snd_motu_spec motu_828orig = {
 	.analog_out_ports = 8,
 };
 
+static struct snd_motu_spec motu_828mk2 = {
+	.name = "828mk2",
+	.protocol = &snd_motu_protocol_v2,
+	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
+		 SND_MOTU_SPEC_TX_MICINST_CHUNK |
+		 SND_MOTU_SPEC_TX_RETURN_CHUNK |
+		 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
+		 SND_MOTU_SPEC_HAS_MIDI,
+
+	.analog_in_ports = 8,
+	.analog_out_ports = 8,
+};
+
 #define SND_MOTU_DEV_ENTRY(model, data)			\
 {							\
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID |	\
@@ -211,6 +224,7 @@ static struct snd_motu_spec motu_828orig = {
 
 static const struct ieee1394_device_id motu_id_table[] = {
 	SND_MOTU_DEV_ENTRY(0x102802, &motu_828orig),
+	SND_MOTU_DEV_ENTRY(0x101800, &motu_828mk2),
 	{ }
 };
 MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index d091f68..65585e7 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -122,6 +122,7 @@ struct snd_motu_spec {
 };
 
 extern const struct snd_motu_protocol snd_motu_protocol_v1;
+extern const struct snd_motu_protocol snd_motu_protocol_v2;
 
 int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
 		    enum amdtp_stream_direction dir,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 18/19] firewire-lib: add a quirk of packet without valid EOH in CIP format
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (16 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 17/19] firewire-motu: add support for MOTU 828mk2 as a model with protocol version 2 Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29  3:54 ` [PATCH 19/19] firewire-motu: add support for MOTU 828mk3 (FireWire/Hybrid) as a model with protocol version 3 Takashi Sakamoto
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In IEC 61883-1, when two quadlets CIP header is used, the top most bit in
second CIP header stands. However, packets from units with MOTU protocol
version 3 have a quirk without this flag. Current packet streaming layer
handles this as protocol error.

This commit adds a new enumeration constant for this quirk, to handle MOTU
protocol version 3.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp-stream.c | 5 +++--
 sound/firewire/amdtp-stream.h | 3 +++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index f9d12f4..112ad03 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -480,8 +480,9 @@ static int handle_in_packet(struct amdtp_stream *s,
 	 * This module supports 'Two-quadlet CIP header with SYT field'.
 	 * For convenience, also check FMT field is AM824 or not.
 	 */
-	if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
-	    ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+	if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+	     ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+	    (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
 		dev_info_ratelimited(&s->unit->device,
 				"Invalid CIP header for AMDTP: %08X:%08X\n",
 				cip_header[0], cip_header[1]);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index d2a3163..a31dfd8 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -29,6 +29,8 @@
  * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
  *	packet is larger than IEC 61883-6 defines. Current implementation
  *	allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ *	valid EOH.
  */
 enum cip_flags {
 	CIP_NONBLOCKING		= 0x00,
@@ -39,6 +41,7 @@ enum cip_flags {
 	CIP_SKIP_DBC_ZERO_CHECK	= 0x10,
 	CIP_EMPTY_HAS_WRONG_DBC	= 0x20,
 	CIP_JUMBO_PAYLOAD	= 0x40,
+	CIP_HEADER_WITHOUT_EOH	= 0x80,
 };
 
 /**
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 19/19] firewire-motu: add support for MOTU 828mk3 (FireWire/Hybrid) as a model with protocol version 3
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (17 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 18/19] firewire-lib: add a quirk of packet without valid EOH in CIP format Takashi Sakamoto
@ 2017-01-29  3:54 ` Takashi Sakamoto
  2017-01-29 13:22 ` [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Jonathan Woithe
  2017-01-29 13:34 ` [FFADO-devel] " Jonathan Woithe
  20 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-29  3:54 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

MOTU 828mk3 (FireWire/Hybrid) is one of third generation in MOTU FireWire
series, produced in 2008/2014. This model consists of three chips for
functionality on IEEE 1394 bus:

 * TI TSB41AB2 (Physical layer for IEEE 1394 bus)
 * Xilinx Spartan-3E FPGA Family (Link layer for IEEE 1394 bus, packet
   processing and data block processing layer)
 * TI TMS320C6722 (Digital signal processing)

This commit adds a support for this model, with its unique protocol as
version 3. This protocol has some additional features to protocol
version 2.

 * Support several optical interfaces.
 * Support a data chunk for return of reverb effect.
 * Have a quirk of tx packets.
 * Support heartbeat asynchronous transaction.

In this protocol, series of transferred packets has some quirks. Below
fields in CIP headers of the packets are out of IEC 61883-1:
 - SID (source node id): always 0x0d
 - DBS (data block size): always always 0x04
 - DBC (data block counter): always 0x00
 - EOH (End of header): always 0x00

Below is an actual sample of transferred packets.

quads CIP1       CIP2
520   0x0D040400 0x22FFFFFF
  8   0x0D040400 0x22FFFFFF
520   0x0D040400 0x22FFFFFF
520   0x0D040400 0x22FFFFFF
  8   0x0D040400 0x22FFFFFF

Status of clock is configured by write transactions to 0x'ffff'f000'0b14,
as well as version 2, while meanings of fields are different. Modes of
optical interfaces are configured by write transactions to
0x'ffff'f000'0c94.

Drivers can register its address to receive the heatbeat transactions.
0x'ffff'f000'0b0c is for the higher part and 0x'ffff'f000'0b10 is for the
lower part. Nevertheless, this feature is not useless for this driver and
this commit ommits it.

Each data block consists of two parts in a point of the number of included
data chunks. In both of 'fixed' and 'differed' parts, the number of
included data blocks are a multiple of 4, thus depending on models there's
some empty data chunks. For example, 828mk3 includes one pair of empty
data chunks in its fixed part. When optical interface is configured to
S/PDIF, 828mk3 includes one pair of empty data chunks in its differed part.
To reduce consumption of CPU cycles with additional conditions/loops, this
commit just exposes these empty chunks to user space as PCM channels.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                 |   1 +
 sound/firewire/motu/Makefile           |   3 +-
 sound/firewire/motu/amdtp-motu.c       |  12 ++
 sound/firewire/motu/motu-protocol-v3.c | 312 +++++++++++++++++++++++++++++++++
 sound/firewire/motu/motu.c             |  18 ++
 sound/firewire/motu/motu.h             |   1 +
 6 files changed, 346 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/motu/motu-protocol-v3.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 1053a1b..2a91fe8 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -148,6 +148,7 @@ config SND_FIREWIRE_MOTU
 	 Say Y here to enable support for FireWire devices which MOTU produced:
 	  * 828
 	  * 828mk2
+	  * 828mk3
 
 	 To compile this driver as a module, choose M here: the module
 	 will be called snd-firewire-motu.
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index d3b9abc..c5d34df 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,4 +1,5 @@
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
 			  motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
-			  motu-protocol-v1.o motu-protocol-v2.o
+			  motu-protocol-v1.o motu-protocol-v2.o \
+			  motu-protocol-v3.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 0930cd8..08bd176 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -11,6 +11,7 @@
 #include "motu.h"
 
 #define CIP_FMT_MOTU		0x02
+#define CIP_FMT_MOTU_TX_V3	0x22
 #define MOTU_FDF_AM824		0x22
 
 /*
@@ -359,6 +360,17 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
 
 	if (dir == AMDTP_IN_STREAM) {
 		process_data_blocks = process_tx_data_blocks;
+
+		/*
+		 * Units of version 3 transmits packets with invalid CIP header
+		 * against IEC 61883-1.
+		 */
+		if (protocol == &snd_motu_protocol_v3) {
+			flags |= CIP_WRONG_DBS |
+				 CIP_SKIP_DBC_ZERO_CHECK |
+				 CIP_HEADER_WITHOUT_EOH;
+			fmt = CIP_FMT_MOTU_TX_V3;
+		}
 	} else {
 		process_data_blocks = process_rx_data_blocks;
 		flags |= CIP_DBC_IS_END_EVENT;
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
new file mode 100644
index 0000000..b463da9
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -0,0 +1,312 @@
+/*
+ * motu-protocol-v3.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 <linux/delay.h>
+#include "motu.h"
+
+#define V3_CLOCK_STATUS_OFFSET		0x0b14
+#define  V3_FETCH_PCM_FRAMES		0x02000000
+#define  V3_CLOCK_RATE_MASK		0x0000ff00
+#define  V3_CLOCK_RATE_SHIFT		8
+#define  V3_CLOCK_SOURCE_MASK		0x000000ff
+#define  V3_CLOCK_SOURCE_SHIFT		8
+
+#define V3_OPT_IFACE_MODE_OFFSET	0x0c94
+#define  V3_ENABLE_OPT_IN_IFACE_A	0x00000001
+#define  V3_ENABLE_OPT_IN_IFACE_B	0x00000002
+#define  V3_ENABLE_OPT_OUT_IFACE_A	0x00000100
+#define  V3_ENABLE_OPT_OUT_IFACE_B	0x00000200
+#define  V3_NO_ADAT_OPT_IN_IFACE_A	0x00010000
+#define  V3_NO_ADAT_OPT_IN_IFACE_B	0x00100000
+#define  V3_NO_ADAT_OPT_OUT_IFACE_A	0x00040000
+#define  V3_NO_ADAT_OPT_OUT_IFACE_B	0x00400000
+
+static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
+	if (data >= ARRAY_SIZE(snd_motu_clock_rates))
+		return -EIO;
+
+	*rate = snd_motu_clock_rates[data];
+
+	return 0;
+}
+
+static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+	__be32 reg;
+	u32 data;
+	bool need_to_wait;
+	int i, err;
+
+	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+		if (snd_motu_clock_rates[i] == rate)
+			break;
+	}
+	if (i == ARRAY_SIZE(snd_motu_clock_rates))
+		return -EINVAL;
+
+	err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
+	data |= i << V3_CLOCK_RATE_SHIFT;
+
+	need_to_wait = data != be32_to_cpu(reg);
+
+	reg = cpu_to_be32(data);
+	err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					 sizeof(reg));
+	if (err < 0)
+		return err;
+
+	if (need_to_wait) {
+		/* Cost expensive. */
+		if (msleep_interruptible(4000) > 0)
+			return -EINTR;
+	}
+
+	return 0;
+}
+
+static int v3_get_clock_source(struct snd_motu *motu,
+			       enum snd_motu_clock_source *src)
+{
+	__be32 reg;
+	u32 data;
+	unsigned int val;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	val = (data & V3_CLOCK_SOURCE_MASK) >> V3_CLOCK_SOURCE_SHIFT;
+	if (val == 0x00) {
+		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+	} else if (val == 0x01) {
+		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+	} else if (val == 0x10) {
+		*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+	} else if (val == 0x18 || val == 0x19) {
+		err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
+						&reg, sizeof(reg));
+		if (err < 0)
+			return err;
+		data = be32_to_cpu(reg);
+
+		if (val == 0x18) {
+			if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
+				*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
+			else
+				*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
+		} else {
+			if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
+				*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
+			else
+				*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
+		}
+	} else {
+		*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+	}
+
+	return 0;
+}
+
+static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return 0;
+	data = be32_to_cpu(reg);
+
+	if (enable)
+		data |= V3_FETCH_PCM_FRAMES;
+	else
+		data &= ~V3_FETCH_PCM_FRAMES;
+
+	reg = cpu_to_be32(data);
+	return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+					  sizeof(reg));
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+				 enum amdtp_stream_direction dir,
+				 enum snd_motu_spec_flags flags,
+				 unsigned char analog_ports)
+{
+	unsigned char pcm_chunks[3] = {0, 0, 0};
+
+	formats->msg_chunks = 2;
+
+	pcm_chunks[0] = analog_ports;
+	pcm_chunks[1] = analog_ports;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+		pcm_chunks[2] = analog_ports;
+
+	if (dir == AMDTP_IN_STREAM) {
+		if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
+			pcm_chunks[0] += 2;
+			pcm_chunks[1] += 2;
+			if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+				pcm_chunks[2] += 2;
+		}
+
+		if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
+			pcm_chunks[0] += 2;
+			pcm_chunks[1] += 2;
+			if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+				pcm_chunks[2] += 2;
+		}
+
+		if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
+			pcm_chunks[0] += 2;
+			pcm_chunks[1] += 2;
+		}
+	} else {
+		/*
+		 * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
+		 */
+		pcm_chunks[0] += 4;
+		pcm_chunks[1] += 4;
+	}
+
+	/*
+	 * At least, packets have two data chunks for S/PDIF on coaxial
+	 * interface.
+	 */
+	pcm_chunks[0] += 2;
+	pcm_chunks[1] += 2;
+
+	/*
+	 * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
+	 * a result, this part can includes empty data chunks.
+	 */
+	formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
+	formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
+	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+		formats->fixed_part_pcm_chunks[2] =
+					round_up(2 + pcm_chunks[2], 4) - 2;
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+				    enum snd_motu_spec_flags flags, u32 data,
+				    u32 a_enable_mask, u32 a_no_adat_mask,
+				    u32 b_enable_mask, u32 b_no_adat_mask)
+{
+	unsigned char pcm_chunks[3] = {0, 0, 0};
+	int i;
+
+	if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
+		if (data & a_no_adat_mask) {
+			/*
+			 * Additional two data chunks for S/PDIF on optical
+			 * interface A. This includes empty data chunks.
+			 */
+			pcm_chunks[0] += 4;
+			pcm_chunks[1] += 4;
+		} else {
+			/*
+			 * Additional data chunks for ADAT on optical interface
+			 * A.
+			 */
+			pcm_chunks[0] += 8;
+			pcm_chunks[1] += 4;
+		}
+	}
+
+	if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
+		if (data & b_no_adat_mask) {
+			/*
+			 * Additional two data chunks for S/PDIF on optical
+			 * interface B. This includes empty data chunks.
+			 */
+			pcm_chunks[0] += 4;
+			pcm_chunks[1] += 4;
+		} else {
+			/*
+			 * Additional data chunks for ADAT on optical interface
+			 * B.
+			 */
+			pcm_chunks[0] += 8;
+			pcm_chunks[1] += 4;
+		}
+	}
+
+	for (i = 0; i < 3; ++i) {
+		if (pcm_chunks[i] > 0)
+			pcm_chunks[i] = round_up(pcm_chunks[i], 4);
+
+		formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
+	}
+}
+
+static int v3_cache_packet_formats(struct snd_motu *motu)
+{
+	__be32 reg;
+	u32 data;
+	int err;
+
+	err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
+					sizeof(reg));
+	if (err < 0)
+		return err;
+	data = be32_to_cpu(reg);
+
+	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+			     motu->spec->flags, motu->spec->analog_in_ports);
+	calculate_differed_part(&motu->tx_packet_formats,
+			motu->spec->flags, data,
+			V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
+			V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
+
+	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+			     motu->spec->flags, motu->spec->analog_out_ports);
+	calculate_differed_part(&motu->rx_packet_formats,
+			motu->spec->flags, data,
+			V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
+			V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
+
+	motu->tx_packet_formats.midi_flag_offset = 8;
+	motu->tx_packet_formats.midi_byte_offset = 7;
+	motu->tx_packet_formats.pcm_byte_offset = 10;
+
+	motu->rx_packet_formats.midi_flag_offset = 8;
+	motu->rx_packet_formats.midi_byte_offset = 7;
+	motu->rx_packet_formats.pcm_byte_offset = 10;
+
+	return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v3 = {
+	.get_clock_rate		= v3_get_clock_rate,
+	.set_clock_rate		= v3_set_clock_rate,
+	.get_clock_source	= v3_get_clock_source,
+	.switch_fetching_mode	= v3_switch_fetching_mode,
+	.cache_packet_formats	= v3_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index d8dada1..9399a03 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -211,6 +211,22 @@ static struct snd_motu_spec motu_828mk2 = {
 	.analog_out_ports = 8,
 };
 
+static struct snd_motu_spec motu_828mk3 = {
+	.name = "828mk3",
+	.protocol = &snd_motu_protocol_v3,
+	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
+		 SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
+		 SND_MOTU_SPEC_TX_MICINST_CHUNK |
+		 SND_MOTU_SPEC_TX_RETURN_CHUNK |
+		 SND_MOTU_SPEC_TX_REVERB_CHUNK |
+		 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
+		 SND_MOTU_SPEC_HAS_OPT_IFACE_B |
+		 SND_MOTU_SPEC_HAS_MIDI,
+
+	.analog_in_ports = 8,
+	.analog_out_ports = 8,
+};
+
 #define SND_MOTU_DEV_ENTRY(model, data)			\
 {							\
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID |	\
@@ -225,6 +241,8 @@ static struct snd_motu_spec motu_828mk2 = {
 static const struct ieee1394_device_id motu_id_table[] = {
 	SND_MOTU_DEV_ENTRY(0x102802, &motu_828orig),
 	SND_MOTU_DEV_ENTRY(0x101800, &motu_828mk2),
+	SND_MOTU_DEV_ENTRY(0x106800, &motu_828mk3),	/* FireWire only. */
+	SND_MOTU_DEV_ENTRY(0x100800, &motu_828mk3),	/* Hybrid. */
 	{ }
 };
 MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 65585e7..d227afb 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -123,6 +123,7 @@ struct snd_motu_spec {
 
 extern const struct snd_motu_protocol snd_motu_protocol_v1;
 extern const struct snd_motu_protocol snd_motu_protocol_v2;
+extern const struct snd_motu_protocol snd_motu_protocol_v3;
 
 int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
 		    enum amdtp_stream_direction dir,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [FFADO-devel] [PATCH 08/19] firewire-motu: add MOTU specific protocol layer
  2017-01-29  3:54 ` [PATCH 08/19] firewire-motu: add MOTU specific protocol layer Takashi Sakamoto
@ 2017-01-29 13:16   ` Jonathan Woithe
  2017-01-30  3:50     ` Takashi Sakamoto
  0 siblings, 1 reply; 28+ messages in thread
From: Jonathan Woithe @ 2017-01-29 13:16 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Sun, Jan 29, 2017 at 12:54:06PM +0900, Takashi Sakamoto wrote:
> This commit adds data block processing layer specific for the MOTU
> protocol. The remarkable point is the way to generate SPH header. Time
> stamps for each data blocks are generated by below calculation:
> 
>  * Using pre-computed table for the number of ticks per event
>   *  44,1kHz: (557 + 123/441)
>   *  48.0kHz: (512 +   0/441)
>   *  88.2kHz: (278 + 282/441)
>   *  96.0kHz: (256 +   0/441)
>   * 176.4kHz: (139 + 141/441)
>   * 192.0kHz: (128 +   0/441)
>  * Accumulate the ticks and set the value to SPH for every events.
>  * This way makes sense only for blocking transmission because this mode
>    transfers fixed number or none of events.
> 
> This calculation assumes that each data block has a PCM frame which is
> sampled according to event timing clock. Current packet streaming layer
> has the same assumption.
> 
> Although this sequence works fine for MOTU FireWire series at sampling
> transmission frequency based on 48.0kHz, it is not enough at the frequency
> based on 44.1kHz. The units generate choppy noise every a few seconds.

Using a fixed number of ticks per event is not guaranteed to work at any
sample rate for MOTU interfaces.  At 48 kHz (and multiples thereof) one
obviously has even divisibility but the fractional accumulation allows for
the irregular 44.1 kHz rates.  For any sample rate, use of fixed tick
intervals will only be stable for devices whose audio clock generator
happens to be very close to its nameplate frequency relative to the firewire
iso cycle timer.  This is because MOTU decouple the audio clock from the
tick frequency.  You could certainly get devices where the clocks are very
well matched but it's really pot luck.  Ultimately all devices will
eventually experience discontinuites due to the unlocked clocks.

The only apparent way to maintain synchronisation with the playback packets
over hours is to make use of the timestamp fields returned by incoming audio
packets from the interface (other operating systems unsurprisingly appear to
take this approach).  The difference between successive timestamps (or the
received timestamps with an offset added) can be used to synthesise the
outgoing timestamps.  Because the SPH timestamps in the incoming packets are
guaranteed to synchronised with the audio clock an outgoing stream whose SPH
timestamp is synthesised with the differences will likewise maintain sync
indefinitely.  I wrote a proof-of-concept test illustrating this method of
SPH timestamp generation for MOTU interfaces back in 2006; stable operation
(no audio discontinuities) across all sample rates was observed for periods
exceeding 24 hours.

One can observe the unlocked nature of the SPH timestamps even at 48 kHz (I
have personally seen it with 828, 828mk2, Traveler, 896HD devices).  As
shown in the pre-computed table, for locked clocks one would expect 512
ticks per event at 48 kHz.  While 512 is certainly the dominant difference,
one does not have to wait long before seeing a difference of 511 or 513
(depending on whether the audio clock runs fast or slow relative to the iso
cycle timer).

Regards
  jonathan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (18 preceding siblings ...)
  2017-01-29  3:54 ` [PATCH 19/19] firewire-motu: add support for MOTU 828mk3 (FireWire/Hybrid) as a model with protocol version 3 Takashi Sakamoto
@ 2017-01-29 13:22 ` Jonathan Woithe
  2017-01-30  3:09   ` Takashi Sakamoto
  2017-01-29 13:34 ` [FFADO-devel] " Jonathan Woithe
  20 siblings, 1 reply; 28+ messages in thread
From: Jonathan Woithe @ 2017-01-29 13:22 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Sun, Jan 29, 2017 at 12:53:58PM +0900, Takashi Sakamoto wrote:
> However, this module cannot handle 828 correctly to generate sound. The
> reason is not clear yet.

There is something odd about the 828 - I have observed this with FFADO. 
While capture works fine, playback remains resolutely muted.  I have
explored various avenues over the last couple of years but have not yet been
able to identify the cause.  As time allows I continue to work on the task.

Regards
  jonathan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series
  2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
                   ` (19 preceding siblings ...)
  2017-01-29 13:22 ` [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Jonathan Woithe
@ 2017-01-29 13:34 ` Jonathan Woithe
  2017-01-30  3:17   ` Takashi Sakamoto
  20 siblings, 1 reply; 28+ messages in thread
From: Jonathan Woithe @ 2017-01-29 13:34 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Sun, Jan 29, 2017 at 12:53:58PM +0900, Takashi Sakamoto wrote:
> This patchset updates a part of my previous RFC, just for MOTU FireWire
> series.
> 
> [RFC][PATCH 00/37] ALSA: firewire: support AMDTP variants
> http://mailman.alsa-project.org/pipermail/alsa-devel/2015-July/094789.html
> 
> This patchset adds support for a part of MOTU FireWire series with their
> functionality of packet streaming. Below models are newly supported:
>  - 828
>  - 828mk2
>  - 828mk3 (FireWire/Hybrid)

I like the general architecture of this driver.

It's not clear to me whether you have utilised some information about these
devices from FFADO or have duplicated the protocol reverse engineering
process yourself.  If the former it would be nice to include a brief
acknowlegement since the FFADO MOTU protocol documents and code represents a
large body of work by several people which brought an otherwise undocumented
protocol into the open.  If the latter then clearly it's a moot point.

Regards
  jonathan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series
  2017-01-29 13:22 ` [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Jonathan Woithe
@ 2017-01-30  3:09   ` Takashi Sakamoto
  0 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-30  3:09 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Jan 29 2017 22:22, Jonathan Woithe wrote:
> On Sun, Jan 29, 2017 at 12:53:58PM +0900, Takashi Sakamoto wrote:
>> However, this module cannot handle 828 correctly to generate sound. The
>> reason is not clear yet.
>
> There is something odd about the 828 - I have observed this with FFADO.
> While capture works fine, playback remains resolutely muted.

Not resolutely. With this module, in the beginning of packet streaming, 
the unit can generate sound for quite a short period.

Currently I have a hypothesis that this issue comes from missing 
synchronization on a path between PDI 1394L21BE and QuickLogic DA828FW. 
As the name implies, the latter IC is designed by S&S research Inc. just 
for this unit, and there's less information.

Don't behave with your over-generalization, if being a reasonable person.


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series
  2017-01-29 13:34 ` [FFADO-devel] " Jonathan Woithe
@ 2017-01-30  3:17   ` Takashi Sakamoto
  0 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-30  3:17 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Jan 29 2017 22:34, Jonathan Woithe wrote:
> On Sun, Jan 29, 2017 at 12:53:58PM +0900, Takashi Sakamoto wrote:
>> This patchset updates a part of my previous RFC, just for MOTU FireWire
>> series.
>>
>> [RFC][PATCH 00/37] ALSA: firewire: support AMDTP variants
>> http://mailman.alsa-project.org/pipermail/alsa-devel/2015-July/094789.html
>>
>> This patchset adds support for a part of MOTU FireWire series with their
>> functionality of packet streaming. Below models are newly supported:
>>  - 828
>>  - 828mk2
>>  - 828mk3 (FireWire/Hybrid)
>
> I like the general architecture of this driver.
>
> It's not clear to me whether you have utilised some information about these
> devices from FFADO or have duplicated the protocol reverse engineering
> process yourself.  If the former it would be nice to include a brief
> acknowlegement since the FFADO MOTU protocol documents and code represents a
> large body of work by several people which brought an otherwise undocumented
> protocol into the open.  If the latter then clearly it's a moot point.

If you had enough time to read codes in this patchset carefully and have 
enough knowledge about each layer related to packet streaming and IEEE 
1394 bus, you wouldn't have such opinion for this patchset.


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 08/19] firewire-motu: add MOTU specific protocol layer
  2017-01-29 13:16   ` [FFADO-devel] " Jonathan Woithe
@ 2017-01-30  3:50     ` Takashi Sakamoto
  2017-01-30  5:04       ` Jonathan Woithe
  0 siblings, 1 reply; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-30  3:50 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Jan 29 2017 22:16, Jonathan Woithe wrote:
> On Sun, Jan 29, 2017 at 12:54:06PM +0900, Takashi Sakamoto wrote:
>> This commit adds data block processing layer specific for the MOTU
>> protocol. The remarkable point is the way to generate SPH header. Time
>> stamps for each data blocks are generated by below calculation:
>>
>>  * Using pre-computed table for the number of ticks per event
>>   *  44,1kHz: (557 + 123/441)
>>   *  48.0kHz: (512 +   0/441)
>>   *  88.2kHz: (278 + 282/441)
>>   *  96.0kHz: (256 +   0/441)
>>   * 176.4kHz: (139 + 141/441)
>>   * 192.0kHz: (128 +   0/441)
>>  * Accumulate the ticks and set the value to SPH for every events.
>>  * This way makes sense only for blocking transmission because this mode
>>    transfers fixed number or none of events.
>>
>> This calculation assumes that each data block has a PCM frame which is
>> sampled according to event timing clock. Current packet streaming layer
>> has the same assumption.
>>
>> Although this sequence works fine for MOTU FireWire series at sampling
>> transmission frequency based on 48.0kHz, it is not enough at the frequency
>> based on 44.1kHz. The units generate choppy noise every a few seconds.
>
> Using a fixed number of ticks per event is not guaranteed to work at any
> sample rate for MOTU interfaces.  At 48 kHz (and multiples thereof) one
> obviously has even divisibility but the fractional accumulation allows for
> the irregular 44.1 kHz rates.  For any sample rate, use of fixed tick
> intervals will only be stable for devices whose audio clock generator
> happens to be very close to its nameplate frequency relative to the firewire
> iso cycle timer.  This is because MOTU decouple the audio clock from the
> tick frequency.  You could certainly get devices where the clocks are very
> well matched but it's really pot luck.  Ultimately all devices will
> eventually experience discontinuites due to the unlocked clocks.

You have a wrong idea to mix up sampling clock, sampling transfer 
frequency in packet streaming layer and time stamps of SPH in data block 
processing layer. In MOTU FireWire series, sampling clock and the other 
clocks seem to be independent. Unless, the system could not get correct 
sequence of PCM frames in tx packets.

> The only apparent way to maintain synchronisation with the playback packets
> over hours is to make use of the timestamp fields returned by incoming audio
> packets from the interface (other operating systems unsurprisingly appear to
> take this approach).  The difference between successive timestamps (or the
> received timestamps with an offset added) can be used to synthesise the
> outgoing timestamps.  Because the SPH timestamps in the incoming packets are
> guaranteed to synchronised with the audio clock an outgoing stream whose SPH
> timestamp is synthesised with the differences will likewise maintain sync
> indefinitely.  I wrote a proof-of-concept test illustrating this method of
> SPH timestamp generation for MOTU interfaces back in 2006; stable operation
> (no audio discontinuities) across all sample rates was observed for periods
> exceeding 24 hours.

This is denied by a fact that I can get different sequence of SPH in 
transmitted packets from the unit when this module puts different 
sequence of SPH to packets to the unit with code modification of this 
module.

In short, the sequence of SPH in tx packets from the unit is dependent 
of the sequence of SPH in rx packets to the unit. Furthermore, this is 
always consistent regardless of selected signals for source of sampling 
clock.

Drivers don't necessarily need to pick up the sequence of SPH from tx 
packets for rx packets.

> One can observe the unlocked nature of the SPH timestamps even at 48 kHz (I
> have personally seen it with 828, 828mk2, Traveler, 896HD devices).  As
> shown in the pre-computed table, for locked clocks one would expect 512
> ticks per event at 48 kHz.  While 512 is certainly the dominant difference,
> one does not have to wait long before seeing a difference of 511 or 513
> (depending on whether the audio clock runs fast or slow relative to the iso
> cycle timer).

Here, you have the misunderstanding, too.

If you have different stories, please show reasoning from enough facts, 
at first. Not from your opinion or memories.


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 08/19] firewire-motu: add MOTU specific protocol layer
  2017-01-30  3:50     ` Takashi Sakamoto
@ 2017-01-30  5:04       ` Jonathan Woithe
  2017-01-30  6:11         ` Takashi Sakamoto
  0 siblings, 1 reply; 28+ messages in thread
From: Jonathan Woithe @ 2017-01-30  5:04 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Mon, Jan 30, 2017 at 12:50:07PM +0900, Takashi Sakamoto wrote:
> In MOTU FireWire series, sampling clock and the other clocks seem to
> be independent.

Agreed.  This is what I was trying to say so I apologise that it was not
clear.

> If you have different stories, please show reasoning from enough facts, at
> first. Not from your opinion or memories.

Sending timestamps derived from a fixed interval did not provide reliable
audio output from these devices in general.  This was observed with real
hardware and tangentially, the hardware did not send SPH timestamps with a
fixed interval either.  At 48 kHz for example, deviations from a difference
of 512 were routinely observed in both directions even with official drivers
on vendor-supported OSes.

Over and out.
  jonathan

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 08/19] firewire-motu: add MOTU specific protocol layer
  2017-01-30  5:04       ` Jonathan Woithe
@ 2017-01-30  6:11         ` Takashi Sakamoto
  0 siblings, 0 replies; 28+ messages in thread
From: Takashi Sakamoto @ 2017-01-30  6:11 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Jan 30 2016 14:04, Jonathan Woithe wrote:
>> If you have different stories, please show reasoning from enough facts, at
>> first. Not from your opinion or memories.
>
> Sending timestamps derived from a fixed interval did not provide reliable
> audio output from these devices in general. This was observed with real
> hardware and tangentially, the hardware did not send SPH timestamps with a
> fixed interval either.  At 48 kHz for example, deviations from a difference
> of 512 were routinely observed in both directions even with official drivers
> on vendor-supported OSes.

This statement includes mixture of facts, your opinions and the lack of 
information:
  - facts:
   - When reading packet dumps, the sequence of SPH in tx/rx packets
     includes time stamps which are not even lapses.
  - opinions:
   - The sequence of SPH with even lapses doesn't bring reliable audio
     output.
  - the others without enough information:
   - As long as _you_ experienced, putting time stamps with even lapses
     to SPH in rx packets causes non-reliable audio output. (But which
     models? the way to generate the time stamp? and so on?)

As long as I investigated, packets from 
828/828mk2/828mk3(FireWire/Hybrid) at 48.0kHz of sampling transmission 
frequency(stf) the sequence of SPH in each data block includes differed 
lapses. In theory, in this stf, the gap between two successive time 
stamp should be 512 ticks at time representation on IEEE 1394 bus, 
however it's sometimes 511 or 513 in fact. This is also the same packets 
from driver for Windows operating system.The algorithm to generate 
exactly the same sequence of SPH is not clear yet.

However, we program IEC 61883-1/6 engine of ALSA firewire stack with an 
assumption that time stamps include even lapses:
http://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/sound/firewire/amdtp-stream.c#n301

As long as handling packets with this engine, the sequence of time stamp 
should includes even lapses for correct calculation. In this reason, I 
use the pre-computed table.


Well, when using a kind of words such as 'in general', 'all', 
'absolutely', 'resolutely', it's a signs of over-generalization and you 
should pay enough attention to what you're going to describe. And when 
describing the facts, subject of contexts is important. You still have a 
tendency of these wrong directions, in my opinion.


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2017-01-30  6:11 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-29  3:53 [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Takashi Sakamoto
2017-01-29  3:53 ` [PATCH 01/19] firewire-motu: add skeleton for Mark of the unicorn (MOTU) " Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 02/19] firewire-motu: postpone sound card registration Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 03/19] firewire-motu: add a structure for model-dependent parameters Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 04/19] firewire-motu: add an abstraction layer for three types of protocols Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 05/19] firewire-lib: record cycle count for the first packet Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 06/19] firewire-lib: add support for source packet header field in CIP header Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 07/19] firewire-lib: enable CIP_DBC_IS_END_EVENT for both directions of stream Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 08/19] firewire-motu: add MOTU specific protocol layer Takashi Sakamoto
2017-01-29 13:16   ` [FFADO-devel] " Jonathan Woithe
2017-01-30  3:50     ` Takashi Sakamoto
2017-01-30  5:04       ` Jonathan Woithe
2017-01-30  6:11         ` Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 09/19] firewire-motu: handle transactions specific for MOTU FireWire models Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 10/19] firewire-motu: add stream management functionality Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 11/19] firewire-motu: add proc node to show current statuc of clock and packet formats Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 12/19] firewire-motu: add PCM functionality Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 13/19] firewire-motu: add MIDI functionality Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 14/19] firewire-motu: add hwdep interface Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 15/19] firewire-motu: enable to read transaction cache via " Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 16/19] firewire-motu: add support for MOTU 828 as a model with protocol version 1 Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 17/19] firewire-motu: add support for MOTU 828mk2 as a model with protocol version 2 Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 18/19] firewire-lib: add a quirk of packet without valid EOH in CIP format Takashi Sakamoto
2017-01-29  3:54 ` [PATCH 19/19] firewire-motu: add support for MOTU 828mk3 (FireWire/Hybrid) as a model with protocol version 3 Takashi Sakamoto
2017-01-29 13:22 ` [FFADO-devel] [PATCH 00/19][RFC v2] ALSA: firewire-motu: new driver for MOTU FireWire series Jonathan Woithe
2017-01-30  3:09   ` Takashi Sakamoto
2017-01-29 13:34 ` [FFADO-devel] " Jonathan Woithe
2017-01-30  3:17   ` Takashi Sakamoto

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.