alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [GIT PULL] [00/29] playback-only DICE driver
@ 2013-10-21 19:19 Clemens Ladisch
  2013-10-21 19:21 ` [PATCH] [01/29] ALSA: add " Clemens Ladisch
                   ` (29 more replies)
  0 siblings, 30 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:19 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Hi Takashi,

please pull this driver for FireWire DACs based on the DICE chip
family.  While the driver supports only PCM playback at the
moment, it fills a gap left by FFADO, which supports only full-
duplex devices.

Thanks,
Clemens
----------------------------------------------------------------

The following changes since commit 6e4664525b1db28f8c4e1130957f70a94c19213e:

  Linux 3.11 (2013-09-02 13:46:10 -0700)

are available in the git repository at:

  git://git.alsa-project.org/alsa-kprivate.git dice-driver-playback-only

for you to fetch changes up to b20be8de1b3972ccf9af72850b045214faa8d830:

  ALSA: dice: restrict the driver to playback-only devices (2013-10-20 22:07:57 +0200)

----------------------------------------------------------------
Clemens Ladisch (28):
      ALSA: add DICE driver
      ALSA: dice: optimize bus reset handling
      ALSA: dice: allow all sample rates
      ALSA: dice: reduce noisy logging
      ALSA: dice, firewire-lib: add blocking mode
      ALSA: dice: fix hang when unplugging a running device
      ALSA: dice: implement hwdep device
      ALSA: dice: clear device lock when closing hwdep device
      ALSA: firewire: introduce amdtp_out_stream_running()
      ALSA: dice: reorganize interface definitions
      ALSA: dice: fix device detection for other vendors
      ALSA: dice: support dual-wire stream format at 192 kHz
      ALSA: dice: optimize reading of consecutive registers
      ALSA: firewire: extend snd_fw_transaction()
      ALSA: dice: avoid superflous write at bus reset
      ALSA: dice: remove 10s period length limit
      ALSA: dice: remove superfluous field
      ALSA: dice: make amdtp_rates[] const
      ALSA: dice: get clock capabilities
      ALSA: dice: allow notifications during initialization
      ALSA: dice: get rate-dependent parameters
      ALSA: dice: dynamic sample rate selection
      ALSA: dice: check clock change timeout
      ALSA: dice: add a proc file to show device information
      ALSA: dice: document quadlet alignment
      ALSA: dice: dice_proc_read: remove wrong typecast
      ALSA: dice: fix detection of Weiss devices
      ALSA: dice: restrict the driver to playback-only devices

Stefan Richter (1):
      ALSA: dice: fix locking

 Documentation/ioctl/ioctl-number.txt |    1 +
 include/uapi/sound/Kbuild            |    1 +
 include/uapi/sound/asound.h          |    3 +-
 include/uapi/sound/firewire.h        |   51 ++
 sound/firewire/Kconfig               |   15 +
 sound/firewire/Makefile              |    2 +
 sound/firewire/amdtp.c               |  209 +++--
 sound/firewire/amdtp.h               |   46 +-
 sound/firewire/cmp.c                 |   50 +-
 sound/firewire/dice-interface.h      |  371 +++++++++
 sound/firewire/dice.c                | 1494 ++++++++++++++++++++++++++++++++++
 sound/firewire/fcp.c                 |    2 +-
 sound/firewire/isight.c              |   43 +-
 sound/firewire/lib.c                 |   24 +-
 sound/firewire/lib.h                 |    7 +-
 sound/firewire/scs1x.c               |    8 +-
 sound/firewire/speakers.c            |   16 +-
 17 files changed, 2192 insertions(+), 151 deletions(-)
 create mode 100644 include/uapi/sound/firewire.h
 create mode 100644 sound/firewire/dice-interface.h
 create mode 100644 sound/firewire/dice.c

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

* [PATCH] [01/29] ALSA: add DICE driver
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
@ 2013-10-21 19:21 ` Clemens Ladisch
  2013-10-21 19:21 ` [PATCH] [02/29] ALSA: dice: optimize bus reset handling Clemens Ladisch
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:21 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

As a start point for further development, this is an incomplete driver
for DICE devices:
- only playback (so no clock source except the bus clock)
- only 44.1 kHz
- no MIDI
- recovery after bus reset is slow
- hwdep device is created, but not actually implemented

Contains compilation fixes by Stefan Richter.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 Documentation/ioctl/ioctl-number.txt |    1
 include/uapi/sound/Kbuild            |    1
 include/uapi/sound/asound.h          |    3
 include/uapi/sound/firewire.h        |   51 ++
 sound/firewire/Kconfig               |   13
 sound/firewire/Makefile              |    2
 sound/firewire/dice.c                | 1008 ++++++++++++++++++++++++++++++++++
 7 files changed, 1078 insertions(+), 1 deletion(-)
 create mode 100644 include/uapi/sound/firewire.h
 create mode 100644 sound/firewire/dice.c

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 2a5f0e1..7cbfa3c 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -138,6 +138,7 @@ Code  Seq#(hex)	Include File		Comments
 'H'	C0-DF	net/bluetooth/cmtp/cmtp.h	conflict!
 'H'	C0-DF	net/bluetooth/bnep/bnep.h	conflict!
 'H'	F1	linux/hid-roccat.h	<mailto:erazor_de@users.sourceforge.net>
+'H'	F8-FA	sound/firewire.h
 'I'	all	linux/isdn.h		conflict!
 'I'	00-0F	drivers/isdn/divert/isdn_divert.h	conflict!
 'I'	40-4F	linux/mISDNif.h		conflict!
diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild
index 0f7d279..a7f2770 100644
--- a/include/uapi/sound/Kbuild
+++ b/include/uapi/sound/Kbuild
@@ -5,6 +5,7 @@ header-y += asound_fm.h
 header-y += compress_offload.h
 header-y += compress_params.h
 header-y += emu10k1.h
+header-y += firewire.h
 header-y += hdsp.h
 header-y += hdspm.h
 header-y += sb16_csp.h
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 041203f..9fc6219 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -93,9 +93,10 @@ enum {
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
 	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
+	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */

 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
 };

 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
new file mode 100644
index 0000000..e86131c
--- /dev/null
+++ b/include/uapi/sound/firewire.h
@@ -0,0 +1,51 @@
+#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED
+#define UAPI_SOUND_FIREWIRE_H_INCLUDED
+
+#include <linux/ioctl.h>
+
+/* events can be read() from the hwdep device */
+
+#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS	0x000010cc
+#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION	0xd1ce004e
+
+struct snd_firewire_event_common {
+	unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
+};
+
+struct snd_firewire_event_lock_status {
+	unsigned int type;
+	unsigned int status; /* 0/1 = unlocked/locked */
+};
+
+struct snd_firewire_event_dice_notification {
+	unsigned int type;
+	unsigned int notification; /* DICE-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;
+};
+
+
+#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info)
+#define SNDRV_FIREWIRE_IOCTL_LOCK      _IO('H', 0xf9)
+#define SNDRV_FIREWIRE_IOCTL_UNLOCK    _IO('H', 0xfa)
+
+#define SNDRV_FIREWIRE_TYPE_DICE	1
+/* Fireworks, AV/C, RME, MOTU, ... */
+
+struct snd_firewire_get_info {
+	unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
+	unsigned int card; /* same as fw_cdev_get_info.card */
+	unsigned char guid[8];
+	char device_name[16]; /* device node in /dev */
+};
+
+/*
+ * SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming.
+ * Returns -EBUSY if the driver is already streaming.
+ */
+
+#endif
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index ea063e1..9153309 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -11,6 +11,19 @@ config SND_FIREWIRE_LIB
 	tristate
 	depends on SND_PCM

+config SND_DICE
+	tristate "DICE devices (EXPERIMENTAL)"
+	select SND_HWDEP
+	select SND_PCM
+	select SND_FIREWIRE_LIB
+	help
+	  Say Y here to include support for many FireWire audio interfaces
+	  based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied
+	  Technologies.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-dice.
+
 config SND_FIREWIRE_SPEAKERS
 	tristate "FireWire speakers"
 	select SND_PCM
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 460179d..5099550 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,10 +1,12 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
+snd-dice-objs := dice.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
new file mode 100644
index 0000000..ac71b2b
--- /dev/null
+++ b/sound/firewire/dice.c
@@ -0,0 +1,1008 @@
+/*
+ * TC Applied Technologies Digital Interface Communications Engine driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#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>
+#include <sound/hwdep.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp.h"
+#include "iso-resources.h"
+#include "lib.h"
+
+#define DICE_PRIVATE_SPACE		0xffffe0000000uLL
+
+/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */
+#define DICE_GLOBAL_OFFSET		0x00
+#define DICE_GLOBAL_SIZE		0x04
+#define DICE_TX_OFFSET			0x08
+#define DICE_TX_SIZE			0x0c
+#define DICE_RX_OFFSET			0x10
+#define DICE_RX_SIZE			0x14
+
+/* pointed to by DICE_GLOBAL_OFFSET */
+#define GLOBAL_OWNER			0x000
+#define  OWNER_NO_OWNER			0xffff000000000000uLL
+#define  OWNER_NODE_SHIFT		48
+#define GLOBAL_NOTIFICATION		0x008
+#define  NOTIFY_RX_CFG_CHG		0x00000001
+#define  NOTIFY_TX_CFG_CHG		0x00000002
+#define  NOTIFY_DUP_ISOC		0x00000004
+#define  NOTIFY_BW_ERR			0x00000008
+#define  NOTIFY_LOCK_CHG		0x00000010
+#define  NOTIFY_CLOCK_ACCEPTED		0x00000020
+#define  NOTIFY_INTERFACE_CHG		0x00000040
+#define  NOTIFY_MESSAGE			0x00100000
+#define GLOBAL_NICK_NAME		0x00c
+#define  NICK_NAME_SIZE			64
+#define GLOBAL_CLOCK_SELECT		0x04c
+#define  CLOCK_SOURCE_MASK		0x000000ff
+#define  CLOCK_SOURCE_AES1		0x00000000
+#define  CLOCK_SOURCE_AES2		0x00000001
+#define  CLOCK_SOURCE_AES3		0x00000002
+#define  CLOCK_SOURCE_AES4		0x00000003
+#define  CLOCK_SOURCE_AES_ANY		0x00000004
+#define  CLOCK_SOURCE_ADAT		0x00000005
+#define  CLOCK_SOURCE_TDIF		0x00000006
+#define  CLOCK_SOURCE_WC		0x00000007
+#define  CLOCK_SOURCE_ARX1		0x00000008
+#define  CLOCK_SOURCE_ARX2		0x00000009
+#define  CLOCK_SOURCE_ARX3		0x0000000a
+#define  CLOCK_SOURCE_ARX4		0x0000000b
+#define  CLOCK_SOURCE_INTERNAL		0x0000000c
+#define  CLOCK_RATE_MASK		0x0000ff00
+#define  CLOCK_RATE_32000		0x00000000
+#define  CLOCK_RATE_44100		0x00000100
+#define  CLOCK_RATE_48000		0x00000200
+#define  CLOCK_RATE_88200		0x00000300
+#define  CLOCK_RATE_96000		0x00000400
+#define  CLOCK_RATE_176400		0x00000500
+#define  CLOCK_RATE_192000		0x00000600
+#define  CLOCK_RATE_ANY_LOW		0x00000700
+#define  CLOCK_RATE_ANY_MID		0x00000800
+#define  CLOCK_RATE_ANY_HIGH		0x00000900
+#define  CLOCK_RATE_NONE		0x00000a00
+#define GLOBAL_ENABLE			0x050
+#define  ENABLE				0x00000001
+#define GLOBAL_STATUS			0x054
+#define  STATUS_SOURCE_LOCKED		0x00000001
+#define  STATUS_RATE_CONFLICT		0x00000002
+#define  STATUS_NOMINAL_RATE_MASK	0x0000ff00
+#define GLOBAL_EXTENDED_STATUS		0x058
+#define  EXT_STATUS_AES1_LOCKED		0x00000001
+#define  EXT_STATUS_AES2_LOCKED		0x00000002
+#define  EXT_STATUS_AES3_LOCKED		0x00000004
+#define  EXT_STATUS_AES4_LOCKED		0x00000008
+#define  EXT_STATUS_ADAT_LOCKED		0x00000010
+#define  EXT_STATUS_TDIF_LOCKED		0x00000020
+#define  EXT_STATUS_ARX1_LOCKED		0x00000040
+#define  EXT_STATUS_ARX2_LOCKED		0x00000080
+#define  EXT_STATUS_ARX3_LOCKED		0x00000100
+#define  EXT_STATUS_ARX4_LOCKED		0x00000200
+#define  EXT_STATUS_WC_LOCKED		0x00000400
+#define  EXT_STATUS_AES1_SLIP		0x00010000
+#define  EXT_STATUS_AES2_SLIP		0x00020000
+#define  EXT_STATUS_AES3_SLIP		0x00040000
+#define  EXT_STATUS_AES4_SLIP		0x00080000
+#define  EXT_STATUS_ADAT_SLIP		0x00100000
+#define  EXT_STATUS_TDIF_SLIP		0x00200000
+#define  EXT_STATUS_ARX1_SLIP		0x00400000
+#define  EXT_STATUS_ARX2_SLIP		0x00800000
+#define  EXT_STATUS_ARX3_SLIP		0x01000000
+#define  EXT_STATUS_ARX4_SLIP		0x02000000
+#define  EXT_STATUS_WC_SLIP		0x04000000
+#define GLOBAL_SAMPLE_RATE		0x05c
+#define GLOBAL_VERSION			0x060
+#define GLOBAL_CLOCK_CAPABILITIES	0x064
+#define  CLOCK_CAP_RATE_32000		0x00000001
+#define  CLOCK_CAP_RATE_44100		0x00000002
+#define  CLOCK_CAP_RATE_48000		0x00000004
+#define  CLOCK_CAP_RATE_88200		0x00000008
+#define  CLOCK_CAP_RATE_96000		0x00000010
+#define  CLOCK_CAP_RATE_176400		0x00000020
+#define  CLOCK_CAP_RATE_192000		0x00000040
+#define  CLOCK_CAP_SOURCE_AES1		0x00010000
+#define  CLOCK_CAP_SOURCE_AES2		0x00020000
+#define  CLOCK_CAP_SOURCE_AES3		0x00040000
+#define  CLOCK_CAP_SOURCE_AES4		0x00080000
+#define  CLOCK_CAP_SOURCE_AES_ANY	0x00100000
+#define  CLOCK_CAP_SOURCE_ADAT		0x00200000
+#define  CLOCK_CAP_SOURCE_TDIF		0x00400000
+#define  CLOCK_CAP_SOURCE_WC		0x00800000
+#define  CLOCK_CAP_SOURCE_ARX1		0x01000000
+#define  CLOCK_CAP_SOURCE_ARX2		0x02000000
+#define  CLOCK_CAP_SOURCE_ARX3		0x04000000
+#define  CLOCK_CAP_SOURCE_ARX4		0x08000000
+#define  CLOCK_CAP_SOURCE_INTERNAL	0x10000000
+#define GLOBAL_CLOCK_SOURCE_NAMES	0x068
+#define  CLOCK_SOURCE_NAMES_SIZE	256
+
+/* pointed to by DICE_TX_OFFSET */
+#define TX_NUMBER			0x000
+#define TX_SIZE				0x004
+/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */
+#define TX_ISOCHRONOUS			0x008
+#define TX_NUMBER_AUDIO			0x00c
+#define TX_NUMBER_MIDI			0x010
+#define TX_SPEED			0x014
+#define TX_NAMES			0x018
+#define  TX_NAMES_SIZE			256
+#define TX_AC3_CAPABILITIES		0x118
+#define TX_AC3_ENABLE			0x11c
+
+/* pointed to by DICE_RX_OFFSET */
+#define RX_NUMBER			0x000
+#define RX_SIZE				0x004
+/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */
+#define RX_ISOCHRONOUS			0x008
+#define RX_SEQ_START			0x00c
+#define RX_NUMBER_AUDIO			0x010
+#define RX_NUMBER_MIDI			0x014
+#define RX_NAMES			0x018
+#define  RX_NAMES_SIZE			256
+#define RX_AC3_CAPABILITIES		0x118
+#define RX_AC3_ENABLE			0x11c
+
+
+#define FIRMWARE_LOAD_SPACE		0xffffe0100000uLL
+
+/* offset from FIRMWARE_LOAD_SPACE */
+#define FIRMWARE_VERSION		0x000
+#define FIRMWARE_OPCODE			0x004
+#define  OPCODE_MASK			0x00000fff
+#define  OPCODE_GET_IMAGE_DESC		0x00000000
+#define  OPCODE_DELETE_IMAGE		0x00000001
+#define  OPCODE_CREATE_IMAGE		0x00000002
+#define  OPCODE_UPLOAD			0x00000003
+#define  OPCODE_UPLOAD_STAT		0x00000004
+#define  OPCODE_RESET_IMAGE		0x00000005
+#define  OPCODE_TEST_ACTION		0x00000006
+#define  OPCODE_GET_RUNNING_IMAGE_VINFO	0x0000000a
+#define  OPCODE_EXECUTE			0x80000000
+#define FIRMWARE_RETURN_STATUS		0x008
+#define FIRMWARE_PROGRESS		0x00c
+#define  PROGRESS_CURR_MASK		0x00000fff
+#define  PROGRESS_MAX_MASK		0x00fff000
+#define  PROGRESS_TOUT_MASK		0x0f000000
+#define  PROGRESS_FLAG			0x80000000
+#define FIRMWARE_CAPABILITIES		0x010
+#define  FL_CAP_AUTOERASE		0x00000001
+#define  FL_CAP_PROGRESS		0x00000002
+#define FIRMWARE_DATA			0x02c
+#define  TEST_CMD_POKE			0x00000001
+#define  TEST_CMD_PEEK			0x00000002
+#define  CMD_GET_AVS_CNT		0x00000003
+#define  CMD_CLR_AVS_CNT		0x00000004
+#define  CMD_SET_MODE			0x00000005
+#define  CMD_SET_MIDIBP			0x00000006
+#define  CMD_GET_AVSPHASE		0x00000007
+#define  CMD_ENABLE_BNC_SYNC		0x00000008
+#define  CMD_PULSE_BNC_SYNC		0x00000009
+#define  CMD_EMUL_SLOW_CMD		0x0000000a
+#define FIRMWARE_TEST_DELAY		0xfd8
+#define FIRMWARE_TEST_BUF		0xfdc
+
+
+/* EAP */
+#define EAP_PRIVATE_SPACE		0xffffe0200000uLL
+
+#define EAP_CAPABILITY_OFFSET		0x000
+#define EAP_CAPABILITY_SIZE		0x004
+/* ... */
+
+#define EAP_ROUTER_CAPS			0x000
+#define  ROUTER_EXPOSED			0x00000001
+#define  ROUTER_READ_ONLY		0x00000002
+#define  ROUTER_FLASH			0x00000004
+#define  MAX_ROUTES_MASK		0xffff0000
+#define EAP_MIXER_CAPS			0x004
+#define  MIXER_EXPOSED			0x00000001
+#define  MIXER_READ_ONLY		0x00000002
+#define  MIXER_FLASH			0x00000004
+#define  MIXER_IN_DEV_MASK		0x000000f0
+#define  MIXER_OUT_DEV_MASK		0x00000f00
+#define  MIXER_INPUTS_MASK		0x00ff0000
+#define  MIXER_OUTPUTS_MASK		0xff000000
+#define EAP_GENERAL_CAPS		0x008
+#define  GENERAL_STREAM_CONFIG		0x00000001
+#define  GENERAL_FLASH			0x00000002
+#define  GENERAL_PEAK			0x00000004
+#define  GENERAL_MAX_TX_STREAMS_MASK	0x000000f0
+#define  GENERAL_MAX_RX_STREAMS_MASK	0x00000f00
+#define  GENERAL_STREAM_CONFIG_FLASH	0x00001000
+#define  GENERAL_CHIP_MASK		0x00ff0000
+#define  GENERAL_CHIP_DICE_II		0x00000000
+#define  GENERAL_CHIP_DICE_MINI		0x00010000
+#define  GENERAL_CHIP_DICE_JR		0x00020000
+
+
+struct dice {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	struct mutex mutex;
+	unsigned int global_offset;
+	unsigned int rx_offset;
+	struct fw_address_handler notification_handler;
+	int owner_generation;
+	bool global_enabled;
+	bool stream_running;
+	struct snd_pcm_substream *pcm;
+	struct fw_iso_resources resources;
+	struct amdtp_out_stream stream;
+};
+
+MODULE_DESCRIPTION("DICE driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static inline u64 global_address(struct dice *dice, unsigned int offset)
+{
+	return DICE_PRIVATE_SPACE + dice->global_offset + offset;
+}
+
+// TODO: rx index
+static inline u64 rx_address(struct dice *dice, unsigned int offset)
+{
+	return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
+}
+
+static int dice_owner_set(struct dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be64 *buffer;
+	int rcode, err, errors = 0;
+
+	buffer = kmalloc(2 * 8, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	for (;;) {
+		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+		buffer[1] = cpu_to_be64(
+			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+			dice->notification_handler.offset);
+
+		dice->owner_generation = device->generation;
+		smp_rmb(); /* node_id vs. generation */
+		rcode = fw_run_transaction(device->card,
+					   TCODE_LOCK_COMPARE_SWAP,
+					   device->node_id,
+					   dice->owner_generation,
+					   device->max_speed,
+					   global_address(dice, GLOBAL_OWNER),
+					   buffer, 2 * 8);
+
+		if (rcode == RCODE_COMPLETE) {
+			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
+				err = 0;
+			} else {
+				dev_err(&dice->unit->device,
+					"device is already in use\n");
+				err = -EBUSY;
+			}
+			break;
+		}
+		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+			dev_err(&dice->unit->device,
+				"setting device owner failed: %s\n",
+				fw_rcode_string(rcode));
+			err = -EIO;
+			break;
+		}
+		msleep(20);
+	}
+
+	kfree(buffer);
+
+	return err;
+}
+
+static int dice_owner_update(struct dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be64 *buffer;
+	int rcode, err, errors = 0;
+
+	if (dice->owner_generation == -1)
+		return 0;
+
+	buffer = kmalloc(2 * 8, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	for (;;) {
+		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+		buffer[1] = cpu_to_be64(
+			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+			dice->notification_handler.offset);
+
+		dice->owner_generation = device->generation;
+		smp_rmb(); /* node_id vs. generation */
+		rcode = fw_run_transaction(device->card,
+					   TCODE_LOCK_COMPARE_SWAP,
+					   device->node_id,
+					   dice->owner_generation,
+					   device->max_speed,
+					   global_address(dice, GLOBAL_OWNER),
+					   buffer, 2 * 8);
+
+		if (rcode == RCODE_COMPLETE) {
+			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
+				err = 0;
+			} else {
+				dev_err(&dice->unit->device,
+					"device is already in use\n");
+				err = -EBUSY;
+			}
+			break;
+		}
+		if (rcode == RCODE_GENERATION) {
+			err = 0; /* try again later */
+			break;
+		}
+		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+			dev_err(&dice->unit->device,
+				"setting device owner failed: %s\n",
+				fw_rcode_string(rcode));
+			err = -EIO;
+			break;
+		}
+		msleep(20);
+	}
+
+	kfree(buffer);
+
+	if (err < 0)
+		dice->owner_generation = -1;
+
+	return err;
+}
+
+static void dice_owner_clear(struct dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be64 *buffer;
+	int rcode, errors = 0;
+
+	buffer = kmalloc(2 * 8, GFP_KERNEL);
+	if (!buffer)
+		return;
+
+	for (;;) {
+		buffer[0] = cpu_to_be64(
+			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+			dice->notification_handler.offset);
+		buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+
+		rcode = fw_run_transaction(device->card,
+					   TCODE_LOCK_COMPARE_SWAP,
+					   device->node_id,
+					   dice->owner_generation,
+					   device->max_speed,
+					   global_address(dice, GLOBAL_OWNER),
+					   buffer, 2 * 8);
+
+		if (rcode == RCODE_COMPLETE)
+			break;
+		if (rcode == RCODE_GENERATION)
+			break;
+		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+			dev_err(&dice->unit->device,
+				"clearing device owner failed: %s\n",
+				fw_rcode_string(rcode));
+			break;
+		}
+		msleep(20);
+	}
+
+	kfree(buffer);
+
+	dice->owner_generation = -1;
+}
+
+static int dice_enable_set(struct dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be32 value;
+	int rcode, err, errors = 0;
+
+	value = cpu_to_be32(ENABLE);
+	for (;;) {
+		rcode = fw_run_transaction(device->card,
+					   TCODE_WRITE_QUADLET_REQUEST,
+					   device->node_id,
+					   dice->owner_generation,
+					   device->max_speed,
+					   global_address(dice, GLOBAL_ENABLE),
+					   &value, 4);
+		if (rcode == RCODE_COMPLETE) {
+			dice->global_enabled = true;
+			err = 0;
+			break;
+		}
+		if (rcode == RCODE_GENERATION) {
+			err = -EAGAIN;
+			break;
+		}
+		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+			dev_err(&dice->unit->device,
+				"device enabling failed: %s\n",
+				fw_rcode_string(rcode));
+			err = -EIO;
+			break;
+		}
+		msleep(20);
+	}
+
+	return err;
+}
+
+static void dice_enable_clear(struct dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be32 value;
+	int rcode, errors = 0;
+
+	value = 0;
+	for (;;) {
+		rcode = fw_run_transaction(device->card,
+					   TCODE_WRITE_QUADLET_REQUEST,
+					   device->node_id,
+					   dice->owner_generation,
+					   device->max_speed,
+					   global_address(dice, GLOBAL_ENABLE),
+					   &value, 4);
+		if (rcode == RCODE_COMPLETE ||
+		    rcode == RCODE_GENERATION)
+			break;
+		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+			dev_err(&dice->unit->device,
+				"device disabling failed: %s\n",
+				fw_rcode_string(rcode));
+			break;
+		}
+		msleep(20);
+	}
+	dice->global_enabled = false;
+}
+
+static void dice_notification(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)
+{
+	struct dice *dice = callback_data;
+
+	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+		return;
+	}
+	if ((offset & 3) != 0) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+	dev_info(&dice->unit->device,
+		 "notification: %08x\n", be32_to_cpup(data));
+	fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static int dice_open(struct snd_pcm_substream *substream)
+{
+	static const struct snd_pcm_hardware hardware = {
+		.info = SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_MMAP_VALID |
+			SNDRV_PCM_INFO_BATCH |
+			SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_BLOCK_TRANSFER,
+		.formats = AMDTP_OUT_PCM_FORMAT_BITS,
+		.rates = SNDRV_PCM_RATE_44100,
+		.rate_min = 44100,
+		.rate_max = 44100,
+		.buffer_bytes_max = 16 * 1024 * 1024,
+		.period_bytes_min = 1,
+		.period_bytes_max = UINT_MAX,
+		.periods_min = 1,
+		.periods_max = UINT_MAX,
+	};
+	struct dice *dice = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	__be32 number_audio, number_midi;
+	int err;
+
+	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+				 rx_address(dice, RX_NUMBER_AUDIO),
+				 &number_audio, 4);
+	if (err < 0)
+		return err;
+	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+				 rx_address(dice, RX_NUMBER_MIDI),
+				 &number_midi, 4);
+	if (err < 0)
+		return err;
+
+	runtime->hw = hardware;
+	runtime->hw.channels_min = be32_to_cpu(number_audio);
+	runtime->hw.channels_max = be32_to_cpu(number_audio);
+
+	amdtp_out_stream_set_rate(&dice->stream, 44100);
+	amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio));
+	amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi));
+
+	err = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+					   5000, 8192000);
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int dice_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static void dice_stop_stream(struct dice *dice)
+{
+	__be32 channel;
+
+	if (dice->stream_running) {
+		dice_enable_clear(dice);
+
+		amdtp_out_stream_stop(&dice->stream);
+
+		channel = cpu_to_be32((u32)-1);
+		snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+				   rx_address(dice, RX_ISOCHRONOUS),
+				   &channel, 4);
+
+		fw_iso_resources_free(&dice->resources);
+
+		dice->stream_running = false;
+	}
+}
+
+static int dice_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *hw_params)
+{
+	struct dice *dice = substream->private_data;
+	int err;
+
+	mutex_lock(&dice->mutex);
+	dice_stop_stream(dice);
+	mutex_unlock(&dice->mutex);
+
+	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+					       params_buffer_bytes(hw_params));
+	if (err < 0)
+		goto error;
+
+	amdtp_out_stream_set_pcm_format(&dice->stream,
+					params_format(hw_params));
+
+	return 0;
+
+error:
+	return err;
+}
+
+static int dice_hw_free(struct snd_pcm_substream *substream)
+{
+	struct dice *dice = substream->private_data;
+
+	mutex_lock(&dice->mutex);
+	dice_stop_stream(dice);
+	mutex_unlock(&dice->mutex);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dice_prepare(struct snd_pcm_substream *substream)
+{
+	struct dice *dice = substream->private_data;
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be32 channel;
+	int err;
+
+	mutex_lock(&dice->mutex);
+
+	if (amdtp_out_streaming_error(&dice->stream))
+		dice_stop_stream(dice);
+
+	if (!dice->stream_running) {
+		err = fw_iso_resources_allocate(&dice->resources,
+				amdtp_out_stream_get_max_payload(&dice->stream),
+				device->max_speed);
+		if (err < 0)
+			goto error;
+
+		//TODO: RX_SEQ_START
+		channel = cpu_to_be32(dice->resources.channel);
+		err = snd_fw_transaction(dice->unit,
+					 TCODE_WRITE_QUADLET_REQUEST,
+					 rx_address(dice, RX_ISOCHRONOUS),
+					 &channel, 4);
+		if (err < 0)
+			goto err_resources;
+
+		err = amdtp_out_stream_start(&dice->stream,
+					     dice->resources.channel,
+					     device->max_speed);
+		if (err < 0)
+			goto err_resources;
+
+		err = dice_enable_set(dice);
+		if (err < 0)
+			goto err_stream;
+
+		dice->stream_running = true;
+	}
+
+	mutex_unlock(&dice->mutex);
+
+	amdtp_out_stream_pcm_prepare(&dice->stream);
+
+	return 0;
+
+err_stream:
+	amdtp_out_stream_stop(&dice->stream);
+err_resources:
+	fw_iso_resources_free(&dice->resources);
+error:
+	mutex_unlock(&dice->mutex);
+
+	return err;
+}
+
+static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct dice *dice = substream->private_data;
+	struct snd_pcm_substream *pcm;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		pcm = substream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pcm = NULL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
+{
+	struct dice *dice = substream->private_data;
+
+	return amdtp_out_stream_pcm_pointer(&dice->stream);
+}
+
+static int dice_create_pcm(struct dice *dice)
+{
+	static struct snd_pcm_ops ops = {
+		.open      = dice_open,
+		.close     = dice_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = dice_hw_params,
+		.hw_free   = dice_hw_free,
+		.prepare   = dice_prepare,
+		.trigger   = dice_trigger,
+		.pointer   = dice_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	__be32 clock;
+	struct snd_pcm *pcm;
+	int err;
+
+	clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100);
+	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_CLOCK_SELECT),
+				 &clock, 4);
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = dice;
+	strcpy(pcm->name, dice->card->shortname);
+	dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	dice->pcm->ops = &ops;
+
+	return 0;
+}
+
+// TODO: implement these
+
+static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+			    long count, loff_t *offset)
+{
+	return -EIO;
+}
+
+static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file)
+{
+	return -EIO;
+}
+
+static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	return 0;
+}
+
+static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+				    poll_table *wait)
+{
+	return POLLERR | POLLHUP;
+}
+
+static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+static int dice_create_hwdep(struct dice *dice)
+{
+	static const struct snd_hwdep_ops ops = {
+		.read         = dice_hwdep_read,
+		.open         = dice_hwdep_open,
+		.release      = dice_hwdep_release,
+		.poll         = dice_hwdep_poll,
+		.ioctl        = dice_hwdep_ioctl,
+		.ioctl_compat = dice_hwdep_ioctl,
+	};
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
+	if (err < 0)
+		return err;
+	strcpy(hwdep->name, "DICE");
+	hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
+	hwdep->ops = ops;
+	hwdep->private_data = dice;
+	hwdep->exclusive = true;
+
+	return 0;
+}
+
+static void dice_card_free(struct snd_card *card)
+{
+	struct dice *dice = card->private_data;
+
+	amdtp_out_stream_destroy(&dice->stream);
+	fw_core_remove_address_handler(&dice->notification_handler);
+	mutex_destroy(&dice->mutex);
+}
+
+static int dice_init_offsets(struct dice *dice)
+{
+	__be32 pointers[6];
+	unsigned int global_size, rx_size;
+	int err;
+
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+				 DICE_PRIVATE_SPACE, &pointers, 6 * 4);
+	if (err < 0)
+		return err;
+
+	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+	global_size = be32_to_cpu(pointers[1]);
+	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+	rx_size = be32_to_cpu(pointers[5]);
+
+	/* some sanity checks to ensure that we actually have a DICE */
+	if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 ||
+	    dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) {
+		dev_err(&dice->unit->device, "invalid register pointers\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void dice_card_strings(struct dice *dice)
+{
+	struct snd_card *card = dice->card;
+	struct fw_device *dev = fw_parent_device(dice->unit);
+	char vendor[32], model[32];
+	unsigned int i;
+	int err;
+
+	strcpy(card->driver, "DICE");
+
+	strcpy(card->shortname, "DICE");
+	BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+				 global_address(dice, GLOBAL_NICK_NAME),
+				 card->shortname, sizeof(card->shortname));
+	if (err >= 0) {
+		/* DICE strings are returned in "always-wrong" endianness */
+		BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
+		for (i = 0; i < sizeof(card->shortname); i += 4)
+			swab32s((u32 *)&card->shortname[i]);
+		card->shortname[sizeof(card->shortname) - 1] = '\0';
+	}
+
+	strcpy(vendor, "?");
+	fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
+	strcpy(model, "?");
+	fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s %s, GUID %08x%08x at %s, S%d",
+		 vendor, model, dev->config_rom[3], dev->config_rom[4],
+		 dev_name(&dice->unit->device), 100 << dev->max_speed);
+
+	strcpy(card->mixername, "DICE");
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
+{
+	struct snd_card *card;
+	struct dice *dice;
+	int err;
+
+	err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, &unit->device);
+
+	dice = card->private_data;
+	dice->card = card;
+	mutex_init(&dice->mutex);
+	dice->unit = unit;
+
+	err = dice_init_offsets(dice);
+	if (err < 0)
+		goto err_mutex;
+
+	dice->notification_handler.length = 4;
+	dice->notification_handler.address_callback = dice_notification;
+	dice->notification_handler.callback_data = dice;
+	err = fw_core_add_address_handler(&dice->notification_handler,
+					  &fw_high_memory_region);
+	if (err < 0)
+		goto err_mutex;
+
+	err = fw_iso_resources_init(&dice->resources, unit);
+	if (err < 0)
+		goto err_notification_handler;
+	dice->resources.channels_mask = 0x00000000ffffffffuLL;
+
+	err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING);
+	if (err < 0)
+		goto err_resources;
+
+	err = dice_owner_set(dice);
+	if (err < 0)
+		goto err_stream;
+
+	card->private_free = dice_card_free;
+
+	dice_card_strings(dice);
+
+	err = dice_create_pcm(dice);
+	if (err < 0)
+		goto error;
+
+	err = dice_create_hwdep(dice);
+	if (err < 0)
+		goto error;
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	dev_set_drvdata(&unit->device, dice);
+
+	return 0;
+
+err_stream:
+	amdtp_out_stream_destroy(&dice->stream);
+err_resources:
+	fw_iso_resources_destroy(&dice->resources);
+err_notification_handler:
+	fw_core_remove_address_handler(&dice->notification_handler);
+err_mutex:
+	mutex_destroy(&dice->mutex);
+error:
+	snd_card_free(card);
+	return err;
+}
+
+static void dice_remove(struct fw_unit *unit)
+{
+	struct dice *dice = dev_get_drvdata(&unit->device);
+
+	snd_card_disconnect(dice->card);
+
+	mutex_lock(&dice->mutex);
+	amdtp_out_stream_pcm_abort(&dice->stream);
+	dice_stop_stream(dice);
+	dice_owner_clear(dice);
+	mutex_unlock(&dice->mutex);
+
+	snd_card_free_when_closed(dice->card);
+}
+
+static void dice_bus_reset(struct fw_unit *unit)
+{
+	struct dice *dice = dev_get_drvdata(&unit->device);
+
+	mutex_lock(&dice->mutex);
+	/*
+	 * XXX is the following true?
+	 * On a bus reset, the DICE firmware disables streaming and then goes
+	 * off contemplating its own navel for hundreds of milliseconds before
+	 * it can react to any of our attempts to reenable streaming.  This
+	 * means that we lose synchronization anyway, so we force our streams
+	 * to stop so that the application can restart them in an orderly
+	 * manner.
+	 */
+	dice_owner_update(dice);
+	amdtp_out_stream_pcm_abort(&dice->stream);
+	dice_stop_stream(dice);
+	mutex_unlock(&dice->mutex);
+}
+
+#define TC_OUI		0x000166
+#define DICE_INTERFACE	0x000001
+
+static const struct ieee1394_device_id dice_id_table[] = {
+	{
+		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
+				IEEE1394_MATCH_VERSION,
+		.specifier_id = TC_OUI,
+		.version      = DICE_INTERFACE,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
+
+static struct fw_driver dice_driver = {
+	.driver   = {
+		.owner	= THIS_MODULE,
+		.name	= KBUILD_MODNAME,
+		.bus	= &fw_bus_type,
+	},
+	.probe    = dice_probe,
+	.update   = dice_bus_reset,
+	.remove   = dice_remove,
+	.id_table = dice_id_table,
+};
+
+static int __init alsa_dice_init(void)
+{
+	return driver_register(&dice_driver.driver);
+}
+
+static void __exit alsa_dice_exit(void)
+{
+	driver_unregister(&dice_driver.driver);
+}
+
+module_init(alsa_dice_init);
+module_exit(alsa_dice_exit);

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

* [PATCH] [02/29] ALSA: dice: optimize bus reset handling
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
  2013-10-21 19:21 ` [PATCH] [01/29] ALSA: add " Clemens Ladisch
@ 2013-10-21 19:21 ` Clemens Ladisch
  2013-10-21 19:22 ` [PATCH] [03/29] ALSA: dice: allow all sample rates Clemens Ladisch
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:21 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

After a bus reset, do not stop the stream completely to avoid having to
reconfigure the device when restarting the stream.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |  148 +++++++++++++++++++++++++++++++------------------
 1 file changed, 93 insertions(+), 55 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index ac71b2b..b081021 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -559,24 +559,92 @@ static int dice_close(struct snd_pcm_substream *substream)
 	return 0;
 }

-static void dice_stop_stream(struct dice *dice)
+static int dice_stream_start_packets(struct dice *dice)
 {
-	__be32 channel;
+	int err;
+
+	if (dice->stream_running)
+		return 0;

-	if (dice->stream_running) {
-		dice_enable_clear(dice);
+	err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
+				     fw_parent_device(dice->unit)->max_speed);
+	if (err < 0)
+		return err;

+	err = dice_enable_set(dice);
+	if (err < 0) {
 		amdtp_out_stream_stop(&dice->stream);
+		return err;
+	}

-		channel = cpu_to_be32((u32)-1);
-		snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
-				   rx_address(dice, RX_ISOCHRONOUS),
-				   &channel, 4);
+	dice->stream_running = true;

-		fw_iso_resources_free(&dice->resources);
+	return 0;
+}

-		dice->stream_running = false;
+static int dice_stream_start(struct dice *dice)
+{
+	__be32 channel;
+	int err;
+
+	if (!dice->resources.allocated) {
+		err = fw_iso_resources_allocate(&dice->resources,
+				amdtp_out_stream_get_max_payload(&dice->stream),
+				fw_parent_device(dice->unit)->max_speed);
+		if (err < 0)
+			goto error;
+
+		channel = cpu_to_be32(dice->resources.channel);
+		err = snd_fw_transaction(dice->unit,
+					 TCODE_WRITE_QUADLET_REQUEST,
+					 rx_address(dice, RX_ISOCHRONOUS),
+					 &channel, 4);
+		if (err < 0)
+			goto err_resources;
 	}
+
+	err = dice_stream_start_packets(dice);
+	if (err < 0)
+		goto err_rx_channel;
+
+	return 0;
+
+err_rx_channel:
+	channel = cpu_to_be32((u32)-1);
+	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4);
+err_resources:
+	fw_iso_resources_free(&dice->resources);
+error:
+	return err;
+}
+
+static void dice_stream_stop_packets(struct dice *dice)
+{
+	if (!dice->stream_running)
+		return;
+
+	dice_enable_clear(dice);
+
+	amdtp_out_stream_stop(&dice->stream);
+
+	dice->stream_running = false;
+}
+
+static void dice_stream_stop(struct dice *dice)
+{
+	__be32 channel;
+
+	dice_stream_stop_packets(dice);
+
+	if (!dice->resources.allocated)
+		return;
+
+	channel = cpu_to_be32((u32)-1);
+	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4);
+
+	fw_iso_resources_free(&dice->resources);
 }

 static int dice_hw_params(struct snd_pcm_substream *substream,
@@ -586,7 +654,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 	int err;

 	mutex_lock(&dice->mutex);
-	dice_stop_stream(dice);
+	dice_stream_stop(dice);
 	mutex_unlock(&dice->mutex);

 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -608,7 +676,7 @@ static int dice_hw_free(struct snd_pcm_substream *substream)
 	struct dice *dice = substream->private_data;

 	mutex_lock(&dice->mutex);
-	dice_stop_stream(dice);
+	dice_stream_stop(dice);
 	mutex_unlock(&dice->mutex);

 	return snd_pcm_lib_free_vmalloc_buffer(substream);
@@ -617,42 +685,17 @@ static int dice_hw_free(struct snd_pcm_substream *substream)
 static int dice_prepare(struct snd_pcm_substream *substream)
 {
 	struct dice *dice = substream->private_data;
-	struct fw_device *device = fw_parent_device(dice->unit);
-	__be32 channel;
 	int err;

 	mutex_lock(&dice->mutex);

 	if (amdtp_out_streaming_error(&dice->stream))
-		dice_stop_stream(dice);
-
-	if (!dice->stream_running) {
-		err = fw_iso_resources_allocate(&dice->resources,
-				amdtp_out_stream_get_max_payload(&dice->stream),
-				device->max_speed);
-		if (err < 0)
-			goto error;
-
-		//TODO: RX_SEQ_START
-		channel = cpu_to_be32(dice->resources.channel);
-		err = snd_fw_transaction(dice->unit,
-					 TCODE_WRITE_QUADLET_REQUEST,
-					 rx_address(dice, RX_ISOCHRONOUS),
-					 &channel, 4);
-		if (err < 0)
-			goto err_resources;
-
-		err = amdtp_out_stream_start(&dice->stream,
-					     dice->resources.channel,
-					     device->max_speed);
-		if (err < 0)
-			goto err_resources;
-
-		err = dice_enable_set(dice);
-		if (err < 0)
-			goto err_stream;
+		dice_stream_stop_packets(dice);

-		dice->stream_running = true;
+	err = dice_stream_start(dice);
+	if (err < 0) {
+		mutex_unlock(&dice->mutex);
+		return err;
 	}

 	mutex_unlock(&dice->mutex);
@@ -660,15 +703,6 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 	amdtp_out_stream_pcm_prepare(&dice->stream);

 	return 0;
-
-err_stream:
-	amdtp_out_stream_stop(&dice->stream);
-err_resources:
-	fw_iso_resources_free(&dice->resources);
-error:
-	mutex_unlock(&dice->mutex);
-
-	return err;
 }

 static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -941,7 +975,7 @@ static void dice_remove(struct fw_unit *unit)

 	mutex_lock(&dice->mutex);
 	amdtp_out_stream_pcm_abort(&dice->stream);
-	dice_stop_stream(dice);
+	dice_stream_stop(dice);
 	dice_owner_clear(dice);
 	mutex_unlock(&dice->mutex);

@@ -953,8 +987,8 @@ static void dice_bus_reset(struct fw_unit *unit)
 	struct dice *dice = dev_get_drvdata(&unit->device);

 	mutex_lock(&dice->mutex);
+
 	/*
-	 * XXX is the following true?
 	 * On a bus reset, the DICE firmware disables streaming and then goes
 	 * off contemplating its own navel for hundreds of milliseconds before
 	 * it can react to any of our attempts to reenable streaming.  This
@@ -962,9 +996,13 @@ static void dice_bus_reset(struct fw_unit *unit)
 	 * to stop so that the application can restart them in an orderly
 	 * manner.
 	 */
-	dice_owner_update(dice);
 	amdtp_out_stream_pcm_abort(&dice->stream);
-	dice_stop_stream(dice);
+	dice_stream_stop_packets(dice);
+
+	dice_owner_update(dice);
+
+	fw_iso_resources_update(&dice->resources);
+
 	mutex_unlock(&dice->mutex);
 }


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [03/29] ALSA: dice: allow all sample rates
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
  2013-10-21 19:21 ` [PATCH] [01/29] ALSA: add " Clemens Ladisch
  2013-10-21 19:21 ` [PATCH] [02/29] ALSA: dice: optimize bus reset handling Clemens Ladisch
@ 2013-10-21 19:22 ` Clemens Ladisch
  2013-10-21 19:23 ` [PATCH] [04/29] ALSA: dice: reduce noisy logging Clemens Ladisch
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:22 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Instead of forcing a constant 44.1 kHz, read the current sample rate
from the device when opening the PCM device.

Actually changing the sample rate requires some separate controller
application.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   55 +++++++++++++++++++++++++++++++++++++------------
 1 file changed, 42 insertions(+), 13 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index b081021..d3f3eb7 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -75,6 +75,7 @@
 #define  CLOCK_RATE_ANY_MID		0x00000800
 #define  CLOCK_RATE_ANY_HIGH		0x00000900
 #define  CLOCK_RATE_NONE		0x00000a00
+#define  CLOCK_RATE_SHIFT		8
 #define GLOBAL_ENABLE			0x050
 #define  ENABLE				0x00000001
 #define GLOBAL_STATUS			0x054
@@ -248,6 +249,16 @@ MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");

+static const unsigned int dice_rates[] = {
+	[0] =  32000,
+	[1] =  44100,
+	[2] =  48000,
+	[3] =  88200,
+	[4] =  96000,
+	[5] = 176400,
+	[6] = 192000,
+};
+
 static inline u64 global_address(struct dice *dice, unsigned int offset)
 {
 	return DICE_PRIVATE_SPACE + dice->global_offset + offset;
@@ -508,9 +519,6 @@ static int dice_open(struct snd_pcm_substream *substream)
 			SNDRV_PCM_INFO_INTERLEAVED |
 			SNDRV_PCM_INFO_BLOCK_TRANSFER,
 		.formats = AMDTP_OUT_PCM_FORMAT_BITS,
-		.rates = SNDRV_PCM_RATE_44100,
-		.rate_min = 44100,
-		.rate_max = 44100,
 		.buffer_bytes_max = 16 * 1024 * 1024,
 		.period_bytes_min = 1,
 		.period_bytes_max = UINT_MAX,
@@ -519,10 +527,21 @@ static int dice_open(struct snd_pcm_substream *substream)
 	};
 	struct dice *dice = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	__be32 number_audio, number_midi;
+	__be32 clock_sel, number_audio, number_midi;
+	unsigned int rate;
 	int err;

 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_CLOCK_SELECT),
+				 &clock_sel, 4);
+	if (err < 0)
+		return err;
+	rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
+	if (rate >= ARRAY_SIZE(dice_rates))
+		return -ENXIO;
+	rate = dice_rates[rate];
+
+	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 rx_address(dice, RX_NUMBER_AUDIO),
 				 &number_audio, 4);
 	if (err < 0)
@@ -534,10 +553,14 @@ static int dice_open(struct snd_pcm_substream *substream)
 		return err;

 	runtime->hw = hardware;
+
+	runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+	snd_pcm_limit_hw_rates(runtime);
+
 	runtime->hw.channels_min = be32_to_cpu(number_audio);
 	runtime->hw.channels_max = be32_to_cpu(number_audio);

-	amdtp_out_stream_set_rate(&dice->stream, 44100);
+	amdtp_out_stream_set_rate(&dice->stream, rate);
 	amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio));
 	amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi));

@@ -746,17 +769,9 @@ static int dice_create_pcm(struct dice *dice)
 		.page      = snd_pcm_lib_get_vmalloc_page,
 		.mmap      = snd_pcm_lib_mmap_vmalloc,
 	};
-	__be32 clock;
 	struct snd_pcm *pcm;
 	int err;

-	clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100);
-	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
-				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &clock, 4);
-	if (err < 0)
-		return err;
-
 	err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
 	if (err < 0)
 		return err;
@@ -897,6 +912,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 {
 	struct snd_card *card;
 	struct dice *dice;
+	__be32 clock_sel;
 	int err;

 	err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card);
@@ -938,6 +954,19 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)

 	dice_card_strings(dice);

+	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_CLOCK_SELECT),
+				 &clock_sel, 4);
+	if (err < 0)
+		goto error;
+	clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK);
+	clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1);
+	err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_CLOCK_SELECT),
+				 &clock_sel, 4);
+	if (err < 0)
+		goto error;
+
 	err = dice_create_pcm(dice);
 	if (err < 0)
 		goto error;

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [04/29] ALSA: dice: reduce noisy logging
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (2 preceding siblings ...)
  2013-10-21 19:22 ` [PATCH] [03/29] ALSA: dice: allow all sample rates Clemens Ladisch
@ 2013-10-21 19:23 ` Clemens Ladisch
  2013-10-21 19:23 ` [PATCH] [05/29] ALSA: dice, firewire-lib: add blocking mode Clemens Ladisch
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:23 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

The notification bits are not of general interest; log them only when
debugging.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index d3f3eb7..02c7b5a 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -505,8 +505,8 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
 		return;
 	}
-	dev_info(&dice->unit->device,
-		 "notification: %08x\n", be32_to_cpup(data));
+	dev_dbg(&dice->unit->device,
+		"notification: %08x\n", be32_to_cpup(data));
 	fw_send_response(card, request, RCODE_COMPLETE);
 }


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [05/29] ALSA: dice, firewire-lib: add blocking mode
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (3 preceding siblings ...)
  2013-10-21 19:23 ` [PATCH] [04/29] ALSA: dice: reduce noisy logging Clemens Ladisch
@ 2013-10-21 19:23 ` Clemens Ladisch
  2013-10-21 19:24 ` [PATCH] [06/29] ALSA: dice: fix hang when unplugging a running device Clemens Ladisch
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:23 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Allow AMDTP output streams to use blocking mode.

Use it for DICE devices, because the old DICE-II chip will in some cases
not be able to lock to non-blocking streams (erratum E7).

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/amdtp.c |   54 ++++++++++++++++++++++++++----------------------
 sound/firewire/amdtp.h |    7 +++++-
 sound/firewire/dice.c  |    2 +-
 3 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index ea995af..efb2e29 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data);
 int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
 			  enum cip_out_flags flags)
 {
-	if (flags != CIP_NONBLOCKING)
-		return -EINVAL;
-
 	s->unit = fw_unit_get(unit);
 	s->flags = flags;
 	s->context = ERR_PTR(-1);
@@ -96,12 +93,20 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
 		return;

 	for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
-		if (rate_info[sfc].rate == rate) {
-			s->sfc = sfc;
-			s->syt_interval = rate_info[sfc].syt_interval;
-			return;
-		}
+		if (rate_info[sfc].rate == rate)
+			goto sfc_found;
 	WARN_ON(1);
+	return;
+
+sfc_found:
+	s->sfc = sfc;
+	s->syt_interval = rate_info[sfc].syt_interval;
+
+	/* default buffering in the device */
+	s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+	if (s->flags & CIP_BLOCKING)
+		/* additional buffering needed to adjust for no-data packets */
+		s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
 }
 EXPORT_SYMBOL(amdtp_out_stream_set_rate);

@@ -110,25 +115,15 @@ EXPORT_SYMBOL(amdtp_out_stream_set_rate);
  * @s: the AMDTP output stream
  *
  * This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
+ * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and
  * amdtp_out_stream_set_midi().
  */
 unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
 {
-	static const unsigned int max_data_blocks[] = {
-		[CIP_SFC_32000]  =  4,
-		[CIP_SFC_44100]  =  6,
-		[CIP_SFC_48000]  =  6,
-		[CIP_SFC_88200]  = 12,
-		[CIP_SFC_96000]  = 12,
-		[CIP_SFC_176400] = 23,
-		[CIP_SFC_192000] = 24,
-	};
-
 	s->data_block_quadlets = s->pcm_channels;
 	s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);

-	return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets;
+	return 8 + s->syt_interval * s->data_block_quadlets * 4;
 }
 EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);

@@ -248,7 +243,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
 	s->last_syt_offset = syt_offset;

 	if (syt_offset < TICKS_PER_CYCLE) {
-		syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+		syt_offset += s->transfer_delay;
 		syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
 		syt += syt_offset % TICKS_PER_CYCLE;

@@ -344,8 +339,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 		return;
 	index = s->packet_index;

-	data_blocks = calculate_data_blocks(s);
 	syt = calculate_syt(s, cycle);
+	if (!(s->flags & CIP_BLOCKING)) {
+		data_blocks = calculate_data_blocks(s);
+	} else {
+		if (syt != 0xffff) {
+			data_blocks = s->syt_interval;
+		} else {
+			data_blocks = 0;
+			syt = 0xffffff;
+		}
+	}

 	buffer = s->buffer.packets[index].buffer;
 	buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@@ -455,9 +459,9 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
  * @speed: firewire speed code
  *
  * The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
- * amdtp_out_stream_set_midi(); and it must be started before any
- * PCM or MIDI device can be started.
+ * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(),
+ * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format();
+ * and it must be started before any PCM or MIDI device can be started.
  */
 int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 {
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index f6103d6..fd4ce30 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -11,9 +11,13 @@
  *	sample_rate/8000 samples, with rounding up or down to adjust
  *	for clock skew and left-over fractional samples.  This should
  *	be used if supported by the device.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ *	SYT_INTERVAL samples, with these two types alternating so that
+ *	the overall sample rate comes out right.
  */
 enum cip_out_flags {
-	CIP_NONBLOCKING = 0,
+	CIP_NONBLOCKING	= 0x00,
+	CIP_BLOCKING	= 0x01,
 };

 /**
@@ -51,6 +55,7 @@ struct amdtp_out_stream {
 				 __be32 *buffer, unsigned int frames);

 	unsigned int syt_interval;
+	unsigned int transfer_delay;
 	unsigned int source_node_id_field;
 	struct iso_packets_buffer buffer;

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 02c7b5a..63446f8 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -942,7 +942,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 		goto err_notification_handler;
 	dice->resources.channels_mask = 0x00000000ffffffffuLL;

-	err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING);
+	err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING);
 	if (err < 0)
 		goto err_resources;


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [06/29] ALSA: dice: fix hang when unplugging a running device
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (4 preceding siblings ...)
  2013-10-21 19:23 ` [PATCH] [05/29] ALSA: dice, firewire-lib: add blocking mode Clemens Ladisch
@ 2013-10-21 19:24 ` Clemens Ladisch
  2013-10-21 19:24 ` [PATCH] [07/29] ALSA: dice: implement hwdep device Clemens Ladisch
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:24 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

When aborting a PCM stream, the xrun is signaled only if the stream is
running.  When disconnecting a PCM stream, calling snd_card_disconnect()
too early would change the stream into a non-running state and thus
prevent the xrun from being noticed by user space.

To prevent this, move the snd_card_disconnect() call after the xrun.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 63446f8..d0575a9 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -1000,12 +1000,15 @@ static void dice_remove(struct fw_unit *unit)
 {
 	struct dice *dice = dev_get_drvdata(&unit->device);

-	snd_card_disconnect(dice->card);
-
 	mutex_lock(&dice->mutex);
+
 	amdtp_out_stream_pcm_abort(&dice->stream);
+
+	snd_card_disconnect(dice->card);
+
 	dice_stream_stop(dice);
 	dice_owner_clear(dice);
+
 	mutex_unlock(&dice->mutex);

 	snd_card_free_when_closed(dice->card);

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [07/29] ALSA: dice: implement hwdep device
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (5 preceding siblings ...)
  2013-10-21 19:24 ` [PATCH] [06/29] ALSA: dice: fix hang when unplugging a running device Clemens Ladisch
@ 2013-10-21 19:24 ` Clemens Ladisch
  2013-10-21 19:25 ` [PATCH] [08/29] ALSA: dice: clear device lock when closing " Clemens Ladisch
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:24 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Implement the hwdep locking and notification mechanisms.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |  225 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 203 insertions(+), 22 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index d0575a9..7225878 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -5,6 +5,7 @@
  * Licensed under the terms of the GNU General Public License, version 2.
  */

+#include <linux/compat.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/firewire.h>
@@ -13,8 +14,11 @@
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
 #include <sound/control.h>
 #include <sound/core.h>
+#include <sound/firewire.h>
 #include <sound/hwdep.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
@@ -233,13 +237,18 @@
 struct dice {
 	struct snd_card *card;
 	struct fw_unit *unit;
+	spinlock_t lock;
 	struct mutex mutex;
 	unsigned int global_offset;
 	unsigned int rx_offset;
 	struct fw_address_handler notification_handler;
 	int owner_generation;
+	int dev_lock_count; /* > 0 driver, < 0 userspace */
+	bool dev_lock_changed;
 	bool global_enabled;
 	bool stream_running;
+	wait_queue_head_t hwdep_wait;
+	u32 notification_bits;
 	struct snd_pcm_substream *pcm;
 	struct fw_iso_resources resources;
 	struct amdtp_out_stream stream;
@@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = {
 	[6] = 192000,
 };

+static void dice_lock_changed(struct dice *dice)
+{
+	dice->dev_lock_changed = true;
+	wake_up(&dice->hwdep_wait);
+}
+
+static int dice_try_lock(struct dice *dice)
+{
+	int err;
+
+	spin_lock_irq(&dice->lock);
+
+	if (dice->dev_lock_count < 0) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	if (dice->dev_lock_count++ == 0)
+		dice_lock_changed(dice);
+	err = 0;
+
+out:
+	spin_unlock_irq(&dice->lock);
+
+	return err;
+}
+
+static void dice_unlock(struct dice *dice)
+{
+	spin_lock_irq(&dice->lock);
+
+	if (WARN_ON(dice->dev_lock_count <= 0))
+		goto out;
+
+	if (--dice->dev_lock_count == 0)
+		dice_lock_changed(dice);
+
+out:
+	spin_unlock_irq(&dice->lock);
+}
+
 static inline u64 global_address(struct dice *dice, unsigned int offset)
 {
 	return DICE_PRIVATE_SPACE + dice->global_offset + offset;
@@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 			      void *data, size_t length, void *callback_data)
 {
 	struct dice *dice = callback_data;
+	unsigned long flags;

 	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
 		fw_send_response(card, request, RCODE_TYPE_ERROR);
@@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
 		return;
 	}
-	dev_dbg(&dice->unit->device,
-		"notification: %08x\n", be32_to_cpup(data));
+	spin_lock_irqsave(&dice->lock, flags);
+	dice->notification_bits |= be32_to_cpup(data);
+	spin_unlock_irqrestore(&dice->lock, flags);
 	fw_send_response(card, request, RCODE_COMPLETE);
+	wake_up(&dice->hwdep_wait);
 }

 static int dice_open(struct snd_pcm_substream *substream)
@@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream)
 	unsigned int rate;
 	int err;

+	err = dice_try_lock(dice);
+	if (err < 0)
+		goto error;
+
 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 global_address(dice, GLOBAL_CLOCK_SELECT),
 				 &clock_sel, 4);
 	if (err < 0)
-		return err;
+		goto err_lock;
 	rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
-	if (rate >= ARRAY_SIZE(dice_rates))
-		return -ENXIO;
+	if (rate >= ARRAY_SIZE(dice_rates)) {
+		err = -ENXIO;
+		goto err_lock;
+	}
 	rate = dice_rates[rate];

 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 rx_address(dice, RX_NUMBER_AUDIO),
 				 &number_audio, 4);
 	if (err < 0)
-		return err;
+		goto err_lock;
 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 rx_address(dice, RX_NUMBER_MIDI),
 				 &number_midi, 4);
 	if (err < 0)
-		return err;
+		goto err_lock;

 	runtime->hw = hardware;

@@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream)
 					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
 					   5000, 8192000);
 	if (err < 0)
-		return err;
+		goto err_lock;

 	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 	if (err < 0)
-		return err;
+		goto err_lock;

 	return 0;
+
+err_lock:
+	dice_unlock(dice);
+error:
+	return err;
 }

 static int dice_close(struct snd_pcm_substream *substream)
 {
+	struct dice *dice = substream->private_data;
+
+	dice_unlock(dice);
+
 	return 0;
 }

@@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice)
 	return 0;
 }

-// TODO: implement these
-
 static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
 			    long count, loff_t *offset)
 {
-	return -EIO;
+	struct dice *dice = hwdep->private_data;
+	DEFINE_WAIT(wait);
+	union snd_firewire_event event;
+
+	spin_lock_irq(&dice->lock);
+
+	while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+		prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+		spin_unlock_irq(&dice->lock);
+		schedule();
+		finish_wait(&dice->hwdep_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		spin_lock_irq(&dice->lock);
+	}
+
+	memset(&event, 0, sizeof(event));
+	if (dice->dev_lock_changed) {
+		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+		event.lock_status.status = dice->dev_lock_count > 0;
+		dice->dev_lock_changed = false;
+
+		count = min(count, (long)sizeof(event.lock_status));
+	} else {
+		event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+		event.dice_notification.notification = dice->notification_bits;
+		dice->notification_bits = 0;
+
+		count = min(count, (long)sizeof(event.dice_notification));
+	}
+
+	spin_unlock_irq(&dice->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
 }

-static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file)
+static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+				    poll_table *wait)
 {
-	return -EIO;
+	struct dice *dice = hwdep->private_data;
+	unsigned int events;
+
+	poll_wait(file, &dice->hwdep_wait, wait);
+
+	spin_lock_irq(&dice->lock);
+	if (dice->dev_lock_changed || dice->notification_bits != 0)
+		events = POLLIN | POLLRDNORM;
+	else
+		events = 0;
+	spin_unlock_irq(&dice->lock);
+
+	return events;
 }

-static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
 {
+	struct fw_device *dev = fw_parent_device(dice->unit);
+	struct snd_firewire_get_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.type = SNDRV_FIREWIRE_TYPE_DICE;
+	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 unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
-				    poll_table *wait)
+static int dice_hwdep_lock(struct dice *dice)
+{
+	int err;
+
+	spin_lock_irq(&dice->lock);
+
+	if (dice->dev_lock_count == 0) {
+		dice->dev_lock_count = -1;
+		err = 0;
+	} else {
+		err = -EBUSY;
+	}
+
+	spin_unlock_irq(&dice->lock);
+
+	return err;
+}
+
+static int dice_hwdep_unlock(struct dice *dice)
 {
-	return POLLERR | POLLHUP;
+	int err;
+
+	spin_lock_irq(&dice->lock);
+
+	if (dice->dev_lock_count == -1) {
+		dice->dev_lock_count = 0;
+		err = 0;
+	} else {
+		err = -EBADFD;
+	}
+
+	spin_unlock_irq(&dice->lock);
+
+	return err;
 }

 static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 			    unsigned int cmd, unsigned long arg)
 {
-	return -EIO;
+	struct dice *dice = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return dice_hwdep_get_info(dice, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return dice_hwdep_lock(dice);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return dice_hwdep_unlock(dice);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+				   unsigned int cmd, unsigned long arg)
+{
+	return dice_hwdep_ioctl(hwdep, file, cmd,
+				(unsigned long)compat_ptr(arg));
 }
+#else
+#define dice_hwdep_compat_ioctl NULL
+#endif

 static int dice_create_hwdep(struct dice *dice)
 {
 	static const struct snd_hwdep_ops ops = {
 		.read         = dice_hwdep_read,
-		.open         = dice_hwdep_open,
-		.release      = dice_hwdep_release,
 		.poll         = dice_hwdep_poll,
 		.ioctl        = dice_hwdep_ioctl,
-		.ioctl_compat = dice_hwdep_ioctl,
+		.ioctl_compat = dice_hwdep_compat_ioctl,
 	};
 	struct snd_hwdep *hwdep;
 	int err;
@@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)

 	dice = card->private_data;
 	dice->card = card;
+	spin_lock_init(&dice->lock);
 	mutex_init(&dice->mutex);
 	dice->unit = unit;
+	init_waitqueue_head(&dice->hwdep_wait);

 	err = dice_init_offsets(dice);
 	if (err < 0)

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [08/29] ALSA: dice: clear device lock when closing hwdep device
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (6 preceding siblings ...)
  2013-10-21 19:24 ` [PATCH] [07/29] ALSA: dice: implement hwdep device Clemens Ladisch
@ 2013-10-21 19:25 ` Clemens Ladisch
  2013-10-21 19:25 ` [PATCH] [09/29] ALSA: firewire: introduce amdtp_out_stream_running() Clemens Ladisch
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:25 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Ensure that misbehaving or aborted userspace programs do not
accidentally keep the lock.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 7225878..ef04089 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -966,6 +966,18 @@ static int dice_hwdep_unlock(struct dice *dice)
 	return err;
 }

+static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct dice *dice = hwdep->private_data;
+
+	spin_lock_irq(&dice->lock);
+	if (dice->dev_lock_count == -1)
+		dice->dev_lock_count = 0;
+	spin_unlock_irq(&dice->lock);
+
+	return 0;
+}
+
 static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 			    unsigned int cmd, unsigned long arg)
 {
@@ -998,6 +1010,7 @@ static int dice_create_hwdep(struct dice *dice)
 {
 	static const struct snd_hwdep_ops ops = {
 		.read         = dice_hwdep_read,
+		.release      = dice_hwdep_release,
 		.poll         = dice_hwdep_poll,
 		.ioctl        = dice_hwdep_ioctl,
 		.ioctl_compat = dice_hwdep_compat_ioctl,

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [09/29] ALSA: firewire: introduce amdtp_out_stream_running()
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (7 preceding siblings ...)
  2013-10-21 19:25 ` [PATCH] [08/29] ALSA: dice: clear device lock when closing " Clemens Ladisch
@ 2013-10-21 19:25 ` Clemens Ladisch
  2013-10-21 19:26 ` [PATCH] [10/29] ALSA: dice: reorganize interface definitions Clemens Ladisch
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:25 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Introduce the helper function amdtp_out_stream_running().  This makes
many checks in amdtp.c clearer and frees the device drivers from having
to track this with a separate variable.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/amdtp.c    |   10 +++++-----
 sound/firewire/amdtp.h    |    6 ++++++
 sound/firewire/dice.c     |   17 +++++------------
 sound/firewire/speakers.c |    8 ++------
 4 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index efb2e29..d56b8e7 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -59,7 +59,7 @@ EXPORT_SYMBOL(amdtp_out_stream_init);
  */
 void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
 {
-	WARN_ON(!IS_ERR(s->context));
+	WARN_ON(amdtp_out_stream_running(s));
 	mutex_destroy(&s->mutex);
 	fw_unit_put(s->unit);
 }
@@ -89,7 +89,7 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
 	};
 	unsigned int sfc;

-	if (WARN_ON(!IS_ERR(s->context)))
+	if (WARN_ON(amdtp_out_stream_running(s)))
 		return;

 	for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
@@ -145,7 +145,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s,
 void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 				     snd_pcm_format_t format)
 {
-	if (WARN_ON(!IS_ERR(s->context)))
+	if (WARN_ON(amdtp_out_stream_running(s)))
 		return;

 	switch (format) {
@@ -481,7 +481,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)

 	mutex_lock(&s->mutex);

-	if (WARN_ON(!IS_ERR(s->context) ||
+	if (WARN_ON(amdtp_out_stream_running(s) ||
 		    (!s->pcm_channels && !s->midi_ports))) {
 		err = -EBADFD;
 		goto err_unlock;
@@ -577,7 +577,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
 {
 	mutex_lock(&s->mutex);

-	if (IS_ERR(s->context)) {
+	if (!amdtp_out_stream_running(s)) {
 		mutex_unlock(&s->mutex);
 		return;
 	}
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index fd4ce30..28b1bf5 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -1,6 +1,7 @@
 #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 #define SOUND_FIREWIRE_AMDTP_H_INCLUDED

+#include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include "packets-buffer.h"
@@ -92,6 +93,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
 unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);

+static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
+{
+	return !IS_ERR(s->context);
+}
+
 /**
  * amdtp_out_stream_set_pcm - configure format of PCM samples
  * @s: the AMDTP output stream to be configured
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index ef04089..3591aeb 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -246,7 +246,6 @@ struct dice {
 	int dev_lock_count; /* > 0 driver, < 0 userspace */
 	bool dev_lock_changed;
 	bool global_enabled;
-	bool stream_running;
 	wait_queue_head_t hwdep_wait;
 	u32 notification_bits;
 	struct snd_pcm_substream *pcm;
@@ -654,7 +653,7 @@ static int dice_stream_start_packets(struct dice *dice)
 {
 	int err;

-	if (dice->stream_running)
+	if (amdtp_out_stream_running(&dice->stream))
 		return 0;

 	err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
@@ -668,8 +667,6 @@ static int dice_stream_start_packets(struct dice *dice)
 		return err;
 	}

-	dice->stream_running = true;
-
 	return 0;
 }

@@ -712,14 +709,10 @@ error:

 static void dice_stream_stop_packets(struct dice *dice)
 {
-	if (!dice->stream_running)
-		return;
-
-	dice_enable_clear(dice);
-
-	amdtp_out_stream_stop(&dice->stream);
-
-	dice->stream_running = false;
+	if (amdtp_out_stream_running(&dice->stream)) {
+		dice_enable_clear(dice);
+		amdtp_out_stream_stop(&dice->stream);
+	}
 }

 static void dice_stream_stop(struct dice *dice)
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 2c63865..0ac5630 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -53,7 +53,6 @@ struct fwspk {
 	struct mutex mutex;
 	struct cmp_connection connection;
 	struct amdtp_out_stream stream;
-	bool stream_running;
 	bool mute;
 	s16 volume[6];
 	s16 volume_min;
@@ -189,10 +188,9 @@ static int fwspk_close(struct snd_pcm_substream *substream)

 static void fwspk_stop_stream(struct fwspk *fwspk)
 {
-	if (fwspk->stream_running) {
+	if (amdtp_out_stream_running(&fwspk->stream)) {
 		amdtp_out_stream_stop(&fwspk->stream);
 		cmp_connection_break(&fwspk->connection);
-		fwspk->stream_running = false;
 	}
 }

@@ -286,7 +284,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
 	if (amdtp_out_streaming_error(&fwspk->stream))
 		fwspk_stop_stream(fwspk);

-	if (!fwspk->stream_running) {
+	if (!amdtp_out_stream_running(&fwspk->stream)) {
 		err = cmp_connection_establish(&fwspk->connection,
 			amdtp_out_stream_get_max_payload(&fwspk->stream));
 		if (err < 0)
@@ -297,8 +295,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
 					fwspk->connection.speed);
 		if (err < 0)
 			goto err_connection;
-
-		fwspk->stream_running = true;
 	}

 	mutex_unlock(&fwspk->mutex);

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [10/29] ALSA: dice: reorganize interface definitions
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (8 preceding siblings ...)
  2013-10-21 19:25 ` [PATCH] [09/29] ALSA: firewire: introduce amdtp_out_stream_running() Clemens Ladisch
@ 2013-10-21 19:26 ` Clemens Ladisch
  2013-10-21 19:26 ` [PATCH] [11/29] ALSA: dice: fix device detection for other vendors Clemens Ladisch
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:26 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Move the DICE interface symbols into a separate header file, and add
more documentation.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice-interface.h |  371 +++++++++++++++++++++++++++++++++++++++
 sound/firewire/dice.c           |  209 ----------------------
 2 files changed, 373 insertions(+), 207 deletions(-)
 create mode 100644 sound/firewire/dice-interface.h

diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h
new file mode 100644
index 0000000..af916b9
--- /dev/null
+++ b/sound/firewire/dice-interface.h
@@ -0,0 +1,371 @@
+#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+
+/*
+ * DICE device interface definitions
+ */
+
+/*
+ * Generally, all registers can be read like memory, i.e., with quadlet read or
+ * block read transactions with any alignment or length.  Writes are not
+ * allowed except where noted; quadlet-sized registers must be written with
+ * a quadlet write transaction.
+ *
+ * All values are in big endian.  The DICE firmware runs on a little-endian CPU
+ * and just byte-swaps _all_ quadlets on the bus, so values without endianness
+ * (e.g. strings) get scrambled and must be byte-swapped again by the driver.
+ */
+
+/*
+ * Streaming is handled by the "DICE driver" interface.  Its registers are
+ * located in this private address space.
+ */
+#define DICE_PRIVATE_SPACE		0xffffe0000000uLL
+
+/*
+ * The registers are organized in several sections, which are organized
+ * separately to allow them to be extended individually.  Whether a register is
+ * supported can be detected by checking its offset against its section's size.
+ *
+ * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/
+ * size values are measured in quadlets.  Read-only.
+ */
+#define DICE_GLOBAL_OFFSET		0x00
+#define DICE_GLOBAL_SIZE		0x04
+#define DICE_TX_OFFSET			0x08
+#define DICE_TX_SIZE			0x0c
+#define DICE_RX_OFFSET			0x10
+#define DICE_RX_SIZE			0x14
+#define DICE_EXT_SYNC_OFFSET		0x18
+#define DICE_EXT_SYNC_SIZE		0x1c
+#define DICE_UNUSED2_OFFSET		0x20
+#define DICE_UNUSED2_SIZE		0x24
+
+/*
+ * Global settings.
+ */
+
+/*
+ * Stores the full 64-bit address (node ID and offset in the node's address
+ * space) where the device will send notifications.  Must be changed with
+ * a compare/swap transaction by the owner.  This register is automatically
+ * cleared on a bus reset.
+ */
+#define GLOBAL_OWNER			0x000
+#define  OWNER_NO_OWNER			0xffff000000000000uLL
+#define  OWNER_NODE_SHIFT		48
+
+/*
+ * A bitmask with asynchronous events; read-only.  When any event(s) happen,
+ * the bits of previous events are cleared, and the value of this register is
+ * also written to the address stored in the owner register.
+ */
+#define GLOBAL_NOTIFICATION		0x008
+/* Some registers in the Rx/Tx sections may have changed. */
+#define  NOTIFY_RX_CFG_CHG		0x00000001
+#define  NOTIFY_TX_CFG_CHG		0x00000002
+/* Lock status of the current clock source may have changed. */
+#define  NOTIFY_LOCK_CHG		0x00000010
+/* Write to the clock select register has been finished. */
+#define  NOTIFY_CLOCK_ACCEPTED		0x00000020
+/* Lock status of some clock source has changed. */
+#define  NOTIFY_EXT_STATUS		0x00000040
+/* Other bits may be used for device-specific events. */
+
+/*
+ * A name that can be customized for each device; read/write.  Padded with zero
+ * bytes.  Quadlets are byte-swapped.  The encoding is whatever the host driver
+ * happens to be using.
+ */
+#define GLOBAL_NICK_NAME		0x00c
+#define  NICK_NAME_SIZE			64
+
+/*
+ * The current sample rate and clock source; read/write.  Whether a clock
+ * source or sample rate is supported is device-specific; the internal clock
+ * source is always available.  Low/mid/high = up to 48/96/192 kHz.  This
+ * register can be changed even while streams are running.
+ */
+#define GLOBAL_CLOCK_SELECT		0x04c
+#define  CLOCK_SOURCE_MASK		0x000000ff
+#define  CLOCK_SOURCE_AES1		0x00000000
+#define  CLOCK_SOURCE_AES2		0x00000001
+#define  CLOCK_SOURCE_AES3		0x00000002
+#define  CLOCK_SOURCE_AES4		0x00000003
+#define  CLOCK_SOURCE_AES_ANY		0x00000004
+#define  CLOCK_SOURCE_ADAT		0x00000005
+#define  CLOCK_SOURCE_TDIF		0x00000006
+#define  CLOCK_SOURCE_WC		0x00000007
+#define  CLOCK_SOURCE_ARX1		0x00000008
+#define  CLOCK_SOURCE_ARX2		0x00000009
+#define  CLOCK_SOURCE_ARX3		0x0000000a
+#define  CLOCK_SOURCE_ARX4		0x0000000b
+#define  CLOCK_SOURCE_INTERNAL		0x0000000c
+#define  CLOCK_RATE_MASK		0x0000ff00
+#define  CLOCK_RATE_32000		0x00000000
+#define  CLOCK_RATE_44100		0x00000100
+#define  CLOCK_RATE_48000		0x00000200
+#define  CLOCK_RATE_88200		0x00000300
+#define  CLOCK_RATE_96000		0x00000400
+#define  CLOCK_RATE_176400		0x00000500
+#define  CLOCK_RATE_192000		0x00000600
+#define  CLOCK_RATE_ANY_LOW		0x00000700
+#define  CLOCK_RATE_ANY_MID		0x00000800
+#define  CLOCK_RATE_ANY_HIGH		0x00000900
+#define  CLOCK_RATE_NONE		0x00000a00
+#define  CLOCK_RATE_SHIFT		8
+
+/*
+ * Enable streaming; read/write.  Writing a non-zero value (re)starts all
+ * streams that have a valid iso channel set; zero stops all streams.  The
+ * streams' parameters must be configured before starting.  This register is
+ * automatically cleared on a bus reset.
+ */
+#define GLOBAL_ENABLE			0x050
+
+/*
+ * Status of the sample clock; read-only.
+ */
+#define GLOBAL_STATUS			0x054
+/* The current clock source is locked. */
+#define  STATUS_SOURCE_LOCKED		0x00000001
+/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */
+#define  STATUS_NOMINAL_RATE_MASK	0x0000ff00
+
+/*
+ * Status of all clock sources; read-only.
+ */
+#define GLOBAL_EXTENDED_STATUS		0x058
+/*
+ * The _LOCKED bits always show the current status; any change generates
+ * a notification.
+ */
+#define  EXT_STATUS_AES1_LOCKED		0x00000001
+#define  EXT_STATUS_AES2_LOCKED		0x00000002
+#define  EXT_STATUS_AES3_LOCKED		0x00000004
+#define  EXT_STATUS_AES4_LOCKED		0x00000008
+#define  EXT_STATUS_ADAT_LOCKED		0x00000010
+#define  EXT_STATUS_TDIF_LOCKED		0x00000020
+#define  EXT_STATUS_ARX1_LOCKED		0x00000040
+#define  EXT_STATUS_ARX2_LOCKED		0x00000080
+#define  EXT_STATUS_ARX3_LOCKED		0x00000100
+#define  EXT_STATUS_ARX4_LOCKED		0x00000200
+#define  EXT_STATUS_WC_LOCKED		0x00000400
+/*
+ * The _SLIP bits do not generate notifications; a set bit indicates that an
+ * error occurred since the last time when this register was read with
+ * a quadlet read transaction.
+ */
+#define  EXT_STATUS_AES1_SLIP		0x00010000
+#define  EXT_STATUS_AES2_SLIP		0x00020000
+#define  EXT_STATUS_AES3_SLIP		0x00040000
+#define  EXT_STATUS_AES4_SLIP		0x00080000
+#define  EXT_STATUS_ADAT_SLIP		0x00100000
+#define  EXT_STATUS_TDIF_SLIP		0x00200000
+#define  EXT_STATUS_ARX1_SLIP		0x00400000
+#define  EXT_STATUS_ARX2_SLIP		0x00800000
+#define  EXT_STATUS_ARX3_SLIP		0x01000000
+#define  EXT_STATUS_ARX4_SLIP		0x02000000
+#define  EXT_STATUS_WC_SLIP		0x04000000
+
+/*
+ * The measured rate of the current clock source, in Hz; read-only.
+ */
+#define GLOBAL_SAMPLE_RATE		0x05c
+
+/*
+ * The version of the DICE driver specification that this device conforms to;
+ * read-only.
+ */
+#define GLOBAL_VERSION			0x060
+
+/* Some old firmware versions do not have the following global registers: */
+
+/*
+ * Supported sample rates and clock sources; read-only.
+ */
+#define GLOBAL_CLOCK_CAPABILITIES	0x064
+#define  CLOCK_CAP_RATE_32000		0x00000001
+#define  CLOCK_CAP_RATE_44100		0x00000002
+#define  CLOCK_CAP_RATE_48000		0x00000004
+#define  CLOCK_CAP_RATE_88200		0x00000008
+#define  CLOCK_CAP_RATE_96000		0x00000010
+#define  CLOCK_CAP_RATE_176400		0x00000020
+#define  CLOCK_CAP_RATE_192000		0x00000040
+#define  CLOCK_CAP_SOURCE_AES1		0x00010000
+#define  CLOCK_CAP_SOURCE_AES2		0x00020000
+#define  CLOCK_CAP_SOURCE_AES3		0x00040000
+#define  CLOCK_CAP_SOURCE_AES4		0x00080000
+#define  CLOCK_CAP_SOURCE_AES_ANY	0x00100000
+#define  CLOCK_CAP_SOURCE_ADAT		0x00200000
+#define  CLOCK_CAP_SOURCE_TDIF		0x00400000
+#define  CLOCK_CAP_SOURCE_WC		0x00800000
+#define  CLOCK_CAP_SOURCE_ARX1		0x01000000
+#define  CLOCK_CAP_SOURCE_ARX2		0x02000000
+#define  CLOCK_CAP_SOURCE_ARX3		0x04000000
+#define  CLOCK_CAP_SOURCE_ARX4		0x08000000
+#define  CLOCK_CAP_SOURCE_INTERNAL	0x10000000
+
+/*
+ * Names of all clock sources; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.  Unused clock sources are included.
+ */
+#define GLOBAL_CLOCK_SOURCE_NAMES	0x068
+#define  CLOCK_SOURCE_NAMES_SIZE	256
+
+/*
+ * Capture stream settings.  This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported capture streams; read-only.
+ */
+#define TX_NUMBER			0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only.  The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define TX_SIZE				0x004
+
+/*
+ * The isochronous channel number on which packets are sent, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define TX_ISOCHRONOUS			0x008
+
+/*
+ * The number of audio channels; read-only.  There will be one quadlet per
+ * channel; the first channel is the first quadlet in a data block.
+ */
+#define TX_NUMBER_AUDIO			0x00c
+
+/*
+ * The number of MIDI ports, 0-8; read-only.  If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define TX_NUMBER_MIDI			0x010
+
+/*
+ * The speed at which the packets are sent, SCODE_100-_400; read/write.
+ */
+#define TX_SPEED			0x014
+
+/*
+ * Names of all audio channels; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define TX_NAMES			0x018
+#define  TX_NAMES_SIZE			256
+
+/*
+ * Audio IEC60958 capabilities; read-only.  Bitmask with one bit per audio
+ * channel.
+ */
+#define TX_AC3_CAPABILITIES		0x118
+
+/*
+ * Send audio data with IEC60958 label; read/write.  Bitmask with one bit per
+ * audio channel.  This register can be changed even while the stream is
+ * running.
+ */
+#define TX_AC3_ENABLE			0x11c
+
+/*
+ * Playback stream settings.  This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported playback streams; read-only.
+ */
+#define RX_NUMBER			0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only.  The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define RX_SIZE				0x004
+
+/*
+ * The isochronous channel number on which packets are received, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define RX_ISOCHRONOUS			0x008
+
+/*
+ * Index of first quadlet to be interpreted; read/write.  If > 0, that many
+ * quadlets at the beginning of each data block will be ignored, and all the
+ * audio and MIDI quadlets will follow.
+ */
+#define RX_SEQ_START			0x00c
+
+/*
+ * The number of audio channels; read-only.  There will be one quadlet per
+ * channel.
+ */
+#define RX_NUMBER_AUDIO			0x010
+
+/*
+ * The number of MIDI ports, 0-8; read-only.  If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define RX_NUMBER_MIDI			0x014
+
+/*
+ * Names of all audio channels; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define RX_NAMES			0x018
+#define  RX_NAMES_SIZE			256
+
+/*
+ * Audio IEC60958 capabilities; read-only.  Bitmask with one bit per audio
+ * channel.
+ */
+#define RX_AC3_CAPABILITIES		0x118
+
+/*
+ * Receive audio data with IEC60958 label; read/write.  Bitmask with one bit
+ * per audio channel.  This register can be changed even while the stream is
+ * running.
+ */
+#define RX_AC3_ENABLE			0x11c
+
+/*
+ * Extended synchronization information.
+ * This section can be read completely with a block read request.
+ */
+
+/*
+ * Current clock source; read-only.
+ */
+#define EXT_SYNC_CLOCK_SOURCE		0x000
+
+/*
+ * Clock source is locked (boolean); read-only.
+ */
+#define EXT_SYNC_LOCKED			0x004
+
+/*
+ * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or
+ * _NONE; read-only.
+ */
+#define EXT_SYNC_RATE			0x008
+
+/*
+ * ADAT user data bits; read-only.
+ */
+#define EXT_SYNC_ADAT_USER_DATA		0x00c
+/* The data bits, if available. */
+#define  ADAT_USER_DATA_MASK		0x0f
+/* The data bits are not available. */
+#define  ADAT_USER_DATA_NO_DATA		0x10
+
+#endif
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 3591aeb..1da1dde 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -26,212 +26,7 @@
 #include "amdtp.h"
 #include "iso-resources.h"
 #include "lib.h"
-
-#define DICE_PRIVATE_SPACE		0xffffe0000000uLL
-
-/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */
-#define DICE_GLOBAL_OFFSET		0x00
-#define DICE_GLOBAL_SIZE		0x04
-#define DICE_TX_OFFSET			0x08
-#define DICE_TX_SIZE			0x0c
-#define DICE_RX_OFFSET			0x10
-#define DICE_RX_SIZE			0x14
-
-/* pointed to by DICE_GLOBAL_OFFSET */
-#define GLOBAL_OWNER			0x000
-#define  OWNER_NO_OWNER			0xffff000000000000uLL
-#define  OWNER_NODE_SHIFT		48
-#define GLOBAL_NOTIFICATION		0x008
-#define  NOTIFY_RX_CFG_CHG		0x00000001
-#define  NOTIFY_TX_CFG_CHG		0x00000002
-#define  NOTIFY_DUP_ISOC		0x00000004
-#define  NOTIFY_BW_ERR			0x00000008
-#define  NOTIFY_LOCK_CHG		0x00000010
-#define  NOTIFY_CLOCK_ACCEPTED		0x00000020
-#define  NOTIFY_INTERFACE_CHG		0x00000040
-#define  NOTIFY_MESSAGE			0x00100000
-#define GLOBAL_NICK_NAME		0x00c
-#define  NICK_NAME_SIZE			64
-#define GLOBAL_CLOCK_SELECT		0x04c
-#define  CLOCK_SOURCE_MASK		0x000000ff
-#define  CLOCK_SOURCE_AES1		0x00000000
-#define  CLOCK_SOURCE_AES2		0x00000001
-#define  CLOCK_SOURCE_AES3		0x00000002
-#define  CLOCK_SOURCE_AES4		0x00000003
-#define  CLOCK_SOURCE_AES_ANY		0x00000004
-#define  CLOCK_SOURCE_ADAT		0x00000005
-#define  CLOCK_SOURCE_TDIF		0x00000006
-#define  CLOCK_SOURCE_WC		0x00000007
-#define  CLOCK_SOURCE_ARX1		0x00000008
-#define  CLOCK_SOURCE_ARX2		0x00000009
-#define  CLOCK_SOURCE_ARX3		0x0000000a
-#define  CLOCK_SOURCE_ARX4		0x0000000b
-#define  CLOCK_SOURCE_INTERNAL		0x0000000c
-#define  CLOCK_RATE_MASK		0x0000ff00
-#define  CLOCK_RATE_32000		0x00000000
-#define  CLOCK_RATE_44100		0x00000100
-#define  CLOCK_RATE_48000		0x00000200
-#define  CLOCK_RATE_88200		0x00000300
-#define  CLOCK_RATE_96000		0x00000400
-#define  CLOCK_RATE_176400		0x00000500
-#define  CLOCK_RATE_192000		0x00000600
-#define  CLOCK_RATE_ANY_LOW		0x00000700
-#define  CLOCK_RATE_ANY_MID		0x00000800
-#define  CLOCK_RATE_ANY_HIGH		0x00000900
-#define  CLOCK_RATE_NONE		0x00000a00
-#define  CLOCK_RATE_SHIFT		8
-#define GLOBAL_ENABLE			0x050
-#define  ENABLE				0x00000001
-#define GLOBAL_STATUS			0x054
-#define  STATUS_SOURCE_LOCKED		0x00000001
-#define  STATUS_RATE_CONFLICT		0x00000002
-#define  STATUS_NOMINAL_RATE_MASK	0x0000ff00
-#define GLOBAL_EXTENDED_STATUS		0x058
-#define  EXT_STATUS_AES1_LOCKED		0x00000001
-#define  EXT_STATUS_AES2_LOCKED		0x00000002
-#define  EXT_STATUS_AES3_LOCKED		0x00000004
-#define  EXT_STATUS_AES4_LOCKED		0x00000008
-#define  EXT_STATUS_ADAT_LOCKED		0x00000010
-#define  EXT_STATUS_TDIF_LOCKED		0x00000020
-#define  EXT_STATUS_ARX1_LOCKED		0x00000040
-#define  EXT_STATUS_ARX2_LOCKED		0x00000080
-#define  EXT_STATUS_ARX3_LOCKED		0x00000100
-#define  EXT_STATUS_ARX4_LOCKED		0x00000200
-#define  EXT_STATUS_WC_LOCKED		0x00000400
-#define  EXT_STATUS_AES1_SLIP		0x00010000
-#define  EXT_STATUS_AES2_SLIP		0x00020000
-#define  EXT_STATUS_AES3_SLIP		0x00040000
-#define  EXT_STATUS_AES4_SLIP		0x00080000
-#define  EXT_STATUS_ADAT_SLIP		0x00100000
-#define  EXT_STATUS_TDIF_SLIP		0x00200000
-#define  EXT_STATUS_ARX1_SLIP		0x00400000
-#define  EXT_STATUS_ARX2_SLIP		0x00800000
-#define  EXT_STATUS_ARX3_SLIP		0x01000000
-#define  EXT_STATUS_ARX4_SLIP		0x02000000
-#define  EXT_STATUS_WC_SLIP		0x04000000
-#define GLOBAL_SAMPLE_RATE		0x05c
-#define GLOBAL_VERSION			0x060
-#define GLOBAL_CLOCK_CAPABILITIES	0x064
-#define  CLOCK_CAP_RATE_32000		0x00000001
-#define  CLOCK_CAP_RATE_44100		0x00000002
-#define  CLOCK_CAP_RATE_48000		0x00000004
-#define  CLOCK_CAP_RATE_88200		0x00000008
-#define  CLOCK_CAP_RATE_96000		0x00000010
-#define  CLOCK_CAP_RATE_176400		0x00000020
-#define  CLOCK_CAP_RATE_192000		0x00000040
-#define  CLOCK_CAP_SOURCE_AES1		0x00010000
-#define  CLOCK_CAP_SOURCE_AES2		0x00020000
-#define  CLOCK_CAP_SOURCE_AES3		0x00040000
-#define  CLOCK_CAP_SOURCE_AES4		0x00080000
-#define  CLOCK_CAP_SOURCE_AES_ANY	0x00100000
-#define  CLOCK_CAP_SOURCE_ADAT		0x00200000
-#define  CLOCK_CAP_SOURCE_TDIF		0x00400000
-#define  CLOCK_CAP_SOURCE_WC		0x00800000
-#define  CLOCK_CAP_SOURCE_ARX1		0x01000000
-#define  CLOCK_CAP_SOURCE_ARX2		0x02000000
-#define  CLOCK_CAP_SOURCE_ARX3		0x04000000
-#define  CLOCK_CAP_SOURCE_ARX4		0x08000000
-#define  CLOCK_CAP_SOURCE_INTERNAL	0x10000000
-#define GLOBAL_CLOCK_SOURCE_NAMES	0x068
-#define  CLOCK_SOURCE_NAMES_SIZE	256
-
-/* pointed to by DICE_TX_OFFSET */
-#define TX_NUMBER			0x000
-#define TX_SIZE				0x004
-/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */
-#define TX_ISOCHRONOUS			0x008
-#define TX_NUMBER_AUDIO			0x00c
-#define TX_NUMBER_MIDI			0x010
-#define TX_SPEED			0x014
-#define TX_NAMES			0x018
-#define  TX_NAMES_SIZE			256
-#define TX_AC3_CAPABILITIES		0x118
-#define TX_AC3_ENABLE			0x11c
-
-/* pointed to by DICE_RX_OFFSET */
-#define RX_NUMBER			0x000
-#define RX_SIZE				0x004
-/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */
-#define RX_ISOCHRONOUS			0x008
-#define RX_SEQ_START			0x00c
-#define RX_NUMBER_AUDIO			0x010
-#define RX_NUMBER_MIDI			0x014
-#define RX_NAMES			0x018
-#define  RX_NAMES_SIZE			256
-#define RX_AC3_CAPABILITIES		0x118
-#define RX_AC3_ENABLE			0x11c
-
-
-#define FIRMWARE_LOAD_SPACE		0xffffe0100000uLL
-
-/* offset from FIRMWARE_LOAD_SPACE */
-#define FIRMWARE_VERSION		0x000
-#define FIRMWARE_OPCODE			0x004
-#define  OPCODE_MASK			0x00000fff
-#define  OPCODE_GET_IMAGE_DESC		0x00000000
-#define  OPCODE_DELETE_IMAGE		0x00000001
-#define  OPCODE_CREATE_IMAGE		0x00000002
-#define  OPCODE_UPLOAD			0x00000003
-#define  OPCODE_UPLOAD_STAT		0x00000004
-#define  OPCODE_RESET_IMAGE		0x00000005
-#define  OPCODE_TEST_ACTION		0x00000006
-#define  OPCODE_GET_RUNNING_IMAGE_VINFO	0x0000000a
-#define  OPCODE_EXECUTE			0x80000000
-#define FIRMWARE_RETURN_STATUS		0x008
-#define FIRMWARE_PROGRESS		0x00c
-#define  PROGRESS_CURR_MASK		0x00000fff
-#define  PROGRESS_MAX_MASK		0x00fff000
-#define  PROGRESS_TOUT_MASK		0x0f000000
-#define  PROGRESS_FLAG			0x80000000
-#define FIRMWARE_CAPABILITIES		0x010
-#define  FL_CAP_AUTOERASE		0x00000001
-#define  FL_CAP_PROGRESS		0x00000002
-#define FIRMWARE_DATA			0x02c
-#define  TEST_CMD_POKE			0x00000001
-#define  TEST_CMD_PEEK			0x00000002
-#define  CMD_GET_AVS_CNT		0x00000003
-#define  CMD_CLR_AVS_CNT		0x00000004
-#define  CMD_SET_MODE			0x00000005
-#define  CMD_SET_MIDIBP			0x00000006
-#define  CMD_GET_AVSPHASE		0x00000007
-#define  CMD_ENABLE_BNC_SYNC		0x00000008
-#define  CMD_PULSE_BNC_SYNC		0x00000009
-#define  CMD_EMUL_SLOW_CMD		0x0000000a
-#define FIRMWARE_TEST_DELAY		0xfd8
-#define FIRMWARE_TEST_BUF		0xfdc
-
-
-/* EAP */
-#define EAP_PRIVATE_SPACE		0xffffe0200000uLL
-
-#define EAP_CAPABILITY_OFFSET		0x000
-#define EAP_CAPABILITY_SIZE		0x004
-/* ... */
-
-#define EAP_ROUTER_CAPS			0x000
-#define  ROUTER_EXPOSED			0x00000001
-#define  ROUTER_READ_ONLY		0x00000002
-#define  ROUTER_FLASH			0x00000004
-#define  MAX_ROUTES_MASK		0xffff0000
-#define EAP_MIXER_CAPS			0x004
-#define  MIXER_EXPOSED			0x00000001
-#define  MIXER_READ_ONLY		0x00000002
-#define  MIXER_FLASH			0x00000004
-#define  MIXER_IN_DEV_MASK		0x000000f0
-#define  MIXER_OUT_DEV_MASK		0x00000f00
-#define  MIXER_INPUTS_MASK		0x00ff0000
-#define  MIXER_OUTPUTS_MASK		0xff000000
-#define EAP_GENERAL_CAPS		0x008
-#define  GENERAL_STREAM_CONFIG		0x00000001
-#define  GENERAL_FLASH			0x00000002
-#define  GENERAL_PEAK			0x00000004
-#define  GENERAL_MAX_TX_STREAMS_MASK	0x000000f0
-#define  GENERAL_MAX_RX_STREAMS_MASK	0x00000f00
-#define  GENERAL_STREAM_CONFIG_FLASH	0x00001000
-#define  GENERAL_CHIP_MASK		0x00ff0000
-#define  GENERAL_CHIP_DICE_II		0x00000000
-#define  GENERAL_CHIP_DICE_MINI		0x00010000
-#define  GENERAL_CHIP_DICE_JR		0x00020000
+#include "dice-interface.h"


 struct dice {
@@ -479,7 +274,7 @@ static int dice_enable_set(struct dice *dice)
 	__be32 value;
 	int rcode, err, errors = 0;

-	value = cpu_to_be32(ENABLE);
+	value = cpu_to_be32(1);
 	for (;;) {
 		rcode = fw_run_transaction(device->card,
 					   TCODE_WRITE_QUADLET_REQUEST,

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [11/29] ALSA: dice: fix device detection for other vendors
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (9 preceding siblings ...)
  2013-10-21 19:26 ` [PATCH] [10/29] ALSA: dice: reorganize interface definitions Clemens Ladisch
@ 2013-10-21 19:26 ` Clemens Ladisch
  2013-10-21 19:27 ` [PATCH] [12/29] ALSA: dice: support dual-wire stream format at 192 kHz Clemens Ladisch
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:26 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

DICE devices do not have a unique specifier ID in their unit directory
(it's always the same as the device vendor's ID), so rely on just the
version ID for driver loading, and use a heuristic in the probe callback
to detect actual DICE devices.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |  102 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 84 insertions(+), 18 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 1da1dde..b4827ff 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -827,28 +827,93 @@ static void dice_card_free(struct snd_card *card)
 	mutex_destroy(&dice->mutex);
 }

+#define DICE_CATEGORY_ID 0x04
+
+static int dice_interface_check(struct fw_unit *unit)
+{
+	static const int min_values[10] = {
+		10, 0x64 / 4,
+		10, 0x18 / 4,
+		10, 0x18 / 4,
+		0, 0,
+		0, 0,
+	};
+	struct fw_device *device = fw_parent_device(unit);
+	struct fw_csr_iterator it;
+	int key, value, vendor = -1, model = -1, err;
+	unsigned int i;
+	__be32 pointers[ARRAY_SIZE(min_values)];
+	__be32 version;
+
+	/*
+	 * Check that GUID and unit directory are constructed according to DICE
+	 * rules, i.e., that the specifier ID is the GUID's OUI, and that the
+	 * GUID chip ID consists of the 8-bit DICE category ID, the 10-bit
+	 * product ID, and a 22-bit serial number.
+	 */
+	fw_csr_iterator_init(&it, unit->directory);
+	while (fw_csr_iterator_next(&it, &key, &value)) {
+		switch (key) {
+		case CSR_SPECIFIER_ID:
+			vendor = value;
+			break;
+		case CSR_MODEL:
+			model = value;
+			break;
+		}
+	}
+	if (device->config_rom[3] != ((vendor << 8) | DICE_CATEGORY_ID) ||
+	    device->config_rom[4] >> 22 != model)
+		return -ENODEV;
+
+	/*
+	 * Check that the sub address spaces exist and are located inside the
+	 * private address space.  The minimum values are chosen so that all
+	 * minimally required registers are included.
+	 */
+	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+				 DICE_PRIVATE_SPACE,
+				 pointers, sizeof(pointers));
+	if (err < 0)
+		return -ENODEV;
+	for (i = 0; i < ARRAY_SIZE(pointers); ++i) {
+		value = be32_to_cpu(pointers[i]);
+		if (value < min_values[i] || value >= 0x40000)
+			return -ENODEV;
+	}
+
+	/*
+	 * Check that the implemented DICE driver specification major version
+	 * number matches.
+	 */
+	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+				 DICE_PRIVATE_SPACE +
+				 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+				 &version, 4);
+	if (err < 0)
+		return -ENODEV;
+	if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
+		dev_err(&unit->device,
+			"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
 static int dice_init_offsets(struct dice *dice)
 {
 	__be32 pointers[6];
-	unsigned int global_size, rx_size;
 	int err;

 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
-				 DICE_PRIVATE_SPACE, &pointers, 6 * 4);
+				 DICE_PRIVATE_SPACE,
+				 pointers, sizeof(pointers));
 	if (err < 0)
 		return err;

 	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
-	global_size = be32_to_cpu(pointers[1]);
 	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
-	rx_size = be32_to_cpu(pointers[5]);
-
-	/* some sanity checks to ensure that we actually have a DICE */
-	if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 ||
-	    dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) {
-		dev_err(&dice->unit->device, "invalid register pointers\n");
-		return -ENXIO;
-	}

 	return 0;
 }
@@ -881,8 +946,8 @@ static void dice_card_strings(struct dice *dice)
 	strcpy(model, "?");
 	fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
 	snprintf(card->longname, sizeof(card->longname),
-		 "%s %s, GUID %08x%08x at %s, S%d",
-		 vendor, model, dev->config_rom[3], dev->config_rom[4],
+		 "%s %s (serial %u) at %s, S%d",
+		 vendor, model, dev->config_rom[4] & 0x3fffff,
 		 dev_name(&dice->unit->device), 100 << dev->max_speed);

 	strcpy(card->mixername, "DICE");
@@ -895,6 +960,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	__be32 clock_sel;
 	int err;

+	err = dice_interface_check(unit);
+	if (err < 0)
+		return err;
+
 	err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card);
 	if (err < 0)
 		return err;
@@ -1020,15 +1089,12 @@ static void dice_bus_reset(struct fw_unit *unit)
 	mutex_unlock(&dice->mutex);
 }

-#define TC_OUI		0x000166
 #define DICE_INTERFACE	0x000001

 static const struct ieee1394_device_id dice_id_table[] = {
 	{
-		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
-				IEEE1394_MATCH_VERSION,
-		.specifier_id = TC_OUI,
-		.version      = DICE_INTERFACE,
+		.match_flags = IEEE1394_MATCH_VERSION,
+		.version     = DICE_INTERFACE,
 	},
 	{ }
 };

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [12/29] ALSA: dice: support dual-wire stream format at 192 kHz
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (10 preceding siblings ...)
  2013-10-21 19:26 ` [PATCH] [11/29] ALSA: dice: fix device detection for other vendors Clemens Ladisch
@ 2013-10-21 19:27 ` Clemens Ladisch
  2013-10-21 19:27 ` [PATCH] [13/29] ALSA: dice: optimize reading of consecutive registers Clemens Ladisch
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:27 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Change the AMDTP streaming code to handle the non-standard stream format
that DICE devices use at sample rates greater than 96 kHz.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/amdtp.c    |  155 +++++++++++++++++++++++++++++++++++++--------
 sound/firewire/amdtp.h    |   41 ++++--------
 sound/firewire/dice.c     |   29 ++++++--
 sound/firewire/speakers.c |    6 +-
 4 files changed, 164 insertions(+), 67 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index d56b8e7..a09c3b3 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -65,42 +65,66 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
 }
 EXPORT_SYMBOL(amdtp_out_stream_destroy);

+unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+	[CIP_SFC_32000]  =  8,
+	[CIP_SFC_44100]  =  8,
+	[CIP_SFC_48000]  =  8,
+	[CIP_SFC_88200]  = 16,
+	[CIP_SFC_96000]  = 16,
+	[CIP_SFC_176400] = 32,
+	[CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
 /**
- * amdtp_out_stream_set_rate - set the sample rate
+ * amdtp_out_stream_set_parameters - set stream parameters
  * @s: the AMDTP output stream to configure
  * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ *                as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
  *
- * The sample rate must be set before the stream is started, and must not be
+ * The parameters must be set before the stream is started, and must not be
  * changed while the stream is running.
  */
-void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
+void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
+				     unsigned int rate,
+				     unsigned int pcm_channels,
+				     unsigned int midi_ports)
 {
-	static const struct {
-		unsigned int rate;
-		unsigned int syt_interval;
-	} rate_info[] = {
-		[CIP_SFC_32000]  = {  32000,  8, },
-		[CIP_SFC_44100]  = {  44100,  8, },
-		[CIP_SFC_48000]  = {  48000,  8, },
-		[CIP_SFC_88200]  = {  88200, 16, },
-		[CIP_SFC_96000]  = {  96000, 16, },
-		[CIP_SFC_176400] = { 176400, 32, },
-		[CIP_SFC_192000] = { 192000, 32, },
+	static const unsigned int rates[] = {
+		[CIP_SFC_32000]  =  32000,
+		[CIP_SFC_44100]  =  44100,
+		[CIP_SFC_48000]  =  48000,
+		[CIP_SFC_88200]  =  88200,
+		[CIP_SFC_96000]  =  96000,
+		[CIP_SFC_176400] = 176400,
+		[CIP_SFC_192000] = 192000,
 	};
 	unsigned int sfc;

 	if (WARN_ON(amdtp_out_stream_running(s)))
 		return;

-	for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
-		if (rate_info[sfc].rate == rate)
+	for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
+		if (rates[sfc] == rate)
 			goto sfc_found;
 	WARN_ON(1);
 	return;

 sfc_found:
+	s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
+	if (s->dual_wire) {
+		sfc -= 2;
+		rate /= 2;
+		pcm_channels *= 2;
+	}
 	s->sfc = sfc;
-	s->syt_interval = rate_info[sfc].syt_interval;
+	s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
+	s->pcm_channels = pcm_channels;
+	s->midi_ports = midi_ports;
+
+	s->syt_interval = amdtp_syt_intervals[sfc];

 	/* default buffering in the device */
 	s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
@@ -108,21 +132,17 @@ sfc_found:
 		/* additional buffering needed to adjust for no-data packets */
 		s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_rate);
+EXPORT_SYMBOL(amdtp_out_stream_set_parameters);

 /**
  * amdtp_out_stream_get_max_payload - get the stream's packet size
  * @s: the AMDTP output stream
  *
  * This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and
- * amdtp_out_stream_set_midi().
+ * with amdtp_out_stream_set_parameters().
  */
 unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
 {
-	s->data_block_quadlets = s->pcm_channels;
-	s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
-
 	return 8 + s->syt_interval * s->data_block_quadlets * 4;
 }
 EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
@@ -133,14 +153,21 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
 static void amdtp_write_s32(struct amdtp_out_stream *s,
 			    struct snd_pcm_substream *pcm,
 			    __be32 *buffer, unsigned int frames);
+static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
+				     struct snd_pcm_substream *pcm,
+				     __be32 *buffer, unsigned int frames);
+static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
+				     struct snd_pcm_substream *pcm,
+				     __be32 *buffer, unsigned int frames);

 /**
  * amdtp_out_stream_set_pcm_format - set the PCM format
  * @s: the AMDTP output stream to configure
  * @format: the format of the ALSA PCM device
  *
- * The sample format must be set before the stream is started, and must not be
- * changed while the stream is running.
+ * The sample format must be set after the other paramters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
  */
 void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 				     snd_pcm_format_t format)
@@ -153,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 		WARN_ON(1);
 		/* fall through */
 	case SNDRV_PCM_FORMAT_S16:
-		s->transfer_samples = amdtp_write_s16;
+		if (s->dual_wire)
+			s->transfer_samples = amdtp_write_s16_dualwire;
+		else
+			s->transfer_samples = amdtp_write_s16;
 		break;
 	case SNDRV_PCM_FORMAT_S32:
-		s->transfer_samples = amdtp_write_s32;
+		if (s->dual_wire)
+			s->transfer_samples = amdtp_write_s32_dualwire;
+		else
+			s->transfer_samples = amdtp_write_s32;
 		break;
 	}
 }
@@ -305,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
 	}
 }

+static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
+				     struct snd_pcm_substream *pcm,
+				     __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
+	const u32 *src;
+
+	channels = s->pcm_channels;
+	src = (void *)runtime->dma_area +
+			s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+	frame_adjust_1 = channels - 1;
+	frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+
+	channels /= 2;
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < channels; ++c) {
+			*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+			src++;
+			buffer += 2;
+		}
+		buffer -= frame_adjust_1;
+		for (c = 0; c < channels; ++c) {
+			*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+			src++;
+			buffer += 2;
+		}
+		buffer -= frame_adjust_2;
+	}
+}
+
+static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
+				     struct snd_pcm_substream *pcm,
+				     __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
+	const u16 *src;
+
+	channels = s->pcm_channels;
+	src = (void *)runtime->dma_area +
+			s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+	frame_adjust_1 = channels - 1;
+	frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+
+	channels /= 2;
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < channels; ++c) {
+			*buffer = cpu_to_be32((*src << 8) | 0x40000000);
+			src++;
+			buffer += 2;
+		}
+		buffer -= frame_adjust_1;
+		for (c = 0; c < channels; ++c) {
+			*buffer = cpu_to_be32((*src << 8) | 0x40000000);
+			src++;
+			buffer += 2;
+		}
+		buffer -= frame_adjust_2;
+	}
+}
+
 static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
 				   __be32 *buffer, unsigned int frames)
 {
@@ -390,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 	s->packet_index = index;

 	if (pcm) {
+		if (s->dual_wire)
+			data_blocks *= 2;
+
 		ptr = s->pcm_buffer_pointer + data_blocks;
 		if (ptr >= pcm->runtime->buffer_size)
 			ptr -= pcm->runtime->buffer_size;
@@ -459,8 +557,7 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
  * @speed: firewire speed code
  *
  * The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(),
- * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format();
+ * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
  * and it must be started before any PCM or MIDI device can be started.
  */
 int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 28b1bf5..f3d03dd 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -15,10 +15,15 @@
  * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
  *	SYT_INTERVAL samples, with these two types alternating so that
  *	the overall sample rate comes out right.
+ * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
+ *	at half the actual sample rate with twice the number of channels;
+ *	two samples of a channel are stored consecutively in the packet.
+ *	Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
  */
 enum cip_out_flags {
 	CIP_NONBLOCKING	= 0x00,
 	CIP_BLOCKING	= 0x01,
+	CIP_HI_DUALWIRE	= 0x02,
 };

 /**
@@ -32,6 +37,7 @@ enum cip_sfc {
 	CIP_SFC_96000  = 4,
 	CIP_SFC_176400 = 5,
 	CIP_SFC_192000 = 6,
+	CIP_SFC_COUNT
 };

 #define AMDTP_OUT_PCM_FORMAT_BITS	(SNDRV_PCM_FMTBIT_S16 | \
@@ -48,6 +54,7 @@ struct amdtp_out_stream {
 	struct mutex mutex;

 	enum cip_sfc sfc;
+	bool dual_wire;
 	unsigned int data_block_quadlets;
 	unsigned int pcm_channels;
 	unsigned int midi_ports;
@@ -80,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
 			  enum cip_out_flags flags);
 void amdtp_out_stream_destroy(struct amdtp_out_stream *s);

-void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate);
+void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
+				     unsigned int rate,
+				     unsigned int pcm_channels,
+				     unsigned int midi_ports);
 unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);

 int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
@@ -93,39 +103,14 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
 unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);

+extern unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+
 static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
 {
 	return !IS_ERR(s->context);
 }

 /**
- * amdtp_out_stream_set_pcm - configure format of PCM samples
- * @s: the AMDTP output stream to be configured
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- *                as AM824 multi-bit linear audio
- *
- * This function must not be called while the stream is running.
- */
-static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s,
-					    unsigned int pcm_channels)
-{
-	s->pcm_channels = pcm_channels;
-}
-
-/**
- * amdtp_out_stream_set_midi - configure format of MIDI data
- * @s: the AMDTP output stream to be configured
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
- *
- * This function must not be called while the stream is running.
- */
-static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s,
-					     unsigned int midi_ports)
-{
-	s->midi_ports = midi_ports;
-}
-
-/**
  * amdtp_out_streaming_error - check for streaming error
  * @s: the AMDTP output stream
  *
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index b4827ff..8804e42 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -375,7 +375,7 @@ static int dice_open(struct snd_pcm_substream *substream)
 	struct dice *dice = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	__be32 clock_sel, number_audio, number_midi;
-	unsigned int rate;
+	unsigned int rate_index, rate;
 	int err;

 	err = dice_try_lock(dice);
@@ -387,12 +387,13 @@ static int dice_open(struct snd_pcm_substream *substream)
 				 &clock_sel, 4);
 	if (err < 0)
 		goto err_lock;
-	rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
-	if (rate >= ARRAY_SIZE(dice_rates)) {
+	rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
+							>> CLOCK_RATE_SHIFT;
+	if (rate_index >= ARRAY_SIZE(dice_rates)) {
 		err = -ENXIO;
 		goto err_lock;
 	}
-	rate = dice_rates[rate];
+	rate = dice_rates[rate_index];

 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 rx_address(dice, RX_NUMBER_AUDIO),
@@ -413,9 +414,20 @@ static int dice_open(struct snd_pcm_substream *substream)
 	runtime->hw.channels_min = be32_to_cpu(number_audio);
 	runtime->hw.channels_max = be32_to_cpu(number_audio);

-	amdtp_out_stream_set_rate(&dice->stream, rate);
-	amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio));
-	amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi));
+	amdtp_out_stream_set_parameters(&dice->stream, rate,
+					be32_to_cpu(number_audio),
+					be32_to_cpu(number_midi));
+
+	err = snd_pcm_hw_constraint_step(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+					 amdtp_syt_intervals[rate_index]);
+	if (err < 0)
+		goto err_lock;
+	err = snd_pcm_hw_constraint_step(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+					 amdtp_syt_intervals[rate_index]);
+	if (err < 0)
+		goto err_lock;

 	err = snd_pcm_hw_constraint_minmax(runtime,
 					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
@@ -993,7 +1005,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 		goto err_notification_handler;
 	dice->resources.channels_mask = 0x00000000ffffffffuLL;

-	err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING);
+	err = amdtp_out_stream_init(&dice->stream, unit,
+				    CIP_BLOCKING | CIP_HI_DUALWIRE);
 	if (err < 0)
 		goto err_resources;

diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 0ac5630..6a68caf 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -245,8 +245,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
 	if (err < 0)
 		goto error;

-	amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params));
-	amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params));
+	amdtp_out_stream_set_parameters(&fwspk->stream,
+					params_rate(hw_params),
+					params_channels(hw_params),
+					0);

 	amdtp_out_stream_set_pcm_format(&fwspk->stream,
 					params_format(hw_params));

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [13/29] ALSA: dice: optimize reading of consecutive registers
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (11 preceding siblings ...)
  2013-10-21 19:27 ` [PATCH] [12/29] ALSA: dice: support dual-wire stream format at 192 kHz Clemens Ladisch
@ 2013-10-21 19:27 ` Clemens Ladisch
  2013-10-21 19:28 ` [PATCH] [14/29] ALSA: firewire: extend snd_fw_transaction() Clemens Ladisch
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:27 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Instead of reading two consecutive register with two quadlet requests,
use one block read request.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   27 +++++++++++----------------
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 8804e42..e1d8dff 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -374,8 +374,8 @@ static int dice_open(struct snd_pcm_substream *substream)
 	};
 	struct dice *dice = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	__be32 clock_sel, number_audio, number_midi;
-	unsigned int rate_index, rate;
+	__be32 clock_sel, data[2];
+	unsigned int rate_index, number_audio, number_midi;
 	int err;

 	err = dice_try_lock(dice);
@@ -393,30 +393,25 @@ static int dice_open(struct snd_pcm_substream *substream)
 		err = -ENXIO;
 		goto err_lock;
 	}
-	rate = dice_rates[rate_index];

-	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 				 rx_address(dice, RX_NUMBER_AUDIO),
-				 &number_audio, 4);
-	if (err < 0)
-		goto err_lock;
-	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
-				 rx_address(dice, RX_NUMBER_MIDI),
-				 &number_midi, 4);
+				 data, 2 * 4);
 	if (err < 0)
 		goto err_lock;
+	number_audio = be32_to_cpu(data[0]);
+	number_midi = be32_to_cpu(data[1]);

 	runtime->hw = hardware;

-	runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+	runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]);
 	snd_pcm_limit_hw_rates(runtime);

-	runtime->hw.channels_min = be32_to_cpu(number_audio);
-	runtime->hw.channels_max = be32_to_cpu(number_audio);
+	runtime->hw.channels_min = number_audio;
+	runtime->hw.channels_max = number_audio;

-	amdtp_out_stream_set_parameters(&dice->stream, rate,
-					be32_to_cpu(number_audio),
-					be32_to_cpu(number_midi));
+	amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index],
+					number_audio, number_midi);

 	err = snd_pcm_hw_constraint_step(runtime, 0,
 					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [14/29] ALSA: firewire: extend snd_fw_transaction()
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (12 preceding siblings ...)
  2013-10-21 19:27 ` [PATCH] [13/29] ALSA: dice: optimize reading of consecutive registers Clemens Ladisch
@ 2013-10-21 19:28 ` Clemens Ladisch
  2013-10-21 19:29 ` [PATCH] [15/29] ALSA: dice: avoid superflous write at bus reset Clemens Ladisch
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:28 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Add a flag to snd_fw_transaction() to allow it to abort when a bus reset
happens.  This removes most of the duplicated error handling loops that
were required around calls to the low-level fw_run_transaction().

Also add a flag to suppress error messages; errors are expected when we
attempt to clean up after the device was unplugged.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/cmp.c      |   50 +++++------
 sound/firewire/dice.c     |  207 ++++++++++++++-------------------------------
 sound/firewire/fcp.c      |    2
 sound/firewire/isight.c   |   43 ++++-----
 sound/firewire/lib.c      |   24 ++++-
 sound/firewire/lib.h      |    7 +-
 sound/firewire/scs1x.c    |    8 +-
 sound/firewire/speakers.c |    2
 8 files changed, 137 insertions(+), 206 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index 645cb0b..efdbf58 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c,
 		      int (*check)(struct cmp_connection *c, __be32 pcr),
 		      enum bus_reset_handling bus_reset_handling)
 {
-	struct fw_device *device = fw_parent_device(c->resources.unit);
-	int generation = c->resources.generation;
-	int rcode, errors = 0;
 	__be32 old_arg, buffer[2];
 	int err;

@@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c,
 		old_arg = buffer[0];
 		buffer[1] = modify(c, buffer[0]);

-		rcode = fw_run_transaction(
-				device->card, TCODE_LOCK_COMPARE_SWAP,
-				device->node_id, generation, device->max_speed,
+		err = snd_fw_transaction(
+				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
 				CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
-				buffer, 8);
-
-		if (rcode == RCODE_COMPLETE) {
-			if (buffer[0] == old_arg) /* success? */
-				break;
-
-			if (check) {
-				err = check(c, buffer[0]);
-				if (err < 0)
-					return err;
-			}
-		} else if (rcode == RCODE_GENERATION)
-			goto bus_reset;
-		else if (rcode_is_permanent_error(rcode) || ++errors >= 3)
-			goto io_error;
+				buffer, 8,
+				FW_FIXED_GENERATION | c->resources.generation);
+
+		if (err < 0) {
+			if (err == -EAGAIN &&
+			    bus_reset_handling == SUCCEED_ON_BUS_RESET)
+				err = 0;
+			return err;
+		}
+
+		if (buffer[0] == old_arg) /* success? */
+			break;
+
+		if (check) {
+			err = check(c, buffer[0]);
+			if (err < 0)
+				return err;
+		}
 	}
 	c->last_pcr_value = buffer[1];

 	return 0;
-
-io_error:
-	cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode));
-	return -EIO;
-
-bus_reset:
-	return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0;
 }


@@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c,

 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 				 CSR_REGISTER_BASE + CSR_IMPR,
-				 &impr_be, 4);
+				 &impr_be, 4, 0);
 	if (err < 0)
 		return err;
 	impr = be32_to_cpu(impr_be);
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index e1d8dff..59d5ca4 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -118,7 +118,7 @@ static int dice_owner_set(struct dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
-	int rcode, err, errors = 0;
+	int err, errors = 0;

 	buffer = kmalloc(2 * 8, GFP_KERNEL);
 	if (!buffer)
@@ -132,31 +132,24 @@ static int dice_owner_set(struct dice *dice)

 		dice->owner_generation = device->generation;
 		smp_rmb(); /* node_id vs. generation */
-		rcode = fw_run_transaction(device->card,
-					   TCODE_LOCK_COMPARE_SWAP,
-					   device->node_id,
-					   dice->owner_generation,
-					   device->max_speed,
-					   global_address(dice, GLOBAL_OWNER),
-					   buffer, 2 * 8);
-
-		if (rcode == RCODE_COMPLETE) {
-			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
-				err = 0;
-			} else {
+		err = snd_fw_transaction(dice->unit,
+					 TCODE_LOCK_COMPARE_SWAP,
+					 global_address(dice, GLOBAL_OWNER),
+					 buffer, 2 * 8,
+					 FW_FIXED_GENERATION |
+							dice->owner_generation);
+
+		if (err == 0) {
+			if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
 				dev_err(&dice->unit->device,
 					"device is already in use\n");
 				err = -EBUSY;
 			}
 			break;
 		}
-		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-			dev_err(&dice->unit->device,
-				"setting device owner failed: %s\n",
-				fw_rcode_string(rcode));
-			err = -EIO;
+		if (err != -EAGAIN || ++errors >= 3)
 			break;
-		}
+
 		msleep(20);
 	}

@@ -169,7 +162,7 @@ static int dice_owner_update(struct dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
-	int rcode, err, errors = 0;
+	int err;

 	if (dice->owner_generation == -1)
 		return 0;
@@ -178,44 +171,26 @@ static int dice_owner_update(struct dice *dice)
 	if (!buffer)
 		return -ENOMEM;

-	for (;;) {
-		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
-		buffer[1] = cpu_to_be64(
-			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
-			dice->notification_handler.offset);
+	buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+	buffer[1] = cpu_to_be64(
+		((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+		dice->notification_handler.offset);

-		dice->owner_generation = device->generation;
-		smp_rmb(); /* node_id vs. generation */
-		rcode = fw_run_transaction(device->card,
-					   TCODE_LOCK_COMPARE_SWAP,
-					   device->node_id,
-					   dice->owner_generation,
-					   device->max_speed,
-					   global_address(dice, GLOBAL_OWNER),
-					   buffer, 2 * 8);
-
-		if (rcode == RCODE_COMPLETE) {
-			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
-				err = 0;
-			} else {
-				dev_err(&dice->unit->device,
-					"device is already in use\n");
-				err = -EBUSY;
-			}
-			break;
-		}
-		if (rcode == RCODE_GENERATION) {
-			err = 0; /* try again later */
-			break;
-		}
-		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+	dice->owner_generation = device->generation;
+	smp_rmb(); /* node_id vs. generation */
+	err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+				 global_address(dice, GLOBAL_OWNER),
+				 buffer, 2 * 8,
+				 FW_FIXED_GENERATION | dice->owner_generation);
+
+	if (err == 0) {
+		if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
 			dev_err(&dice->unit->device,
-				"setting device owner failed: %s\n",
-				fw_rcode_string(rcode));
-			err = -EIO;
-			break;
+				"device is already in use\n");
+			err = -EBUSY;
 		}
-		msleep(20);
+	} else if (err == -EAGAIN) {
+		err = 0; /* try again later */
 	}

 	kfree(buffer);
@@ -230,38 +205,19 @@ static void dice_owner_clear(struct dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
-	int rcode, errors = 0;

 	buffer = kmalloc(2 * 8, GFP_KERNEL);
 	if (!buffer)
 		return;

-	for (;;) {
-		buffer[0] = cpu_to_be64(
-			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
-			dice->notification_handler.offset);
-		buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
-
-		rcode = fw_run_transaction(device->card,
-					   TCODE_LOCK_COMPARE_SWAP,
-					   device->node_id,
-					   dice->owner_generation,
-					   device->max_speed,
-					   global_address(dice, GLOBAL_OWNER),
-					   buffer, 2 * 8);
-
-		if (rcode == RCODE_COMPLETE)
-			break;
-		if (rcode == RCODE_GENERATION)
-			break;
-		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-			dev_err(&dice->unit->device,
-				"clearing device owner failed: %s\n",
-				fw_rcode_string(rcode));
-			break;
-		}
-		msleep(20);
-	}
+	buffer[0] = cpu_to_be64(
+		((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+		dice->notification_handler.offset);
+	buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+	snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+			   global_address(dice, GLOBAL_OWNER),
+			   buffer, 2 * 8, FW_QUIET |
+			   FW_FIXED_GENERATION | dice->owner_generation);

 	kfree(buffer);

@@ -270,67 +226,32 @@ static void dice_owner_clear(struct dice *dice)

 static int dice_enable_set(struct dice *dice)
 {
-	struct fw_device *device = fw_parent_device(dice->unit);
 	__be32 value;
-	int rcode, err, errors = 0;
+	int err;

 	value = cpu_to_be32(1);
-	for (;;) {
-		rcode = fw_run_transaction(device->card,
-					   TCODE_WRITE_QUADLET_REQUEST,
-					   device->node_id,
-					   dice->owner_generation,
-					   device->max_speed,
-					   global_address(dice, GLOBAL_ENABLE),
-					   &value, 4);
-		if (rcode == RCODE_COMPLETE) {
-			dice->global_enabled = true;
-			err = 0;
-			break;
-		}
-		if (rcode == RCODE_GENERATION) {
-			err = -EAGAIN;
-			break;
-		}
-		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-			dev_err(&dice->unit->device,
-				"device enabling failed: %s\n",
-				fw_rcode_string(rcode));
-			err = -EIO;
-			break;
-		}
-		msleep(20);
-	}
+	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_ENABLE),
+				 &value, 4,
+				 FW_FIXED_GENERATION | dice->owner_generation);
+	if (err < 0)
+		return err;

-	return err;
+	dice->global_enabled = true;
+
+	return 0;
 }

 static void dice_enable_clear(struct dice *dice)
 {
-	struct fw_device *device = fw_parent_device(dice->unit);
 	__be32 value;
-	int rcode, errors = 0;

 	value = 0;
-	for (;;) {
-		rcode = fw_run_transaction(device->card,
-					   TCODE_WRITE_QUADLET_REQUEST,
-					   device->node_id,
-					   dice->owner_generation,
-					   device->max_speed,
-					   global_address(dice, GLOBAL_ENABLE),
-					   &value, 4);
-		if (rcode == RCODE_COMPLETE ||
-		    rcode == RCODE_GENERATION)
-			break;
-		if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-			dev_err(&dice->unit->device,
-				"device disabling failed: %s\n",
-				fw_rcode_string(rcode));
-			break;
-		}
-		msleep(20);
-	}
+	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   global_address(dice, GLOBAL_ENABLE),
+			   &value, 4, FW_QUIET |
+			   FW_FIXED_GENERATION | dice->owner_generation);
+
 	dice->global_enabled = false;
 }

@@ -384,7 +305,7 @@ static int dice_open(struct snd_pcm_substream *substream)

 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
 				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &clock_sel, 4);
+				 &clock_sel, 4, 0);
 	if (err < 0)
 		goto err_lock;
 	rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
@@ -396,7 +317,7 @@ static int dice_open(struct snd_pcm_substream *substream)

 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 				 rx_address(dice, RX_NUMBER_AUDIO),
-				 data, 2 * 4);
+				 data, 2 * 4, 0);
 	if (err < 0)
 		goto err_lock;
 	number_audio = be32_to_cpu(data[0]);
@@ -488,7 +409,7 @@ static int dice_stream_start(struct dice *dice)
 		err = snd_fw_transaction(dice->unit,
 					 TCODE_WRITE_QUADLET_REQUEST,
 					 rx_address(dice, RX_ISOCHRONOUS),
-					 &channel, 4);
+					 &channel, 4, 0);
 		if (err < 0)
 			goto err_resources;
 	}
@@ -502,7 +423,7 @@ static int dice_stream_start(struct dice *dice)
 err_rx_channel:
 	channel = cpu_to_be32((u32)-1);
 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
-			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4);
+			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
 err_resources:
 	fw_iso_resources_free(&dice->resources);
 error:
@@ -528,7 +449,7 @@ static void dice_stream_stop(struct dice *dice)

 	channel = cpu_to_be32((u32)-1);
 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
-			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4);
+			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);

 	fw_iso_resources_free(&dice->resources);
 }
@@ -880,7 +801,7 @@ static int dice_interface_check(struct fw_unit *unit)
 	 */
 	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
 				 DICE_PRIVATE_SPACE,
-				 pointers, sizeof(pointers));
+				 pointers, sizeof(pointers), 0);
 	if (err < 0)
 		return -ENODEV;
 	for (i = 0; i < ARRAY_SIZE(pointers); ++i) {
@@ -896,7 +817,7 @@ static int dice_interface_check(struct fw_unit *unit)
 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 				 DICE_PRIVATE_SPACE +
 				 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
-				 &version, 4);
+				 &version, 4, 0);
 	if (err < 0)
 		return -ENODEV;
 	if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
@@ -915,7 +836,7 @@ static int dice_init_offsets(struct dice *dice)

 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 				 DICE_PRIVATE_SPACE,
-				 pointers, sizeof(pointers));
+				 pointers, sizeof(pointers), 0);
 	if (err < 0)
 		return err;

@@ -939,7 +860,7 @@ static void dice_card_strings(struct dice *dice)
 	BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 				 global_address(dice, GLOBAL_NICK_NAME),
-				 card->shortname, sizeof(card->shortname));
+				 card->shortname, sizeof(card->shortname), 0);
 	if (err >= 0) {
 		/* DICE strings are returned in "always-wrong" endianness */
 		BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
@@ -1015,14 +936,14 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)

 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &clock_sel, 4);
+				 &clock_sel, 4, 0);
 	if (err < 0)
 		goto error;
 	clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK);
 	clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1);
 	err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST,
 				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &clock_sel, 4);
+				 &clock_sel, 4, 0);
 	if (err < 0)
 		goto error;

diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index ec578b5..860c080 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit,
 					  : TCODE_WRITE_BLOCK_REQUEST;
 		ret = snd_fw_transaction(t.unit, tcode,
 					 CSR_REGISTER_BASE + CSR_FCP_COMMAND,
-					 (void *)command, command_size);
+					 (void *)command, command_size, 0);
 		if (ret < 0)
 			break;

diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 58a5afe..fd42e6b 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle,

 static int isight_connect(struct isight *isight)
 {
-	int ch, err, rcode, errors = 0;
+	int ch, err;
 	__be32 value;

 retry_after_bus_reset:
@@ -230,27 +230,19 @@ retry_after_bus_reset:
 	}

 	value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
-	for (;;) {
-		rcode = fw_run_transaction(
-				isight->device->card,
-				TCODE_WRITE_QUADLET_REQUEST,
-				isight->device->node_id,
-				isight->resources.generation,
-				isight->device->max_speed,
-				isight->audio_base + REG_ISO_TX_CONFIG,
-				&value, 4);
-		if (rcode == RCODE_COMPLETE) {
-			return 0;
-		} else if (rcode == RCODE_GENERATION) {
-			fw_iso_resources_free(&isight->resources);
-			goto retry_after_bus_reset;
-		} else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-			err = -EIO;
-			goto err_resources;
-		}
-		msleep(5);
+	err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 isight->audio_base + REG_ISO_TX_CONFIG,
+				 &value, 4, FW_FIXED_GENERATION |
+				 isight->resources.generation);
+	if (err == -EAGAIN) {
+		fw_iso_resources_free(&isight->resources);
+		goto retry_after_bus_reset;
+	} else if (err < 0) {
+		goto err_resources;
 	}

+	return 0;
+
 err_resources:
 	fw_iso_resources_free(&isight->resources);
 error:
@@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream,
 static int reg_read(struct isight *isight, int offset, __be32 *value)
 {
 	return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
-				  isight->audio_base + offset, value, 4);
+				  isight->audio_base + offset, value, 4, 0);
 }

 static int reg_write(struct isight *isight, int offset, __be32 value)
 {
 	return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
-				  isight->audio_base + offset, &value, 4);
+				  isight->audio_base + offset, &value, 4, 0);
 }

 static void isight_stop_streaming(struct isight *isight)
 {
+	__be32 value;
+
 	if (!isight->context)
 		return;

@@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight)
 	fw_iso_context_destroy(isight->context);
 	isight->context = NULL;
 	fw_iso_resources_free(&isight->resources);
-	reg_write(isight, REG_AUDIO_ENABLE, 0);
+	value = 0;
+	snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   isight->audio_base + REG_AUDIO_ENABLE,
+			   &value, 4, FW_QUIET);
 }

 static int isight_hw_free(struct snd_pcm_substream *substream)
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 14eb414..7409edb 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -11,7 +11,7 @@
 #include <linux/module.h>
 #include "lib.h"

-#define ERROR_RETRY_DELAY_MS	5
+#define ERROR_RETRY_DELAY_MS	20

 /**
  * snd_fw_transaction - send a request and wait for its completion
@@ -20,6 +20,9 @@
  * @offset: the address in the target's address space
  * @buffer: input/output data
  * @length: length of @buffer
+ * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
+ *         request only in that generation; use %FW_QUIET to suppress error
+ *         messages
  *
  * Submits an asynchronous request to the target device, and waits for the
  * response.  The node ID and the current generation are derived from @unit.
@@ -27,14 +30,18 @@
  * Returns zero on success, or a negative error code.
  */
 int snd_fw_transaction(struct fw_unit *unit, int tcode,
-		       u64 offset, void *buffer, size_t length)
+		       u64 offset, void *buffer, size_t length,
+		       unsigned int flags)
 {
 	struct fw_device *device = fw_parent_device(unit);
 	int generation, rcode, tries = 0;

+	generation = flags & FW_GENERATION_MASK;
 	for (;;) {
-		generation = device->generation;
-		smp_rmb(); /* node_id vs. generation */
+		if (!(flags & FW_FIXED_GENERATION)) {
+			generation = device->generation;
+			smp_rmb(); /* node_id vs. generation */
+		}
 		rcode = fw_run_transaction(device->card, tcode,
 					   device->node_id, generation,
 					   device->max_speed, offset,
@@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
 		if (rcode == RCODE_COMPLETE)
 			return 0;

+		if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
+			return -EAGAIN;
+
 		if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
-			dev_err(&unit->device, "transaction failed: %s\n",
-				fw_rcode_string(rcode));
+			if (!(flags & FW_QUIET))
+				dev_err(&unit->device,
+					"transaction failed: %s\n",
+					fw_rcode_string(rcode));
 			return -EIO;
 		}

diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index aef3014..02cfabc 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -6,8 +6,13 @@

 struct fw_unit;

+#define FW_GENERATION_MASK	0x00ff
+#define FW_FIXED_GENERATION	0x0100
+#define FW_QUIET		0x0200
+
 int snd_fw_transaction(struct fw_unit *unit, int tcode,
-		       u64 offset, void *buffer, size_t length);
+		       u64 offset, void *buffer, size_t length,
+		       unsigned int flags);

 /* returns true if retrying the transaction would not make sense */
 static inline bool rcode_is_permanent_error(int rcode)
diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c
index 505fc81..858023c 100644
--- a/sound/firewire/scs1x.c
+++ b/sound/firewire/scs1x.c
@@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs)
 	data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
 			   scs->hss_handler.offset);
 	err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
-				 HSS1394_ADDRESS, &data, 8);
+				 HSS1394_ADDRESS, &data, 8, 0);
 	if (err < 0)
 		dev_err(&scs->unit->device, "HSS1394 communication failed\n");

@@ -455,12 +455,16 @@ err_card:
 static void scs_update(struct fw_unit *unit)
 {
 	struct scs *scs = dev_get_drvdata(&unit->device);
+	int generation;
 	__be64 data;

 	data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
 			   scs->hss_handler.offset);
+	generation = fw_parent_device(unit)->generation;
+	smp_rmb(); /* node_id vs. generation */
 	snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
-			   HSS1394_ADDRESS, &data, 8);
+			   HSS1394_ADDRESS, &data, 8,
+			   FW_FIXED_GENERATION | generation);
 }

 static void scs_remove(struct fw_unit *unit)
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 6a68caf..eb3f7dc 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -647,7 +647,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit)
 	int err;

 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-				 OXFORD_FIRMWARE_ID_ADDRESS, &data, 4);
+				 OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0);
 	return err >= 0 ? be32_to_cpu(data) : 0;
 }


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [15/29] ALSA: dice: avoid superflous write at bus reset
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (13 preceding siblings ...)
  2013-10-21 19:28 ` [PATCH] [14/29] ALSA: firewire: extend snd_fw_transaction() Clemens Ladisch
@ 2013-10-21 19:29 ` Clemens Ladisch
  2013-10-21 19:29 ` [PATCH] [16/29] ALSA: dice: remove 10s period length limit Clemens Ladisch
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:29 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

When a bus reset happens, the enable register is automatically cleared,
so we do not need to clear it manually when stopping the stream.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 59d5ca4..cfa98a8 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -246,6 +246,9 @@ static void dice_enable_clear(struct dice *dice)
 {
 	__be32 value;

+	if (!dice->global_enabled)
+		return;
+
 	value = 0;
 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
 			   global_address(dice, GLOBAL_ENABLE),
@@ -1009,6 +1012,8 @@ static void dice_bus_reset(struct fw_unit *unit)
 	 * manner.
 	 */
 	amdtp_out_stream_pcm_abort(&dice->stream);
+
+	dice->global_enabled = false;
 	dice_stream_stop_packets(dice);

 	dice_owner_update(dice);

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [16/29] ALSA: dice: remove 10s period length limit
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (14 preceding siblings ...)
  2013-10-21 19:29 ` [PATCH] [15/29] ALSA: dice: avoid superflous write at bus reset Clemens Ladisch
@ 2013-10-21 19:29 ` Clemens Ladisch
  2013-10-21 19:30 ` [PATCH] [17/29] ALSA: dice: remove superfluous field Clemens Ladisch
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:29 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Since commit f2b3614cefb6 (Don't check DMA time-out too shortly), we
need no longer to restrict the period length to less than 10 s.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index cfa98a8..57ceb13 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -350,7 +350,7 @@ static int dice_open(struct snd_pcm_substream *substream)

 	err = snd_pcm_hw_constraint_minmax(runtime,
 					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-					   5000, 8192000);
+					   5000, UINT_MAX);
 	if (err < 0)
 		goto err_lock;


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [17/29] ALSA: dice: remove superfluous field
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (15 preceding siblings ...)
  2013-10-21 19:29 ` [PATCH] [16/29] ALSA: dice: remove 10s period length limit Clemens Ladisch
@ 2013-10-21 19:30 ` Clemens Ladisch
  2013-10-21 19:31 ` [PATCH] [18/29] ALSA: dice: fix locking Clemens Ladisch
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:30 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

The pcm field was not actually used.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 57ceb13..2d198ae 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -43,7 +43,6 @@ struct dice {
 	bool global_enabled;
 	wait_queue_head_t hwdep_wait;
 	u32 notification_bits;
-	struct snd_pcm_substream *pcm;
 	struct fw_iso_resources resources;
 	struct amdtp_out_stream stream;
 };
@@ -564,8 +563,7 @@ static int dice_create_pcm(struct dice *dice)
 		return err;
 	pcm->private_data = dice;
 	strcpy(pcm->name, dice->card->shortname);
-	dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
-	dice->pcm->ops = &ops;
+	pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops;

 	return 0;
 }

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [18/29] ALSA: dice: fix locking
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (16 preceding siblings ...)
  2013-10-21 19:30 ` [PATCH] [17/29] ALSA: dice: remove superfluous field Clemens Ladisch
@ 2013-10-21 19:31 ` Clemens Ladisch
  2013-10-21 19:31 ` [PATCH] [19/29] ALSA: dice: make amdtp_rates[] const Clemens Ladisch
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:31 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

From: Stefan Richter <stefanr@s5r6.in-berlin.de>

Avoid a lock inversion between dice->mutex and pcm->open_mutex.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 2d198ae..2d3a04e 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -981,12 +981,12 @@ static void dice_remove(struct fw_unit *unit)
 {
 	struct dice *dice = dev_get_drvdata(&unit->device);

-	mutex_lock(&dice->mutex);
-
 	amdtp_out_stream_pcm_abort(&dice->stream);

 	snd_card_disconnect(dice->card);

+	mutex_lock(&dice->mutex);
+
 	dice_stream_stop(dice);
 	dice_owner_clear(dice);

@@ -999,8 +999,6 @@ static void dice_bus_reset(struct fw_unit *unit)
 {
 	struct dice *dice = dev_get_drvdata(&unit->device);

-	mutex_lock(&dice->mutex);
-
 	/*
 	 * On a bus reset, the DICE firmware disables streaming and then goes
 	 * off contemplating its own navel for hundreds of milliseconds before
@@ -1011,6 +1009,8 @@ static void dice_bus_reset(struct fw_unit *unit)
 	 */
 	amdtp_out_stream_pcm_abort(&dice->stream);

+	mutex_lock(&dice->mutex);
+
 	dice->global_enabled = false;
 	dice_stream_stop_packets(dice);


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [19/29] ALSA: dice: make amdtp_rates[] const
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (17 preceding siblings ...)
  2013-10-21 19:31 ` [PATCH] [18/29] ALSA: dice: fix locking Clemens Ladisch
@ 2013-10-21 19:31 ` Clemens Ladisch
  2013-10-21 19:32 ` [PATCH] [20/29] ALSA: dice: get clock capabilities Clemens Ladisch
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:31 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/amdtp.c |    2 +-
 sound/firewire/amdtp.h |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index a09c3b3..5540f70 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -65,7 +65,7 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
 }
 EXPORT_SYMBOL(amdtp_out_stream_destroy);

-unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 	[CIP_SFC_32000]  =  8,
 	[CIP_SFC_44100]  =  8,
 	[CIP_SFC_48000]  =  8,
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index f3d03dd..839ebf8 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -103,7 +103,7 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
 unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);

-extern unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];

 static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
 {

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [20/29] ALSA: dice: get clock capabilities
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (18 preceding siblings ...)
  2013-10-21 19:31 ` [PATCH] [19/29] ALSA: dice: make amdtp_rates[] const Clemens Ladisch
@ 2013-10-21 19:32 ` Clemens Ladisch
  2013-10-21 19:32 ` [PATCH] [21/29] ALSA: dice: allow notifications during initialization Clemens Ladisch
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:32 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

In preparation for sample rate selection support, ensure that the driver
knows about the device's clock capabilities.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 2d3a04e..06fef47 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -36,6 +36,7 @@ struct dice {
 	struct mutex mutex;
 	unsigned int global_offset;
 	unsigned int rx_offset;
+	unsigned int clock_caps;
 	struct fw_address_handler notification_handler;
 	int owner_generation;
 	int dev_lock_count; /* > 0 driver, < 0 userspace */
@@ -830,9 +831,10 @@ static int dice_interface_check(struct fw_unit *unit)
 	return 0;
 }

-static int dice_init_offsets(struct dice *dice)
+static int dice_read_params(struct dice *dice)
 {
 	__be32 pointers[6];
+	__be32 value;
 	int err;

 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
@@ -844,6 +846,23 @@ static int dice_init_offsets(struct dice *dice)
 	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
 	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;

+	/* some very old firmwares don't tell about their clock support */
+	if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) {
+		err = snd_fw_transaction(
+				dice->unit, TCODE_READ_QUADLET_REQUEST,
+				global_address(dice, GLOBAL_CLOCK_CAPABILITIES),
+				&value, 4, 0);
+		if (err < 0)
+			return err;
+		dice->clock_caps = be32_to_cpu(value);
+	} else {
+		/* this should be supported by any device */
+		dice->clock_caps = CLOCK_CAP_RATE_44100 |
+				   CLOCK_CAP_RATE_48000 |
+				   CLOCK_CAP_SOURCE_ARX1 |
+				   CLOCK_CAP_SOURCE_INTERNAL;
+	}
+
 	return 0;
 }

@@ -905,7 +924,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	dice->unit = unit;
 	init_waitqueue_head(&dice->hwdep_wait);

-	err = dice_init_offsets(dice);
+	err = dice_read_params(dice);
 	if (err < 0)
 		goto err_mutex;


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [21/29] ALSA: dice: allow notifications during initialization
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (19 preceding siblings ...)
  2013-10-21 19:32 ` [PATCH] [20/29] ALSA: dice: get clock capabilities Clemens Ladisch
@ 2013-10-21 19:32 ` Clemens Ladisch
  2013-10-21 19:33 ` [PATCH] [22/29] ALSA: dice: get rate-dependent parameters Clemens Ladisch
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:32 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Reorganize the initialization order so that the driver can receive
notifications earlier.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 06fef47..49b47ba 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -924,10 +924,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	dice->unit = unit;
 	init_waitqueue_head(&dice->hwdep_wait);

-	err = dice_read_params(dice);
-	if (err < 0)
-		goto err_mutex;
-
 	dice->notification_handler.length = 4;
 	dice->notification_handler.address_callback = dice_notification;
 	dice->notification_handler.callback_data = dice;
@@ -936,9 +932,17 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto err_mutex;

-	err = fw_iso_resources_init(&dice->resources, unit);
+	err = dice_owner_set(dice);
 	if (err < 0)
 		goto err_notification_handler;
+
+	err = dice_read_params(dice);
+	if (err < 0)
+		goto err_owner;
+
+	err = fw_iso_resources_init(&dice->resources, unit);
+	if (err < 0)
+		goto err_owner;
 	dice->resources.channels_mask = 0x00000000ffffffffuLL;

 	err = amdtp_out_stream_init(&dice->stream, unit,
@@ -946,10 +950,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto err_resources;

-	err = dice_owner_set(dice);
-	if (err < 0)
-		goto err_stream;
-
 	card->private_free = dice_card_free;

 	dice_card_strings(dice);
@@ -983,10 +983,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)

 	return 0;

-err_stream:
-	amdtp_out_stream_destroy(&dice->stream);
 err_resources:
 	fw_iso_resources_destroy(&dice->resources);
+err_owner:
+	dice_owner_clear(dice);
 err_notification_handler:
 	fw_core_remove_address_handler(&dice->notification_handler);
 err_mutex:

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [22/29] ALSA: dice: get rate-dependent parameters
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (20 preceding siblings ...)
  2013-10-21 19:32 ` [PATCH] [21/29] ALSA: dice: allow notifications during initialization Clemens Ladisch
@ 2013-10-21 19:33 ` Clemens Ladisch
  2013-10-21 19:34 ` [PATCH] [23/29] ALSA: dice: dynamic sample rate selection Clemens Ladisch
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:33 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

In preparation for sample rate selection support, read the stream
parameters that might change when running at different sample rates.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 90 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 49b47ba..e6bba6d 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -6,10 +6,12 @@
  */

 #include <linux/compat.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/firewire.h>
 #include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
@@ -37,11 +39,14 @@ struct dice {
 	unsigned int global_offset;
 	unsigned int rx_offset;
 	unsigned int clock_caps;
+	unsigned int rx_channels[3];
+	unsigned int rx_midi_ports[3];
 	struct fw_address_handler notification_handler;
 	int owner_generation;
 	int dev_lock_count; /* > 0 driver, < 0 userspace */
 	bool dev_lock_changed;
 	bool global_enabled;
+	struct completion clock_accepted;
 	wait_queue_head_t hwdep_wait;
 	u32 notification_bits;
 	struct fw_iso_resources resources;
@@ -53,15 +58,23 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");

 static const unsigned int dice_rates[] = {
+	/* mode 0 */
 	[0] =  32000,
 	[1] =  44100,
 	[2] =  48000,
+	/* mode 1 */
 	[3] =  88200,
 	[4] =  96000,
+	/* mode 2 */
 	[5] = 176400,
 	[6] = 192000,
 };

+static unsigned int rate_index_to_mode(unsigned int rate_index)
+{
+	return ((int)rate_index - 1) / 2;
+}
+
 static void dice_lock_changed(struct dice *dice)
 {
 	dice->dev_lock_changed = true;
@@ -264,6 +277,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 			      void *data, size_t length, void *callback_data)
 {
 	struct dice *dice = callback_data;
+	u32 bits;
 	unsigned long flags;

 	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
@@ -274,10 +288,17 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
 		return;
 	}
+
+	bits = be32_to_cpup(data);
+
 	spin_lock_irqsave(&dice->lock, flags);
-	dice->notification_bits |= be32_to_cpup(data);
+	dice->notification_bits |= bits;
 	spin_unlock_irqrestore(&dice->lock, flags);
+
 	fw_send_response(card, request, RCODE_COMPLETE);
+
+	if (bits & NOTIFY_CLOCK_ACCEPTED)
+		complete(&dice->clock_accepted);
 	wake_up(&dice->hwdep_wait);
 }

@@ -457,6 +478,26 @@ static void dice_stream_stop(struct dice *dice)
 	fw_iso_resources_free(&dice->resources);
 }

+static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
+{
+	__be32 value;
+	int err;
+
+	INIT_COMPLETION(dice->clock_accepted);
+
+	value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1);
+	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 global_address(dice, GLOBAL_CLOCK_SELECT),
+				 &value, 4, 0);
+	if (err < 0)
+		return err;
+
+	wait_for_completion_timeout(&dice->clock_accepted,
+				    msecs_to_jiffies(100));
+
+	return 0;
+}
+
 static int dice_hw_params(struct snd_pcm_substream *substream,
 			  struct snd_pcm_hw_params *hw_params)
 {
@@ -831,11 +872,51 @@ static int dice_interface_check(struct fw_unit *unit)
 	return 0;
 }

+static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i)
+		if ((dice->clock_caps & (1 << i)) &&
+		    rate_index_to_mode(i) == mode)
+			return i;
+
+	return -1;
+}
+
+static int dice_read_mode_params(struct dice *dice, unsigned int mode)
+{
+	__be32 values[2];
+	int rate_index, err;
+
+	rate_index = highest_supported_mode_rate(dice, mode);
+	if (rate_index < 0) {
+		dice->rx_channels[mode] = 0;
+		dice->rx_midi_ports[mode] = 0;
+		return 0;
+	}
+
+	err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+	if (err < 0)
+		return err;
+
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+				 rx_address(dice, RX_NUMBER_AUDIO),
+				 values, 2 * 4, 0);
+	if (err < 0)
+		return err;
+
+	dice->rx_channels[mode]   = be32_to_cpu(values[0]);
+	dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
+
+	return 0;
+}
+
 static int dice_read_params(struct dice *dice)
 {
 	__be32 pointers[6];
 	__be32 value;
-	int err;
+	int mode, err;

 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 				 DICE_PRIVATE_SPACE,
@@ -863,6 +944,12 @@ static int dice_read_params(struct dice *dice)
 				   CLOCK_CAP_SOURCE_INTERNAL;
 	}

+	for (mode = 2; mode >= 0; --mode) {
+		err = dice_read_mode_params(dice, mode);
+		if (err < 0)
+			return err;
+	}
+
 	return 0;
 }

@@ -922,6 +1009,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	spin_lock_init(&dice->lock);
 	mutex_init(&dice->mutex);
 	dice->unit = unit;
+	init_completion(&dice->clock_accepted);
 	init_waitqueue_head(&dice->hwdep_wait);

 	dice->notification_handler.length = 4;

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [23/29] ALSA: dice: dynamic sample rate selection
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (21 preceding siblings ...)
  2013-10-21 19:33 ` [PATCH] [22/29] ALSA: dice: get rate-dependent parameters Clemens Ladisch
@ 2013-10-21 19:34 ` Clemens Ladisch
  2013-10-21 19:34 ` [PATCH] [24/29] ALSA: dice: check clock change timeout Clemens Ladisch
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Instead of relying of some control panel application to configure some
fixed sample rate, allow applications to set it automatically.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |  137 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 102 insertions(+), 35 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index e6bba6d..61dd00c 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = {
 	[6] = 192000,
 };

+static unsigned int rate_to_index(unsigned int rate)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+		if (dice_rates[i] == rate)
+			return i;
+
+	return 0;
+}
+
 static unsigned int rate_index_to_mode(unsigned int rate_index)
 {
 	return ((int)rate_index - 1) / 2;
@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 	wake_up(&dice->hwdep_wait);
 }

+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct dice *dice = rule->private;
+	const struct snd_interval *channels =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *rate =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval allowed_rates = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, mode;
+
+	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
+		mode = rate_index_to_mode(i);
+		if ((dice->clock_caps & (1 << i)) &&
+		    snd_interval_test(channels, dice->rx_channels[mode])) {
+			allowed_rates.min = min(allowed_rates.min,
+						dice_rates[i]);
+			allowed_rates.max = max(allowed_rates.max,
+						dice_rates[i]);
+		}
+	}
+
+	return snd_interval_refine(rate, &allowed_rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+				    struct snd_pcm_hw_rule *rule)
+{
+	struct dice *dice = rule->private;
+	const struct snd_interval *rate =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval allowed_channels = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, mode;
+
+	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+		if ((dice->clock_caps & (1 << i)) &&
+		    snd_interval_test(rate, dice_rates[i])) {
+			mode = rate_index_to_mode(i);
+			allowed_channels.min = min(allowed_channels.min,
+						   dice->rx_channels[mode]);
+			allowed_channels.max = max(allowed_channels.max,
+						   dice->rx_channels[mode]);
+		}
+
+	return snd_interval_refine(channels, &allowed_channels);
+}
+
 static int dice_open(struct snd_pcm_substream *substream)
 {
 	static const struct snd_pcm_hardware hardware = {
@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream)
 			SNDRV_PCM_INFO_INTERLEAVED |
 			SNDRV_PCM_INFO_BLOCK_TRANSFER,
 		.formats = AMDTP_OUT_PCM_FORMAT_BITS,
+		.channels_min = UINT_MAX,
+		.channels_max = 0,
 		.buffer_bytes_max = 16 * 1024 * 1024,
 		.period_bytes_min = 1,
 		.period_bytes_max = UINT_MAX,
@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream)
 	};
 	struct dice *dice = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	__be32 clock_sel, data[2];
-	unsigned int rate_index, number_audio, number_midi;
+	unsigned int i;
 	int err;

 	err = dice_try_lock(dice);
 	if (err < 0)
 		goto error;

-	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
-				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &clock_sel, 4, 0);
-	if (err < 0)
-		goto err_lock;
-	rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
-							>> CLOCK_RATE_SHIFT;
-	if (rate_index >= ARRAY_SIZE(dice_rates)) {
-		err = -ENXIO;
-		goto err_lock;
-	}
-
-	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
-				 rx_address(dice, RX_NUMBER_AUDIO),
-				 data, 2 * 4, 0);
-	if (err < 0)
-		goto err_lock;
-	number_audio = be32_to_cpu(data[0]);
-	number_midi = be32_to_cpu(data[1]);
-
 	runtime->hw = hardware;

-	runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]);
+	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+		if (dice->clock_caps & (1 << i))
+			runtime->hw.rates |=
+				snd_pcm_rate_to_rate_bit(dice_rates[i]);
 	snd_pcm_limit_hw_rates(runtime);

-	runtime->hw.channels_min = number_audio;
-	runtime->hw.channels_max = number_audio;
+	for (i = 0; i < 3; ++i)
+		if (dice->rx_channels[i]) {
+			runtime->hw.channels_min = min(runtime->hw.channels_min,
+						       dice->rx_channels[i]);
+			runtime->hw.channels_max = max(runtime->hw.channels_max,
+						       dice->rx_channels[i]);
+		}

-	amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index],
-					number_audio, number_midi);
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  dice_rate_constraint, dice,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		goto err_lock;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  dice_channels_constraint, dice,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		goto err_lock;

 	err = snd_pcm_hw_constraint_step(runtime, 0,
-					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-					 amdtp_syt_intervals[rate_index]);
+					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
 	if (err < 0)
 		goto err_lock;
 	err = snd_pcm_hw_constraint_step(runtime, 0,
-					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-					 amdtp_syt_intervals[rate_index]);
+					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
 	if (err < 0)
 		goto err_lock;

@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 			  struct snd_pcm_hw_params *hw_params)
 {
 	struct dice *dice = substream->private_data;
+	unsigned int rate_index, mode;
 	int err;

 	mutex_lock(&dice->mutex);
@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 					       params_buffer_bytes(hw_params));
 	if (err < 0)
-		goto error;
+		return err;

+	rate_index = rate_to_index(params_rate(hw_params));
+	err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+	if (err < 0)
+		return err;
+
+	mode = rate_index_to_mode(rate_index);
+	amdtp_out_stream_set_parameters(&dice->stream,
+					params_rate(hw_params),
+					params_channels(hw_params),
+					dice->rx_midi_ports[mode]);
 	amdtp_out_stream_set_pcm_format(&dice->stream,
 					params_format(hw_params));

 	return 0;
-
-error:
-	return err;
 }

 static int dice_hw_free(struct snd_pcm_substream *substream)

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [24/29] ALSA: dice: check clock change timeout
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (22 preceding siblings ...)
  2013-10-21 19:34 ` [PATCH] [23/29] ALSA: dice: dynamic sample rate selection Clemens Ladisch
@ 2013-10-21 19:34 ` Clemens Ladisch
  2013-10-21 19:34 ` [PATCH] [25/29] ALSA: dice: add a proc file to show device information Clemens Ladisch
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Output a warning if the wait for the clock change notification times
out.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 61dd00c..3395c8b 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -551,8 +551,9 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
 	if (err < 0)
 		return err;

-	wait_for_completion_timeout(&dice->clock_accepted,
-				    msecs_to_jiffies(100));
+	if (!wait_for_completion_timeout(&dice->clock_accepted,
+					 msecs_to_jiffies(100)))
+		dev_warn(&dice->unit->device, "clock change timed out\n");

 	return 0;
 }

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [25/29] ALSA: dice: add a proc file to show device information
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (23 preceding siblings ...)
  2013-10-21 19:34 ` [PATCH] [24/29] ALSA: dice: check clock change timeout Clemens Ladisch
@ 2013-10-21 19:34 ` Clemens Ladisch
  2013-10-21 19:35 ` [PATCH] [26/29] ALSA: dice: document quadlet alignment Clemens Ladisch
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

For easier debugging, add a proc file to show the device's capabilities
and current status.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |  246 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 246 insertions(+)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 3395c8b..25a9636 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -22,6 +22,7 @@
 #include <sound/core.h>
 #include <sound/firewire.h>
 #include <sound/hwdep.h>
+#include <sound/info.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -857,6 +858,249 @@ static int dice_create_hwdep(struct dice *dice)
 	return 0;
 }

+static int dice_proc_read_mem(struct dice *dice, void *buffer,
+			      unsigned int offset_q, unsigned int quadlets)
+{
+	unsigned int i;
+	int err;
+
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+				 DICE_PRIVATE_SPACE + 4 * offset_q,
+				 buffer, 4 * quadlets, 0);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < quadlets; ++i)
+		be32_to_cpus(&((u32 *)buffer)[i]);
+
+	return 0;
+}
+
+static const char *str_from_array(const char *const strs[], unsigned int count,
+				  unsigned int i)
+{
+	if (i < count)
+		return strs[i];
+	else
+		return "(unknown)";
+}
+
+static void dice_proc_fixup_string(char *s, unsigned int size)
+{
+	unsigned int i;
+
+	for (i = 0; i < size; i += 4)
+		cpu_to_le32s((u32 *)(s + i));
+
+	for (i = 0; i < size - 2; ++i) {
+		if (s[i] == '\0')
+			return;
+		if (s[i] == '\\' && s[i + 1] == '\\') {
+			s[i + 2] = '\0';
+			return;
+		}
+	}
+	s[size - 1] = '\0';
+}
+
+static void dice_proc_read(struct snd_info_entry *entry,
+			   struct snd_info_buffer *buffer)
+{
+	static const char *const section_names[5] = {
+		"global", "tx", "rx", "ext_sync", "unused2"
+	};
+	static const char *const clock_sources[] = {
+		"aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
+		"wc", "arx1", "arx2", "arx3", "arx4", "internal"
+	};
+	static const char *const rates[] = {
+		"32000", "44100", "48000", "88200", "96000", "176400", "192000",
+		"any low", "any mid", "any high", "none"
+	};
+	struct dice *dice = entry->private_data;
+	u32 sections[ARRAY_SIZE(section_names) * 2];
+	struct {
+		u32 number;
+		u32 size;
+	} tx_rx_header;
+	union {
+		struct {
+			u32 owner_hi, owner_lo;
+			u32 notification;
+			char nick_name[NICK_NAME_SIZE];
+			u32 clock_select;
+			u32 enable;
+			u32 status;
+			u32 extended_status;
+			u32 sample_rate;
+			u32 version;
+			u32 clock_caps;
+			char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
+		} global;
+		struct {
+			u32 iso;
+			u32 number_audio;
+			u32 number_midi;
+			u32 speed;
+			char names[TX_NAMES_SIZE];
+			u32 ac3_caps;
+			u32 ac3_enable;
+		} tx;
+		struct {
+			u32 iso;
+			u32 seq_start;
+			u32 number_audio;
+			u32 number_midi;
+			char names[RX_NAMES_SIZE];
+			u32 ac3_caps;
+			u32 ac3_enable;
+		} rx;
+		struct {
+			u32 clock_source;
+			u32 locked;
+			u32 rate;
+			u32 adat_user_data;
+		} ext_sync;
+	} buf;
+	unsigned int quadlets, stream, i;
+
+	if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
+		return;
+	snd_iprintf(buffer, "sections:\n");
+	for (i = 0; i < ARRAY_SIZE(section_names); ++i)
+		snd_iprintf(buffer, "  %s: offset %u, size %u\n",
+			    section_names[i],
+			    sections[i * 2], sections[i * 2 + 1]);
+
+	quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
+	if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
+		return;
+	snd_iprintf(buffer, "global:\n");
+	snd_iprintf(buffer, "  owner: %04x:%04x%08x\n",
+		    buf.global.owner_hi >> 16,
+		    buf.global.owner_hi & 0xffff, buf.global.owner_lo);
+	snd_iprintf(buffer, "  notification: %08x\n", buf.global.notification);
+	dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
+	snd_iprintf(buffer, "  nick name: %s\n", buf.global.nick_name);
+	snd_iprintf(buffer, "  clock select: %s %s\n",
+		    str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
+				   buf.global.clock_select & CLOCK_SOURCE_MASK),
+		    str_from_array(rates, ARRAY_SIZE(rates),
+				   (buf.global.clock_select & CLOCK_RATE_MASK)
+				   >> CLOCK_RATE_SHIFT));
+	snd_iprintf(buffer, "  enable: %u\n", buf.global.enable);
+	snd_iprintf(buffer, "  status: %slocked %s\n",
+		    buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
+		    str_from_array(rates, ARRAY_SIZE(rates),
+				   (buf.global.status &
+				    STATUS_NOMINAL_RATE_MASK)
+				   >> CLOCK_RATE_SHIFT));
+	snd_iprintf(buffer, "  ext status: %08x\n", buf.global.extended_status);
+	snd_iprintf(buffer, "  sample rate: %u\n", buf.global.sample_rate);
+	snd_iprintf(buffer, "  version: %u.%u.%u.%u\n",
+		    (buf.global.version >> 24) & 0xff,
+		    (buf.global.version >> 16) & 0xff,
+		    (buf.global.version >>  8) & 0xff,
+		    (buf.global.version >>  0) & 0xff);
+	if (quadlets >= 90) {
+		snd_iprintf(buffer, "  clock caps:");
+		for (i = 0; i <= 6; ++i)
+			if (buf.global.clock_caps & (1 << i))
+				snd_iprintf(buffer, " %s", rates[i]);
+		for (i = 0; i <= 12; ++i)
+			if (buf.global.clock_caps & (1 << (16 + i)))
+				snd_iprintf(buffer, " %s", clock_sources[i]);
+		snd_iprintf(buffer, "\n");
+		dice_proc_fixup_string(buf.global.clock_source_names,
+				       CLOCK_SOURCE_NAMES_SIZE);
+		snd_iprintf(buffer, "  clock source names: %s\n",
+			    buf.global.clock_source_names);
+	}
+
+	if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
+		return;
+	quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx));
+	for (stream = 0; stream < tx_rx_header.number; ++stream) {
+		if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
+				       stream * tx_rx_header.size,
+				       quadlets) < 0)
+			break;
+		snd_iprintf(buffer, "tx %u:\n", stream);
+		snd_iprintf(buffer, "  iso channel: %d\n", (int)buf.tx.iso);
+		snd_iprintf(buffer, "  audio channels: %u\n",
+			    buf.tx.number_audio);
+		snd_iprintf(buffer, "  midi ports: %u\n", buf.tx.number_midi);
+		snd_iprintf(buffer, "  speed: S%u\n", 100u << buf.tx.speed);
+		if (quadlets >= 68) {
+			dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
+			snd_iprintf(buffer, "  names: %s\n", buf.tx.names);
+		}
+		if (quadlets >= 70) {
+			snd_iprintf(buffer, "  ac3 caps: %08x\n",
+				    buf.tx.ac3_caps);
+			snd_iprintf(buffer, "  ac3 enable: %08x\n",
+				    buf.tx.ac3_enable);
+		}
+	}
+
+	if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
+		return;
+	quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx));
+	for (stream = 0; stream < tx_rx_header.number; ++stream) {
+		if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
+				       stream * tx_rx_header.size,
+				       quadlets) < 0)
+			break;
+		snd_iprintf(buffer, "rx %u:\n", stream);
+		snd_iprintf(buffer, "  iso channel: %d\n", (int)buf.rx.iso);
+		snd_iprintf(buffer, "  sequence start: %u\n",
+			    (int)buf.rx.seq_start);
+		snd_iprintf(buffer, "  audio channels: %u\n",
+			    buf.rx.number_audio);
+		snd_iprintf(buffer, "  midi ports: %u\n", buf.rx.number_midi);
+		if (quadlets >= 68) {
+			dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
+			snd_iprintf(buffer, "  names: %s\n", buf.rx.names);
+		}
+		if (quadlets >= 70) {
+			snd_iprintf(buffer, "  ac3 caps: %08x\n",
+				    buf.rx.ac3_caps);
+			snd_iprintf(buffer, "  ac3 enable: %08x\n",
+				    buf.rx.ac3_enable);
+		}
+	}
+
+	quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
+	if (quadlets >= 4) {
+		if (dice_proc_read_mem(dice, &buf.ext_sync,
+				       sections[6], 4) < 0)
+			return;
+		snd_iprintf(buffer, "ext status:\n");
+		snd_iprintf(buffer, "  clock source: %s\n",
+			    str_from_array(clock_sources,
+					   ARRAY_SIZE(clock_sources),
+					   buf.ext_sync.clock_source));
+		snd_iprintf(buffer, "  locked: %u\n", buf.ext_sync.locked);
+		snd_iprintf(buffer, "  rate: %s\n",
+			    str_from_array(rates, ARRAY_SIZE(rates),
+					   buf.ext_sync.rate));
+		snd_iprintf(buffer, "  adat user data: ");
+		if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
+			snd_iprintf(buffer, "-\n");
+		else
+			snd_iprintf(buffer, "%x\n",
+				    buf.ext_sync.adat_user_data);
+	}
+}
+
+static void dice_create_proc(struct dice *dice)
+{
+	struct snd_info_entry *entry;
+
+	if (!snd_card_proc_new(dice->card, "dice", &entry))
+		snd_info_set_text_ops(entry, dice, dice_proc_read);
+}
+
 static void dice_card_free(struct snd_card *card)
 {
 	struct dice *dice = card->private_data;
@@ -1131,6 +1375,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto error;

+	dice_create_proc(dice);
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [26/29] ALSA: dice: document quadlet alignment
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (24 preceding siblings ...)
  2013-10-21 19:34 ` [PATCH] [25/29] ALSA: dice: add a proc file to show device information Clemens Ladisch
@ 2013-10-21 19:35 ` Clemens Ladisch
  2013-10-21 19:40 ` [PATCH] [27/29] ALSA: dice: dice_proc_read: remove wrong typecast Clemens Ladisch
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Doing accesses without quadlet alignment is a bad idea because the
firmware's byte-swapping would garble the data; clarify this in the
documentation.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice-interface.h |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h
index af916b9..27b044f 100644
--- a/sound/firewire/dice-interface.h
+++ b/sound/firewire/dice-interface.h
@@ -7,9 +7,9 @@

 /*
  * Generally, all registers can be read like memory, i.e., with quadlet read or
- * block read transactions with any alignment or length.  Writes are not
- * allowed except where noted; quadlet-sized registers must be written with
- * a quadlet write transaction.
+ * block read transactions with at least quadlet-aligned offset and length.
+ * Writes are not allowed except where noted; quadlet-sized registers must be
+ * written with a quadlet write transaction.
  *
  * All values are in big endian.  The DICE firmware runs on a little-endian CPU
  * and just byte-swaps _all_ quadlets on the bus, so values without endianness

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [27/29] ALSA: dice: dice_proc_read: remove wrong typecast
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (25 preceding siblings ...)
  2013-10-21 19:35 ` [PATCH] [26/29] ALSA: dice: document quadlet alignment Clemens Ladisch
@ 2013-10-21 19:40 ` Clemens Ladisch
  2013-10-21 19:41 ` [PATCH] [28/29] ALSA: dice: fix detection of Weiss devices Clemens Ladisch
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:40 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

Remove a wrong typecast that resulted from a copy-and-paste error.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 25a9636..5f0f102 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -1053,8 +1053,7 @@ static void dice_proc_read(struct snd_info_entry *entry,
 			break;
 		snd_iprintf(buffer, "rx %u:\n", stream);
 		snd_iprintf(buffer, "  iso channel: %d\n", (int)buf.rx.iso);
-		snd_iprintf(buffer, "  sequence start: %u\n",
-			    (int)buf.rx.seq_start);
+		snd_iprintf(buffer, "  sequence start: %u\n", buf.rx.seq_start);
 		snd_iprintf(buffer, "  audio channels: %u\n",
 			    buf.rx.number_audio);
 		snd_iprintf(buffer, "  midi ports: %u\n", buf.rx.number_midi);

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [28/29] ALSA: dice: fix detection of Weiss devices
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (26 preceding siblings ...)
  2013-10-21 19:40 ` [PATCH] [27/29] ALSA: dice: dice_proc_read: remove wrong typecast Clemens Ladisch
@ 2013-10-21 19:41 ` Clemens Ladisch
  2013-10-21 19:42 ` [PATCH] [29/29] ALSA: dice: restrict the driver to playback-only devices Clemens Ladisch
  2013-10-24  9:39 ` [GIT PULL] [00/29] playback-only DICE driver Takashi Iwai
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:41 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

While most DICE devices keep TCAT's default category ID of 0x04, Weiss
devices identify themselves with 0x00.

Reported-by: Rolf Anderegg <rolf.anderegg@weiss.ch>
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/dice.c |   17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 5f0f102..49d630b 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -1109,7 +1109,10 @@ static void dice_card_free(struct snd_card *card)
 	mutex_destroy(&dice->mutex);
 }

-#define DICE_CATEGORY_ID 0x04
+#define OUI_WEISS		0x001c6a
+
+#define DICE_CATEGORY_ID	0x04
+#define WEISS_CATEGORY_ID	0x00

 static int dice_interface_check(struct fw_unit *unit)
 {
@@ -1123,15 +1126,15 @@ static int dice_interface_check(struct fw_unit *unit)
 	struct fw_device *device = fw_parent_device(unit);
 	struct fw_csr_iterator it;
 	int key, value, vendor = -1, model = -1, err;
-	unsigned int i;
+	unsigned int category, i;
 	__be32 pointers[ARRAY_SIZE(min_values)];
 	__be32 version;

 	/*
 	 * Check that GUID and unit directory are constructed according to DICE
 	 * rules, i.e., that the specifier ID is the GUID's OUI, and that the
-	 * GUID chip ID consists of the 8-bit DICE category ID, the 10-bit
-	 * product ID, and a 22-bit serial number.
+	 * GUID chip ID consists of the 8-bit category ID, the 10-bit product
+	 * ID, and a 22-bit serial number.
 	 */
 	fw_csr_iterator_init(&it, unit->directory);
 	while (fw_csr_iterator_next(&it, &key, &value)) {
@@ -1144,7 +1147,11 @@ static int dice_interface_check(struct fw_unit *unit)
 			break;
 		}
 	}
-	if (device->config_rom[3] != ((vendor << 8) | DICE_CATEGORY_ID) ||
+	if (vendor == OUI_WEISS)
+		category = WEISS_CATEGORY_ID;
+	else
+		category = DICE_CATEGORY_ID;
+	if (device->config_rom[3] != ((vendor << 8) | category) ||
 	    device->config_rom[4] >> 22 != model)
 		return -ENODEV;


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* [PATCH] [29/29] ALSA: dice: restrict the driver to playback-only devices
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (27 preceding siblings ...)
  2013-10-21 19:41 ` [PATCH] [28/29] ALSA: dice: fix detection of Weiss devices Clemens Ladisch
@ 2013-10-21 19:42 ` Clemens Ladisch
  2013-10-24  9:39 ` [GIT PULL] [00/29] playback-only DICE driver Takashi Iwai
  29 siblings, 0 replies; 31+ messages in thread
From: Clemens Ladisch @ 2013-10-21 19:42 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, linux1394-devel

At the moment, this driver supports only playback, while FFADO supports
(only) full-duplex devices.  So, prevent conflicts by not claiming
devices that would be better handled by FFADO.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 sound/firewire/Kconfig |   10 ++++++----
 sound/firewire/dice.c  |    9 +++++++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 9153309..b3e274f 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -12,14 +12,16 @@ config SND_FIREWIRE_LIB
 	depends on SND_PCM

 config SND_DICE
-	tristate "DICE devices (EXPERIMENTAL)"
+	tristate "DICE-based DACs (EXPERIMENTAL)"
 	select SND_HWDEP
 	select SND_PCM
 	select SND_FIREWIRE_LIB
 	help
-	  Say Y here to include support for many FireWire audio interfaces
-	  based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied
-	  Technologies.
+	  Say Y here to include support for many DACs based on the DICE
+	  chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
+
+	  At the moment, this driver supports playback only.  If you
+	  want to use devices that support capturing, use FFADO instead.

 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-dice.
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 49d630b..6feee66 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -1128,6 +1128,7 @@ static int dice_interface_check(struct fw_unit *unit)
 	int key, value, vendor = -1, model = -1, err;
 	unsigned int category, i;
 	__be32 pointers[ARRAY_SIZE(min_values)];
+	__be32 tx_data[4];
 	__be32 version;

 	/*
@@ -1171,6 +1172,14 @@ static int dice_interface_check(struct fw_unit *unit)
 			return -ENODEV;
 	}

+	/* We support playback only. Let capture devices be handled by FFADO. */
+	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+				 DICE_PRIVATE_SPACE +
+				 be32_to_cpu(pointers[2]) * 4,
+				 tx_data, sizeof(tx_data), 0);
+	if (err < 0 || (tx_data[0] && tx_data[3]))
+		return -ENODEV;
+
 	/*
 	 * Check that the implemented DICE driver specification major version
 	 * number matches.

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk

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

* Re: [GIT PULL] [00/29] playback-only DICE driver
  2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
                   ` (28 preceding siblings ...)
  2013-10-21 19:42 ` [PATCH] [29/29] ALSA: dice: restrict the driver to playback-only devices Clemens Ladisch
@ 2013-10-24  9:39 ` Takashi Iwai
  29 siblings, 0 replies; 31+ messages in thread
From: Takashi Iwai @ 2013-10-24  9:39 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel

At Mon, 21 Oct 2013 21:19:23 +0200,
Clemens Ladisch wrote:
> 
> Hi Takashi,
> 
> please pull this driver for FireWire DACs based on the DICE chip
> family.  While the driver supports only PCM playback at the
> moment, it fills a gap left by FFADO, which supports only full-
> duplex devices.

I already pulled it yesterday.  Sorry, forgot to inform you.
Thanks.


Takashi

> 
> Thanks,
> Clemens
> ----------------------------------------------------------------
> 
> The following changes since commit 6e4664525b1db28f8c4e1130957f70a94c19213e:
> 
>   Linux 3.11 (2013-09-02 13:46:10 -0700)
> 
> are available in the git repository at:
> 
>   git://git.alsa-project.org/alsa-kprivate.git dice-driver-playback-only
> 
> for you to fetch changes up to b20be8de1b3972ccf9af72850b045214faa8d830:
> 
>   ALSA: dice: restrict the driver to playback-only devices (2013-10-20 22:07:57 +0200)
> 
> ----------------------------------------------------------------
> Clemens Ladisch (28):
>       ALSA: add DICE driver
>       ALSA: dice: optimize bus reset handling
>       ALSA: dice: allow all sample rates
>       ALSA: dice: reduce noisy logging
>       ALSA: dice, firewire-lib: add blocking mode
>       ALSA: dice: fix hang when unplugging a running device
>       ALSA: dice: implement hwdep device
>       ALSA: dice: clear device lock when closing hwdep device
>       ALSA: firewire: introduce amdtp_out_stream_running()
>       ALSA: dice: reorganize interface definitions
>       ALSA: dice: fix device detection for other vendors
>       ALSA: dice: support dual-wire stream format at 192 kHz
>       ALSA: dice: optimize reading of consecutive registers
>       ALSA: firewire: extend snd_fw_transaction()
>       ALSA: dice: avoid superflous write at bus reset
>       ALSA: dice: remove 10s period length limit
>       ALSA: dice: remove superfluous field
>       ALSA: dice: make amdtp_rates[] const
>       ALSA: dice: get clock capabilities
>       ALSA: dice: allow notifications during initialization
>       ALSA: dice: get rate-dependent parameters
>       ALSA: dice: dynamic sample rate selection
>       ALSA: dice: check clock change timeout
>       ALSA: dice: add a proc file to show device information
>       ALSA: dice: document quadlet alignment
>       ALSA: dice: dice_proc_read: remove wrong typecast
>       ALSA: dice: fix detection of Weiss devices
>       ALSA: dice: restrict the driver to playback-only devices
> 
> Stefan Richter (1):
>       ALSA: dice: fix locking
> 
>  Documentation/ioctl/ioctl-number.txt |    1 +
>  include/uapi/sound/Kbuild            |    1 +
>  include/uapi/sound/asound.h          |    3 +-
>  include/uapi/sound/firewire.h        |   51 ++
>  sound/firewire/Kconfig               |   15 +
>  sound/firewire/Makefile              |    2 +
>  sound/firewire/amdtp.c               |  209 +++--
>  sound/firewire/amdtp.h               |   46 +-
>  sound/firewire/cmp.c                 |   50 +-
>  sound/firewire/dice-interface.h      |  371 +++++++++
>  sound/firewire/dice.c                | 1494 ++++++++++++++++++++++++++++++++++
>  sound/firewire/fcp.c                 |    2 +-
>  sound/firewire/isight.c              |   43 +-
>  sound/firewire/lib.c                 |   24 +-
>  sound/firewire/lib.h                 |    7 +-
>  sound/firewire/scs1x.c               |    8 +-
>  sound/firewire/speakers.c            |   16 +-
>  17 files changed, 2192 insertions(+), 151 deletions(-)
>  create mode 100644 include/uapi/sound/firewire.h
>  create mode 100644 sound/firewire/dice-interface.h
>  create mode 100644 sound/firewire/dice.c
> 

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

end of thread, other threads:[~2013-10-24  9:36 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
2013-10-21 19:21 ` [PATCH] [01/29] ALSA: add " Clemens Ladisch
2013-10-21 19:21 ` [PATCH] [02/29] ALSA: dice: optimize bus reset handling Clemens Ladisch
2013-10-21 19:22 ` [PATCH] [03/29] ALSA: dice: allow all sample rates Clemens Ladisch
2013-10-21 19:23 ` [PATCH] [04/29] ALSA: dice: reduce noisy logging Clemens Ladisch
2013-10-21 19:23 ` [PATCH] [05/29] ALSA: dice, firewire-lib: add blocking mode Clemens Ladisch
2013-10-21 19:24 ` [PATCH] [06/29] ALSA: dice: fix hang when unplugging a running device Clemens Ladisch
2013-10-21 19:24 ` [PATCH] [07/29] ALSA: dice: implement hwdep device Clemens Ladisch
2013-10-21 19:25 ` [PATCH] [08/29] ALSA: dice: clear device lock when closing " Clemens Ladisch
2013-10-21 19:25 ` [PATCH] [09/29] ALSA: firewire: introduce amdtp_out_stream_running() Clemens Ladisch
2013-10-21 19:26 ` [PATCH] [10/29] ALSA: dice: reorganize interface definitions Clemens Ladisch
2013-10-21 19:26 ` [PATCH] [11/29] ALSA: dice: fix device detection for other vendors Clemens Ladisch
2013-10-21 19:27 ` [PATCH] [12/29] ALSA: dice: support dual-wire stream format at 192 kHz Clemens Ladisch
2013-10-21 19:27 ` [PATCH] [13/29] ALSA: dice: optimize reading of consecutive registers Clemens Ladisch
2013-10-21 19:28 ` [PATCH] [14/29] ALSA: firewire: extend snd_fw_transaction() Clemens Ladisch
2013-10-21 19:29 ` [PATCH] [15/29] ALSA: dice: avoid superflous write at bus reset Clemens Ladisch
2013-10-21 19:29 ` [PATCH] [16/29] ALSA: dice: remove 10s period length limit Clemens Ladisch
2013-10-21 19:30 ` [PATCH] [17/29] ALSA: dice: remove superfluous field Clemens Ladisch
2013-10-21 19:31 ` [PATCH] [18/29] ALSA: dice: fix locking Clemens Ladisch
2013-10-21 19:31 ` [PATCH] [19/29] ALSA: dice: make amdtp_rates[] const Clemens Ladisch
2013-10-21 19:32 ` [PATCH] [20/29] ALSA: dice: get clock capabilities Clemens Ladisch
2013-10-21 19:32 ` [PATCH] [21/29] ALSA: dice: allow notifications during initialization Clemens Ladisch
2013-10-21 19:33 ` [PATCH] [22/29] ALSA: dice: get rate-dependent parameters Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [23/29] ALSA: dice: dynamic sample rate selection Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [24/29] ALSA: dice: check clock change timeout Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [25/29] ALSA: dice: add a proc file to show device information Clemens Ladisch
2013-10-21 19:35 ` [PATCH] [26/29] ALSA: dice: document quadlet alignment Clemens Ladisch
2013-10-21 19:40 ` [PATCH] [27/29] ALSA: dice: dice_proc_read: remove wrong typecast Clemens Ladisch
2013-10-21 19:41 ` [PATCH] [28/29] ALSA: dice: fix detection of Weiss devices Clemens Ladisch
2013-10-21 19:42 ` [PATCH] [29/29] ALSA: dice: restrict the driver to playback-only devices Clemens Ladisch
2013-10-24  9:39 ` [GIT PULL] [00/29] playback-only DICE driver Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).