All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers
@ 2014-10-26 13:03 Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 01/29] ALSA: dice: Rename structure and its members Takashi Sakamoto
                   ` (31 more replies)
  0 siblings, 32 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This patchset is to enable ALSA Dice driver to handle more models, to
improve ALSA firewire-speakers driver as OXFW driver. This patchset is
for linux-next branch and all of my work for Linux 3.19.

== Features:
Currently, ALSA Dice driver is limited for playback-only devices. The aim
of this patchset is to add supports for:
 - Full-duplex streams with synchronization
 - Both PCM capture and playback
 - Both MIDI capture and playback

Currently, ALSA firewire-speakers driver is only for two playback-only models.
And this driver has a bug for one of them. The aim of this patchset is to fix
the bug and to add supports for more models with:
 - Both PCM capture and playback
 - Both MIDI capture and playback

The renew drivers doesn't support:
 - Synchronization between multiple devices on the same bus
   For this purpose, please join in user-space driver project (FFADO)
 - Any control functionality for internal DSP
   For this purpose, please use ffado-dbus-server/ffado-mixer,
   or write your own application


== Updates from my previous post:
[alsa-devel] [PATCH 00/14] ALSA: Dice enhancement
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-October/081890.html
 - Fix typo
 - Minor code change

[alsa-devel] [RFC][PATCH 00/15 v4] OXFW driver, a succesor to firewire-speakers
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-May/076581.html
 - Support more Mackie (Loud) models by name detection
 - Fix a bug to handle FireWave
 - Use AV/C Stream Format Information command to set sampling rate


== Remarks:
 - When the Dice driver is loaded, user-space driver fails to register
   notification address, thus cannot handle device for streaming. See:
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081731.html
 - OXFW chipset seems to transmit wrong values for timestamp, thus the
   driver don't apply full-duplex streams with synchronization


== Supported Dice-based models (as long as I know):
TC Electronic
 - Desktop Konnekt 6
 - Impact Twin
 - Konnekt 8/24D/Live
 - Studio Konnekt 48
PreSonus
 - FireStudio 26x26/Mobile/Project/Tube/LightPipe
 - StudioLive 1602/1642/2442
 - FireStation
Mackie
 - Onyx Blackbird
 - Onyx i-series (latter models)
Focusrite
 - Saffire Pro 14/24/24 DSP/26/40/56
M-Audio
 - Profire 610/2626
Alessis
 - IO 24/26
 - MultiMix 12/16
Weiss
 - ADC2
 - Vesta
 - Mineva
 - AFI 1
 - TAG DAC 1
 - INT 202
 - DAC 202
 - Flex 5000
DnR
 - Axus FireWire I/O card 16x16
Lexicon
 - I-ONIX FW 810S
Allen and Heath
 - Zed R16
Midas
 - Venice F32

== Supported OXFW-based models (as long as I know):
Griffin:
 - FireWave
LaCie:
 - Firewire Speakers
Behringer:
 - F-Control Audio 202
Mackie (Loud):
 - Onyx-i series (former models)
 - Onyx Satellite
 - Tapco Link.Firewire
 - d.2 pro/d.4 pro
 - U.420/U.420d

Takashi Sakamoto (29):
  ALSA: dice: Rename structure and its members
  ALSA: dice: Move file to its own directory
  ALSA: dice: Split transaction functionality into a file
  ALSA: dice: Split stream functionality into a file
  ALSA: dice: Split PCM functionality into a file
  ALSA: dice: Split hwdep functionality into a file
  ALSA: dice: Split proc interface into a file
  ALSA: dice: Add new functions for constraints of PCM parameters
  ALSA: dice: Change the way to start stream
  ALSA: dice: Add support for duplex streams with synchronization
  ALSA: dice: Support for non SYT-Match sampling clock source mode
  ALSA: dice: Add support for capturing PCM samples
  ALSA: dice: Add support for MIDI capture/playback
  ALSA: dice: remove experimental state
  ALSA: speakers: Rename to oxfw and rename some members
  ALSA: oxfw: Move to its own directory
  ALSA: oxfw: Split stream functionality to a new file and add a header
    file
  ALSA: oxfw: Split PCM functionality to a new file
  ALSA: oxfw: Split control functionality to a new file
  ALSA: oxfw: Change the way to name card
  ALSA: oxfw: Add support for AV/C stream format command to get/set
    supported stream formation
  ALSA: oxfw: Change the way to make PCM rules/constraints
  ALSA: oxfw: Add proc interface for debugging purpose
  ALSA: oxfw: Change the way to start stream
  ALSA: oxfw: Add support for Behringer/Mackie devices
  ALSA: oxfw: Add support AMDTP in-stream
  ALSA: oxfw: add support for capturing PCM samples
  ALSA: oxfw: Add support for capture/playback MIDI messages
  ALSA: oxfw: Add hwdep interface

 include/uapi/sound/asound.h                |    3 +-
 include/uapi/sound/firewire.h              |    3 +-
 sound/firewire/Kconfig                     |   26 +-
 sound/firewire/Makefile                    |    6 +-
 sound/firewire/dice.c                      | 1511 ----------------------------
 sound/firewire/dice/Makefile               |    3 +
 sound/firewire/dice/dice-hwdep.c           |  190 ++++
 sound/firewire/{ => dice}/dice-interface.h |    0
 sound/firewire/dice/dice-midi.c            |  176 ++++
 sound/firewire/dice/dice-pcm.c             |  404 ++++++++
 sound/firewire/dice/dice-proc.c            |  252 +++++
 sound/firewire/dice/dice-stream.c          |  423 ++++++++
 sound/firewire/dice/dice-transaction.c     |  382 +++++++
 sound/firewire/dice/dice.c                 |  359 +++++++
 sound/firewire/dice/dice.h                 |  189 ++++
 sound/firewire/oxfw/Makefile               |    3 +
 sound/firewire/oxfw/oxfw-command.c         |  153 +++
 sound/firewire/oxfw/oxfw-control.c         |  283 ++++++
 sound/firewire/oxfw/oxfw-hwdep.c           |  190 ++++
 sound/firewire/oxfw/oxfw-midi.c            |  189 ++++
 sound/firewire/oxfw/oxfw-pcm.c             |  399 ++++++++
 sound/firewire/oxfw/oxfw-proc.c            |  113 +++
 sound/firewire/oxfw/oxfw-stream.c          |  702 +++++++++++++
 sound/firewire/oxfw/oxfw.c                 |  310 ++++++
 sound/firewire/oxfw/oxfw.h                 |  145 +++
 sound/firewire/speakers.c                  |  792 ---------------
 26 files changed, 4887 insertions(+), 2319 deletions(-)
 delete mode 100644 sound/firewire/dice.c
 create mode 100644 sound/firewire/dice/Makefile
 create mode 100644 sound/firewire/dice/dice-hwdep.c
 rename sound/firewire/{ => dice}/dice-interface.h (100%)
 create mode 100644 sound/firewire/dice/dice-midi.c
 create mode 100644 sound/firewire/dice/dice-pcm.c
 create mode 100644 sound/firewire/dice/dice-proc.c
 create mode 100644 sound/firewire/dice/dice-stream.c
 create mode 100644 sound/firewire/dice/dice-transaction.c
 create mode 100644 sound/firewire/dice/dice.c
 create mode 100644 sound/firewire/dice/dice.h
 create mode 100644 sound/firewire/oxfw/Makefile
 create mode 100644 sound/firewire/oxfw/oxfw-command.c
 create mode 100644 sound/firewire/oxfw/oxfw-control.c
 create mode 100644 sound/firewire/oxfw/oxfw-hwdep.c
 create mode 100644 sound/firewire/oxfw/oxfw-midi.c
 create mode 100644 sound/firewire/oxfw/oxfw-pcm.c
 create mode 100644 sound/firewire/oxfw/oxfw-proc.c
 create mode 100644 sound/firewire/oxfw/oxfw-stream.c
 create mode 100644 sound/firewire/oxfw/oxfw.c
 create mode 100644 sound/firewire/oxfw/oxfw.h
 delete mode 100644 sound/firewire/speakers.c

-- 
1.9.1

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

* [PATCH 01/29] ALSA: dice: Rename structure and its members
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 02/29] ALSA: dice: Move file to its own directory Takashi Sakamoto
                   ` (30 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Currently, dice driver supports AMDTP out-stream. In followed commits, AMDTP
in-stream will be supported but current name of members in dice structure
are not propper. This commit renames these members to proper name.

Additionally, for easily distinguishing local symbols from structure tag,
rename dice tag into snd_dice.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice.c | 162 +++++++++++++++++++++++++-------------------------
 1 file changed, 81 insertions(+), 81 deletions(-)

diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index e3a04d6..c7c8495 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -32,7 +32,7 @@
 #include "dice-interface.h"
 
 
-struct dice {
+struct snd_dice {
 	struct snd_card *card;
 	struct fw_unit *unit;
 	spinlock_t lock;
@@ -50,8 +50,8 @@ struct dice {
 	struct completion clock_accepted;
 	wait_queue_head_t hwdep_wait;
 	u32 notification_bits;
-	struct fw_iso_resources resources;
-	struct amdtp_stream stream;
+	struct fw_iso_resources rx_resources;
+	struct amdtp_stream rx_stream;
 };
 
 MODULE_DESCRIPTION("DICE driver");
@@ -87,13 +87,13 @@ 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)
+static void dice_lock_changed(struct snd_dice *dice)
 {
 	dice->dev_lock_changed = true;
 	wake_up(&dice->hwdep_wait);
 }
 
-static int dice_try_lock(struct dice *dice)
+static int dice_try_lock(struct snd_dice *dice)
 {
 	int err;
 
@@ -114,7 +114,7 @@ out:
 	return err;
 }
 
-static void dice_unlock(struct dice *dice)
+static void dice_unlock(struct snd_dice *dice)
 {
 	spin_lock_irq(&dice->lock);
 
@@ -128,18 +128,18 @@ out:
 	spin_unlock_irq(&dice->lock);
 }
 
-static inline u64 global_address(struct dice *dice, unsigned int offset)
+static inline u64 global_address(struct snd_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)
+static inline u64 rx_address(struct snd_dice *dice, unsigned int offset)
 {
 	return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
 }
 
-static int dice_owner_set(struct dice *dice)
+static int dice_owner_set(struct snd_dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
@@ -183,7 +183,7 @@ static int dice_owner_set(struct dice *dice)
 	return err;
 }
 
-static int dice_owner_update(struct dice *dice)
+static int dice_owner_update(struct snd_dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
@@ -226,7 +226,7 @@ static int dice_owner_update(struct dice *dice)
 	return err;
 }
 
-static void dice_owner_clear(struct dice *dice)
+static void dice_owner_clear(struct snd_dice *dice)
 {
 	struct fw_device *device = fw_parent_device(dice->unit);
 	__be64 *buffer;
@@ -249,7 +249,7 @@ static void dice_owner_clear(struct dice *dice)
 	dice->owner_generation = -1;
 }
 
-static int dice_enable_set(struct dice *dice)
+static int dice_enable_set(struct snd_dice *dice)
 {
 	__be32 value;
 	int err;
@@ -267,7 +267,7 @@ static int dice_enable_set(struct dice *dice)
 	return 0;
 }
 
-static void dice_enable_clear(struct dice *dice)
+static void dice_enable_clear(struct snd_dice *dice)
 {
 	__be32 value;
 
@@ -288,7 +288,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 			      int generation, unsigned long long offset,
 			      void *data, size_t length, void *callback_data)
 {
-	struct dice *dice = callback_data;
+	struct snd_dice *dice = callback_data;
 	u32 bits;
 	unsigned long flags;
 
@@ -317,7 +317,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 				struct snd_pcm_hw_rule *rule)
 {
-	struct dice *dice = rule->private;
+	struct snd_dice *dice = rule->private;
 	const struct snd_interval *channels =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 	struct snd_interval *rate =
@@ -344,7 +344,7 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 				    struct snd_pcm_hw_rule *rule)
 {
-	struct dice *dice = rule->private;
+	struct snd_dice *dice = rule->private;
 	const struct snd_interval *rate =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
 	struct snd_interval *channels =
@@ -384,7 +384,7 @@ static int dice_open(struct snd_pcm_substream *substream)
 		.periods_min = 1,
 		.periods_max = UINT_MAX,
 	};
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	unsigned int i;
 	int err;
@@ -420,7 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		goto err_lock;
 
-	err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
+	err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
 	if (err < 0)
 		goto err_lock;
 
@@ -434,47 +434,47 @@ error:
 
 static int dice_close(struct snd_pcm_substream *substream)
 {
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 
 	dice_unlock(dice);
 
 	return 0;
 }
 
-static int dice_stream_start_packets(struct dice *dice)
+static int dice_stream_start_packets(struct snd_dice *dice)
 {
 	int err;
 
-	if (amdtp_stream_running(&dice->stream))
+	if (amdtp_stream_running(&dice->rx_stream))
 		return 0;
 
-	err = amdtp_stream_start(&dice->stream, dice->resources.channel,
+	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
 				 fw_parent_device(dice->unit)->max_speed);
 	if (err < 0)
 		return err;
 
 	err = dice_enable_set(dice);
 	if (err < 0) {
-		amdtp_stream_stop(&dice->stream);
+		amdtp_stream_stop(&dice->rx_stream);
 		return err;
 	}
 
 	return 0;
 }
 
-static int dice_stream_start(struct dice *dice)
+static int dice_stream_start(struct snd_dice *dice)
 {
 	__be32 channel;
 	int err;
 
-	if (!dice->resources.allocated) {
-		err = fw_iso_resources_allocate(&dice->resources,
-				amdtp_stream_get_max_payload(&dice->stream),
+	if (!dice->rx_resources.allocated) {
+		err = fw_iso_resources_allocate(&dice->rx_resources,
+				amdtp_stream_get_max_payload(&dice->rx_stream),
 				fw_parent_device(dice->unit)->max_speed);
 		if (err < 0)
 			goto error;
 
-		channel = cpu_to_be32(dice->resources.channel);
+		channel = cpu_to_be32(dice->rx_resources.channel);
 		err = snd_fw_transaction(dice->unit,
 					 TCODE_WRITE_QUADLET_REQUEST,
 					 rx_address(dice, RX_ISOCHRONOUS),
@@ -494,36 +494,36 @@ err_rx_channel:
 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
 			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
 err_resources:
-	fw_iso_resources_free(&dice->resources);
+	fw_iso_resources_free(&dice->rx_resources);
 error:
 	return err;
 }
 
-static void dice_stream_stop_packets(struct dice *dice)
+static void dice_stream_stop_packets(struct snd_dice *dice)
 {
-	if (amdtp_stream_running(&dice->stream)) {
+	if (amdtp_stream_running(&dice->rx_stream)) {
 		dice_enable_clear(dice);
-		amdtp_stream_stop(&dice->stream);
+		amdtp_stream_stop(&dice->rx_stream);
 	}
 }
 
-static void dice_stream_stop(struct dice *dice)
+static void dice_stream_stop(struct snd_dice *dice)
 {
 	__be32 channel;
 
 	dice_stream_stop_packets(dice);
 
-	if (!dice->resources.allocated)
+	if (!dice->rx_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, 0);
 
-	fw_iso_resources_free(&dice->resources);
+	fw_iso_resources_free(&dice->rx_resources);
 }
 
-static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
+static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate)
 {
 	__be32 value;
 	int err;
@@ -547,7 +547,7 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
 static int dice_hw_params(struct snd_pcm_substream *substream,
 			  struct snd_pcm_hw_params *hw_params)
 {
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 	unsigned int rate_index, mode, rate, channels, i;
 	int err;
 
@@ -585,24 +585,24 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 
 		rate /= 2;
 		channels *= 2;
-		dice->stream.double_pcm_frames = true;
+		dice->rx_stream.double_pcm_frames = true;
 	} else {
-		dice->stream.double_pcm_frames = false;
+		dice->rx_stream.double_pcm_frames = false;
 	}
 
 	mode = rate_index_to_mode(rate_index);
-	amdtp_stream_set_parameters(&dice->stream, rate, channels,
+	amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
 				    dice->rx_midi_ports[mode]);
 	if (rate_index > 4) {
 		channels /= 2;
 
 		for (i = 0; i < channels; i++) {
-			dice->stream.pcm_positions[i] = i * 2;
-			dice->stream.pcm_positions[i + channels] = i * 2 + 1;
+			dice->rx_stream.pcm_positions[i] = i * 2;
+			dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
 		}
 	}
 
-	amdtp_stream_set_pcm_format(&dice->stream,
+	amdtp_stream_set_pcm_format(&dice->rx_stream,
 				    params_format(hw_params));
 
 	return 0;
@@ -610,7 +610,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 
 static int dice_hw_free(struct snd_pcm_substream *substream)
 {
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 
 	mutex_lock(&dice->mutex);
 	dice_stream_stop(dice);
@@ -621,12 +621,12 @@ 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 snd_dice *dice = substream->private_data;
 	int err;
 
 	mutex_lock(&dice->mutex);
 
-	if (amdtp_streaming_error(&dice->stream))
+	if (amdtp_streaming_error(&dice->rx_stream))
 		dice_stream_stop_packets(dice);
 
 	err = dice_stream_start(dice);
@@ -637,14 +637,14 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 
 	mutex_unlock(&dice->mutex);
 
-	amdtp_stream_pcm_prepare(&dice->stream);
+	amdtp_stream_pcm_prepare(&dice->rx_stream);
 
 	return 0;
 }
 
 static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
 {
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 	struct snd_pcm_substream *pcm;
 
 	switch (cmd) {
@@ -657,19 +657,19 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
 	default:
 		return -EINVAL;
 	}
-	amdtp_stream_pcm_trigger(&dice->stream, pcm);
+	amdtp_stream_pcm_trigger(&dice->rx_stream, pcm);
 
 	return 0;
 }
 
 static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
 {
-	struct dice *dice = substream->private_data;
+	struct snd_dice *dice = substream->private_data;
 
-	return amdtp_stream_pcm_pointer(&dice->stream);
+	return amdtp_stream_pcm_pointer(&dice->rx_stream);
 }
 
-static int dice_create_pcm(struct dice *dice)
+static int dice_create_pcm(struct snd_dice *dice)
 {
 	static struct snd_pcm_ops ops = {
 		.open      = dice_open,
@@ -699,7 +699,7 @@ static int dice_create_pcm(struct dice *dice)
 static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
 			    long count, loff_t *offset)
 {
-	struct dice *dice = hwdep->private_data;
+	struct snd_dice *dice = hwdep->private_data;
 	DEFINE_WAIT(wait);
 	union snd_firewire_event event;
 
@@ -741,7 +741,7 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
 static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 				    poll_table *wait)
 {
-	struct dice *dice = hwdep->private_data;
+	struct snd_dice *dice = hwdep->private_data;
 	unsigned int events;
 
 	poll_wait(file, &dice->hwdep_wait, wait);
@@ -756,7 +756,7 @@ static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 	return events;
 }
 
-static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
+static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg)
 {
 	struct fw_device *dev = fw_parent_device(dice->unit);
 	struct snd_firewire_get_info info;
@@ -775,7 +775,7 @@ static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
 	return 0;
 }
 
-static int dice_hwdep_lock(struct dice *dice)
+static int dice_hwdep_lock(struct snd_dice *dice)
 {
 	int err;
 
@@ -793,7 +793,7 @@ static int dice_hwdep_lock(struct dice *dice)
 	return err;
 }
 
-static int dice_hwdep_unlock(struct dice *dice)
+static int dice_hwdep_unlock(struct snd_dice *dice)
 {
 	int err;
 
@@ -813,7 +813,7 @@ static int dice_hwdep_unlock(struct dice *dice)
 
 static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 {
-	struct dice *dice = hwdep->private_data;
+	struct snd_dice *dice = hwdep->private_data;
 
 	spin_lock_irq(&dice->lock);
 	if (dice->dev_lock_count == -1)
@@ -826,7 +826,7 @@ static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 			    unsigned int cmd, unsigned long arg)
 {
-	struct dice *dice = hwdep->private_data;
+	struct snd_dice *dice = hwdep->private_data;
 
 	switch (cmd) {
 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
@@ -851,7 +851,7 @@ static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 #define dice_hwdep_compat_ioctl NULL
 #endif
 
-static int dice_create_hwdep(struct dice *dice)
+static int dice_create_hwdep(struct snd_dice *dice)
 {
 	static const struct snd_hwdep_ops ops = {
 		.read         = dice_hwdep_read,
@@ -875,7 +875,7 @@ static int dice_create_hwdep(struct dice *dice)
 	return 0;
 }
 
-static int dice_proc_read_mem(struct dice *dice, void *buffer,
+static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
 			      unsigned int offset_q, unsigned int quadlets)
 {
 	unsigned int i;
@@ -898,8 +898,8 @@ static const char *str_from_array(const char *const strs[], unsigned int count,
 {
 	if (i < count)
 		return strs[i];
-	else
-		return "(unknown)";
+
+	return "(unknown)";
 }
 
 static void dice_proc_fixup_string(char *s, unsigned int size)
@@ -934,7 +934,7 @@ static void dice_proc_read(struct snd_info_entry *entry,
 		"32000", "44100", "48000", "88200", "96000", "176400", "192000",
 		"any low", "any mid", "any high", "none"
 	};
-	struct dice *dice = entry->private_data;
+	struct snd_dice *dice = entry->private_data;
 	u32 sections[ARRAY_SIZE(section_names) * 2];
 	struct {
 		u32 number;
@@ -1109,7 +1109,7 @@ static void dice_proc_read(struct snd_info_entry *entry,
 	}
 }
 
-static void dice_create_proc(struct dice *dice)
+static void dice_create_proc(struct snd_dice *dice)
 {
 	struct snd_info_entry *entry;
 
@@ -1119,9 +1119,9 @@ static void dice_create_proc(struct dice *dice)
 
 static void dice_card_free(struct snd_card *card)
 {
-	struct dice *dice = card->private_data;
+	struct snd_dice *dice = card->private_data;
 
-	amdtp_stream_destroy(&dice->stream);
+	amdtp_stream_destroy(&dice->rx_stream);
 	fw_core_remove_address_handler(&dice->notification_handler);
 	mutex_destroy(&dice->mutex);
 }
@@ -1216,7 +1216,7 @@ static int dice_interface_check(struct fw_unit *unit)
 	return 0;
 }
 
-static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
+static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode)
 {
 	int i;
 
@@ -1228,7 +1228,7 @@ static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
 	return -1;
 }
 
-static int dice_read_mode_params(struct dice *dice, unsigned int mode)
+static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 {
 	__be32 values[2];
 	int rate_index, err;
@@ -1256,7 +1256,7 @@ static int dice_read_mode_params(struct dice *dice, unsigned int mode)
 	return 0;
 }
 
-static int dice_read_params(struct dice *dice)
+static int dice_read_params(struct snd_dice *dice)
 {
 	__be32 pointers[6];
 	__be32 value;
@@ -1297,7 +1297,7 @@ static int dice_read_params(struct dice *dice)
 	return 0;
 }
 
-static void dice_card_strings(struct dice *dice)
+static void dice_card_strings(struct snd_dice *dice)
 {
 	struct snd_card *card = dice->card;
 	struct fw_device *dev = fw_parent_device(dice->unit);
@@ -1335,7 +1335,7 @@ static void dice_card_strings(struct dice *dice)
 static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 {
 	struct snd_card *card;
-	struct dice *dice;
+	struct snd_dice *dice;
 	__be32 clock_sel;
 	int err;
 
@@ -1372,12 +1372,12 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto err_owner;
 
-	err = fw_iso_resources_init(&dice->resources, unit);
+	err = fw_iso_resources_init(&dice->rx_resources, unit);
 	if (err < 0)
 		goto err_owner;
-	dice->resources.channels_mask = 0x00000000ffffffffuLL;
+	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
 
-	err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
+	err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM,
 				CIP_BLOCKING);
 	if (err < 0)
 		goto err_resources;
@@ -1418,7 +1418,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	return 0;
 
 err_resources:
-	fw_iso_resources_destroy(&dice->resources);
+	fw_iso_resources_destroy(&dice->rx_resources);
 err_owner:
 	dice_owner_clear(dice);
 err_notification_handler:
@@ -1432,9 +1432,9 @@ error:
 
 static void dice_remove(struct fw_unit *unit)
 {
-	struct dice *dice = dev_get_drvdata(&unit->device);
+	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
-	amdtp_stream_pcm_abort(&dice->stream);
+	amdtp_stream_pcm_abort(&dice->rx_stream);
 
 	snd_card_disconnect(dice->card);
 
@@ -1450,7 +1450,7 @@ static void dice_remove(struct fw_unit *unit)
 
 static void dice_bus_reset(struct fw_unit *unit)
 {
-	struct dice *dice = dev_get_drvdata(&unit->device);
+	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
 	/*
 	 * On a bus reset, the DICE firmware disables streaming and then goes
@@ -1460,7 +1460,7 @@ static void dice_bus_reset(struct fw_unit *unit)
 	 * to stop so that the application can restart them in an orderly
 	 * manner.
 	 */
-	amdtp_stream_pcm_abort(&dice->stream);
+	amdtp_stream_pcm_abort(&dice->rx_stream);
 
 	mutex_lock(&dice->mutex);
 
@@ -1469,7 +1469,7 @@ static void dice_bus_reset(struct fw_unit *unit)
 
 	dice_owner_update(dice);
 
-	fw_iso_resources_update(&dice->resources);
+	fw_iso_resources_update(&dice->rx_resources);
 
 	mutex_unlock(&dice->mutex);
 }
-- 
1.9.1

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

* [PATCH 02/29] ALSA: dice: Move file to its own directory
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 01/29] ALSA: dice: Rename structure and its members Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-11-18 12:57   ` Clemens Ladisch
  2014-10-26 13:03 ` [PATCH 03/29] ALSA: dice: Split transaction functionality into a file Takashi Sakamoto
                   ` (29 subsequent siblings)
  31 siblings, 1 reply; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In followed commits, dice driver is split into several files. For easily
managing these files, this commit adds subdirectory and move file into
the directory.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Makefile                    |  3 +--
 sound/firewire/dice/Makefile               |  2 ++
 sound/firewire/{ => dice}/dice-interface.h |  0
 sound/firewire/{ => dice}/dice.c           | 19 ++++++++++---------
 4 files changed, 13 insertions(+), 11 deletions(-)
 create mode 100644 sound/firewire/dice/Makefile
 rename sound/firewire/{ => dice}/dice-interface.h (100%)
 rename sound/firewire/{ => dice}/dice.c (99%)

diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index fad8d49..c50761c 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,12 +1,11 @@
 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_DICE) += dice/
 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/Makefile b/sound/firewire/dice/Makefile
new file mode 100644
index 0000000..af05d7e
--- /dev/null
+++ b/sound/firewire/dice/Makefile
@@ -0,0 +1,2 @@
+snd-dice-objs := dice.o
+obj-m += snd-dice.o
diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice/dice-interface.h
similarity index 100%
rename from sound/firewire/dice-interface.h
rename to sound/firewire/dice/dice-interface.h
diff --git a/sound/firewire/dice.c b/sound/firewire/dice/dice.c
similarity index 99%
rename from sound/firewire/dice.c
rename to sound/firewire/dice/dice.c
index c7c8495..2c3158f 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -26,9 +26,9 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include "amdtp.h"
-#include "iso-resources.h"
-#include "lib.h"
+#include "../amdtp.h"
+#include "../iso-resources.h"
+#include "../lib.h"
 #include "dice-interface.h"
 
 
@@ -133,7 +133,7 @@ static inline u64 global_address(struct snd_dice *dice, unsigned int offset)
 	return DICE_PRIVATE_SPACE + dice->global_offset + offset;
 }
 
-// TODO: rx index
+/* TODO: rx index */
 static inline u64 rx_address(struct snd_dice *dice, unsigned int offset)
 {
 	return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
@@ -721,13 +721,14 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
 		event.lock_status.status = dice->dev_lock_count > 0;
 		dice->dev_lock_changed = false;
 
-		count = min(count, (long)sizeof(event.lock_status));
+		count = min_t(long, count, sizeof(event.lock_status));
 	} else {
-		event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+		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));
+		count = min_t(long, count, sizeof(event.dice_notification));
 	}
 
 	spin_unlock_irq(&dice->lock);
@@ -1448,7 +1449,7 @@ static void dice_remove(struct fw_unit *unit)
 	snd_card_free_when_closed(dice->card);
 }
 
-static void dice_bus_reset(struct fw_unit *unit)
+static void dice_update(struct fw_unit *unit)
 {
 	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
@@ -1492,7 +1493,7 @@ static struct fw_driver dice_driver = {
 		.bus	= &fw_bus_type,
 	},
 	.probe    = dice_probe,
-	.update   = dice_bus_reset,
+	.update   = dice_update,
 	.remove   = dice_remove,
 	.id_table = dice_id_table,
 };
-- 
1.9.1

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

* [PATCH 03/29] ALSA: dice: Split transaction functionality into a file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 01/29] ALSA: dice: Rename structure and its members Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 02/29] ALSA: dice: Move file to its own directory Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 04/29] ALSA: dice: Split stream " Takashi Sakamoto
                   ` (28 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a file with some helper functions for transaction, and move
some codes into the file with some arrangements.

For Dice chipset, well-known FCP or AV/C commands are not used to control
devices. It's achieved by read/write transactions into specific addresses.

Dice's address area is split into 5 areas. Each area has its own role. The
offset for each area can be got by reading head of the address area. By
reading these areas, drivers can get to know device status. By writing these
areas, drivers can change device status.

Dice has a specific mechanism called as 'notification'. When device status is
changed, Dice devices tells the event by sending transaction. This notification
is sent to an address which drivers register in advance. But this causes an
issue to drivers.

To handle the notification, drivers need to allocate its own callback function
to the address region in host controller. This region is exclusive. For the
other applications, drivers must give a mechanism to read the received
notification. For this purpose, Dice driver already implements hwdep interface.

Dice chipset doesn't allow drivers to register several addresses. In this
reason, when this driver is applied to a device, the other drivers should
_not_ try to register its own address to the device.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile           |   2 +-
 sound/firewire/dice/dice-transaction.c | 387 ++++++++++++++++++++++++++
 sound/firewire/dice/dice.c             | 489 +++++++--------------------------
 sound/firewire/dice/dice.h             | 160 +++++++++++
 4 files changed, 654 insertions(+), 384 deletions(-)
 create mode 100644 sound/firewire/dice/dice-transaction.c
 create mode 100644 sound/firewire/dice/dice.h

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index af05d7e..9f473cb 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,2 +1,2 @@
-snd-dice-objs := dice.o
+snd-dice-objs := dice-transaction.o dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
new file mode 100644
index 0000000..a9b98e0
--- /dev/null
+++ b/sound/firewire/dice/dice-transaction.c
@@ -0,0 +1,387 @@
+/*
+ * dice_transaction.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+#define NOTIFICATION_TIMEOUT_MS	100
+
+static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
+		       u64 offset)
+{
+	switch (type) {
+	case SND_DICE_ADDR_TYPE_TX:
+		offset += dice->tx_offset;
+		break;
+	case SND_DICE_ADDR_TYPE_RX:
+		offset += dice->rx_offset;
+		break;
+	case SND_DICE_ADDR_TYPE_SYNC:
+		offset += dice->sync_offset;
+		break;
+	case SND_DICE_ADDR_TYPE_RSRV:
+		offset += dice->rsrv_offset;
+		break;
+	case SND_DICE_ADDR_TYPE_GLOBAL:
+	default:
+		offset += dice->global_offset;
+		break;
+	};
+	offset += DICE_PRIVATE_SPACE;
+	return offset;
+}
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+			       enum snd_dice_addr_type type,
+			       unsigned int offset, void *buf, unsigned int len)
+{
+	return snd_fw_transaction(dice->unit,
+				  (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
+					       TCODE_WRITE_BLOCK_REQUEST,
+				  get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+int snd_dice_transaction_read(struct snd_dice *dice,
+			      enum snd_dice_addr_type type, unsigned int offset,
+			      void *buf, unsigned int len)
+{
+	return snd_fw_transaction(dice->unit,
+				  (len == 4) ? TCODE_READ_QUADLET_REQUEST :
+					       TCODE_READ_BLOCK_REQUEST,
+				  get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
+{
+	return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+						info, 4);
+}
+
+static int set_clock_info(struct snd_dice *dice,
+			  unsigned int rate, unsigned int source)
+{
+	unsigned int retries = 3;
+	unsigned int i;
+	__be32 info;
+	u32 mask;
+	u32 clock;
+	int err;
+retry:
+	err = get_clock_info(dice, &info);
+	if (err < 0)
+		goto end;
+
+	clock = be32_to_cpu(info);
+	if (source != UINT_MAX) {
+		mask = CLOCK_SOURCE_MASK;
+		clock &= ~mask;
+		clock |= source;
+	}
+	if (rate != UINT_MAX) {
+		for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+			if (snd_dice_rates[i] == rate)
+				break;
+		}
+		if (i == ARRAY_SIZE(snd_dice_rates)) {
+			err = -EINVAL;
+			goto end;
+		}
+
+		mask = CLOCK_RATE_MASK;
+		clock &= ~mask;
+		clock |= i << CLOCK_RATE_SHIFT;
+	}
+	info = cpu_to_be32(clock);
+
+	if (completion_done(&dice->clock_accepted))
+		reinit_completion(&dice->clock_accepted);
+
+	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+						&info, 4);
+	if (err < 0)
+		goto end;
+
+	/* Timeout means it's invalid request, probably bus reset occurred. */
+	if (wait_for_completion_timeout(&dice->clock_accepted,
+			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+		if (retries-- == 0) {
+			err = -ETIMEDOUT;
+			goto end;
+		}
+
+		err = snd_dice_transaction_reinit(dice);
+		if (err < 0)
+			goto end;
+
+		msleep(500);	/* arbitrary */
+		goto retry;
+	}
+end:
+	return err;
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+					  unsigned int *source)
+{
+	__be32 info;
+	int err;
+
+	err = get_clock_info(dice, &info);
+	if (err >= 0)
+		*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
+
+	return err;
+}
+int snd_dice_transaction_set_clock_source(struct snd_dice *dice,
+					  unsigned int source)
+{
+	return set_clock_info(dice, UINT_MAX, source);
+}
+
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
+{
+	__be32 info;
+	unsigned int index;
+	int err;
+
+	err = get_clock_info(dice, &info);
+	if (err < 0)
+		goto end;
+
+	index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
+	if (index >= SND_DICE_RATES_COUNT) {
+		err = -ENOSYS;
+		goto end;
+	}
+
+	*rate = snd_dice_rates[index];
+end:
+	return err;
+}
+int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
+{
+	return set_clock_info(dice, rate, UINT_MAX);
+}
+
+int snd_dice_transaction_set_enable(struct snd_dice *dice)
+{
+	__be32 value;
+	int err = 0;
+
+	if (dice->global_enabled)
+		goto end;
+
+	value = cpu_to_be32(1);
+	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+					     GLOBAL_ENABLE),
+				 &value, 4,
+				 FW_FIXED_GENERATION | dice->owner_generation);
+	if (err < 0)
+		goto end;
+
+	dice->global_enabled = true;
+end:
+	return err;
+}
+
+void snd_dice_transaction_clear_enable(struct snd_dice *dice)
+{
+	__be32 value;
+
+	value = 0;
+	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+				       GLOBAL_ENABLE),
+			   &value, 4, FW_QUIET |
+			   FW_FIXED_GENERATION | dice->owner_generation);
+
+	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 snd_dice *dice = callback_data;
+	u32 bits;
+	unsigned long flags;
+
+	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;
+	}
+
+	bits = be32_to_cpup(data);
+
+	spin_lock_irqsave(&dice->lock, flags);
+	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);
+}
+
+static int register_notification_address(struct snd_dice *dice, bool retry)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be64 *buffer;
+	unsigned int retries;
+	int err;
+
+	retries = (retry) ? 3 : 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 */
+		err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+					 get_subaddr(dice,
+						     SND_DICE_ADDR_TYPE_GLOBAL,
+						     GLOBAL_OWNER),
+					 buffer, 2 * 8,
+					 FW_FIXED_GENERATION |
+							dice->owner_generation);
+		if (err == 0) {
+			/* success */
+			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
+				break;
+			/* The address seems to be already registered. */
+			if (buffer[0] == buffer[1])
+				break;
+
+			dev_err(&dice->unit->device,
+				"device is already in use\n");
+			err = -EBUSY;
+		}
+		if (err != -EAGAIN || retries-- > 0)
+			break;
+
+		msleep(20);
+	}
+
+	kfree(buffer);
+
+	if (err < 0)
+		dice->owner_generation = -1;
+
+	return err;
+}
+
+static void unregister_notification_address(struct snd_dice *dice)
+{
+	struct fw_device *device = fw_parent_device(dice->unit);
+	__be64 *buffer;
+
+	buffer = kmalloc(2 * 8, GFP_KERNEL);
+	if (buffer == NULL)
+		return;
+
+	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,
+			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+				       GLOBAL_OWNER),
+			   buffer, 2 * 8, FW_QUIET |
+			   FW_FIXED_GENERATION | dice->owner_generation);
+
+	kfree(buffer);
+
+	dice->owner_generation = -1;
+}
+
+void snd_dice_transaction_destroy(struct snd_dice *dice)
+{
+	struct fw_address_handler *handler = &dice->notification_handler;
+
+	if (handler->callback_data == NULL)
+		return;
+
+	unregister_notification_address(dice);
+
+	fw_core_remove_address_handler(handler);
+	handler->callback_data = NULL;
+}
+
+int snd_dice_transaction_reinit(struct snd_dice *dice)
+{
+	struct fw_address_handler *handler = &dice->notification_handler;
+
+	if (handler->callback_data == NULL)
+		return -EINVAL;
+
+	return register_notification_address(dice, false);
+}
+
+int snd_dice_transaction_init(struct snd_dice *dice)
+{
+	struct fw_address_handler *handler = &dice->notification_handler;
+	__be32 *pointers;
+	int err;
+
+	/* Use the same way which dice_interface_check() does. */
+	pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
+	if (pointers == NULL)
+		return -ENOMEM;
+
+	/* Get offsets for sub-addresses */
+	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+				 DICE_PRIVATE_SPACE,
+				 pointers, sizeof(__be32) * 10, 0);
+	if (err < 0)
+		goto end;
+
+	/* Allocation callback in address space over host controller */
+	handler->length = 4;
+	handler->address_callback = dice_notification;
+	handler->callback_data = dice;
+	err = fw_core_add_address_handler(handler, &fw_high_memory_region);
+	if (err < 0) {
+		handler->callback_data = NULL;
+		goto end;
+	}
+
+	/* Register the address space */
+	err = register_notification_address(dice, true);
+	if (err < 0) {
+		fw_core_remove_address_handler(handler);
+		handler->callback_data = NULL;
+		goto end;
+	}
+
+	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
+	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+	dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
+	dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
+
+	/* Set up later. */
+	if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
+		dice->clock_caps = 1;
+end:
+	kfree(pointers);
+	return err;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 2c3158f..ddf05fd 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -5,60 +5,13 @@
  * Licensed under the terms of the GNU General Public License, version 2.
  */
 
-#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>
-#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/info.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include "../amdtp.h"
-#include "../iso-resources.h"
-#include "../lib.h"
-#include "dice-interface.h"
-
-
-struct snd_dice {
-	struct snd_card *card;
-	struct fw_unit *unit;
-	spinlock_t lock;
-	struct mutex mutex;
-	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 rx_resources;
-	struct amdtp_stream rx_stream;
-};
+#include "dice.h"
 
 MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
-static const unsigned int dice_rates[] = {
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
 	/* mode 0 */
 	[0] =  32000,
 	[1] =  44100,
@@ -75,8 +28,8 @@ 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)
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
+		if (snd_dice_rates[i] == rate)
 			return i;
 
 	return 0;
@@ -128,192 +81,6 @@ out:
 	spin_unlock_irq(&dice->lock);
 }
 
-static inline u64 global_address(struct snd_dice *dice, unsigned int offset)
-{
-	return DICE_PRIVATE_SPACE + dice->global_offset + offset;
-}
-
-/* TODO: rx index */
-static inline u64 rx_address(struct snd_dice *dice, unsigned int offset)
-{
-	return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
-}
-
-static int dice_owner_set(struct snd_dice *dice)
-{
-	struct fw_device *device = fw_parent_device(dice->unit);
-	__be64 *buffer;
-	int 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 */
-		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 (err != -EAGAIN || ++errors >= 3)
-			break;
-
-		msleep(20);
-	}
-
-	kfree(buffer);
-
-	return err;
-}
-
-static int dice_owner_update(struct snd_dice *dice)
-{
-	struct fw_device *device = fw_parent_device(dice->unit);
-	__be64 *buffer;
-	int err;
-
-	if (dice->owner_generation == -1)
-		return 0;
-
-	buffer = kmalloc(2 * 8, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	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 */
-	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;
-		}
-	} else if (err == -EAGAIN) {
-		err = 0; /* try again later */
-	}
-
-	kfree(buffer);
-
-	if (err < 0)
-		dice->owner_generation = -1;
-
-	return err;
-}
-
-static void dice_owner_clear(struct snd_dice *dice)
-{
-	struct fw_device *device = fw_parent_device(dice->unit);
-	__be64 *buffer;
-
-	buffer = kmalloc(2 * 8, GFP_KERNEL);
-	if (!buffer)
-		return;
-
-	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);
-
-	dice->owner_generation = -1;
-}
-
-static int dice_enable_set(struct snd_dice *dice)
-{
-	__be32 value;
-	int err;
-
-	value = cpu_to_be32(1);
-	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;
-
-	dice->global_enabled = true;
-
-	return 0;
-}
-
-static void dice_enable_clear(struct snd_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),
-			   &value, 4, FW_QUIET |
-			   FW_FIXED_GENERATION | dice->owner_generation);
-
-	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 snd_dice *dice = callback_data;
-	u32 bits;
-	unsigned long flags;
-
-	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;
-	}
-
-	bits = be32_to_cpup(data);
-
-	spin_lock_irqsave(&dice->lock, flags);
-	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);
-}
-
 static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 				struct snd_pcm_hw_rule *rule)
 {
@@ -327,14 +94,14 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 	};
 	unsigned int i, mode;
 
-	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
+	for (i = 0; i < ARRAY_SIZE(snd_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]);
+						snd_dice_rates[i]);
 			allowed_rates.max = max(allowed_rates.max,
-						dice_rates[i]);
+						snd_dice_rates[i]);
 		}
 	}
 
@@ -354,9 +121,9 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 	};
 	unsigned int i, mode;
 
-	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
 		if ((dice->clock_caps & (1 << i)) &&
-		    snd_interval_test(rate, dice_rates[i])) {
+		    snd_interval_test(rate, snd_dice_rates[i])) {
 			mode = rate_index_to_mode(i);
 			allowed_channels.min = min(allowed_channels.min,
 						   dice->rx_channels[mode]);
@@ -395,10 +162,10 @@ static int dice_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = hardware;
 
-	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
 		if (dice->clock_caps & (1 << i))
 			runtime->hw.rates |=
-				snd_pcm_rate_to_rate_bit(dice_rates[i]);
+				snd_pcm_rate_to_rate_bit(snd_dice_rates[i]);
 	snd_pcm_limit_hw_rates(runtime);
 
 	for (i = 0; i < 3; ++i)
@@ -453,7 +220,7 @@ static int dice_stream_start_packets(struct snd_dice *dice)
 	if (err < 0)
 		return err;
 
-	err = dice_enable_set(dice);
+	err = snd_dice_transaction_set_enable(dice);
 	if (err < 0) {
 		amdtp_stream_stop(&dice->rx_stream);
 		return err;
@@ -475,10 +242,8 @@ static int dice_stream_start(struct snd_dice *dice)
 			goto error;
 
 		channel = cpu_to_be32(dice->rx_resources.channel);
-		err = snd_fw_transaction(dice->unit,
-					 TCODE_WRITE_QUADLET_REQUEST,
-					 rx_address(dice, RX_ISOCHRONOUS),
-					 &channel, 4, 0);
+		err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
+						    &channel, 4);
 		if (err < 0)
 			goto err_resources;
 	}
@@ -491,8 +256,7 @@ static int dice_stream_start(struct snd_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, 0);
+	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
 err_resources:
 	fw_iso_resources_free(&dice->rx_resources);
 error:
@@ -502,7 +266,7 @@ error:
 static void dice_stream_stop_packets(struct snd_dice *dice)
 {
 	if (amdtp_stream_running(&dice->rx_stream)) {
-		dice_enable_clear(dice);
+		snd_dice_transaction_clear_enable(dice);
 		amdtp_stream_stop(&dice->rx_stream);
 	}
 }
@@ -517,33 +281,11 @@ static void dice_stream_stop(struct snd_dice *dice)
 		return;
 
 	channel = cpu_to_be32((u32)-1);
-	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
-			   rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
+	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
 
 	fw_iso_resources_free(&dice->rx_resources);
 }
 
-static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate)
-{
-	__be32 value;
-	int err;
-
-	reinit_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;
-
-	if (!wait_for_completion_timeout(&dice->clock_accepted,
-					 msecs_to_jiffies(100)))
-		dev_warn(&dice->unit->device, "clock change timed out\n");
-
-	return 0;
-}
-
 static int dice_hw_params(struct snd_pcm_substream *substream,
 			  struct snd_pcm_hw_params *hw_params)
 {
@@ -561,8 +303,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 		return err;
 
 	rate = params_rate(hw_params);
-	rate_index = rate_to_index(rate);
-	err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+	err = snd_dice_transaction_set_rate(dice, rate);
 	if (err < 0)
 		return err;
 
@@ -577,6 +318,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 	 * be aligned to SYT_INTERVAL.
 	 */
 	channels = params_channels(hw_params);
+	rate_index = rate_to_index(rate);
 	if (rate_index > 4) {
 		if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
 			err = -ENOSYS;
@@ -1118,15 +860,6 @@ static void dice_create_proc(struct snd_dice *dice)
 		snd_info_set_text_ops(entry, dice, dice_proc_read);
 }
 
-static void dice_card_free(struct snd_card *card)
-{
-	struct snd_dice *dice = card->private_data;
-
-	amdtp_stream_destroy(&dice->rx_stream);
-	fw_core_remove_address_handler(&dice->notification_handler);
-	mutex_destroy(&dice->mutex);
-}
-
 #define OUI_WEISS		0x001c6a
 
 #define DICE_CATEGORY_ID	0x04
@@ -1143,12 +876,17 @@ 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;
+	int key, val, vendor = -1, model = -1, err;
 	unsigned int category, i;
-	__be32 pointers[ARRAY_SIZE(min_values)];
+	__be32 *pointers, value;
 	__be32 tx_data[4];
 	__be32 version;
 
+	pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
+				 GFP_KERNEL);
+	if (pointers == NULL)
+		return -ENOMEM;
+
 	/*
 	 * 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
@@ -1156,13 +894,13 @@ static int dice_interface_check(struct fw_unit *unit)
 	 * ID, and a 22-bit serial number.
 	 */
 	fw_csr_iterator_init(&it, unit->directory);
-	while (fw_csr_iterator_next(&it, &key, &value)) {
+	while (fw_csr_iterator_next(&it, &key, &val)) {
 		switch (key) {
 		case CSR_SPECIFIER_ID:
-			vendor = value;
+			vendor = val;
 			break;
 		case CSR_MODEL:
-			model = value;
+			model = val;
 			break;
 		}
 	}
@@ -1171,8 +909,10 @@ static int dice_interface_check(struct fw_unit *unit)
 	else
 		category = DICE_CATEGORY_ID;
 	if (device->config_rom[3] != ((vendor << 8) | category) ||
-	    device->config_rom[4] >> 22 != model)
-		return -ENODEV;
+	    device->config_rom[4] >> 22 != model) {
+		err = -ENODEV;
+		goto end;
+	}
 
 	/*
 	 * Check that the sub address spaces exist and are located inside the
@@ -1180,14 +920,18 @@ static int dice_interface_check(struct fw_unit *unit)
 	 * minimally required registers are included.
 	 */
 	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
-				 DICE_PRIVATE_SPACE,
-				 pointers, sizeof(pointers), 0);
-	if (err < 0)
-		return -ENODEV;
-	for (i = 0; i < ARRAY_SIZE(pointers); ++i) {
+				 DICE_PRIVATE_SPACE, pointers,
+				 sizeof(__be32) * ARRAY_SIZE(min_values), 0);
+	if (err < 0) {
+		err = -ENODEV;
+		goto end;
+	}
+	for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
 		value = be32_to_cpu(pointers[i]);
-		if (value < min_values[i] || value >= 0x40000)
-			return -ENODEV;
+		if (value < min_values[i] || value >= 0x40000) {
+			err = -ENODEV;
+			goto end;
+		}
 	}
 
 	/* We support playback only. Let capture devices be handled by FFADO. */
@@ -1195,8 +939,10 @@ static int dice_interface_check(struct fw_unit *unit)
 				 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;
+	if (err < 0 || (tx_data[0] && tx_data[3])) {
+		err = -ENODEV;
+		goto end;
+	}
 
 	/*
 	 * Check that the implemented DICE driver specification major version
@@ -1206,22 +952,25 @@ static int dice_interface_check(struct fw_unit *unit)
 				 DICE_PRIVATE_SPACE +
 				 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
 				 &version, 4, 0);
-	if (err < 0)
-		return -ENODEV;
+	if (err < 0) {
+		err = -ENODEV;
+		goto end;
+	}
 	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;
+		err = -ENODEV;
+		goto end;
 	}
-
-	return 0;
+end:
+	return err;
 }
 
 static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode)
 {
 	int i;
 
-	for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i)
+	for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i)
 		if ((dice->clock_caps & (1 << i)) &&
 		    rate_index_to_mode(i) == mode)
 			return i;
@@ -1241,13 +990,12 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 		return 0;
 	}
 
-	err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+	err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]);
 	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);
+	err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
+					   values, sizeof(values));
 	if (err < 0)
 		return err;
 
@@ -1259,25 +1007,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 
 static int dice_read_params(struct snd_dice *dice)
 {
-	__be32 pointers[6];
 	__be32 value;
 	int mode, err;
 
-	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
-				 DICE_PRIVATE_SPACE,
-				 pointers, sizeof(pointers), 0);
-	if (err < 0)
-		return err;
-
-	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 (dice->clock_caps > 0) {
+		err = snd_dice_transaction_read_global(dice,
+						GLOBAL_CLOCK_CAPABILITIES,
+						&value, 4);
 		if (err < 0)
 			return err;
 		dice->clock_caps = be32_to_cpu(value);
@@ -1310,9 +1047,9 @@ static void dice_card_strings(struct snd_dice *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), 0);
+	err = snd_dice_transaction_read_global(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);
@@ -1333,70 +1070,50 @@ static void dice_card_strings(struct snd_dice *dice)
 	strcpy(card->mixername, "DICE");
 }
 
+static void dice_card_free(struct snd_card *card)
+{
+	struct snd_dice *dice = card->private_data;
+
+	snd_dice_transaction_destroy(dice);
+	mutex_destroy(&dice->mutex);
+}
+
 static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 {
 	struct snd_card *card;
 	struct snd_dice *dice;
-	__be32 clock_sel;
 	int err;
 
 	err = dice_interface_check(unit);
 	if (err < 0)
-		return err;
+		goto end;
 
 	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
 			   sizeof(*dice), &card);
 	if (err < 0)
-		return err;
+		goto end;
 
 	dice = card->private_data;
 	dice->card = card;
+	dice->unit = unit;
+	card->private_free = dice_card_free;
+
 	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;
-	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 = dice_owner_set(dice);
+	err = snd_dice_transaction_init(dice);
 	if (err < 0)
-		goto err_notification_handler;
+		goto error;
 
 	err = dice_read_params(dice);
 	if (err < 0)
-		goto err_owner;
-
-	err = fw_iso_resources_init(&dice->rx_resources, unit);
-	if (err < 0)
-		goto err_owner;
-	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
-
-	err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM,
-				CIP_BLOCKING);
-	if (err < 0)
-		goto err_resources;
-
-	card->private_free = dice_card_free;
+		goto error;
 
 	dice_card_strings(dice);
 
-	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-				 global_address(dice, GLOBAL_CLOCK_SELECT),
-				 &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, 0);
+	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
 	if (err < 0)
 		goto error;
 
@@ -1410,22 +1127,28 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	dice_create_proc(dice);
 
-	err = snd_card_register(card);
+	err = fw_iso_resources_init(&dice->rx_resources, unit);
 	if (err < 0)
 		goto error;
+	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
 
-	dev_set_drvdata(&unit->device, dice);
+	err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM,
+				CIP_BLOCKING);
+	if (err < 0) {
+		fw_iso_resources_destroy(&dice->rx_resources);
+		goto error;
+	}
 
-	return 0;
+	err = snd_card_register(card);
+	if (err < 0) {
+		amdtp_stream_destroy(&dice->rx_stream);
+		fw_iso_resources_destroy(&dice->rx_resources);
+		goto error;
+	}
 
-err_resources:
-	fw_iso_resources_destroy(&dice->rx_resources);
-err_owner:
-	dice_owner_clear(dice);
-err_notification_handler:
-	fw_core_remove_address_handler(&dice->notification_handler);
-err_mutex:
-	mutex_destroy(&dice->mutex);
+	dev_set_drvdata(&unit->device, dice);
+end:
+	return err;
 error:
 	snd_card_free(card);
 	return err;
@@ -1442,7 +1165,6 @@ static void dice_remove(struct fw_unit *unit)
 	mutex_lock(&dice->mutex);
 
 	dice_stream_stop(dice);
-	dice_owner_clear(dice);
 
 	mutex_unlock(&dice->mutex);
 
@@ -1453,6 +1175,11 @@ static void dice_update(struct fw_unit *unit)
 {
 	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
+	/* The handler address register becomes initialized. */
+	snd_dice_transaction_reinit(dice);
+
+	dice->global_enabled = false;
+
 	/*
 	 * On a bus reset, the DICE firmware disables streaming and then goes
 	 * off contemplating its own navel for hundreds of milliseconds before
@@ -1465,11 +1192,7 @@ static void dice_update(struct fw_unit *unit)
 
 	mutex_lock(&dice->mutex);
 
-	dice->global_enabled = false;
 	dice_stream_stop_packets(dice);
-
-	dice_owner_update(dice);
-
 	fw_iso_resources_update(&dice->rx_resources);
 
 	mutex_unlock(&dice->mutex);
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
new file mode 100644
index 0000000..c756e62
--- /dev/null
+++ b/sound/firewire/dice/dice.h
@@ -0,0 +1,160 @@
+/*
+ * dice.h - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DICE_H_INCLUDED
+#define SOUND_DICE_H_INCLUDED
+
+#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>
+#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/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../amdtp.h"
+#include "../iso-resources.h"
+#include "../lib.h"
+#include "dice-interface.h"
+
+struct snd_dice {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	spinlock_t lock;
+	struct mutex mutex;
+
+	/* Offsets for sub-addresses */
+	unsigned int global_offset;
+	unsigned int rx_offset;
+	unsigned int tx_offset;
+	unsigned int sync_offset;
+	unsigned int rsrv_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 rx_resources;
+	struct amdtp_stream rx_stream;
+};
+
+enum snd_dice_addr_type {
+	SND_DICE_ADDR_TYPE_PRIVATE,
+	SND_DICE_ADDR_TYPE_GLOBAL,
+	SND_DICE_ADDR_TYPE_TX,
+	SND_DICE_ADDR_TYPE_RX,
+	SND_DICE_ADDR_TYPE_SYNC,
+	SND_DICE_ADDR_TYPE_RSRV,
+};
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+			       enum snd_dice_addr_type type,
+			       unsigned int offset,
+			       void *buf, unsigned int len);
+int snd_dice_transaction_read(struct snd_dice *dice,
+			      enum snd_dice_addr_type type, unsigned int offset,
+			      void *buf, unsigned int len);
+
+static inline int snd_dice_transaction_write_global(struct snd_dice *dice,
+						    unsigned int offset,
+						    void *buf, unsigned int len)
+{
+	return snd_dice_transaction_write(dice,
+					  SND_DICE_ADDR_TYPE_GLOBAL, offset,
+					  buf, len);
+}
+static inline int snd_dice_transaction_read_global(struct snd_dice *dice,
+						   unsigned int offset,
+						   void *buf, unsigned int len)
+{
+	return snd_dice_transaction_read(dice,
+					 SND_DICE_ADDR_TYPE_GLOBAL, offset,
+					 buf, len);
+}
+static inline int snd_dice_transaction_write_tx(struct snd_dice *dice,
+						unsigned int offset,
+						void *buf, unsigned int len)
+{
+	return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset,
+					  buf, len);
+}
+static inline int snd_dice_transaction_read_tx(struct snd_dice *dice,
+					       unsigned int offset,
+					       void *buf, unsigned int len)
+{
+	return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset,
+					 buf, len);
+}
+static inline int snd_dice_transaction_write_rx(struct snd_dice *dice,
+						unsigned int offset,
+						void *buf, unsigned int len)
+{
+	return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset,
+					  buf, len);
+}
+static inline int snd_dice_transaction_read_rx(struct snd_dice *dice,
+					       unsigned int offset,
+					       void *buf, unsigned int len)
+{
+	return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset,
+					 buf, len);
+}
+static inline int snd_dice_transaction_write_sync(struct snd_dice *dice,
+						  unsigned int offset,
+						  void *buf, unsigned int len)
+{
+	return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+					  buf, len);
+}
+static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
+						 unsigned int offset,
+						 void *buf, unsigned int len)
+{
+	return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+					 buf, len);
+}
+
+int snd_dice_transaction_set_clock_source(struct snd_dice *dice,
+					  unsigned int source);
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+					  unsigned int *source);
+int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
+int snd_dice_transaction_set_enable(struct snd_dice *dice);
+void snd_dice_transaction_clear_enable(struct snd_dice *dice);
+int snd_dice_transaction_init(struct snd_dice *dice);
+int snd_dice_transaction_reinit(struct snd_dice *dice);
+void snd_dice_transaction_destroy(struct snd_dice *dice);
+
+#define SND_DICE_RATES_COUNT	7
+extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
+
+#endif
-- 
1.9.1

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

* [PATCH 04/29] ALSA: dice: Split stream functionality into a file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (2 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 03/29] ALSA: dice: Split transaction functionality into a file Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 05/29] ALSA: dice: Split PCM " Takashi Sakamoto
                   ` (27 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a file with some helper functions for streaming, and move some
codes into the file with some arrangements.

Well-known CMP is not used to start/stop streams for Dice chipset. It's
achieved by writing to specific address. We call this way as 'enable'.
When devices are 'enabled', streaming starts in registered isochronous channel.
Some helper functions are already implemented in previous commit.

Basically, the stream is compliant to IEC 61883-6, so-called as AMDTP. But Dice
has a specific quirk, so called-as 'Dual Wire'. This quirk is applied at
176.4/192.0kHz. In this mode, each packet includes double number of events than
number in the specification, and stream runs at a half of sampling rate.

There is another quirk at bus reset. Dice chipset handles drivers' request but
don't re-enable streaming. So stream should be stopped.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile      |   2 +-
 sound/firewire/dice/dice-stream.c | 204 ++++++++++++++++++++++++++
 sound/firewire/dice/dice.c        | 300 +++++++++-----------------------------
 sound/firewire/dice/dice.h        |  14 ++
 4 files changed, 289 insertions(+), 231 deletions(-)
 create mode 100644 sound/firewire/dice/dice-stream.c

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 9f473cb..867864c 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,2 +1,2 @@
-snd-dice-objs := dice-transaction.o dice.o
+snd-dice-objs := dice-transaction.o dice-stream.o dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
new file mode 100644
index 0000000..a319a3e
--- /dev/null
+++ b/sound/firewire/dice/dice-stream.c
@@ -0,0 +1,204 @@
+/*
+ * dice_stream.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
+	/* mode 0 */
+	[0] =  32000,
+	[1] =  44100,
+	[2] =  48000,
+	/* mode 1 */
+	[3] =  88200,
+	[4] =  96000,
+	/* mode 2 */
+	[5] = 176400,
+	[6] = 192000,
+};
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+				  unsigned int *mode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+		if (!(dice->clock_caps & BIT(i)))
+			continue;
+		if (snd_dice_rates[i] != rate)
+			continue;
+
+		*mode = (i - 1) / 2;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+int snd_dice_stream_start_packets(struct snd_dice *dice)
+{
+	int err;
+
+	if (amdtp_stream_running(&dice->rx_stream))
+		return 0;
+
+	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
+				 fw_parent_device(dice->unit)->max_speed);
+	if (err < 0)
+		return err;
+
+	err = snd_dice_transaction_set_enable(dice);
+	if (err < 0) {
+		amdtp_stream_stop(&dice->rx_stream);
+		return err;
+	}
+
+	return 0;
+}
+
+int snd_dice_stream_start(struct snd_dice *dice)
+{
+	__be32 channel;
+	int err;
+
+	if (!dice->rx_resources.allocated) {
+		err = fw_iso_resources_allocate(&dice->rx_resources,
+				amdtp_stream_get_max_payload(&dice->rx_stream),
+				fw_parent_device(dice->unit)->max_speed);
+		if (err < 0)
+			goto error;
+
+		channel = cpu_to_be32(dice->rx_resources.channel);
+		err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS,
+						    &channel, 4);
+		if (err < 0)
+			goto err_resources;
+	}
+
+	err = snd_dice_stream_start_packets(dice);
+	if (err < 0)
+		goto err_rx_channel;
+
+	return 0;
+
+err_rx_channel:
+	channel = cpu_to_be32((u32)-1);
+	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+err_resources:
+	fw_iso_resources_free(&dice->rx_resources);
+error:
+	return err;
+}
+
+void snd_dice_stream_stop_packets(struct snd_dice *dice)
+{
+	if (amdtp_stream_running(&dice->rx_stream)) {
+		snd_dice_transaction_clear_enable(dice);
+		amdtp_stream_stop(&dice->rx_stream);
+	}
+}
+
+void snd_dice_stream_stop(struct snd_dice *dice)
+{
+	__be32 channel;
+
+	snd_dice_stream_stop_packets(dice);
+
+	if (!dice->rx_resources.allocated)
+		return;
+
+	channel = cpu_to_be32((u32)-1);
+	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+
+	fw_iso_resources_free(&dice->rx_resources);
+}
+
+int snd_dice_stream_init(struct snd_dice *dice)
+{
+	int err;
+
+	err = fw_iso_resources_init(&dice->rx_resources, dice->unit);
+	if (err < 0)
+		goto end;
+	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
+
+	err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM,
+				CIP_BLOCKING);
+	if (err < 0)
+		goto error;
+
+	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
+	if (err < 0)
+		goto error;
+end:
+	return err;
+error:
+	amdtp_stream_destroy(&dice->rx_stream);
+	fw_iso_resources_destroy(&dice->rx_resources);
+	return err;
+}
+
+void snd_dice_stream_destroy(struct snd_dice *dice)
+{
+	amdtp_stream_pcm_abort(&dice->rx_stream);
+	snd_dice_stream_stop(dice);
+	amdtp_stream_destroy(&dice->rx_stream);
+	fw_iso_resources_destroy(&dice->rx_resources);
+}
+
+void snd_dice_stream_update(struct snd_dice *dice)
+{
+	/*
+	 * 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.
+	 */
+	amdtp_stream_pcm_abort(&dice->rx_stream);
+	snd_dice_stream_stop_packets(dice);
+	fw_iso_resources_update(&dice->rx_resources);
+}
+
+static void dice_lock_changed(struct snd_dice *dice)
+{
+	dice->dev_lock_changed = true;
+	wake_up(&dice->hwdep_wait);
+}
+
+int snd_dice_stream_lock_try(struct snd_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;
+}
+
+void snd_dice_stream_lock_release(struct snd_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);
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index ddf05fd..588a89e 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -11,127 +11,62 @@ MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
-const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
-	/* mode 0 */
-	[0] =  32000,
-	[1] =  44100,
-	[2] =  48000,
-	/* mode 1 */
-	[3] =  88200,
-	[4] =  96000,
-	/* mode 2 */
-	[5] = 176400,
-	[6] = 192000,
-};
-
-static unsigned int rate_to_index(unsigned int rate)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
-		if (snd_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;
-}
-
-static void dice_lock_changed(struct snd_dice *dice)
-{
-	dice->dev_lock_changed = true;
-	wake_up(&dice->hwdep_wait);
-}
-
-static int dice_try_lock(struct snd_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 snd_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 int dice_rate_constraint(struct snd_pcm_hw_params *params,
 				struct snd_pcm_hw_rule *rule)
 {
 	struct snd_dice *dice = rule->private;
-	const struct snd_interval *channels =
+
+	const struct snd_interval *c =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval *rate =
+	struct snd_interval *r =
 		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval allowed_rates = {
+	struct snd_interval rates = {
 		.min = UINT_MAX, .max = 0, .integer = 1
 	};
-	unsigned int i, mode;
+	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
 
 	for (i = 0; i < ARRAY_SIZE(snd_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,
-						snd_dice_rates[i]);
-			allowed_rates.max = max(allowed_rates.max,
-						snd_dice_rates[i]);
-		}
+		rate = snd_dice_rates[i];
+		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+			continue;
+
+		if (!snd_interval_test(c, pcm_channels[mode]))
+			continue;
+
+		rates.min = min(rates.min, rate);
+		rates.max = max(rates.max, rate);
 	}
 
-	return snd_interval_refine(rate, &allowed_rates);
+	return snd_interval_refine(r, &rates);
 }
 
 static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 				    struct snd_pcm_hw_rule *rule)
 {
 	struct snd_dice *dice = rule->private;
-	const struct snd_interval *rate =
+
+	const struct snd_interval *r =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *channels =
+	struct snd_interval *c =
 		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval allowed_channels = {
+	struct snd_interval channels = {
 		.min = UINT_MAX, .max = 0, .integer = 1
 	};
-	unsigned int i, mode;
+	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
 
-	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
-		if ((dice->clock_caps & (1 << i)) &&
-		    snd_interval_test(rate, snd_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]);
-		}
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+		rate = snd_dice_rates[i];
+		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+			continue;
 
-	return snd_interval_refine(channels, &allowed_channels);
+		if (!snd_interval_test(r, rate))
+			continue;
+
+		channels.min = min(channels.min, pcm_channels[mode]);
+		channels.max = max(channels.max, pcm_channels[mode]);
+	}
+
+	return snd_interval_refine(c, &channels);
 }
 
 static int dice_open(struct snd_pcm_substream *substream)
@@ -156,7 +91,7 @@ static int dice_open(struct snd_pcm_substream *substream)
 	unsigned int i;
 	int err;
 
-	err = dice_try_lock(dice);
+	err = snd_dice_stream_lock_try(dice);
 	if (err < 0)
 		goto error;
 
@@ -194,7 +129,7 @@ static int dice_open(struct snd_pcm_substream *substream)
 	return 0;
 
 err_lock:
-	dice_unlock(dice);
+	snd_dice_stream_lock_release(dice);
 error:
 	return err;
 }
@@ -203,98 +138,20 @@ static int dice_close(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
-	dice_unlock(dice);
-
-	return 0;
-}
-
-static int dice_stream_start_packets(struct snd_dice *dice)
-{
-	int err;
-
-	if (amdtp_stream_running(&dice->rx_stream))
-		return 0;
-
-	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
-				 fw_parent_device(dice->unit)->max_speed);
-	if (err < 0)
-		return err;
-
-	err = snd_dice_transaction_set_enable(dice);
-	if (err < 0) {
-		amdtp_stream_stop(&dice->rx_stream);
-		return err;
-	}
+	snd_dice_stream_lock_release(dice);
 
 	return 0;
 }
 
-static int dice_stream_start(struct snd_dice *dice)
-{
-	__be32 channel;
-	int err;
-
-	if (!dice->rx_resources.allocated) {
-		err = fw_iso_resources_allocate(&dice->rx_resources,
-				amdtp_stream_get_max_payload(&dice->rx_stream),
-				fw_parent_device(dice->unit)->max_speed);
-		if (err < 0)
-			goto error;
-
-		channel = cpu_to_be32(dice->rx_resources.channel);
-		err = snd_dice_transaction_write_tx(dice, TX_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_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
-err_resources:
-	fw_iso_resources_free(&dice->rx_resources);
-error:
-	return err;
-}
-
-static void dice_stream_stop_packets(struct snd_dice *dice)
-{
-	if (amdtp_stream_running(&dice->rx_stream)) {
-		snd_dice_transaction_clear_enable(dice);
-		amdtp_stream_stop(&dice->rx_stream);
-	}
-}
-
-static void dice_stream_stop(struct snd_dice *dice)
-{
-	__be32 channel;
-
-	dice_stream_stop_packets(dice);
-
-	if (!dice->rx_resources.allocated)
-		return;
-
-	channel = cpu_to_be32((u32)-1);
-	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
-
-	fw_iso_resources_free(&dice->rx_resources);
-}
-
 static int dice_hw_params(struct snd_pcm_substream *substream,
 			  struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_dice *dice = substream->private_data;
-	unsigned int rate_index, mode, rate, channels, i;
+	unsigned int mode, rate, channels, i;
 	int err;
 
 	mutex_lock(&dice->mutex);
-	dice_stream_stop(dice);
+	snd_dice_stream_stop(dice);
 	mutex_unlock(&dice->mutex);
 
 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -307,6 +164,10 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 	if (err < 0)
 		return err;
 
+	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+	if (err < 0)
+		return err;
+
 	/*
 	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
 	 * one data block of AMDTP packet. Thus sampling transfer frequency is
@@ -318,8 +179,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 	 * be aligned to SYT_INTERVAL.
 	 */
 	channels = params_channels(hw_params);
-	rate_index = rate_to_index(rate);
-	if (rate_index > 4) {
+	if (mode > 1) {
 		if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
 			err = -ENOSYS;
 			return err;
@@ -332,10 +192,9 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 		dice->rx_stream.double_pcm_frames = false;
 	}
 
-	mode = rate_index_to_mode(rate_index);
 	amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
 				    dice->rx_midi_ports[mode]);
-	if (rate_index > 4) {
+	if (mode > 4) {
 		channels /= 2;
 
 		for (i = 0; i < channels; i++) {
@@ -355,7 +214,7 @@ static int dice_hw_free(struct snd_pcm_substream *substream)
 	struct snd_dice *dice = substream->private_data;
 
 	mutex_lock(&dice->mutex);
-	dice_stream_stop(dice);
+	snd_dice_stream_stop(dice);
 	mutex_unlock(&dice->mutex);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
@@ -369,9 +228,9 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 	mutex_lock(&dice->mutex);
 
 	if (amdtp_streaming_error(&dice->rx_stream))
-		dice_stream_stop_packets(dice);
+		snd_dice_stream_stop_packets(dice);
 
-	err = dice_stream_start(dice);
+	err = snd_dice_stream_start(dice);
 	if (err < 0) {
 		mutex_unlock(&dice->mutex);
 		return err;
@@ -966,31 +825,37 @@ end:
 	return err;
 }
 
-static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode)
+static int highest_supported_mode_rate(struct snd_dice *dice,
+				       unsigned int mode, unsigned int *rate)
 {
-	int i;
+	unsigned int i, m;
 
-	for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i)
-		if ((dice->clock_caps & (1 << i)) &&
-		    rate_index_to_mode(i) == mode)
-			return i;
+	for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
+		*rate = snd_dice_rates[i - 1];
+		if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
+			continue;
+		if (mode == m)
+			break;
+	}
+	if (i == 0)
+		return -EINVAL;
 
-	return -1;
+	return 0;
 }
 
 static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 {
 	__be32 values[2];
-	int rate_index, err;
+	unsigned int rate;
+	int err;
 
-	rate_index = highest_supported_mode_rate(dice, mode);
-	if (rate_index < 0) {
+	if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
 		dice->rx_channels[mode] = 0;
 		dice->rx_midi_ports[mode] = 0;
 		return 0;
 	}
 
-	err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]);
+	err = snd_dice_transaction_set_rate(dice, rate);
 	if (err < 0)
 		return err;
 
@@ -1113,10 +978,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	dice_card_strings(dice);
 
-	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
-	if (err < 0)
-		goto error;
-
 	err = dice_create_pcm(dice);
 	if (err < 0)
 		goto error;
@@ -1127,22 +988,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	dice_create_proc(dice);
 
-	err = fw_iso_resources_init(&dice->rx_resources, unit);
+	err = snd_dice_stream_init(dice);
 	if (err < 0)
 		goto error;
-	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
-
-	err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM,
-				CIP_BLOCKING);
-	if (err < 0) {
-		fw_iso_resources_destroy(&dice->rx_resources);
-		goto error;
-	}
 
 	err = snd_card_register(card);
 	if (err < 0) {
-		amdtp_stream_destroy(&dice->rx_stream);
-		fw_iso_resources_destroy(&dice->rx_resources);
+		snd_dice_stream_destroy(dice);
 		goto error;
 	}
 
@@ -1158,13 +1010,11 @@ static void dice_remove(struct fw_unit *unit)
 {
 	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
-	amdtp_stream_pcm_abort(&dice->rx_stream);
-
 	snd_card_disconnect(dice->card);
 
 	mutex_lock(&dice->mutex);
 
-	dice_stream_stop(dice);
+	snd_dice_stream_destroy(dice);
 
 	mutex_unlock(&dice->mutex);
 
@@ -1178,22 +1028,12 @@ static void dice_update(struct fw_unit *unit)
 	/* The handler address register becomes initialized. */
 	snd_dice_transaction_reinit(dice);
 
-	dice->global_enabled = false;
-
-	/*
-	 * 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.
-	 */
-	amdtp_stream_pcm_abort(&dice->rx_stream);
-
 	mutex_lock(&dice->mutex);
 
-	dice_stream_stop_packets(dice);
-	fw_iso_resources_update(&dice->rx_resources);
+	/* The enable register becomes initialized, then streams are stopped. */
+	dice->global_enabled = false;
+
+	snd_dice_stream_stop_packets(dice);
 
 	mutex_unlock(&dice->mutex);
 }
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index c756e62..ca4090d 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -157,4 +157,18 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
 #define SND_DICE_RATES_COUNT	7
 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
+				  unsigned int rate, unsigned int *mode);
+
+int snd_dice_stream_start_packets(struct snd_dice *dice);
+int snd_dice_stream_start(struct snd_dice *dice);
+void snd_dice_stream_stop_packets(struct snd_dice *dice);
+void snd_dice_stream_stop(struct snd_dice *dice);
+int snd_dice_stream_init(struct snd_dice *dice);
+void snd_dice_stream_destroy(struct snd_dice *dice);
+void snd_dice_stream_update(struct snd_dice *dice);
+
+int snd_dice_stream_lock_try(struct snd_dice *dice);
+void snd_dice_stream_lock_release(struct snd_dice *dice);
+
 #endif
-- 
1.9.1

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

* [PATCH 05/29] ALSA: dice: Split PCM functionality into a file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (3 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 04/29] ALSA: dice: Split stream " Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 06/29] ALSA: dice: Split hwdep " Takashi Sakamoto
                   ` (26 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a file and move some codes related to PCM functionality.

Currently PCM playback is supported. PCM capture will be supported in followed
commits.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile      |   2 +-
 sound/firewire/dice/dice-pcm.c    | 296 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/dice/dice-stream.c |   9 +-
 sound/firewire/dice/dice.c        | 288 +------------------------------------
 sound/firewire/dice/dice.h        |   2 +
 5 files changed, 305 insertions(+), 292 deletions(-)
 create mode 100644 sound/firewire/dice/dice-pcm.c

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 867864c..1ddaeca 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,2 +1,2 @@
-snd-dice-objs := dice-transaction.o dice-stream.o dice.o
+snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
new file mode 100644
index 0000000..deacb53
--- /dev/null
+++ b/sound/firewire/dice/dice-pcm.c
@@ -0,0 +1,296 @@
+/*
+ * dice_pcm.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_dice *dice = rule->private;
+
+	const struct snd_interval *c =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *r =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval rates = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
+
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+		rate = snd_dice_rates[i];
+		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+			continue;
+
+		if (!snd_interval_test(c, pcm_channels[mode]))
+			continue;
+
+		rates.min = min(rates.min, rate);
+		rates.max = max(rates.max, rate);
+	}
+
+	return snd_interval_refine(r, &rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+				    struct snd_pcm_hw_rule *rule)
+{
+	struct snd_dice *dice = rule->private;
+
+	const struct snd_interval *r =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *c =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval channels = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
+
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+		rate = snd_dice_rates[i];
+		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+			continue;
+
+		if (!snd_interval_test(r, rate))
+			continue;
+
+		channels.min = min(channels.min, pcm_channels[mode]);
+		channels.max = max(channels.max, pcm_channels[mode]);
+	}
+
+	return snd_interval_refine(c, &channels);
+}
+
+
+static int pcm_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,
+		.channels_min = UINT_MAX,
+		.channels_max = 0,
+		.buffer_bytes_max = 16 * 1024 * 1024,
+		.period_bytes_min = 1,
+		.period_bytes_max = UINT_MAX,
+		.periods_min = 1,
+		.periods_max = UINT_MAX,
+	};
+	struct snd_dice *dice = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int i;
+	int err;
+
+	err = snd_dice_stream_lock_try(dice);
+	if (err < 0)
+		goto error;
+
+	runtime->hw = hardware;
+
+	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+		if (dice->clock_caps & (1 << i))
+			runtime->hw.rates |=
+				snd_pcm_rate_to_rate_bit(snd_dice_rates[i]);
+	}
+	snd_pcm_limit_hw_rates(runtime);
+
+	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]);
+		}
+	}
+
+	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 = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
+	if (err < 0)
+		goto err_lock;
+
+	return 0;
+
+err_lock:
+	snd_dice_stream_lock_release(dice);
+error:
+	return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	snd_dice_stream_lock_release(dice);
+
+	return 0;
+}
+
+static int playback_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_dice *dice = substream->private_data;
+	unsigned int mode, rate, channels, i;
+	int err;
+
+	mutex_lock(&dice->mutex);
+	snd_dice_stream_stop(dice);
+	mutex_unlock(&dice->mutex);
+
+	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+					       params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+
+	rate = params_rate(hw_params);
+	err = snd_dice_transaction_set_rate(dice, rate);
+	if (err < 0)
+		return err;
+
+	if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+		return err;
+
+	/*
+	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+	 * one data block of AMDTP packet. Thus sampling transfer frequency is
+	 * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+	 * transferred on AMDTP packets at 96 kHz. Two successive samples of a
+	 * channel are stored consecutively in the packet. This quirk is called
+	 * as 'Dual Wire'.
+	 * For this quirk, blocking mode is required and PCM buffer size should
+	 * be aligned to SYT_INTERVAL.
+	 */
+	channels = params_channels(hw_params);
+	if (mode > 1) {
+		if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
+			err = -ENOSYS;
+			return err;
+		}
+
+		rate /= 2;
+		channels *= 2;
+		dice->rx_stream.double_pcm_frames = true;
+	} else {
+		dice->rx_stream.double_pcm_frames = false;
+	}
+
+	amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
+				    dice->rx_midi_ports[mode]);
+	if (mode > 1) {
+		channels /= 2;
+
+		for (i = 0; i < channels; i++) {
+			dice->rx_stream.pcm_positions[i] = i * 2;
+			dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
+		}
+	}
+
+	amdtp_stream_set_pcm_format(&dice->rx_stream,
+				    params_format(hw_params));
+
+	return 0;
+}
+
+static int playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	mutex_lock(&dice->mutex);
+	snd_dice_stream_stop(dice);
+	mutex_unlock(&dice->mutex);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+	int err;
+
+	mutex_lock(&dice->mutex);
+
+	if (amdtp_streaming_error(&dice->rx_stream))
+		snd_dice_stream_stop_packets(dice);
+
+	err = snd_dice_stream_start(dice);
+	if (err < 0) {
+		mutex_unlock(&dice->mutex);
+		return err;
+	}
+
+	mutex_unlock(&dice->mutex);
+
+	amdtp_stream_pcm_prepare(&dice->rx_stream);
+
+	return 0;
+}
+
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	return amdtp_stream_pcm_pointer(&dice->rx_stream);
+}
+
+int snd_dice_create_pcm(struct snd_dice *dice)
+{
+	static struct snd_pcm_ops playback_ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = playback_hw_params,
+		.hw_free   = playback_hw_free,
+		.prepare   = playback_prepare,
+		.trigger   = playback_trigger,
+		.pointer   = playback_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = dice;
+	strcpy(pcm->name, dice->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+
+	return 0;
+}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index a319a3e..ed10fea 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -96,10 +96,11 @@ error:
 
 void snd_dice_stream_stop_packets(struct snd_dice *dice)
 {
-	if (amdtp_stream_running(&dice->rx_stream)) {
-		snd_dice_transaction_clear_enable(dice);
-		amdtp_stream_stop(&dice->rx_stream);
-	}
+	if (!amdtp_stream_running(&dice->rx_stream))
+		return;
+
+	snd_dice_transaction_clear_enable(dice);
+	amdtp_stream_stop(&dice->rx_stream);
 }
 
 void snd_dice_stream_stop(struct snd_dice *dice)
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 588a89e..4f0356c 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -11,292 +11,6 @@ MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
-				struct snd_pcm_hw_rule *rule)
-{
-	struct snd_dice *dice = rule->private;
-
-	const struct snd_interval *c =
-		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval *r =
-		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval rates = {
-		.min = UINT_MAX, .max = 0, .integer = 1
-	};
-	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
-
-	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-		rate = snd_dice_rates[i];
-		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-			continue;
-
-		if (!snd_interval_test(c, pcm_channels[mode]))
-			continue;
-
-		rates.min = min(rates.min, rate);
-		rates.max = max(rates.max, rate);
-	}
-
-	return snd_interval_refine(r, &rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
-				    struct snd_pcm_hw_rule *rule)
-{
-	struct snd_dice *dice = rule->private;
-
-	const struct snd_interval *r =
-		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *c =
-		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval channels = {
-		.min = UINT_MAX, .max = 0, .integer = 1
-	};
-	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
-
-	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-		rate = snd_dice_rates[i];
-		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-			continue;
-
-		if (!snd_interval_test(r, rate))
-			continue;
-
-		channels.min = min(channels.min, pcm_channels[mode]);
-		channels.max = max(channels.max, pcm_channels[mode]);
-	}
-
-	return snd_interval_refine(c, &channels);
-}
-
-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,
-		.channels_min = UINT_MAX,
-		.channels_max = 0,
-		.buffer_bytes_max = 16 * 1024 * 1024,
-		.period_bytes_min = 1,
-		.period_bytes_max = UINT_MAX,
-		.periods_min = 1,
-		.periods_max = UINT_MAX,
-	};
-	struct snd_dice *dice = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned int i;
-	int err;
-
-	err = snd_dice_stream_lock_try(dice);
-	if (err < 0)
-		goto error;
-
-	runtime->hw = hardware;
-
-	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
-		if (dice->clock_caps & (1 << i))
-			runtime->hw.rates |=
-				snd_pcm_rate_to_rate_bit(snd_dice_rates[i]);
-	snd_pcm_limit_hw_rates(runtime);
-
-	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]);
-		}
-
-	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 = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
-	if (err < 0)
-		goto err_lock;
-
-	return 0;
-
-err_lock:
-	snd_dice_stream_lock_release(dice);
-error:
-	return err;
-}
-
-static int dice_close(struct snd_pcm_substream *substream)
-{
-	struct snd_dice *dice = substream->private_data;
-
-	snd_dice_stream_lock_release(dice);
-
-	return 0;
-}
-
-static int dice_hw_params(struct snd_pcm_substream *substream,
-			  struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_dice *dice = substream->private_data;
-	unsigned int mode, rate, channels, i;
-	int err;
-
-	mutex_lock(&dice->mutex);
-	snd_dice_stream_stop(dice);
-	mutex_unlock(&dice->mutex);
-
-	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-					       params_buffer_bytes(hw_params));
-	if (err < 0)
-		return err;
-
-	rate = params_rate(hw_params);
-	err = snd_dice_transaction_set_rate(dice, rate);
-	if (err < 0)
-		return err;
-
-	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
-	if (err < 0)
-		return err;
-
-	/*
-	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
-	 * one data block of AMDTP packet. Thus sampling transfer frequency is
-	 * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
-	 * transferred on AMDTP packets at 96 kHz. Two successive samples of a
-	 * channel are stored consecutively in the packet. This quirk is called
-	 * as 'Dual Wire'.
-	 * For this quirk, blocking mode is required and PCM buffer size should
-	 * be aligned to SYT_INTERVAL.
-	 */
-	channels = params_channels(hw_params);
-	if (mode > 1) {
-		if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
-			err = -ENOSYS;
-			return err;
-		}
-
-		rate /= 2;
-		channels *= 2;
-		dice->rx_stream.double_pcm_frames = true;
-	} else {
-		dice->rx_stream.double_pcm_frames = false;
-	}
-
-	amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
-				    dice->rx_midi_ports[mode]);
-	if (mode > 4) {
-		channels /= 2;
-
-		for (i = 0; i < channels; i++) {
-			dice->rx_stream.pcm_positions[i] = i * 2;
-			dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
-		}
-	}
-
-	amdtp_stream_set_pcm_format(&dice->rx_stream,
-				    params_format(hw_params));
-
-	return 0;
-}
-
-static int dice_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_dice *dice = substream->private_data;
-
-	mutex_lock(&dice->mutex);
-	snd_dice_stream_stop(dice);
-	mutex_unlock(&dice->mutex);
-
-	return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int dice_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_dice *dice = substream->private_data;
-	int err;
-
-	mutex_lock(&dice->mutex);
-
-	if (amdtp_streaming_error(&dice->rx_stream))
-		snd_dice_stream_stop_packets(dice);
-
-	err = snd_dice_stream_start(dice);
-	if (err < 0) {
-		mutex_unlock(&dice->mutex);
-		return err;
-	}
-
-	mutex_unlock(&dice->mutex);
-
-	amdtp_stream_pcm_prepare(&dice->rx_stream);
-
-	return 0;
-}
-
-static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_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_stream_pcm_trigger(&dice->rx_stream, pcm);
-
-	return 0;
-}
-
-static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_dice *dice = substream->private_data;
-
-	return amdtp_stream_pcm_pointer(&dice->rx_stream);
-}
-
-static int dice_create_pcm(struct snd_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,
-	};
-	struct snd_pcm *pcm;
-	int 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);
-	pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops;
-
-	return 0;
-}
-
 static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
 			    long count, loff_t *offset)
 {
@@ -978,7 +692,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	dice_card_strings(dice);
 
-	err = dice_create_pcm(dice);
+	err = snd_dice_create_pcm(dice);
 	if (err < 0)
 		goto error;
 
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index ca4090d..4d9e55b 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -171,4 +171,6 @@ void snd_dice_stream_update(struct snd_dice *dice);
 int snd_dice_stream_lock_try(struct snd_dice *dice);
 void snd_dice_stream_lock_release(struct snd_dice *dice);
 
+int snd_dice_create_pcm(struct snd_dice *dice);
+
 #endif
-- 
1.9.1

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

* [PATCH 06/29] ALSA: dice: Split hwdep functionality into a file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (4 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 05/29] ALSA: dice: Split PCM " Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 07/29] ALSA: dice: Split proc interface " Takashi Sakamoto
                   ` (25 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a file and move some codes related to hwdep functionality.

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

Additionally, this interface give a way to read Dice notification.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile     |   3 +-
 sound/firewire/dice/dice-hwdep.c | 190 +++++++++++++++++++++++++++++++++++++++
 sound/firewire/dice/dice.c       | 182 +------------------------------------
 sound/firewire/dice/dice.h       |   2 +
 4 files changed, 195 insertions(+), 182 deletions(-)
 create mode 100644 sound/firewire/dice/dice-hwdep.c

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 1ddaeca..73b0e38 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,2 +1,3 @@
-snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o
+snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \
+		 dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c
new file mode 100644
index 0000000..a4dc02a
--- /dev/null
+++ b/sound/firewire/dice/dice-hwdep.c
@@ -0,0 +1,190 @@
+/*
+ * dice_hwdep.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+			    long count, loff_t *offset)
+{
+	struct snd_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_t(long, count, 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_t(long, count, sizeof(event.dice_notification));
+	}
+
+	spin_unlock_irq(&dice->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+			       poll_table *wait)
+{
+	struct snd_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 hwdep_get_info(struct snd_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 int hwdep_lock(struct snd_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 hwdep_unlock(struct snd_dice *dice)
+{
+	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 hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct snd_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 hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct snd_dice *dice = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return hwdep_get_info(dice, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return hwdep_lock(dice);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return hwdep_unlock(dice);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+			      unsigned int cmd, unsigned long arg)
+{
+	return hwdep_ioctl(hwdep, file, cmd,
+			   (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dice_create_hwdep(struct snd_dice *dice)
+{
+	static const struct snd_hwdep_ops ops = {
+		.read         = hwdep_read,
+		.release      = hwdep_release,
+		.poll         = hwdep_poll,
+		.ioctl        = hwdep_ioctl,
+		.ioctl_compat = hwdep_compat_ioctl,
+	};
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(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;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 4f0356c..e7d665a 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -11,186 +11,6 @@ MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
-static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
-			    long count, loff_t *offset)
-{
-	struct snd_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_t(long, count, 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_t(long, count, sizeof(event.dice_notification));
-	}
-
-	spin_unlock_irq(&dice->lock);
-
-	if (copy_to_user(buf, &event, count))
-		return -EFAULT;
-
-	return count;
-}
-
-static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
-				    poll_table *wait)
-{
-	struct snd_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_get_info(struct snd_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 int dice_hwdep_lock(struct snd_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 snd_dice *dice)
-{
-	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_release(struct snd_hwdep *hwdep, struct file *file)
-{
-	struct snd_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)
-{
-	struct snd_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 snd_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,
-	};
-	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 int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
 			      unsigned int offset_q, unsigned int quadlets)
 {
@@ -696,7 +516,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto error;
 
-	err = dice_create_hwdep(dice);
+	err = snd_dice_create_hwdep(dice);
 	if (err < 0)
 		goto error;
 
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 4d9e55b..dcc8c78 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -173,4 +173,6 @@ void snd_dice_stream_lock_release(struct snd_dice *dice);
 
 int snd_dice_create_pcm(struct snd_dice *dice);
 
+int snd_dice_create_hwdep(struct snd_dice *dice);
+
 #endif
-- 
1.9.1

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

* [PATCH 07/29] ALSA: dice: Split proc interface into a file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (5 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 06/29] ALSA: dice: Split hwdep " Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 08/29] ALSA: dice: Add new functions for constraints of PCM parameters Takashi Sakamoto
                   ` (24 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a file and move some codes related to proc output.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile    |   4 +-
 sound/firewire/dice/dice-proc.c | 252 ++++++++++++++++++++++++++++++++++++++++
 sound/firewire/dice/dice.c      | 244 +-------------------------------------
 sound/firewire/dice/dice.h      |   2 +
 4 files changed, 257 insertions(+), 245 deletions(-)
 create mode 100644 sound/firewire/dice/dice-proc.c

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 73b0e38..9a48289 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,3 +1,3 @@
-snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \
-		 dice.o
+snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-pcm.o \
+		 dice-hwdep.o dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c
new file mode 100644
index 0000000..f5c1d1b
--- /dev/null
+++ b/sound/firewire/dice/dice-proc.c
@@ -0,0 +1,252 @@
+/*
+ * dice_proc.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static int dice_proc_read_mem(struct snd_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];
+
+	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 snd_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) / 4);
+	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) / 4);
+	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", 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);
+	}
+}
+
+void snd_dice_create_proc(struct snd_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);
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index e7d665a..7e8ecd3 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -11,248 +11,6 @@ MODULE_DESCRIPTION("DICE driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 
-static int dice_proc_read_mem(struct snd_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];
-
-	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 snd_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) / 4);
-	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) / 4);
-	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", 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 snd_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);
-}
-
 #define OUI_WEISS		0x001c6a
 
 #define DICE_CATEGORY_ID	0x04
@@ -520,7 +278,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 	if (err < 0)
 		goto error;
 
-	dice_create_proc(dice);
+	snd_dice_create_proc(dice);
 
 	err = snd_dice_stream_init(dice);
 	if (err < 0)
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index dcc8c78..969189a 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -175,4 +175,6 @@ int snd_dice_create_pcm(struct snd_dice *dice);
 
 int snd_dice_create_hwdep(struct snd_dice *dice);
 
+void snd_dice_create_proc(struct snd_dice *dice);
+
 #endif
-- 
1.9.1

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

* [PATCH 08/29] ALSA: dice: Add new functions for constraints of PCM parameters
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (6 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 07/29] ALSA: dice: Split proc interface " Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 09/29] ALSA: dice: Change the way to start stream Takashi Sakamoto
                   ` (23 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a new functions and some arrangement for PCM restriction.
This arrangement is due to the number of channels which each Dice device has.

I note that minimum number for period becomes 2, instead of 1 because its PCM
functionality has SNDRV_PCM_INFO_BATCH, this means that the driver uses double
(or more) buffering so the minimum number for period should be 2.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/dice-pcm.c | 109 ++++++++++++++++++++++++-----------------
 1 file changed, 65 insertions(+), 44 deletions(-)

diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index deacb53..2e531bd 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -67,71 +67,92 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 	return snd_interval_refine(c, &channels);
 }
 
-
-static int pcm_open(struct snd_pcm_substream *substream)
+static void limit_channels_and_rates(struct snd_dice *dice,
+				     struct snd_pcm_runtime *runtime,
+				     unsigned int *pcm_channels)
 {
-	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,
-		.channels_min = UINT_MAX,
-		.channels_max = 0,
-		.buffer_bytes_max = 16 * 1024 * 1024,
-		.period_bytes_min = 1,
-		.period_bytes_max = UINT_MAX,
-		.periods_min = 1,
-		.periods_max = UINT_MAX,
-	};
-	struct snd_dice *dice = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned int i;
-	int err;
+	struct snd_pcm_hardware *hw = &runtime->hw;
+	unsigned int i, rate, mode;
 
-	err = snd_dice_stream_lock_try(dice);
-	if (err < 0)
-		goto error;
-
-	runtime->hw = hardware;
+	hw->channels_min = UINT_MAX;
+	hw->channels_max = 0;
 
 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-		if (dice->clock_caps & (1 << i))
-			runtime->hw.rates |=
-				snd_pcm_rate_to_rate_bit(snd_dice_rates[i]);
+		rate = snd_dice_rates[i];
+		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+			continue;
+		hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+
+		if (pcm_channels[mode] == 0)
+			continue;
+		hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+		hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
 	}
+
 	snd_pcm_limit_hw_rates(runtime);
+}
 
-	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]);
-		}
-	}
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+	hw->periods_min = 2;			/* SNDRV_PCM_INFO_BATCH */
+	hw->periods_max = UINT_MAX;
+
+	hw->period_bytes_min = 4 * hw->channels_max;    /* byte for a frame */
+
+	/* Just to prevent from allocating much pages. */
+	hw->period_bytes_max = hw->period_bytes_min * 2048;
+	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int init_hw_info(struct snd_dice *dice,
+			struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_pcm_hardware *hw = &runtime->hw;
+	int err;
+
+	hw->info = SNDRV_PCM_INFO_MMAP |
+		   SNDRV_PCM_INFO_MMAP_VALID |
+		   SNDRV_PCM_INFO_BATCH |
+		   SNDRV_PCM_INFO_INTERLEAVED |
+		   SNDRV_PCM_INFO_BLOCK_TRANSFER;
+	hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+
+	limit_channels_and_rates(dice, runtime, dice->rx_channels);
+	limit_period_and_buffer(hw);
 
 	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;
+		goto end;
 	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;
+		goto end;
 
 	err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
-	if (err < 0)
-		goto err_lock;
+end:
+	return err;
+}
 
-	return 0;
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+	int err;
 
-err_lock:
+	err = snd_dice_stream_lock_try(dice);
+	if (err < 0)
+		goto end;
+
+	err = init_hw_info(dice, substream);
+	if (err < 0)
+		goto err_locked;
+end:
+	return err;
+err_locked:
 	snd_dice_stream_lock_release(dice);
-error:
 	return err;
 }
 
-- 
1.9.1

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

* [PATCH 09/29] ALSA: dice: Change the way to start stream
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (7 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 08/29] ALSA: dice: Add new functions for constraints of PCM parameters Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 10/29] ALSA: dice: Add support for duplex streams with synchronization Takashi Sakamoto
                   ` (22 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Streaming functionality can start streams when rate is given but currently
some codes are in PCM functionality.

This commit changes the way to start stream and add some arrangement
to make it easy to understand the way.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/dice-pcm.c    |  79 ++-------------
 sound/firewire/dice/dice-stream.c | 207 ++++++++++++++++++++++++++++----------
 sound/firewire/dice/dice.c        |  13 +--
 sound/firewire/dice/dice.h        |   4 +-
 4 files changed, 161 insertions(+), 142 deletions(-)

diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 2e531bd..904cb80 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -169,74 +169,18 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
 			      struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_dice *dice = substream->private_data;
-	unsigned int mode, rate, channels, i;
-	int err;
-
-	mutex_lock(&dice->mutex);
-	snd_dice_stream_stop(dice);
-	mutex_unlock(&dice->mutex);
-
-	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-					       params_buffer_bytes(hw_params));
-	if (err < 0)
-		return err;
-
-	rate = params_rate(hw_params);
-	err = snd_dice_transaction_set_rate(dice, rate);
-	if (err < 0)
-		return err;
-
-	if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-		return err;
-
-	/*
-	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
-	 * one data block of AMDTP packet. Thus sampling transfer frequency is
-	 * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
-	 * transferred on AMDTP packets at 96 kHz. Two successive samples of a
-	 * channel are stored consecutively in the packet. This quirk is called
-	 * as 'Dual Wire'.
-	 * For this quirk, blocking mode is required and PCM buffer size should
-	 * be aligned to SYT_INTERVAL.
-	 */
-	channels = params_channels(hw_params);
-	if (mode > 1) {
-		if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
-			err = -ENOSYS;
-			return err;
-		}
-
-		rate /= 2;
-		channels *= 2;
-		dice->rx_stream.double_pcm_frames = true;
-	} else {
-		dice->rx_stream.double_pcm_frames = false;
-	}
-
-	amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
-				    dice->rx_midi_ports[mode]);
-	if (mode > 1) {
-		channels /= 2;
-
-		for (i = 0; i < channels; i++) {
-			dice->rx_stream.pcm_positions[i] = i * 2;
-			dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
-		}
-	}
-
 	amdtp_stream_set_pcm_format(&dice->rx_stream,
 				    params_format(hw_params));
 
-	return 0;
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
 }
 
 static int playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
-	mutex_lock(&dice->mutex);
 	snd_dice_stream_stop(dice);
-	mutex_unlock(&dice->mutex);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -246,22 +190,11 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 	struct snd_dice *dice = substream->private_data;
 	int err;
 
-	mutex_lock(&dice->mutex);
-
-	if (amdtp_streaming_error(&dice->rx_stream))
-		snd_dice_stream_stop_packets(dice);
+	err = snd_dice_stream_start(dice, substream->runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&dice->rx_stream);
 
-	err = snd_dice_stream_start(dice);
-	if (err < 0) {
-		mutex_unlock(&dice->mutex);
-		return err;
-	}
-
-	mutex_unlock(&dice->mutex);
-
-	amdtp_stream_pcm_prepare(&dice->rx_stream);
-
-	return 0;
+	return err;
 }
 
 static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index ed10fea..6bb340d 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -9,6 +9,8 @@
 
 #include "dice.h"
 
+#define	CALLBACK_TIMEOUT	200
+
 const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
 	/* mode 0 */
 	[0] =  32000,
@@ -39,83 +41,169 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
 	return -EINVAL;
 }
 
-int snd_dice_stream_start_packets(struct snd_dice *dice)
+static void release_resources(struct snd_dice *dice)
+{
+	unsigned int channel;
+
+	/* Reset channel number */
+	channel = cpu_to_be32((u32)-1);
+	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+
+	fw_iso_resources_free(&dice->rx_resources);
+}
+
+static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes)
 {
+	unsigned int channel;
 	int err;
 
-	if (amdtp_stream_running(&dice->rx_stream))
-		return 0;
+	err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes,
+				fw_parent_device(dice->unit)->max_speed);
+	if (err < 0)
+		goto end;
 
-	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
-				 fw_parent_device(dice->unit)->max_speed);
+	/* Set channel number */
+	channel = cpu_to_be32(dice->rx_resources.channel);
+	err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+					    &channel, 4);
 	if (err < 0)
-		return err;
+		release_resources(dice);
+end:
+	return err;
+}
 
-	err = snd_dice_transaction_set_enable(dice);
-	if (err < 0) {
-		amdtp_stream_stop(&dice->rx_stream);
-		return err;
-	}
+static void stop_stream(struct snd_dice *dice)
+{
+	if (!amdtp_stream_running(&dice->rx_stream))
+		return;
 
-	return 0;
+	amdtp_stream_pcm_abort(&dice->rx_stream);
+	amdtp_stream_stop(&dice->rx_stream);
+	release_resources(dice);
 }
 
-int snd_dice_stream_start(struct snd_dice *dice)
+static int start_stream(struct snd_dice *dice, unsigned int rate)
 {
-	__be32 channel;
+	unsigned int i, mode, pcm_chs, midi_ports;
 	int err;
 
-	if (!dice->rx_resources.allocated) {
-		err = fw_iso_resources_allocate(&dice->rx_resources,
-				amdtp_stream_get_max_payload(&dice->rx_stream),
-				fw_parent_device(dice->unit)->max_speed);
-		if (err < 0)
-			goto error;
-
-		channel = cpu_to_be32(dice->rx_resources.channel);
-		err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS,
-						    &channel, 4);
-		if (err < 0)
-			goto err_resources;
+	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+	 * one data block of AMDTP packet. Thus sampling transfer frequency is
+	 * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+	 * transferred on AMDTP packets at 96 kHz. Two successive samples of a
+	 * channel are stored consecutively in the packet. This quirk is called
+	 * as 'Dual Wire'.
+	 * For this quirk, blocking mode is required and PCM buffer size should
+	 * be aligned to SYT_INTERVAL.
+	 */
+	pcm_chs = dice->rx_channels[mode];
+	midi_ports = dice->rx_midi_ports[mode];
+	if (mode > 1) {
+		rate /= 2;
+		pcm_chs *= 2;
+		dice->rx_stream.double_pcm_frames = true;
+	} else {
+		dice->rx_stream.double_pcm_frames = false;
 	}
 
-	err = snd_dice_stream_start_packets(dice);
-	if (err < 0)
-		goto err_rx_channel;
+	amdtp_stream_set_parameters(&dice->rx_stream, rate,
+				    pcm_chs, midi_ports);
+	if (mode > 1) {
+		pcm_chs /= 2;
 
-	return 0;
+		for (i = 0; i < pcm_chs; i++) {
+			dice->rx_stream.pcm_positions[i] = i * 2;
+			dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1;
+		}
+	}
 
-err_rx_channel:
-	channel = cpu_to_be32((u32)-1);
-	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
-err_resources:
-	fw_iso_resources_free(&dice->rx_resources);
-error:
+	err = keep_resources(dice,
+			     amdtp_stream_get_max_payload(&dice->rx_stream));
+	if (err < 0) {
+		dev_err(&dice->unit->device,
+			"fail to keep isochronous resources\n");
+		goto end;
+	}
+
+	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
+				 fw_parent_device(dice->unit)->max_speed);
+	if (err < 0)
+		release_resources(dice);
+end:
 	return err;
 }
 
-void snd_dice_stream_stop_packets(struct snd_dice *dice)
+int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
 {
-	if (!amdtp_stream_running(&dice->rx_stream))
-		return;
+	unsigned int curr_rate;
+	int err;
 
-	snd_dice_transaction_clear_enable(dice);
-	amdtp_stream_stop(&dice->rx_stream);
+	mutex_lock(&dice->mutex);
+
+	/* Some packet queueing errors. */
+	if (amdtp_streaming_error(&dice->rx_stream))
+		stop_stream(dice);
+
+	/* Stop stream if rate is different. */
+	err = snd_dice_transaction_get_rate(dice, &curr_rate);
+	if (err < 0) {
+		dev_err(&dice->unit->device,
+			"fail to get sampling rate\n");
+		goto end;
+	}
+	if (rate != curr_rate)
+		stop_stream(dice);
+
+	if (!amdtp_stream_running(&dice->rx_stream)) {
+		snd_dice_transaction_clear_enable(dice);
+
+		err = snd_dice_transaction_set_rate(dice, rate);
+		if (err < 0) {
+			dev_err(&dice->unit->device,
+				"fail to set sampling rate\n");
+			goto end;
+		}
+
+		/* Start stream. */
+		err = start_stream(dice, rate);
+		if (err < 0) {
+			dev_err(&dice->unit->device,
+				"fail to start AMDTP stream\n");
+			goto end;
+		}
+		err = snd_dice_transaction_set_enable(dice);
+		if (err < 0) {
+			dev_err(&dice->unit->device,
+				"fail to enable interface\n");
+			stop_stream(dice);
+			goto end;
+		}
+
+		if (!amdtp_stream_wait_callback(&dice->rx_stream,
+						CALLBACK_TIMEOUT)) {
+			snd_dice_transaction_clear_enable(dice);
+			stop_stream(dice);
+			err = -ETIMEDOUT;
+		}
+	}
+end:
+	mutex_unlock(&dice->mutex);
+	return err;
 }
 
 void snd_dice_stream_stop(struct snd_dice *dice)
 {
-	__be32 channel;
-
-	snd_dice_stream_stop_packets(dice);
-
-	if (!dice->rx_resources.allocated)
-		return;
+	mutex_lock(&dice->mutex);
 
-	channel = cpu_to_be32((u32)-1);
-	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+	snd_dice_transaction_clear_enable(dice);
+	stop_stream(dice);
 
-	fw_iso_resources_free(&dice->rx_resources);
+	mutex_unlock(&dice->mutex);
 }
 
 int snd_dice_stream_init(struct snd_dice *dice)
@@ -145,10 +233,14 @@ error:
 
 void snd_dice_stream_destroy(struct snd_dice *dice)
 {
-	amdtp_stream_pcm_abort(&dice->rx_stream);
-	snd_dice_stream_stop(dice);
+	mutex_lock(&dice->mutex);
+
+	snd_dice_transaction_clear_enable(dice);
+	stop_stream(dice);
 	amdtp_stream_destroy(&dice->rx_stream);
 	fw_iso_resources_destroy(&dice->rx_resources);
+
+	mutex_unlock(&dice->mutex);
 }
 
 void snd_dice_stream_update(struct snd_dice *dice)
@@ -161,9 +253,16 @@ void snd_dice_stream_update(struct snd_dice *dice)
 	 * to stop so that the application can restart them in an orderly
 	 * manner.
 	 */
-	amdtp_stream_pcm_abort(&dice->rx_stream);
-	snd_dice_stream_stop_packets(dice);
+	mutex_lock(&dice->mutex);
+
+	/* The enable register becomes initialized, then streams are stopped. */
+	dice->global_enabled = false;
+
+	stop_stream(dice);
+
 	fw_iso_resources_update(&dice->rx_resources);
+
+	mutex_unlock(&dice->mutex);
 }
 
 static void dice_lock_changed(struct snd_dice *dice)
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 7e8ecd3..7bddd29 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -304,12 +304,8 @@ static void dice_remove(struct fw_unit *unit)
 
 	snd_card_disconnect(dice->card);
 
-	mutex_lock(&dice->mutex);
-
 	snd_dice_stream_destroy(dice);
 
-	mutex_unlock(&dice->mutex);
-
 	snd_card_free_when_closed(dice->card);
 }
 
@@ -320,14 +316,7 @@ static void dice_update(struct fw_unit *unit)
 	/* The handler address register becomes initialized. */
 	snd_dice_transaction_reinit(dice);
 
-	mutex_lock(&dice->mutex);
-
-	/* The enable register becomes initialized, then streams are stopped. */
-	dice->global_enabled = false;
-
-	snd_dice_stream_stop_packets(dice);
-
-	mutex_unlock(&dice->mutex);
+	snd_dice_stream_update(dice);
 }
 
 #define DICE_INTERFACE	0x000001
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 969189a..8be530f 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -160,9 +160,7 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
 				  unsigned int rate, unsigned int *mode);
 
-int snd_dice_stream_start_packets(struct snd_dice *dice);
-int snd_dice_stream_start(struct snd_dice *dice);
-void snd_dice_stream_stop_packets(struct snd_dice *dice);
+int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate);
 void snd_dice_stream_stop(struct snd_dice *dice);
 int snd_dice_stream_init(struct snd_dice *dice);
 void snd_dice_stream_destroy(struct snd_dice *dice);
-- 
1.9.1

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

* [PATCH 10/29] ALSA: dice: Add support for duplex streams with synchronization
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (8 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 09/29] ALSA: dice: Change the way to start stream Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 11/29] ALSA: dice: Support for non SYT-Match sampling clock source mode Takashi Sakamoto
                   ` (21 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds support for AMDTP in-stream. As a result, Dice driver
supports full duplex streams with synchronization.

AMDTP can transfer timestamps in its packets. By handling the timestamp,
devices can synchronize to the other devices or drivers on the same bus.

When Dice chipset is 'enabled', it starts streams with correct settings.
This 'enable' register is global, thus, when a stream is started to run,
an opposite stream can't start unless turning off 'enable'. Therefore
a pair of streams must be running. This causes a loss of CPU usage when
single stream is needed for neither playbacking or capturing.

This commit assumes that playback-only models also have a functionality
to transmit stream for delivering timestamps.

Currently, sampling clock source is restricted to SYT-Match mode. This is
improved in followed commit. I note that at SYT-Match mode, Dice can select
from 4 streams for synchronization but this driver uses the 1st stream only
for simplicity.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/dice-pcm.c    |   4 +-
 sound/firewire/dice/dice-stream.c | 230 ++++++++++++++++++++++++++++----------
 sound/firewire/dice/dice.c        |  29 +++--
 sound/firewire/dice/dice.h        |  26 +++--
 4 files changed, 202 insertions(+), 87 deletions(-)

diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 904cb80..78b5408 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -180,7 +180,7 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
-	snd_dice_stream_stop(dice);
+	snd_dice_stream_stop_duplex(dice);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -190,7 +190,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 	struct snd_dice *dice = substream->private_data;
 	int err;
 
-	err = snd_dice_stream_start(dice, substream->runtime->rate);
+	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
 	if (err >= 0)
 		amdtp_stream_pcm_prepare(&dice->rx_stream);
 
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index 6bb340d..edd6be3 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -41,55 +41,79 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
 	return -EINVAL;
 }
 
-static void release_resources(struct snd_dice *dice)
+static void release_resources(struct snd_dice *dice,
+			      struct fw_iso_resources *resources)
 {
 	unsigned int channel;
 
 	/* Reset channel number */
 	channel = cpu_to_be32((u32)-1);
-	snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
-
-	fw_iso_resources_free(&dice->rx_resources);
+	if (resources == &dice->tx_resources)
+		snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
+					      &channel, 4);
+	else
+		snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+					      &channel, 4);
+
+	fw_iso_resources_free(resources);
 }
 
-static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes)
+static int keep_resources(struct snd_dice *dice,
+			  struct fw_iso_resources *resources,
+			  unsigned int max_payload_bytes)
 {
 	unsigned int channel;
 	int err;
 
-	err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes,
+	err = fw_iso_resources_allocate(resources, max_payload_bytes,
 				fw_parent_device(dice->unit)->max_speed);
 	if (err < 0)
 		goto end;
 
 	/* Set channel number */
-	channel = cpu_to_be32(dice->rx_resources.channel);
-	err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-					    &channel, 4);
+	channel = cpu_to_be32(resources->channel);
+	if (resources == &dice->tx_resources)
+		err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
+						    &channel, 4);
+	else
+		err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+						    &channel, 4);
 	if (err < 0)
-		release_resources(dice);
+		release_resources(dice, resources);
 end:
 	return err;
 }
 
-static void stop_stream(struct snd_dice *dice)
+static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
 {
-	if (!amdtp_stream_running(&dice->rx_stream))
-		return;
+	amdtp_stream_pcm_abort(stream);
+	amdtp_stream_stop(stream);
 
-	amdtp_stream_pcm_abort(&dice->rx_stream);
-	amdtp_stream_stop(&dice->rx_stream);
-	release_resources(dice);
+	if (stream == &dice->tx_stream)
+		release_resources(dice, &dice->tx_resources);
+	else
+		release_resources(dice, &dice->rx_resources);
 }
 
-static int start_stream(struct snd_dice *dice, unsigned int rate)
+static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
+			unsigned int rate)
 {
+	struct fw_iso_resources *resources;
 	unsigned int i, mode, pcm_chs, midi_ports;
 	int err;
 
 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
 	if (err < 0)
 		goto end;
+	if (stream == &dice->tx_stream) {
+		resources = &dice->tx_resources;
+		pcm_chs = dice->tx_channels[mode];
+		midi_ports = dice->tx_midi_ports[mode];
+	} else {
+		resources = &dice->rx_resources;
+		pcm_chs = dice->rx_channels[mode];
+		midi_ports = dice->rx_midi_ports[mode];
+	}
 
 	/*
 	 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
@@ -101,53 +125,73 @@ static int start_stream(struct snd_dice *dice, unsigned int rate)
 	 * For this quirk, blocking mode is required and PCM buffer size should
 	 * be aligned to SYT_INTERVAL.
 	 */
-	pcm_chs = dice->rx_channels[mode];
-	midi_ports = dice->rx_midi_ports[mode];
 	if (mode > 1) {
 		rate /= 2;
 		pcm_chs *= 2;
-		dice->rx_stream.double_pcm_frames = true;
+		stream->double_pcm_frames = true;
 	} else {
-		dice->rx_stream.double_pcm_frames = false;
+		stream->double_pcm_frames = false;
 	}
 
-	amdtp_stream_set_parameters(&dice->rx_stream, rate,
-				    pcm_chs, midi_ports);
+	amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
 	if (mode > 1) {
 		pcm_chs /= 2;
 
 		for (i = 0; i < pcm_chs; i++) {
-			dice->rx_stream.pcm_positions[i] = i * 2;
-			dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1;
+			stream->pcm_positions[i] = i * 2;
+			stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
 		}
 	}
 
-	err = keep_resources(dice,
-			     amdtp_stream_get_max_payload(&dice->rx_stream));
+	err = keep_resources(dice, resources,
+			     amdtp_stream_get_max_payload(stream));
 	if (err < 0) {
 		dev_err(&dice->unit->device,
 			"fail to keep isochronous resources\n");
 		goto end;
 	}
 
-	err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
+	err = amdtp_stream_start(stream, resources->channel,
 				 fw_parent_device(dice->unit)->max_speed);
 	if (err < 0)
-		release_resources(dice);
+		release_resources(dice, resources);
 end:
 	return err;
 }
 
-int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
+static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
+{
+	/* Currently, clock source is fixed at SYT-Match mode. */
+	*sync_mode = 0;
+	return 0;
+}
+
+int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 {
+	struct amdtp_stream *master, *slave;
 	unsigned int curr_rate;
-	int err;
+	enum cip_flags sync_mode;
+	int err = 0;
+
+	if (atomic_read(&dice->substreams_counter) == 0)
+		goto end;
 
 	mutex_lock(&dice->mutex);
 
+	err = get_sync_mode(dice, &sync_mode);
+	if (err < 0)
+		goto end;
+	if (sync_mode == CIP_SYNC_TO_DEVICE) {
+		master = &dice->tx_stream;
+		slave  = &dice->rx_stream;
+	} else {
+		master = &dice->rx_stream;
+		slave  = &dice->tx_stream;
+	}
+
 	/* Some packet queueing errors. */
-	if (amdtp_streaming_error(&dice->rx_stream))
-		stop_stream(dice);
+	if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
+		stop_stream(dice, master);
 
 	/* Stop stream if rate is different. */
 	err = snd_dice_transaction_get_rate(dice, &curr_rate);
@@ -157,11 +201,14 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
 		goto end;
 	}
 	if (rate != curr_rate)
-		stop_stream(dice);
+		stop_stream(dice, master);
 
-	if (!amdtp_stream_running(&dice->rx_stream)) {
+	if (!amdtp_stream_running(master)) {
+		stop_stream(dice, slave);
 		snd_dice_transaction_clear_enable(dice);
 
+		amdtp_stream_set_sync(sync_mode, master, slave);
+
 		err = snd_dice_transaction_set_rate(dice, rate);
 		if (err < 0) {
 			dev_err(&dice->unit->device,
@@ -169,25 +216,35 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
 			goto end;
 		}
 
-		/* Start stream. */
-		err = start_stream(dice, rate);
+		/* Start both streams. */
+		err = start_stream(dice, master, rate);
+		if (err < 0) {
+			dev_err(&dice->unit->device,
+				"fail to start AMDTP master stream\n");
+			goto end;
+		}
+		err = start_stream(dice, slave, rate);
 		if (err < 0) {
 			dev_err(&dice->unit->device,
-				"fail to start AMDTP stream\n");
+				"fail to start AMDTP slave stream\n");
+			stop_stream(dice, master);
 			goto end;
 		}
 		err = snd_dice_transaction_set_enable(dice);
 		if (err < 0) {
 			dev_err(&dice->unit->device,
 				"fail to enable interface\n");
-			stop_stream(dice);
+			stop_stream(dice, master);
+			stop_stream(dice, slave);
 			goto end;
 		}
 
-		if (!amdtp_stream_wait_callback(&dice->rx_stream,
-						CALLBACK_TIMEOUT)) {
+		/* Wait first callbacks */
+		if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) ||
+		    !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
 			snd_dice_transaction_clear_enable(dice);
-			stop_stream(dice);
+			stop_stream(dice, master);
+			stop_stream(dice, slave);
 			err = -ETIMEDOUT;
 		}
 	}
@@ -196,54 +253,101 @@ end:
 	return err;
 }
 
-void snd_dice_stream_stop(struct snd_dice *dice)
+void snd_dice_stream_stop_duplex(struct snd_dice *dice)
 {
+	if (atomic_read(&dice->substreams_counter) > 0)
+		return;
+
 	mutex_lock(&dice->mutex);
 
 	snd_dice_transaction_clear_enable(dice);
-	stop_stream(dice);
+
+	stop_stream(dice, &dice->tx_stream);
+	stop_stream(dice, &dice->rx_stream);
 
 	mutex_unlock(&dice->mutex);
 }
 
-int snd_dice_stream_init(struct snd_dice *dice)
+static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
 {
 	int err;
+	struct fw_iso_resources *resources;
+	enum amdtp_stream_direction dir;
+
+	if (stream == &dice->tx_stream) {
+		resources = &dice->tx_resources;
+		dir = AMDTP_IN_STREAM;
+	} else {
+		resources = &dice->rx_resources;
+		dir = AMDTP_OUT_STREAM;
+	}
 
-	err = fw_iso_resources_init(&dice->rx_resources, dice->unit);
+	err = fw_iso_resources_init(resources, dice->unit);
 	if (err < 0)
 		goto end;
-	dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
+	resources->channels_mask = 0x00000000ffffffffuLL;
 
-	err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM,
-				CIP_BLOCKING);
+	err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+	if (err < 0) {
+		amdtp_stream_destroy(stream);
+		fw_iso_resources_destroy(resources);
+	}
+end:
+	return err;
+}
+
+static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+{
+	amdtp_stream_destroy(stream);
+
+	if (stream == &dice->tx_stream)
+		fw_iso_resources_destroy(&dice->tx_resources);
+	else
+		fw_iso_resources_destroy(&dice->rx_resources);
+}
+
+int snd_dice_stream_init_duplex(struct snd_dice *dice)
+{
+	int err;
+
+	atomic_set(&dice->substreams_counter, 0);
+
+	err = init_stream(dice, &dice->tx_stream);
 	if (err < 0)
-		goto error;
+		goto end;
 
-	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
+	err = init_stream(dice, &dice->rx_stream);
 	if (err < 0)
-		goto error;
+		goto end;
+
+	/* Currently, clock source is fixed at SYT-Match mode. */
+	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
+	if (err < 0) {
+		destroy_stream(dice, &dice->rx_stream);
+		destroy_stream(dice, &dice->tx_stream);
+	}
 end:
 	return err;
-error:
-	amdtp_stream_destroy(&dice->rx_stream);
-	fw_iso_resources_destroy(&dice->rx_resources);
-	return err;
 }
 
-void snd_dice_stream_destroy(struct snd_dice *dice)
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
 {
 	mutex_lock(&dice->mutex);
 
 	snd_dice_transaction_clear_enable(dice);
-	stop_stream(dice);
-	amdtp_stream_destroy(&dice->rx_stream);
-	fw_iso_resources_destroy(&dice->rx_resources);
+
+	stop_stream(dice, &dice->tx_stream);
+	destroy_stream(dice, &dice->tx_stream);
+
+	stop_stream(dice, &dice->rx_stream);
+	destroy_stream(dice, &dice->rx_stream);
+
+	atomic_set(&dice->substreams_counter, 0);
 
 	mutex_unlock(&dice->mutex);
 }
 
-void snd_dice_stream_update(struct snd_dice *dice)
+void snd_dice_stream_update_duplex(struct snd_dice *dice)
 {
 	/*
 	 * On a bus reset, the DICE firmware disables streaming and then goes
@@ -258,9 +362,11 @@ void snd_dice_stream_update(struct snd_dice *dice)
 	/* The enable register becomes initialized, then streams are stopped. */
 	dice->global_enabled = false;
 
-	stop_stream(dice);
+	stop_stream(dice, &dice->rx_stream);
+	stop_stream(dice, &dice->tx_stream);
 
 	fw_iso_resources_update(&dice->rx_resources);
+	fw_iso_resources_update(&dice->tx_resources);
 
 	mutex_unlock(&dice->mutex);
 }
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 7bddd29..2390cc6 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -30,7 +30,6 @@ static int dice_interface_check(struct fw_unit *unit)
 	int key, val, vendor = -1, model = -1, err;
 	unsigned int category, i;
 	__be32 *pointers, value;
-	__be32 tx_data[4];
 	__be32 version;
 
 	pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
@@ -85,16 +84,6 @@ static int dice_interface_check(struct fw_unit *unit)
 		}
 	}
 
-	/* 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])) {
-		err = -ENODEV;
-		goto end;
-	}
-
 	/*
 	 * Check that the implemented DICE driver specification major version
 	 * number matches.
@@ -142,6 +131,8 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 	int err;
 
 	if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
+		dice->tx_channels[mode] = 0;
+		dice->tx_midi_ports[mode] = 0;
 		dice->rx_channels[mode] = 0;
 		dice->rx_midi_ports[mode] = 0;
 		return 0;
@@ -151,6 +142,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
 	if (err < 0)
 		return err;
 
+	err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
+					   values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	dice->tx_channels[mode]   = be32_to_cpu(values[0]);
+	dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
+
 	err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
 					   values, sizeof(values));
 	if (err < 0)
@@ -280,13 +279,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	snd_dice_create_proc(dice);
 
-	err = snd_dice_stream_init(dice);
+	err = snd_dice_stream_init_duplex(dice);
 	if (err < 0)
 		goto error;
 
 	err = snd_card_register(card);
 	if (err < 0) {
-		snd_dice_stream_destroy(dice);
+		snd_dice_stream_destroy_duplex(dice);
 		goto error;
 	}
 
@@ -304,7 +303,7 @@ static void dice_remove(struct fw_unit *unit)
 
 	snd_card_disconnect(dice->card);
 
-	snd_dice_stream_destroy(dice);
+	snd_dice_stream_destroy_duplex(dice);
 
 	snd_card_free_when_closed(dice->card);
 }
@@ -316,7 +315,7 @@ static void dice_update(struct fw_unit *unit)
 	/* The handler address register becomes initialized. */
 	snd_dice_transaction_reinit(dice);
 
-	snd_dice_stream_update(dice);
+	snd_dice_stream_update_duplex(dice);
 }
 
 #define DICE_INTERFACE	0x000001
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 8be530f..a868485 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -52,18 +52,28 @@ struct snd_dice {
 	unsigned int rsrv_offset;
 
 	unsigned int clock_caps;
+	unsigned int tx_channels[3];
 	unsigned int rx_channels[3];
+	unsigned int tx_midi_ports[3];
 	unsigned int rx_midi_ports[3];
+
 	struct fw_address_handler notification_handler;
 	int owner_generation;
+	u32 notification_bits;
+
+	/* For uapi */
 	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;
+
+	/* For streaming */
+	struct fw_iso_resources tx_resources;
 	struct fw_iso_resources rx_resources;
+	struct amdtp_stream tx_stream;
 	struct amdtp_stream rx_stream;
+	bool global_enabled;
+	struct completion clock_accepted;
+	atomic_t substreams_counter;
 };
 
 enum snd_dice_addr_type {
@@ -160,11 +170,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
 				  unsigned int rate, unsigned int *mode);
 
-int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate);
-void snd_dice_stream_stop(struct snd_dice *dice);
-int snd_dice_stream_init(struct snd_dice *dice);
-void snd_dice_stream_destroy(struct snd_dice *dice);
-void snd_dice_stream_update(struct snd_dice *dice);
+int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
+void snd_dice_stream_stop_duplex(struct snd_dice *dice);
+int snd_dice_stream_init_duplex(struct snd_dice *dice);
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
+void snd_dice_stream_update_duplex(struct snd_dice *dice);
 
 int snd_dice_stream_lock_try(struct snd_dice *dice);
 void snd_dice_stream_lock_release(struct snd_dice *dice);
-- 
1.9.1

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

* [PATCH 11/29] ALSA: dice: Support for non SYT-Match sampling clock source mode
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (9 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 10/29] ALSA: dice: Add support for duplex streams with synchronization Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 12/29] ALSA: dice: Add support for capturing PCM samples Takashi Sakamoto
                   ` (20 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit allows this driver to handle devices with non SYT-Match
sampling clock source.

When sampling clock source is SYT-Match mode, devices handle
'presentation timestamp' in received packets and generates sampling clock
according to the information. In this case, driver is synchronization master
and must transfer correct value in SYT field of each packets in outgoing
stream, then the outgoing stream is a master stream.

On the other hand, non SYT-Match mode, devices do this. So drivers must pick
up the value in SYT field of incoming packets and use the value for outgoing
stream. Currently firewire-lib module achieve this work.

Furthermore, without SYT-Match and internal clock source, the sampling rate
should be fixed for the other devices connected to the handled device. This
commit add a restriction of sampling rate at this situation.

With these implementations, this driver has no need to set clock source.
This commit remove set function.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/dice-pcm.c         | 35 ++++++++++++++++++++++++++++++++++
 sound/firewire/dice/dice-stream.c      | 35 ++++++++++++++++++++++------------
 sound/firewire/dice/dice-transaction.c |  5 -----
 sound/firewire/dice/dice.h             |  2 --
 4 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 78b5408..c7d84bc 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -140,6 +140,8 @@ end:
 static int pcm_open(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
+	unsigned int source, rate;
+	bool internal;
 	int err;
 
 	err = snd_dice_stream_lock_try(dice);
@@ -149,6 +151,39 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	err = init_hw_info(dice, substream);
 	if (err < 0)
 		goto err_locked;
+
+	err = snd_dice_transaction_get_clock_source(dice, &source);
+	if (err < 0)
+		goto err_locked;
+	switch (source) {
+	case CLOCK_SOURCE_AES1:
+	case CLOCK_SOURCE_AES2:
+	case CLOCK_SOURCE_AES3:
+	case CLOCK_SOURCE_AES4:
+	case CLOCK_SOURCE_AES_ANY:
+	case CLOCK_SOURCE_ADAT:
+	case CLOCK_SOURCE_TDIF:
+	case CLOCK_SOURCE_WC:
+		internal = false;
+		break;
+	default:
+		internal = true;
+		break;
+	}
+
+	/*
+	 * When source of clock is not internal, available sampling rate is
+	 * limited at current sampling rate.
+	 */
+	if (!internal) {
+		err = snd_dice_transaction_get_rate(dice, &rate);
+		if (err < 0)
+			goto err_locked;
+		substream->runtime->hw.rate_min = rate;
+		substream->runtime->hw.rate_max = rate;
+	}
+
+	snd_pcm_set_sync(substream);
 end:
 	return err;
 err_locked:
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index edd6be3..bc3715b 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -161,9 +161,29 @@ end:
 
 static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
 {
-	/* Currently, clock source is fixed at SYT-Match mode. */
-	*sync_mode = 0;
-	return 0;
+	u32 source;
+	int err;
+
+	err = snd_dice_transaction_get_clock_source(dice, &source);
+	if (err < 0)
+		goto end;
+
+	switch (source) {
+	/* So-called 'SYT Match' modes, sync_to_syt value of packets received */
+	case CLOCK_SOURCE_ARX4:	/* in 4th stream */
+	case CLOCK_SOURCE_ARX3:	/* in 3rd stream */
+	case CLOCK_SOURCE_ARX2:	/* in 2nd stream */
+		err = -ENOSYS;
+		break;
+	case CLOCK_SOURCE_ARX1:	/* in 1st stream, which this driver uses */
+		*sync_mode = 0;
+		break;
+	default:
+		*sync_mode = CIP_SYNC_TO_DEVICE;
+		break;
+	}
+end:
+	return err;
 }
 
 int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
@@ -317,15 +337,6 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
 		goto end;
 
 	err = init_stream(dice, &dice->rx_stream);
-	if (err < 0)
-		goto end;
-
-	/* Currently, clock source is fixed at SYT-Match mode. */
-	err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
-	if (err < 0) {
-		destroy_stream(dice, &dice->rx_stream);
-		destroy_stream(dice, &dice->tx_stream);
-	}
 end:
 	return err;
 }
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
index a9b98e0..b128ee3 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -137,11 +137,6 @@ int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
 
 	return err;
 }
-int snd_dice_transaction_set_clock_source(struct snd_dice *dice,
-					  unsigned int source)
-{
-	return set_clock_info(dice, UINT_MAX, source);
-}
 
 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
 {
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index a868485..8ea5cb4 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -152,8 +152,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
 					 buf, len);
 }
 
-int snd_dice_transaction_set_clock_source(struct snd_dice *dice,
-					  unsigned int source);
 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
 					  unsigned int *source);
 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
-- 
1.9.1

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

* [PATCH 12/29] ALSA: dice: Add support for capturing PCM samples
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (10 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 11/29] ALSA: dice: Support for non SYT-Match sampling clock source mode Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 13/29] ALSA: dice: Add support for MIDI capture/playback Takashi Sakamoto
                   ` (19 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a support for capturing PCM samples.

When opposite PCM substream is already running, available sampling rate is
limited at current one.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig         |   3 -
 sound/firewire/dice/dice-pcm.c | 147 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 133 insertions(+), 17 deletions(-)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 46dff64..c8e704c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -20,9 +20,6 @@ config SND_DICE
 	  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/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index c7d84bc..3427a21 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -12,7 +12,8 @@
 static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 				struct snd_pcm_hw_rule *rule)
 {
-	struct snd_dice *dice = rule->private;
+	struct snd_pcm_substream *substream = rule->private;
+	struct snd_dice *dice = substream->private_data;
 
 	const struct snd_interval *c =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
@@ -21,7 +22,12 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 	struct snd_interval rates = {
 		.min = UINT_MAX, .max = 0, .integer = 1
 	};
-	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
+	unsigned int i, rate, mode, *pcm_channels;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		pcm_channels = dice->tx_channels;
+	else
+		pcm_channels = dice->rx_channels;
 
 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
 		rate = snd_dice_rates[i];
@@ -41,7 +47,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 				    struct snd_pcm_hw_rule *rule)
 {
-	struct snd_dice *dice = rule->private;
+	struct snd_pcm_substream *substream = rule->private;
+	struct snd_dice *dice = substream->private_data;
 
 	const struct snd_interval *r =
 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
@@ -50,7 +57,12 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 	struct snd_interval channels = {
 		.min = UINT_MAX, .max = 0, .integer = 1
 	};
-	unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
+	unsigned int i, rate, mode, *pcm_channels;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		pcm_channels = dice->tx_channels;
+	else
+		pcm_channels = dice->rx_channels;
 
 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
 		rate = snd_dice_rates[i];
@@ -109,30 +121,42 @@ static int init_hw_info(struct snd_dice *dice,
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_hardware *hw = &runtime->hw;
+	struct amdtp_stream *stream;
+	unsigned int *pcm_channels;
 	int err;
 
 	hw->info = SNDRV_PCM_INFO_MMAP |
 		   SNDRV_PCM_INFO_MMAP_VALID |
 		   SNDRV_PCM_INFO_BATCH |
 		   SNDRV_PCM_INFO_INTERLEAVED |
+		   SNDRV_PCM_INFO_JOINT_DUPLEX |
 		   SNDRV_PCM_INFO_BLOCK_TRANSFER;
-	hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
 
-	limit_channels_and_rates(dice, runtime, dice->rx_channels);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+		stream = &dice->tx_stream;
+		pcm_channels = dice->tx_channels;
+	} else {
+		hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+		stream = &dice->rx_stream;
+		pcm_channels = dice->rx_channels;
+	}
+
+	limit_channels_and_rates(dice, runtime, pcm_channels);
 	limit_period_and_buffer(hw);
 
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-				  dice_rate_constraint, dice,
+				  dice_rate_constraint, substream,
 				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 	if (err < 0)
 		goto end;
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-				  dice_channels_constraint, dice,
+				  dice_channels_constraint, substream,
 				  SNDRV_PCM_HW_PARAM_RATE, -1);
 	if (err < 0)
 		goto end;
 
-	err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
+	err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
 end:
 	return err;
 }
@@ -172,10 +196,12 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	}
 
 	/*
-	 * When source of clock is not internal, available sampling rate is
-	 * limited at current sampling rate.
+	 * When source of clock is not internal or any PCM streams are running,
+	 * available sampling rate is limited at current sampling rate.
 	 */
-	if (!internal) {
+	if (!internal ||
+	    amdtp_stream_pcm_running(&dice->tx_stream) ||
+	    amdtp_stream_pcm_running(&dice->rx_stream)) {
 		err = snd_dice_transaction_get_rate(dice, &rate);
 		if (err < 0)
 			goto err_locked;
@@ -200,10 +226,28 @@ static int pcm_close(struct snd_pcm_substream *substream)
 	return 0;
 }
 
+static int capture_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		atomic_inc(&dice->substreams_counter);
+
+	amdtp_stream_set_pcm_format(&dice->tx_stream,
+				    params_format(hw_params));
+
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
 static int playback_hw_params(struct snd_pcm_substream *substream,
 			      struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_dice *dice = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		atomic_inc(&dice->substreams_counter);
+
 	amdtp_stream_set_pcm_format(&dice->rx_stream,
 				    params_format(hw_params));
 
@@ -211,15 +255,41 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
 						params_buffer_bytes(hw_params));
 }
 
+static int capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		atomic_dec(&dice->substreams_counter);
+
+	snd_dice_stream_stop_duplex(dice);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
 static int playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		atomic_dec(&dice->substreams_counter);
+
 	snd_dice_stream_stop_duplex(dice);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+	int err;
+
+	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&dice->tx_stream);
+
+	return 0;
+}
 static int playback_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
@@ -232,6 +302,23 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 	return err;
 }
 
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
 static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_dice *dice = substream->private_data;
@@ -250,6 +337,12 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 	return 0;
 }
 
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_dice *dice = substream->private_data;
+
+	return amdtp_stream_pcm_pointer(&dice->tx_stream);
+}
 static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
@@ -259,6 +352,18 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
 
 int snd_dice_create_pcm(struct snd_dice *dice)
 {
+	static struct snd_pcm_ops capture_ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = capture_hw_params,
+		.hw_free   = capture_hw_free,
+		.prepare   = capture_prepare,
+		.trigger   = capture_trigger,
+		.pointer   = capture_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
 	static struct snd_pcm_ops playback_ops = {
 		.open      = pcm_open,
 		.close     = pcm_close,
@@ -272,14 +377,28 @@ int snd_dice_create_pcm(struct snd_dice *dice)
 		.mmap      = snd_pcm_lib_mmap_vmalloc,
 	};
 	struct snd_pcm *pcm;
+	unsigned int i, capture, playback;
 	int err;
 
-	err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
+	capture = playback = 0;
+	for (i = 0; i < 3; i++) {
+		if (dice->tx_channels[i] > 0)
+			capture = 1;
+		if (dice->rx_channels[i] > 0)
+			playback = 1;
+	}
+
+	err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
 	if (err < 0)
 		return err;
 	pcm->private_data = dice;
 	strcpy(pcm->name, dice->card->shortname);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+
+	if (capture > 0)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+
+	if (playback > 0)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
 
 	return 0;
 }
-- 
1.9.1

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

* [PATCH 13/29] ALSA: dice: Add support for MIDI capture/playback
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (11 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 12/29] ALSA: dice: Add support for capturing PCM samples Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 14/29] ALSA: dice: remove experimental state Takashi Sakamoto
                   ` (18 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds a support for MIDI capture/playback

When MIDI substrams already start streaming and PCM substreams are going to
join at different sampling rate, streams are stopped once. Then sampling rate
is changed and streams are restarted.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/Makefile      |   4 +-
 sound/firewire/dice/dice-midi.c   | 176 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/dice/dice-stream.c |   2 +
 sound/firewire/dice/dice.c        |   4 +
 sound/firewire/dice/dice.h        |   3 +
 5 files changed, 187 insertions(+), 2 deletions(-)
 create mode 100644 sound/firewire/dice/dice-midi.c

diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 9a48289..9ef228e 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,3 +1,3 @@
-snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-pcm.o \
-		 dice-hwdep.o dice.o
+snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
+		 dice-pcm.o dice-hwdep.o dice.o
 obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
new file mode 100644
index 0000000..4f4eae7
--- /dev/null
+++ b/sound/firewire/dice/dice-midi.c
@@ -0,0 +1,176 @@
+/*
+ * dice_midi.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "dice.h"
+
+static int capture_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_dice *dice = substream->rmidi->private_data;
+	int err;
+
+	err = snd_dice_stream_lock_try(dice);
+	if (err < 0)
+		goto end;
+
+	atomic_inc(&dice->substreams_counter);
+	err = snd_dice_stream_start_duplex(dice, 0);
+	if (err < 0)
+		snd_dice_stream_lock_release(dice);
+end:
+	return err;
+}
+
+static int playback_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_dice *dice = substream->rmidi->private_data;
+	int err;
+
+	err = snd_dice_stream_lock_try(dice);
+	if (err < 0)
+		goto end;
+
+	atomic_inc(&dice->substreams_counter);
+	err = snd_dice_stream_start_duplex(dice, 0);
+	if (err < 0)
+		snd_dice_stream_lock_release(dice);
+end:
+	return err;
+}
+
+static int capture_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_dice *dice = substream->rmidi->private_data;
+
+	atomic_dec(&dice->substreams_counter);
+	snd_dice_stream_stop_duplex(dice);
+
+	snd_dice_stream_lock_release(dice);
+	return 0;
+}
+
+static int playback_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_dice *dice = substream->rmidi->private_data;
+
+	atomic_dec(&dice->substreams_counter);
+	snd_dice_stream_stop_duplex(dice);
+
+	snd_dice_stream_lock_release(dice);
+	return 0;
+}
+
+static void capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_dice *dice = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dice->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&dice->tx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&dice->tx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static void playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_dice *dice = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dice->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&dice->rx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&dice->rx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static struct snd_rawmidi_ops capture_ops = {
+	.open		= capture_open,
+	.close		= capture_close,
+	.trigger	= capture_trigger,
+};
+
+static struct snd_rawmidi_ops playback_ops = {
+	.open		= playback_open,
+	.close		= playback_close,
+	.trigger	= playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dice *dice,
+				     struct snd_rawmidi_str *str)
+{
+	struct snd_rawmidi_substream *subs;
+
+	list_for_each_entry(subs, &str->substreams, list) {
+		snprintf(subs->name, sizeof(subs->name),
+			 "%s MIDI %d", dice->card->shortname, subs->number + 1);
+	}
+}
+
+int snd_dice_create_midi(struct snd_dice *dice)
+{
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *str;
+	unsigned int i, midi_in_ports, midi_out_ports;
+	int err;
+
+	midi_in_ports = midi_out_ports = 0;
+	for (i = 0; i < 3; i++) {
+		midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
+		midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
+	}
+
+	if (midi_in_ports + midi_out_ports == 0)
+		return 0;
+
+	/* create midi ports */
+	err = snd_rawmidi_new(dice->card, dice->card->driver, 0,
+			      midi_out_ports, midi_in_ports,
+			      &rmidi);
+	if (err < 0)
+		return err;
+
+	snprintf(rmidi->name, sizeof(rmidi->name),
+		 "%s MIDI", dice->card->shortname);
+	rmidi->private_data = dice;
+
+	if (midi_in_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+				    &capture_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+		set_midi_substream_names(dice, str);
+	}
+
+	if (midi_out_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+				    &playback_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+		set_midi_substream_names(dice, str);
+	}
+
+	if ((midi_out_ports > 0) && (midi_in_ports > 0))
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return 0;
+}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index bc3715b..37d1aab 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -220,6 +220,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 			"fail to get sampling rate\n");
 		goto end;
 	}
+	if (rate == 0)
+		rate = curr_rate;
 	if (rate != curr_rate)
 		stop_stream(dice, master);
 
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 2390cc6..b6b2c92 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -279,6 +279,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 
 	snd_dice_create_proc(dice);
 
+	err = snd_dice_create_midi(dice);
+	if (err < 0)
+		goto error;
+
 	err = snd_dice_stream_init_duplex(dice);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 8ea5cb4..dbfb988 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -32,6 +32,7 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
 
 #include "../amdtp.h"
 #include "../iso-resources.h"
@@ -183,4 +184,6 @@ int snd_dice_create_hwdep(struct snd_dice *dice);
 
 void snd_dice_create_proc(struct snd_dice *dice);
 
+int snd_dice_create_midi(struct snd_dice *dice);
+
 #endif
-- 
1.9.1

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

* [PATCH 14/29] ALSA: dice: remove experimental state
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (12 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 13/29] ALSA: dice: Add support for MIDI capture/playback Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 15/29] ALSA: speakers: Rename to oxfw and rename some members Takashi Sakamoto
                   ` (17 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Some developers test this driver, thus it's better to remove its
experimental state.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index c8e704c..860e21c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -13,12 +13,12 @@ config SND_FIREWIRE_LIB
 	select SND_RAWMIDI
 
 config SND_DICE
-	tristate "DICE-based DACs (EXPERIMENTAL)"
+	tristate "DICE-based DACs support"
 	select SND_HWDEP
 	select SND_FIREWIRE_LIB
 	help
 	  Say Y here to include support for many DACs based on the DICE
-	  chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
+	  chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-dice.
-- 
1.9.1

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

* [PATCH 15/29] ALSA: speakers: Rename to oxfw and rename some members
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (13 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 14/29] ALSA: dice: remove experimental state Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 16/29] ALSA: oxfw: Move to its own directory Takashi Sakamoto
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit renames 'firewire-speakers' to 'oxfw' to enhance support for
devices which based on OXFW970/971. A line for MODULE_ALIAS is added.

Additionally, to help for works in followed paches, some members
in private structure are renamed.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                |  12 +-
 sound/firewire/Makefile               |   4 +-
 sound/firewire/{speakers.c => oxfw.c} | 341 +++++++++++++++++-----------------
 3 files changed, 180 insertions(+), 177 deletions(-)
 rename sound/firewire/{speakers.c => oxfw.c} (61%)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 860e21c..0932860 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -23,15 +23,17 @@ config SND_DICE
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-dice.
 
-config SND_FIREWIRE_SPEAKERS
-	tristate "FireWire speakers"
+config SND_OXFW
+	tristate "Oxford Semiconductor FW970/971 chipset support"
 	select SND_FIREWIRE_LIB
 	help
-	  Say Y here to include support for the Griffin FireWave Surround
-	  and the LaCie FireWire Speakers.
+	  Say Y here to include support for FireWire devices based on
+	  Oxford Semiconductor FW970/971 chipset.
+	   * Griffin Firewave
+	   * LaCie Firewire Speakers
 
 	  To compile this driver as a module, choose M here: the module
-	  will be called snd-firewire-speakers.
+	  will be called snd-oxfw.
 
 config SND_ISIGHT
 	tristate "Apple iSight microphone"
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index c50761c..021b877 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,13 +1,13 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
-snd-firewire-speakers-objs := speakers.o
+snd-oxfw-objs := oxfw.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o
 
 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_DICE) += dice/
-obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
 obj-$(CONFIG_SND_FIREWORKS) += fireworks/
 obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/speakers.c b/sound/firewire/oxfw.c
similarity index 61%
rename from sound/firewire/speakers.c
rename to sound/firewire/oxfw.c
index 768d40d..b7498e9 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/oxfw.c
@@ -1,5 +1,5 @@
 /*
- * OXFW970-based speakers driver
+ * oxfw.c - a part of driver for OXFW970/971 based devices
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  * Licensed under the terms of the GNU General Public License, version 2.
@@ -45,22 +45,23 @@ struct device_info {
 	u8 volume_fb_id;
 };
 
-struct fwspk {
+struct snd_oxfw {
 	struct snd_card *card;
 	struct fw_unit *unit;
 	const struct device_info *device_info;
 	struct mutex mutex;
-	struct cmp_connection connection;
-	struct amdtp_stream stream;
+	struct cmp_connection in_conn;
+	struct amdtp_stream rx_stream;
 	bool mute;
 	s16 volume[6];
 	s16 volume_min;
 	s16 volume_max;
 };
 
-MODULE_DESCRIPTION("FireWire speakers driver");
+MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("snd-firewire-speakers");
 
 static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
 				    struct snd_pcm_hw_rule *rule)
@@ -137,7 +138,7 @@ static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
 	return 0;
 }
 
-static int fwspk_open(struct snd_pcm_substream *substream)
+static int oxfw_open(struct snd_pcm_substream *substream)
 {
 	static const struct snd_pcm_hardware hardware = {
 		.info = SNDRV_PCM_INFO_MMAP |
@@ -154,66 +155,66 @@ static int fwspk_open(struct snd_pcm_substream *substream)
 		.periods_min = 1,
 		.periods_max = UINT_MAX,
 	};
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
 	runtime->hw = hardware;
 
-	err = fwspk->device_info->pcm_constraints(runtime);
+	err = oxfw->device_info->pcm_constraints(runtime);
 	if (err < 0)
 		return err;
 	err = snd_pcm_limit_hw_rates(runtime);
 	if (err < 0)
 		return err;
 
-	err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
+	err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime);
 	if (err < 0)
 		return err;
 
 	return 0;
 }
 
-static int fwspk_close(struct snd_pcm_substream *substream)
+static int oxfw_close(struct snd_pcm_substream *substream)
 {
 	return 0;
 }
 
-static void fwspk_stop_stream(struct fwspk *fwspk)
+static void oxfw_stop_stream(struct snd_oxfw *oxfw)
 {
-	if (amdtp_stream_running(&fwspk->stream)) {
-		amdtp_stream_stop(&fwspk->stream);
-		cmp_connection_break(&fwspk->connection);
+	if (amdtp_stream_running(&oxfw->rx_stream)) {
+		amdtp_stream_stop(&oxfw->rx_stream);
+		cmp_connection_break(&oxfw->in_conn);
 	}
 }
 
-static int fwspk_hw_params(struct snd_pcm_substream *substream,
+static int oxfw_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *hw_params)
 {
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 	int err;
 
-	mutex_lock(&fwspk->mutex);
-	fwspk_stop_stream(fwspk);
-	mutex_unlock(&fwspk->mutex);
+	mutex_lock(&oxfw->mutex);
+	oxfw_stop_stream(oxfw);
+	mutex_unlock(&oxfw->mutex);
 
 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 					       params_buffer_bytes(hw_params));
 	if (err < 0)
 		goto error;
 
-	amdtp_stream_set_parameters(&fwspk->stream,
+	amdtp_stream_set_parameters(&oxfw->rx_stream,
 				    params_rate(hw_params),
 				    params_channels(hw_params),
 				    0);
 
-	amdtp_stream_set_pcm_format(&fwspk->stream,
+	amdtp_stream_set_pcm_format(&oxfw->rx_stream,
 				    params_format(hw_params));
 
-	err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
+	err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params),
 				      AVC_GENERAL_PLUG_DIR_IN, 0);
 	if (err < 0) {
-		dev_err(&fwspk->unit->device, "failed to set sample rate\n");
+		dev_err(&oxfw->unit->device, "failed to set sample rate\n");
 		goto err_buffer;
 	}
 
@@ -225,57 +226,57 @@ error:
 	return err;
 }
 
-static int fwspk_hw_free(struct snd_pcm_substream *substream)
+static int oxfw_hw_free(struct snd_pcm_substream *substream)
 {
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 
-	mutex_lock(&fwspk->mutex);
-	fwspk_stop_stream(fwspk);
-	mutex_unlock(&fwspk->mutex);
+	mutex_lock(&oxfw->mutex);
+	oxfw_stop_stream(oxfw);
+	mutex_unlock(&oxfw->mutex);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
-static int fwspk_prepare(struct snd_pcm_substream *substream)
+static int oxfw_prepare(struct snd_pcm_substream *substream)
 {
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 	int err;
 
-	mutex_lock(&fwspk->mutex);
+	mutex_lock(&oxfw->mutex);
 
-	if (amdtp_streaming_error(&fwspk->stream))
-		fwspk_stop_stream(fwspk);
+	if (amdtp_streaming_error(&oxfw->rx_stream))
+		oxfw_stop_stream(oxfw);
 
-	if (!amdtp_stream_running(&fwspk->stream)) {
-		err = cmp_connection_establish(&fwspk->connection,
-			amdtp_stream_get_max_payload(&fwspk->stream));
+	if (!amdtp_stream_running(&oxfw->rx_stream)) {
+		err = cmp_connection_establish(&oxfw->in_conn,
+			amdtp_stream_get_max_payload(&oxfw->rx_stream));
 		if (err < 0)
 			goto err_mutex;
 
-		err = amdtp_stream_start(&fwspk->stream,
-					 fwspk->connection.resources.channel,
-					 fwspk->connection.speed);
+		err = amdtp_stream_start(&oxfw->rx_stream,
+					 oxfw->in_conn.resources.channel,
+					 oxfw->in_conn.speed);
 		if (err < 0)
 			goto err_connection;
 	}
 
-	mutex_unlock(&fwspk->mutex);
+	mutex_unlock(&oxfw->mutex);
 
-	amdtp_stream_pcm_prepare(&fwspk->stream);
+	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
 
 	return 0;
 
 err_connection:
-	cmp_connection_break(&fwspk->connection);
+	cmp_connection_break(&oxfw->in_conn);
 err_mutex:
-	mutex_unlock(&fwspk->mutex);
+	mutex_unlock(&oxfw->mutex);
 
 	return err;
 }
 
-static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
+static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd)
 {
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 	struct snd_pcm_substream *pcm;
 
 	switch (cmd) {
@@ -288,39 +289,39 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
 	default:
 		return -EINVAL;
 	}
-	amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
+	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
 	return 0;
 }
 
-static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream)
 {
-	struct fwspk *fwspk = substream->private_data;
+	struct snd_oxfw *oxfw = substream->private_data;
 
-	return amdtp_stream_pcm_pointer(&fwspk->stream);
+	return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
 }
 
-static int fwspk_create_pcm(struct fwspk *fwspk)
+static int oxfw_create_pcm(struct snd_oxfw *oxfw)
 {
 	static struct snd_pcm_ops ops = {
-		.open      = fwspk_open,
-		.close     = fwspk_close,
+		.open      = oxfw_open,
+		.close     = oxfw_close,
 		.ioctl     = snd_pcm_lib_ioctl,
-		.hw_params = fwspk_hw_params,
-		.hw_free   = fwspk_hw_free,
-		.prepare   = fwspk_prepare,
-		.trigger   = fwspk_trigger,
-		.pointer   = fwspk_pointer,
+		.hw_params = oxfw_hw_params,
+		.hw_free   = oxfw_hw_free,
+		.prepare   = oxfw_prepare,
+		.trigger   = oxfw_trigger,
+		.pointer   = oxfw_pointer,
 		.page      = snd_pcm_lib_get_vmalloc_page,
 		.mmap      = snd_pcm_lib_mmap_vmalloc,
 	};
 	struct snd_pcm *pcm;
 	int err;
 
-	err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm);
+	err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm);
 	if (err < 0)
 		return err;
-	pcm->private_data = fwspk;
-	strcpy(pcm->name, fwspk->device_info->short_name);
+	pcm->private_data = oxfw;
+	strcpy(pcm->name, oxfw->device_info->short_name);
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
 	return 0;
 }
@@ -332,7 +333,7 @@ enum control_attribute {
 	CTL_CURRENT	= 0x10,
 };
 
-static int fwspk_mute_command(struct fwspk *fwspk, bool *value,
+static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
 			      enum control_action action)
 {
 	u8 *buf;
@@ -353,7 +354,7 @@ static int fwspk_mute_command(struct fwspk *fwspk, bool *value,
 	buf[1] = 0x08;			/* audio unit 0 */
 	buf[2] = 0xb8;			/* FUNCTION BLOCK */
 	buf[3] = 0x81;			/* function block type: feature */
-	buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */
+	buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
 	buf[5] = 0x10;			/* control attribute: current */
 	buf[6] = 0x02;			/* selector length */
 	buf[7] = 0x00;			/* audio channel number */
@@ -364,16 +365,16 @@ static int fwspk_mute_command(struct fwspk *fwspk, bool *value,
 	else
 		buf[10] = *value ? 0x70 : 0x60;
 
-	err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe);
+	err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
 	if (err < 0)
 		goto error;
 	if (err < 11) {
-		dev_err(&fwspk->unit->device, "short FCP response\n");
+		dev_err(&oxfw->unit->device, "short FCP response\n");
 		err = -EIO;
 		goto error;
 	}
 	if (buf[0] != response_ok) {
-		dev_err(&fwspk->unit->device, "mute command failed\n");
+		dev_err(&oxfw->unit->device, "mute command failed\n");
 		err = -EIO;
 		goto error;
 	}
@@ -388,7 +389,7 @@ error:
 	return err;
 }
 
-static int fwspk_volume_command(struct fwspk *fwspk, s16 *value,
+static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
 				unsigned int channel,
 				enum control_attribute attribute,
 				enum control_action action)
@@ -411,7 +412,7 @@ static int fwspk_volume_command(struct fwspk *fwspk, s16 *value,
 	buf[1] = 0x08;			/* audio unit 0 */
 	buf[2] = 0xb8;			/* FUNCTION BLOCK */
 	buf[3] = 0x81;			/* function block type: feature */
-	buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */
+	buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
 	buf[5] = attribute;		/* control attribute */
 	buf[6] = 0x02;			/* selector length */
 	buf[7] = channel;		/* audio channel number */
@@ -425,16 +426,16 @@ static int fwspk_volume_command(struct fwspk *fwspk, s16 *value,
 		buf[11] = *value;
 	}
 
-	err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe);
+	err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
 	if (err < 0)
 		goto error;
 	if (err < 12) {
-		dev_err(&fwspk->unit->device, "short FCP response\n");
+		dev_err(&oxfw->unit->device, "short FCP response\n");
 		err = -EIO;
 		goto error;
 	}
 	if (buf[0] != response_ok) {
-		dev_err(&fwspk->unit->device, "volume command failed\n");
+		dev_err(&oxfw->unit->device, "volume command failed\n");
 		err = -EIO;
 		goto error;
 	}
@@ -449,75 +450,75 @@ error:
 	return err;
 }
 
-static int fwspk_mute_get(struct snd_kcontrol *control,
+static int oxfw_mute_get(struct snd_kcontrol *control,
 			  struct snd_ctl_elem_value *value)
 {
-	struct fwspk *fwspk = control->private_data;
+	struct snd_oxfw *oxfw = control->private_data;
 
-	value->value.integer.value[0] = !fwspk->mute;
+	value->value.integer.value[0] = !oxfw->mute;
 
 	return 0;
 }
 
-static int fwspk_mute_put(struct snd_kcontrol *control,
+static int oxfw_mute_put(struct snd_kcontrol *control,
 			  struct snd_ctl_elem_value *value)
 {
-	struct fwspk *fwspk = control->private_data;
+	struct snd_oxfw *oxfw = control->private_data;
 	bool mute;
 	int err;
 
 	mute = !value->value.integer.value[0];
 
-	if (mute == fwspk->mute)
+	if (mute == oxfw->mute)
 		return 0;
 
-	err = fwspk_mute_command(fwspk, &mute, CTL_WRITE);
+	err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
 	if (err < 0)
 		return err;
-	fwspk->mute = mute;
+	oxfw->mute = mute;
 
 	return 1;
 }
 
-static int fwspk_volume_info(struct snd_kcontrol *control,
+static int oxfw_volume_info(struct snd_kcontrol *control,
 			     struct snd_ctl_elem_info *info)
 {
-	struct fwspk *fwspk = control->private_data;
+	struct snd_oxfw *oxfw = control->private_data;
 
 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	info->count = fwspk->device_info->mixer_channels;
-	info->value.integer.min = fwspk->volume_min;
-	info->value.integer.max = fwspk->volume_max;
+	info->count = oxfw->device_info->mixer_channels;
+	info->value.integer.min = oxfw->volume_min;
+	info->value.integer.max = oxfw->volume_max;
 
 	return 0;
 }
 
 static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
 
-static int fwspk_volume_get(struct snd_kcontrol *control,
+static int oxfw_volume_get(struct snd_kcontrol *control,
 			    struct snd_ctl_elem_value *value)
 {
-	struct fwspk *fwspk = control->private_data;
+	struct snd_oxfw *oxfw = control->private_data;
 	unsigned int i;
 
-	for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
-		value->value.integer.value[channel_map[i]] = fwspk->volume[i];
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+		value->value.integer.value[channel_map[i]] = oxfw->volume[i];
 
 	return 0;
 }
 
-static int fwspk_volume_put(struct snd_kcontrol *control,
+static int oxfw_volume_put(struct snd_kcontrol *control,
 			  struct snd_ctl_elem_value *value)
 {
-	struct fwspk *fwspk = control->private_data;
+	struct snd_oxfw *oxfw = control->private_data;
 	unsigned int i, changed_channels;
 	bool equal_values = true;
 	s16 volume;
 	int err;
 
-	for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
-		if (value->value.integer.value[i] < fwspk->volume_min ||
-		    value->value.integer.value[i] > fwspk->volume_max)
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+		if (value->value.integer.value[i] < oxfw->volume_min ||
+		    value->value.integer.value[i] > oxfw->volume_max)
 			return -EINVAL;
 		if (value->value.integer.value[i] !=
 		    value->value.integer.value[0])
@@ -525,74 +526,74 @@ static int fwspk_volume_put(struct snd_kcontrol *control,
 	}
 
 	changed_channels = 0;
-	for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
 		if (value->value.integer.value[channel_map[i]] !=
-							fwspk->volume[i])
+							oxfw->volume[i])
 			changed_channels |= 1 << (i + 1);
 
 	if (equal_values && changed_channels != 0)
 		changed_channels = 1 << 0;
 
-	for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) {
+	for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
 		volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
 		if (changed_channels & (1 << i)) {
-			err = fwspk_volume_command(fwspk, &volume, i,
+			err = oxfw_volume_command(oxfw, &volume, i,
 						   CTL_CURRENT, CTL_WRITE);
 			if (err < 0)
 				return err;
 		}
 		if (i > 0)
-			fwspk->volume[i - 1] = volume;
+			oxfw->volume[i - 1] = volume;
 	}
 
 	return changed_channels != 0;
 }
 
-static int fwspk_create_mixer(struct fwspk *fwspk)
+static int oxfw_create_mixer(struct snd_oxfw *oxfw)
 {
 	static const struct snd_kcontrol_new controls[] = {
 		{
 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 			.name = "PCM Playback Switch",
 			.info = snd_ctl_boolean_mono_info,
-			.get = fwspk_mute_get,
-			.put = fwspk_mute_put,
+			.get = oxfw_mute_get,
+			.put = oxfw_mute_put,
 		},
 		{
 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 			.name = "PCM Playback Volume",
-			.info = fwspk_volume_info,
-			.get = fwspk_volume_get,
-			.put = fwspk_volume_put,
+			.info = oxfw_volume_info,
+			.get = oxfw_volume_get,
+			.put = oxfw_volume_put,
 		},
 	};
 	unsigned int i, first_ch;
 	int err;
 
-	err = fwspk_volume_command(fwspk, &fwspk->volume_min,
+	err = oxfw_volume_command(oxfw, &oxfw->volume_min,
 				   0, CTL_MIN, CTL_READ);
 	if (err < 0)
 		return err;
-	err = fwspk_volume_command(fwspk, &fwspk->volume_max,
+	err = oxfw_volume_command(oxfw, &oxfw->volume_max,
 				   0, CTL_MAX, CTL_READ);
 	if (err < 0)
 		return err;
 
-	err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ);
+	err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
 	if (err < 0)
 		return err;
 
-	first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1;
-	for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
-		err = fwspk_volume_command(fwspk, &fwspk->volume[i],
+	first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+		err = oxfw_volume_command(oxfw, &oxfw->volume[i],
 					   first_ch + i, CTL_CURRENT, CTL_READ);
 		if (err < 0)
 			return err;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(controls); ++i) {
-		err = snd_ctl_add(fwspk->card,
-				  snd_ctl_new1(&controls[i], fwspk));
+		err = snd_ctl_add(oxfw->card,
+				  snd_ctl_new1(&controls[i], oxfw));
 		if (err < 0)
 			return err;
 	}
@@ -600,7 +601,7 @@ static int fwspk_create_mixer(struct fwspk *fwspk)
 	return 0;
 }
 
-static u32 fwspk_read_firmware_version(struct fw_unit *unit)
+static u32 oxfw_read_firmware_version(struct fw_unit *unit)
 {
 	__be32 data;
 	int err;
@@ -610,63 +611,63 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit)
 	return err >= 0 ? be32_to_cpu(data) : 0;
 }
 
-static void fwspk_card_free(struct snd_card *card)
+static void oxfw_card_free(struct snd_card *card)
 {
-	struct fwspk *fwspk = card->private_data;
+	struct snd_oxfw *oxfw = card->private_data;
 
-	amdtp_stream_destroy(&fwspk->stream);
-	cmp_connection_destroy(&fwspk->connection);
-	fw_unit_put(fwspk->unit);
-	mutex_destroy(&fwspk->mutex);
+	amdtp_stream_destroy(&oxfw->rx_stream);
+	cmp_connection_destroy(&oxfw->in_conn);
+	fw_unit_put(oxfw->unit);
+	mutex_destroy(&oxfw->mutex);
 }
 
-static int fwspk_probe(struct fw_unit *unit,
+static int oxfw_probe(struct fw_unit *unit,
 		       const struct ieee1394_device_id *id)
 {
 	struct fw_device *fw_dev = fw_parent_device(unit);
 	struct snd_card *card;
-	struct fwspk *fwspk;
+	struct snd_oxfw *oxfw;
 	u32 firmware;
 	int err;
 
 	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
-			   sizeof(*fwspk), &card);
+			   sizeof(*oxfw), &card);
 	if (err < 0)
 		return err;
 
-	fwspk = card->private_data;
-	fwspk->card = card;
-	mutex_init(&fwspk->mutex);
-	fwspk->unit = fw_unit_get(unit);
-	fwspk->device_info = (const struct device_info *)id->driver_data;
+	oxfw = card->private_data;
+	oxfw->card = card;
+	mutex_init(&oxfw->mutex);
+	oxfw->unit = fw_unit_get(unit);
+	oxfw->device_info = (const struct device_info *)id->driver_data;
 
-	err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
+	err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0);
 	if (err < 0)
 		goto err_unit;
 
-	err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
+	err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM,
 				CIP_NONBLOCKING);
 	if (err < 0)
 		goto err_connection;
 
-	card->private_free = fwspk_card_free;
+	card->private_free = oxfw_card_free;
 
-	strcpy(card->driver, fwspk->device_info->driver_name);
-	strcpy(card->shortname, fwspk->device_info->short_name);
-	firmware = fwspk_read_firmware_version(unit);
+	strcpy(card->driver, oxfw->device_info->driver_name);
+	strcpy(card->shortname, oxfw->device_info->short_name);
+	firmware = oxfw_read_firmware_version(unit);
 	snprintf(card->longname, sizeof(card->longname),
 		 "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
-		 fwspk->device_info->long_name,
+		 oxfw->device_info->long_name,
 		 firmware >> 20, firmware & 0xffff,
 		 fw_dev->config_rom[3], fw_dev->config_rom[4],
 		 dev_name(&unit->device), 100 << fw_dev->max_speed);
-	strcpy(card->mixername, "OXFW970");
+	strcpy(card->mixername, "OXFW");
 
-	err = fwspk_create_pcm(fwspk);
+	err = oxfw_create_pcm(oxfw);
 	if (err < 0)
 		goto error;
 
-	err = fwspk_create_mixer(fwspk);
+	err = oxfw_create_mixer(oxfw);
 	if (err < 0)
 		goto error;
 
@@ -674,49 +675,49 @@ static int fwspk_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
-	dev_set_drvdata(&unit->device, fwspk);
+	dev_set_drvdata(&unit->device, oxfw);
 
 	return 0;
 
 err_connection:
-	cmp_connection_destroy(&fwspk->connection);
+	cmp_connection_destroy(&oxfw->in_conn);
 err_unit:
-	fw_unit_put(fwspk->unit);
-	mutex_destroy(&fwspk->mutex);
+	fw_unit_put(oxfw->unit);
+	mutex_destroy(&oxfw->mutex);
 error:
 	snd_card_free(card);
 	return err;
 }
 
-static void fwspk_bus_reset(struct fw_unit *unit)
+static void oxfw_bus_reset(struct fw_unit *unit)
 {
-	struct fwspk *fwspk = dev_get_drvdata(&unit->device);
+	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
-	fcp_bus_reset(fwspk->unit);
+	fcp_bus_reset(oxfw->unit);
 
-	if (cmp_connection_update(&fwspk->connection) < 0) {
-		amdtp_stream_pcm_abort(&fwspk->stream);
-		mutex_lock(&fwspk->mutex);
-		fwspk_stop_stream(fwspk);
-		mutex_unlock(&fwspk->mutex);
+	if (cmp_connection_update(&oxfw->in_conn) < 0) {
+		amdtp_stream_pcm_abort(&oxfw->rx_stream);
+		mutex_lock(&oxfw->mutex);
+		oxfw_stop_stream(oxfw);
+		mutex_unlock(&oxfw->mutex);
 		return;
 	}
 
-	amdtp_stream_update(&fwspk->stream);
+	amdtp_stream_update(&oxfw->rx_stream);
 }
 
-static void fwspk_remove(struct fw_unit *unit)
+static void oxfw_remove(struct fw_unit *unit)
 {
-	struct fwspk *fwspk = dev_get_drvdata(&unit->device);
+	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
-	amdtp_stream_pcm_abort(&fwspk->stream);
-	snd_card_disconnect(fwspk->card);
+	amdtp_stream_pcm_abort(&oxfw->rx_stream);
+	snd_card_disconnect(oxfw->card);
 
-	mutex_lock(&fwspk->mutex);
-	fwspk_stop_stream(fwspk);
-	mutex_unlock(&fwspk->mutex);
+	mutex_lock(&oxfw->mutex);
+	oxfw_stop_stream(oxfw);
+	mutex_unlock(&oxfw->mutex);
 
-	snd_card_free_when_closed(fwspk->card);
+	snd_card_free_when_closed(oxfw->card);
 }
 
 static const struct device_info griffin_firewave = {
@@ -739,7 +740,7 @@ static const struct device_info lacie_speakers = {
 	.volume_fb_id = 0x01,
 };
 
-static const struct ieee1394_device_id fwspk_id_table[] = {
+static const struct ieee1394_device_id oxfw_id_table[] = {
 	{
 		.match_flags  = IEEE1394_MATCH_VENDOR_ID |
 				IEEE1394_MATCH_MODEL_ID |
@@ -764,29 +765,29 @@ static const struct ieee1394_device_id fwspk_id_table[] = {
 	},
 	{ }
 };
-MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table);
+MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
 
-static struct fw_driver fwspk_driver = {
+static struct fw_driver oxfw_driver = {
 	.driver   = {
 		.owner	= THIS_MODULE,
 		.name	= KBUILD_MODNAME,
 		.bus	= &fw_bus_type,
 	},
-	.probe    = fwspk_probe,
-	.update   = fwspk_bus_reset,
-	.remove   = fwspk_remove,
-	.id_table = fwspk_id_table,
+	.probe    = oxfw_probe,
+	.update   = oxfw_bus_reset,
+	.remove   = oxfw_remove,
+	.id_table = oxfw_id_table,
 };
 
-static int __init alsa_fwspk_init(void)
+static int __init snd_oxfw_init(void)
 {
-	return driver_register(&fwspk_driver.driver);
+	return driver_register(&oxfw_driver.driver);
 }
 
-static void __exit alsa_fwspk_exit(void)
+static void __exit snd_oxfw_exit(void)
 {
-	driver_unregister(&fwspk_driver.driver);
+	driver_unregister(&oxfw_driver.driver);
 }
 
-module_init(alsa_fwspk_init);
-module_exit(alsa_fwspk_exit);
+module_init(snd_oxfw_init);
+module_exit(snd_oxfw_exit);
-- 
1.9.1

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

* [PATCH 16/29] ALSA: oxfw: Move to its own directory
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (14 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 15/29] ALSA: speakers: Rename to oxfw and rename some members Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 17/29] ALSA: oxfw: Split stream functionality to a new file and add a header file Takashi Sakamoto
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Followed commits add much codes. To make the work easy, this commit creates
own directory and move current file to it.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Makefile          | 3 +--
 sound/firewire/oxfw/Makefile     | 2 ++
 sound/firewire/{ => oxfw}/oxfw.c | 8 ++++----
 3 files changed, 7 insertions(+), 6 deletions(-)
 create mode 100644 sound/firewire/oxfw/Makefile
 rename sound/firewire/{ => oxfw}/oxfw.c (99%)

diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 021b877..edbe61d 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,6 +1,5 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o
 
@@ -10,4 +9,4 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
 obj-$(CONFIG_SND_FIREWORKS) += fireworks/
 obj-$(CONFIG_SND_BEBOB) += bebob/
-obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += oxfw/
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
new file mode 100644
index 0000000..9ca49c0
--- /dev/null
+++ b/sound/firewire/oxfw/Makefile
@@ -0,0 +1,2 @@
+snd-oxfw-objs := oxfw.o
+obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw.c b/sound/firewire/oxfw/oxfw.c
similarity index 99%
rename from sound/firewire/oxfw.c
rename to sound/firewire/oxfw/oxfw.c
index b7498e9..55c687e 100644
--- a/sound/firewire/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -17,10 +17,10 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include "cmp.h"
-#include "fcp.h"
-#include "amdtp.h"
-#include "lib.h"
+#include "../cmp.h"
+#include "../fcp.h"
+#include "../amdtp.h"
+#include "../lib.h"
 
 #define OXFORD_FIRMWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x50000)
 /* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
-- 
1.9.1

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

* [PATCH 17/29] ALSA: oxfw: Split stream functionality to a new file and add a header file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (15 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 16/29] ALSA: oxfw: Move to its own directory Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 18/29] ALSA: oxfw: Split PCM functionality to a new file Takashi Sakamoto
                   ` (14 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This is a help for works in followed patches.

And this commit remove 'fw_unit_get()/fw_unit_put()' because these
are called by helper functions in 'snd-firewire-lib'.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/Makefile      |   2 +-
 sound/firewire/oxfw/oxfw-stream.c |  92 ++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw.c        | 136 ++++++--------------------------------
 sound/firewire/oxfw/oxfw.h        |  56 ++++++++++++++++
 4 files changed, 168 insertions(+), 118 deletions(-)
 create mode 100644 sound/firewire/oxfw/oxfw-stream.c
 create mode 100644 sound/firewire/oxfw/oxfw.h

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 9ca49c0..e15c4c0 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,2 +1,2 @@
-snd-oxfw-objs := oxfw.o
+snd-oxfw-objs := oxfw-stream.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
new file mode 100644
index 0000000..c262b56
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -0,0 +1,92 @@
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw)
+{
+	int err;
+
+	err = cmp_connection_init(&oxfw->in_conn, oxfw->unit,
+				  CMP_INPUT, 0);
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_init(&oxfw->rx_stream, oxfw->unit,
+				AMDTP_OUT_STREAM, CIP_NONBLOCKING);
+	if (err < 0) {
+		amdtp_stream_destroy(&oxfw->rx_stream);
+		cmp_connection_destroy(&oxfw->in_conn);
+	}
+end:
+	return err;
+}
+
+static void stop_stream(struct snd_oxfw *oxfw)
+{
+	amdtp_stream_pcm_abort(&oxfw->rx_stream);
+	amdtp_stream_stop(&oxfw->rx_stream);
+	cmp_connection_break(&oxfw->in_conn);
+}
+
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw)
+{
+	int err = 0;
+
+	mutex_lock(&oxfw->mutex);
+
+	if (amdtp_streaming_error(&oxfw->rx_stream))
+		stop_stream(oxfw);
+
+	if (amdtp_stream_running(&oxfw->rx_stream))
+		goto end;
+
+	err = cmp_connection_establish(&oxfw->in_conn,
+			amdtp_stream_get_max_payload(&oxfw->rx_stream));
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_start(&oxfw->rx_stream,
+				 oxfw->in_conn.resources.channel,
+				 oxfw->in_conn.speed);
+	if (err < 0)
+		stop_stream(oxfw);
+end:
+	mutex_unlock(&oxfw->mutex);
+	return err;
+}
+
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw)
+{
+	mutex_lock(&oxfw->mutex);
+	stop_stream(oxfw);
+	mutex_unlock(&oxfw->mutex);
+}
+
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw)
+{
+	mutex_lock(&oxfw->mutex);
+
+	stop_stream(oxfw);
+
+	amdtp_stream_destroy(&oxfw->rx_stream);
+	cmp_connection_destroy(&oxfw->in_conn);
+
+	mutex_unlock(&oxfw->mutex);
+}
+
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw)
+{
+	if (cmp_connection_update(&oxfw->in_conn) < 0) {
+		mutex_lock(&oxfw->mutex);
+		stop_stream(oxfw);
+		mutex_unlock(&oxfw->mutex);
+	} else {
+		amdtp_stream_update(&oxfw->rx_stream);
+	}
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 55c687e..9445861 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -5,22 +5,7 @@
  * Licensed under the terms of the GNU General Public License, version 2.
  */
 
-#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/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include "../cmp.h"
-#include "../fcp.h"
-#include "../amdtp.h"
-#include "../lib.h"
+#include "oxfw.h"
 
 #define OXFORD_FIRMWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x50000)
 /* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
@@ -35,29 +20,6 @@
 #define SPECIFIER_1394TA	0x00a02d
 #define VERSION_AVC		0x010001
 
-struct device_info {
-	const char *driver_name;
-	const char *short_name;
-	const char *long_name;
-	int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
-	unsigned int mixer_channels;
-	u8 mute_fb_id;
-	u8 volume_fb_id;
-};
-
-struct snd_oxfw {
-	struct snd_card *card;
-	struct fw_unit *unit;
-	const struct device_info *device_info;
-	struct mutex mutex;
-	struct cmp_connection in_conn;
-	struct amdtp_stream rx_stream;
-	bool mute;
-	s16 volume[6];
-	s16 volume_min;
-	s16 volume_max;
-};
-
 MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
@@ -180,23 +142,13 @@ static int oxfw_close(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static void oxfw_stop_stream(struct snd_oxfw *oxfw)
-{
-	if (amdtp_stream_running(&oxfw->rx_stream)) {
-		amdtp_stream_stop(&oxfw->rx_stream);
-		cmp_connection_break(&oxfw->in_conn);
-	}
-}
-
 static int oxfw_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 	int err;
 
-	mutex_lock(&oxfw->mutex);
-	oxfw_stop_stream(oxfw);
-	mutex_unlock(&oxfw->mutex);
+	snd_oxfw_stream_stop_simplex(oxfw);
 
 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 					       params_buffer_bytes(hw_params));
@@ -230,9 +182,7 @@ static int oxfw_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	mutex_lock(&oxfw->mutex);
-	oxfw_stop_stream(oxfw);
-	mutex_unlock(&oxfw->mutex);
+	snd_oxfw_stream_stop_simplex(oxfw);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -242,35 +192,14 @@ static int oxfw_prepare(struct snd_pcm_substream *substream)
 	struct snd_oxfw *oxfw = substream->private_data;
 	int err;
 
-	mutex_lock(&oxfw->mutex);
-
-	if (amdtp_streaming_error(&oxfw->rx_stream))
-		oxfw_stop_stream(oxfw);
+	snd_oxfw_stream_stop_simplex(oxfw);
 
-	if (!amdtp_stream_running(&oxfw->rx_stream)) {
-		err = cmp_connection_establish(&oxfw->in_conn,
-			amdtp_stream_get_max_payload(&oxfw->rx_stream));
-		if (err < 0)
-			goto err_mutex;
-
-		err = amdtp_stream_start(&oxfw->rx_stream,
-					 oxfw->in_conn.resources.channel,
-					 oxfw->in_conn.speed);
-		if (err < 0)
-			goto err_connection;
-	}
-
-	mutex_unlock(&oxfw->mutex);
+	err = snd_oxfw_stream_start_simplex(oxfw);
+	if (err < 0)
+		goto end;
 
 	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
-
-	return 0;
-
-err_connection:
-	cmp_connection_break(&oxfw->in_conn);
-err_mutex:
-	mutex_unlock(&oxfw->mutex);
-
+end:
 	return err;
 }
 
@@ -615,9 +544,6 @@ static void oxfw_card_free(struct snd_card *card)
 {
 	struct snd_oxfw *oxfw = card->private_data;
 
-	amdtp_stream_destroy(&oxfw->rx_stream);
-	cmp_connection_destroy(&oxfw->in_conn);
-	fw_unit_put(oxfw->unit);
 	mutex_destroy(&oxfw->mutex);
 }
 
@@ -635,23 +561,13 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		return err;
 
+	card->private_free = oxfw_card_free;
 	oxfw = card->private_data;
 	oxfw->card = card;
 	mutex_init(&oxfw->mutex);
-	oxfw->unit = fw_unit_get(unit);
+	oxfw->unit = unit;
 	oxfw->device_info = (const struct device_info *)id->driver_data;
 
-	err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0);
-	if (err < 0)
-		goto err_unit;
-
-	err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM,
-				CIP_NONBLOCKING);
-	if (err < 0)
-		goto err_connection;
-
-	card->private_free = oxfw_card_free;
-
 	strcpy(card->driver, oxfw->device_info->driver_name);
 	strcpy(card->shortname, oxfw->device_info->short_name);
 	firmware = oxfw_read_firmware_version(unit);
@@ -671,19 +587,18 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
-	err = snd_card_register(card);
+	err = snd_oxfw_stream_init_simplex(oxfw);
 	if (err < 0)
 		goto error;
 
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_oxfw_stream_destroy_simplex(oxfw);
+		goto error;
+	}
 	dev_set_drvdata(&unit->device, oxfw);
 
 	return 0;
-
-err_connection:
-	cmp_connection_destroy(&oxfw->in_conn);
-err_unit:
-	fw_unit_put(oxfw->unit);
-	mutex_destroy(&oxfw->mutex);
 error:
 	snd_card_free(card);
 	return err;
@@ -694,29 +609,16 @@ static void oxfw_bus_reset(struct fw_unit *unit)
 	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
 	fcp_bus_reset(oxfw->unit);
-
-	if (cmp_connection_update(&oxfw->in_conn) < 0) {
-		amdtp_stream_pcm_abort(&oxfw->rx_stream);
-		mutex_lock(&oxfw->mutex);
-		oxfw_stop_stream(oxfw);
-		mutex_unlock(&oxfw->mutex);
-		return;
-	}
-
-	amdtp_stream_update(&oxfw->rx_stream);
+	snd_oxfw_stream_update_simplex(oxfw);
 }
 
 static void oxfw_remove(struct fw_unit *unit)
 {
 	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
-	amdtp_stream_pcm_abort(&oxfw->rx_stream);
-	snd_card_disconnect(oxfw->card);
-
-	mutex_lock(&oxfw->mutex);
-	oxfw_stop_stream(oxfw);
-	mutex_unlock(&oxfw->mutex);
+	snd_oxfw_stream_destroy_simplex(oxfw);
 
+	snd_card_disconnect(oxfw->card);
 	snd_card_free_when_closed(oxfw->card);
 }
 
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
new file mode 100644
index 0000000..e5836e0
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.h
@@ -0,0 +1,56 @@
+/*
+ * oxfw.h - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#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/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+
+struct device_info {
+	const char *driver_name;
+	const char *short_name;
+	const char *long_name;
+	int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
+	unsigned int mixer_channels;
+	u8 mute_fb_id;
+	u8 volume_fb_id;
+};
+
+struct snd_oxfw {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	const struct device_info *device_info;
+	struct mutex mutex;
+	struct cmp_connection in_conn;
+	struct amdtp_stream rx_stream;
+	bool mute;
+	s16 volume[6];
+	s16 volume_min;
+	s16 volume_max;
+};
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 18/29] ALSA: oxfw: Split PCM functionality to a new file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (16 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 17/29] ALSA: oxfw: Split stream functionality to a new file and add a header file Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 19/29] ALSA: oxfw: Split control " Takashi Sakamoto
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This is a help for works in followed patches.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/Makefile   |   2 +-
 sound/firewire/oxfw/oxfw-pcm.c | 239 +++++++++++++++++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw.c     | 232 +--------------------------------------
 sound/firewire/oxfw/oxfw.h     |   4 +
 4 files changed, 245 insertions(+), 232 deletions(-)
 create mode 100644 sound/firewire/oxfw/oxfw-pcm.c

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index e15c4c0..7fb4d09 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,2 +1,2 @@
-snd-oxfw-objs := oxfw-stream.o oxfw.o
+snd-oxfw-objs := oxfw-stream.o oxfw-pcm.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
new file mode 100644
index 0000000..1fd76ce
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -0,0 +1,239 @@
+/*
+ * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
+				    struct snd_pcm_hw_rule *rule)
+{
+	static unsigned int stereo_rates[] = { 48000, 96000 };
+	struct snd_interval *channels =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *rate =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+	/* two channels work only at 48/96 kHz */
+	if (snd_interval_max(channels) < 6)
+		return snd_interval_list(rate, 2, stereo_rates, 0);
+	return 0;
+}
+
+static int firewave_channels_constraint(struct snd_pcm_hw_params *params,
+					struct snd_pcm_hw_rule *rule)
+{
+	static const struct snd_interval all_channels = { .min = 6, .max = 6 };
+	struct snd_interval *rate =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	/* 32/44.1 kHz work only with all six channels */
+	if (snd_interval_max(rate) < 48000)
+		return snd_interval_refine(channels, &all_channels);
+	return 0;
+}
+
+int firewave_constraints(struct snd_pcm_runtime *runtime)
+{
+	static unsigned int channels_list[] = { 2, 6 };
+	static struct snd_pcm_hw_constraint_list channels_list_constraint = {
+		.count = 2,
+		.list = channels_list,
+	};
+	int err;
+
+	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
+			    SNDRV_PCM_RATE_44100 |
+			    SNDRV_PCM_RATE_48000 |
+			    SNDRV_PCM_RATE_96000;
+	runtime->hw.channels_max = 6;
+
+	err = snd_pcm_hw_constraint_list(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_CHANNELS,
+					 &channels_list_constraint);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  firewave_rate_constraint, NULL,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  firewave_channels_constraint, NULL,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
+{
+	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
+			    SNDRV_PCM_RATE_44100 |
+			    SNDRV_PCM_RATE_48000 |
+			    SNDRV_PCM_RATE_88200 |
+			    SNDRV_PCM_RATE_96000;
+
+	return 0;
+}
+
+static int pcm_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,
+		.channels_min = 2,
+		.channels_max = 2,
+		.buffer_bytes_max = 4 * 1024 * 1024,
+		.period_bytes_min = 1,
+		.period_bytes_max = UINT_MAX,
+		.periods_min = 1,
+		.periods_max = UINT_MAX,
+	};
+	struct snd_oxfw *oxfw = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	bool used;
+	int err;
+
+	err = cmp_connection_check_used(&oxfw->in_conn, &used);
+	if ((err < 0) || used)
+		goto end;
+
+	runtime->hw = hardware;
+
+	err = oxfw->device_info->pcm_constraints(runtime);
+	if (err < 0)
+		goto end;
+	err = snd_pcm_limit_hw_rates(runtime);
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime);
+end:
+	return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+	int err;
+
+	snd_oxfw_stream_stop_simplex(oxfw);
+
+	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+					       params_buffer_bytes(hw_params));
+	if (err < 0)
+		goto error;
+
+	amdtp_stream_set_parameters(&oxfw->rx_stream,
+				    params_rate(hw_params),
+				    params_channels(hw_params),
+				    0);
+
+	amdtp_stream_set_pcm_format(&oxfw->rx_stream,
+				    params_format(hw_params));
+
+	err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params),
+				      AVC_GENERAL_PLUG_DIR_IN, 0);
+	if (err < 0) {
+		dev_err(&oxfw->unit->device, "failed to set sample rate\n");
+		goto err_buffer;
+	}
+
+	return 0;
+
+err_buffer:
+	snd_pcm_lib_free_vmalloc_buffer(substream);
+error:
+	return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	snd_oxfw_stream_stop_simplex(oxfw);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+	int err;
+
+	snd_oxfw_stream_stop_simplex(oxfw);
+
+	err = snd_oxfw_stream_start_simplex(oxfw);
+	if (err < 0)
+		goto end;
+
+	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
+end:
+	return err;
+}
+
+static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&oxfw->rx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&oxfw->rx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
+}
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
+{
+	static struct snd_pcm_ops ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = pcm_hw_params,
+		.hw_free   = pcm_hw_free,
+		.prepare   = pcm_prepare,
+		.trigger   = pcm_trigger,
+		.pointer   = pcm_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = oxfw;
+	strcpy(pcm->name, oxfw->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
+	return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 9445861..2210669 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -25,236 +25,6 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("snd-firewire-speakers");
 
-static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
-				    struct snd_pcm_hw_rule *rule)
-{
-	static unsigned int stereo_rates[] = { 48000, 96000 };
-	struct snd_interval *channels =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval *rate =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-
-	/* two channels work only at 48/96 kHz */
-	if (snd_interval_max(channels) < 6)
-		return snd_interval_list(rate, 2, stereo_rates, 0);
-	return 0;
-}
-
-static int firewave_channels_constraint(struct snd_pcm_hw_params *params,
-					struct snd_pcm_hw_rule *rule)
-{
-	static const struct snd_interval all_channels = { .min = 6, .max = 6 };
-	struct snd_interval *rate =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *channels =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-
-	/* 32/44.1 kHz work only with all six channels */
-	if (snd_interval_max(rate) < 48000)
-		return snd_interval_refine(channels, &all_channels);
-	return 0;
-}
-
-static int firewave_constraints(struct snd_pcm_runtime *runtime)
-{
-	static unsigned int channels_list[] = { 2, 6 };
-	static struct snd_pcm_hw_constraint_list channels_list_constraint = {
-		.count = 2,
-		.list = channels_list,
-	};
-	int err;
-
-	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
-			    SNDRV_PCM_RATE_44100 |
-			    SNDRV_PCM_RATE_48000 |
-			    SNDRV_PCM_RATE_96000;
-	runtime->hw.channels_max = 6;
-
-	err = snd_pcm_hw_constraint_list(runtime, 0,
-					 SNDRV_PCM_HW_PARAM_CHANNELS,
-					 &channels_list_constraint);
-	if (err < 0)
-		return err;
-	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-				  firewave_rate_constraint, NULL,
-				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-	if (err < 0)
-		return err;
-	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-				  firewave_channels_constraint, NULL,
-				  SNDRV_PCM_HW_PARAM_RATE, -1);
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-
-static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
-{
-	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
-			    SNDRV_PCM_RATE_44100 |
-			    SNDRV_PCM_RATE_48000 |
-			    SNDRV_PCM_RATE_88200 |
-			    SNDRV_PCM_RATE_96000;
-
-	return 0;
-}
-
-static int oxfw_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,
-		.channels_min = 2,
-		.channels_max = 2,
-		.buffer_bytes_max = 4 * 1024 * 1024,
-		.period_bytes_min = 1,
-		.period_bytes_max = UINT_MAX,
-		.periods_min = 1,
-		.periods_max = UINT_MAX,
-	};
-	struct snd_oxfw *oxfw = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int err;
-
-	runtime->hw = hardware;
-
-	err = oxfw->device_info->pcm_constraints(runtime);
-	if (err < 0)
-		return err;
-	err = snd_pcm_limit_hw_rates(runtime);
-	if (err < 0)
-		return err;
-
-	err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime);
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-
-static int oxfw_close(struct snd_pcm_substream *substream)
-{
-	return 0;
-}
-
-static int oxfw_hw_params(struct snd_pcm_substream *substream,
-			   struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_oxfw *oxfw = substream->private_data;
-	int err;
-
-	snd_oxfw_stream_stop_simplex(oxfw);
-
-	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-					       params_buffer_bytes(hw_params));
-	if (err < 0)
-		goto error;
-
-	amdtp_stream_set_parameters(&oxfw->rx_stream,
-				    params_rate(hw_params),
-				    params_channels(hw_params),
-				    0);
-
-	amdtp_stream_set_pcm_format(&oxfw->rx_stream,
-				    params_format(hw_params));
-
-	err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params),
-				      AVC_GENERAL_PLUG_DIR_IN, 0);
-	if (err < 0) {
-		dev_err(&oxfw->unit->device, "failed to set sample rate\n");
-		goto err_buffer;
-	}
-
-	return 0;
-
-err_buffer:
-	snd_pcm_lib_free_vmalloc_buffer(substream);
-error:
-	return err;
-}
-
-static int oxfw_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_oxfw *oxfw = substream->private_data;
-
-	snd_oxfw_stream_stop_simplex(oxfw);
-
-	return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int oxfw_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_oxfw *oxfw = substream->private_data;
-	int err;
-
-	snd_oxfw_stream_stop_simplex(oxfw);
-
-	err = snd_oxfw_stream_start_simplex(oxfw);
-	if (err < 0)
-		goto end;
-
-	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
-end:
-	return err;
-}
-
-static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_oxfw *oxfw = 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_stream_pcm_trigger(&oxfw->rx_stream, pcm);
-	return 0;
-}
-
-static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_oxfw *oxfw = substream->private_data;
-
-	return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
-}
-
-static int oxfw_create_pcm(struct snd_oxfw *oxfw)
-{
-	static struct snd_pcm_ops ops = {
-		.open      = oxfw_open,
-		.close     = oxfw_close,
-		.ioctl     = snd_pcm_lib_ioctl,
-		.hw_params = oxfw_hw_params,
-		.hw_free   = oxfw_hw_free,
-		.prepare   = oxfw_prepare,
-		.trigger   = oxfw_trigger,
-		.pointer   = oxfw_pointer,
-		.page      = snd_pcm_lib_get_vmalloc_page,
-		.mmap      = snd_pcm_lib_mmap_vmalloc,
-	};
-	struct snd_pcm *pcm;
-	int err;
-
-	err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm);
-	if (err < 0)
-		return err;
-	pcm->private_data = oxfw;
-	strcpy(pcm->name, oxfw->device_info->short_name);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
-	return 0;
-}
-
 enum control_action { CTL_READ, CTL_WRITE };
 enum control_attribute {
 	CTL_MIN		= 0x02,
@@ -579,7 +349,7 @@ static int oxfw_probe(struct fw_unit *unit,
 		 dev_name(&unit->device), 100 << fw_dev->max_speed);
 	strcpy(card->mixername, "OXFW");
 
-	err = oxfw_create_pcm(oxfw);
+	err = snd_oxfw_create_pcm(oxfw);
 	if (err < 0)
 		goto error;
 
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index e5836e0..1196db8 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -54,3 +54,7 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
+
+int firewave_constraints(struct snd_pcm_runtime *runtime);
+int lacie_speakers_constraints(struct snd_pcm_runtime *runtime);
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 19/29] ALSA: oxfw: Split control functionality to a new file
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (17 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 18/29] ALSA: oxfw: Split PCM functionality to a new file Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 20/29] ALSA: oxfw: Change the way to name card Takashi Sakamoto
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This is a help for works in followed patches.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/Makefile       |   2 +-
 sound/firewire/oxfw/oxfw-control.c | 283 +++++++++++++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw.c         | 277 +-----------------------------------
 sound/firewire/oxfw/oxfw.h         |   2 +
 4 files changed, 287 insertions(+), 277 deletions(-)
 create mode 100644 sound/firewire/oxfw/oxfw-control.c

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 7fb4d09..0cf48fd 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,2 +1,2 @@
-snd-oxfw-objs := oxfw-stream.o oxfw-pcm.o oxfw.o
+snd-oxfw-objs := oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-control.c
new file mode 100644
index 0000000..02a1cb9
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-control.c
@@ -0,0 +1,283 @@
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+enum control_action { CTL_READ, CTL_WRITE };
+enum control_attribute {
+	CTL_MIN		= 0x02,
+	CTL_MAX		= 0x03,
+	CTL_CURRENT	= 0x10,
+};
+
+static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
+			     enum control_action action)
+{
+	u8 *buf;
+	u8 response_ok;
+	int err;
+
+	buf = kmalloc(11, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (action == CTL_READ) {
+		buf[0] = 0x01;		/* AV/C, STATUS */
+		response_ok = 0x0c;	/*       STABLE */
+	} else {
+		buf[0] = 0x00;		/* AV/C, CONTROL */
+		response_ok = 0x09;	/*       ACCEPTED */
+	}
+	buf[1] = 0x08;			/* audio unit 0 */
+	buf[2] = 0xb8;			/* FUNCTION BLOCK */
+	buf[3] = 0x81;			/* function block type: feature */
+	buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
+	buf[5] = 0x10;			/* control attribute: current */
+	buf[6] = 0x02;			/* selector length */
+	buf[7] = 0x00;			/* audio channel number */
+	buf[8] = 0x01;			/* control selector: mute */
+	buf[9] = 0x01;			/* control data length */
+	if (action == CTL_READ)
+		buf[10] = 0xff;
+	else
+		buf[10] = *value ? 0x70 : 0x60;
+
+	err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
+	if (err < 0)
+		goto error;
+	if (err < 11) {
+		dev_err(&oxfw->unit->device, "short FCP response\n");
+		err = -EIO;
+		goto error;
+	}
+	if (buf[0] != response_ok) {
+		dev_err(&oxfw->unit->device, "mute command failed\n");
+		err = -EIO;
+		goto error;
+	}
+	if (action == CTL_READ)
+		*value = buf[10] == 0x70;
+
+	err = 0;
+
+error:
+	kfree(buf);
+
+	return err;
+}
+
+static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
+			       unsigned int channel,
+			       enum control_attribute attribute,
+			       enum control_action action)
+{
+	u8 *buf;
+	u8 response_ok;
+	int err;
+
+	buf = kmalloc(12, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (action == CTL_READ) {
+		buf[0] = 0x01;		/* AV/C, STATUS */
+		response_ok = 0x0c;	/*       STABLE */
+	} else {
+		buf[0] = 0x00;		/* AV/C, CONTROL */
+		response_ok = 0x09;	/*       ACCEPTED */
+	}
+	buf[1] = 0x08;			/* audio unit 0 */
+	buf[2] = 0xb8;			/* FUNCTION BLOCK */
+	buf[3] = 0x81;			/* function block type: feature */
+	buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
+	buf[5] = attribute;		/* control attribute */
+	buf[6] = 0x02;			/* selector length */
+	buf[7] = channel;		/* audio channel number */
+	buf[8] = 0x02;			/* control selector: volume */
+	buf[9] = 0x02;			/* control data length */
+	if (action == CTL_READ) {
+		buf[10] = 0xff;
+		buf[11] = 0xff;
+	} else {
+		buf[10] = *value >> 8;
+		buf[11] = *value;
+	}
+
+	err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
+	if (err < 0)
+		goto error;
+	if (err < 12) {
+		dev_err(&oxfw->unit->device, "short FCP response\n");
+		err = -EIO;
+		goto error;
+	}
+	if (buf[0] != response_ok) {
+		dev_err(&oxfw->unit->device, "volume command failed\n");
+		err = -EIO;
+		goto error;
+	}
+	if (action == CTL_READ)
+		*value = (buf[10] << 8) | buf[11];
+
+	err = 0;
+
+error:
+	kfree(buf);
+
+	return err;
+}
+
+static int oxfw_mute_get(struct snd_kcontrol *control,
+			 struct snd_ctl_elem_value *value)
+{
+	struct snd_oxfw *oxfw = control->private_data;
+
+	value->value.integer.value[0] = !oxfw->mute;
+
+	return 0;
+}
+
+static int oxfw_mute_put(struct snd_kcontrol *control,
+			 struct snd_ctl_elem_value *value)
+{
+	struct snd_oxfw *oxfw = control->private_data;
+	bool mute;
+	int err;
+
+	mute = !value->value.integer.value[0];
+
+	if (mute == oxfw->mute)
+		return 0;
+
+	err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
+	if (err < 0)
+		return err;
+	oxfw->mute = mute;
+
+	return 1;
+}
+
+static int oxfw_volume_info(struct snd_kcontrol *control,
+			    struct snd_ctl_elem_info *info)
+{
+	struct snd_oxfw *oxfw = control->private_data;
+
+	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	info->count = oxfw->device_info->mixer_channels;
+	info->value.integer.min = oxfw->volume_min;
+	info->value.integer.max = oxfw->volume_max;
+
+	return 0;
+}
+
+static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
+
+static int oxfw_volume_get(struct snd_kcontrol *control,
+			   struct snd_ctl_elem_value *value)
+{
+	struct snd_oxfw *oxfw = control->private_data;
+	unsigned int i;
+
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+		value->value.integer.value[channel_map[i]] = oxfw->volume[i];
+
+	return 0;
+}
+
+static int oxfw_volume_put(struct snd_kcontrol *control,
+			   struct snd_ctl_elem_value *value)
+{
+	struct snd_oxfw *oxfw = control->private_data;
+	unsigned int i, changed_channels;
+	bool equal_values = true;
+	s16 volume;
+	int err;
+
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+		if (value->value.integer.value[i] < oxfw->volume_min ||
+		    value->value.integer.value[i] > oxfw->volume_max)
+			return -EINVAL;
+		if (value->value.integer.value[i] !=
+		    value->value.integer.value[0])
+			equal_values = false;
+	}
+
+	changed_channels = 0;
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+		if (value->value.integer.value[channel_map[i]] !=
+							oxfw->volume[i])
+			changed_channels |= 1 << (i + 1);
+
+	if (equal_values && changed_channels != 0)
+		changed_channels = 1 << 0;
+
+	for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
+		volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
+		if (changed_channels & (1 << i)) {
+			err = oxfw_volume_command(oxfw, &volume, i,
+						   CTL_CURRENT, CTL_WRITE);
+			if (err < 0)
+				return err;
+		}
+		if (i > 0)
+			oxfw->volume[i - 1] = volume;
+	}
+
+	return changed_channels != 0;
+}
+
+int snd_oxfw_create_mixer(struct snd_oxfw *oxfw)
+{
+	static const struct snd_kcontrol_new controls[] = {
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "PCM Playback Switch",
+			.info = snd_ctl_boolean_mono_info,
+			.get = oxfw_mute_get,
+			.put = oxfw_mute_put,
+		},
+		{
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.name = "PCM Playback Volume",
+			.info = oxfw_volume_info,
+			.get = oxfw_volume_get,
+			.put = oxfw_volume_put,
+		},
+	};
+	unsigned int i, first_ch;
+	int err;
+
+	err = oxfw_volume_command(oxfw, &oxfw->volume_min,
+				   0, CTL_MIN, CTL_READ);
+	if (err < 0)
+		return err;
+	err = oxfw_volume_command(oxfw, &oxfw->volume_max,
+				   0, CTL_MAX, CTL_READ);
+	if (err < 0)
+		return err;
+
+	err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
+	if (err < 0)
+		return err;
+
+	first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
+	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+		err = oxfw_volume_command(oxfw, &oxfw->volume[i],
+					   first_ch + i, CTL_CURRENT, CTL_READ);
+		if (err < 0)
+			return err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(controls); ++i) {
+		err = snd_ctl_add(oxfw->card,
+				  snd_ctl_new1(&controls[i], oxfw));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 2210669..236a55e 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -25,281 +25,6 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("snd-firewire-speakers");
 
-enum control_action { CTL_READ, CTL_WRITE };
-enum control_attribute {
-	CTL_MIN		= 0x02,
-	CTL_MAX		= 0x03,
-	CTL_CURRENT	= 0x10,
-};
-
-static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
-			      enum control_action action)
-{
-	u8 *buf;
-	u8 response_ok;
-	int err;
-
-	buf = kmalloc(11, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	if (action == CTL_READ) {
-		buf[0] = 0x01;		/* AV/C, STATUS */
-		response_ok = 0x0c;	/*       STABLE */
-	} else {
-		buf[0] = 0x00;		/* AV/C, CONTROL */
-		response_ok = 0x09;	/*       ACCEPTED */
-	}
-	buf[1] = 0x08;			/* audio unit 0 */
-	buf[2] = 0xb8;			/* FUNCTION BLOCK */
-	buf[3] = 0x81;			/* function block type: feature */
-	buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
-	buf[5] = 0x10;			/* control attribute: current */
-	buf[6] = 0x02;			/* selector length */
-	buf[7] = 0x00;			/* audio channel number */
-	buf[8] = 0x01;			/* control selector: mute */
-	buf[9] = 0x01;			/* control data length */
-	if (action == CTL_READ)
-		buf[10] = 0xff;
-	else
-		buf[10] = *value ? 0x70 : 0x60;
-
-	err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
-	if (err < 0)
-		goto error;
-	if (err < 11) {
-		dev_err(&oxfw->unit->device, "short FCP response\n");
-		err = -EIO;
-		goto error;
-	}
-	if (buf[0] != response_ok) {
-		dev_err(&oxfw->unit->device, "mute command failed\n");
-		err = -EIO;
-		goto error;
-	}
-	if (action == CTL_READ)
-		*value = buf[10] == 0x70;
-
-	err = 0;
-
-error:
-	kfree(buf);
-
-	return err;
-}
-
-static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
-				unsigned int channel,
-				enum control_attribute attribute,
-				enum control_action action)
-{
-	u8 *buf;
-	u8 response_ok;
-	int err;
-
-	buf = kmalloc(12, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	if (action == CTL_READ) {
-		buf[0] = 0x01;		/* AV/C, STATUS */
-		response_ok = 0x0c;	/*       STABLE */
-	} else {
-		buf[0] = 0x00;		/* AV/C, CONTROL */
-		response_ok = 0x09;	/*       ACCEPTED */
-	}
-	buf[1] = 0x08;			/* audio unit 0 */
-	buf[2] = 0xb8;			/* FUNCTION BLOCK */
-	buf[3] = 0x81;			/* function block type: feature */
-	buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
-	buf[5] = attribute;		/* control attribute */
-	buf[6] = 0x02;			/* selector length */
-	buf[7] = channel;		/* audio channel number */
-	buf[8] = 0x02;			/* control selector: volume */
-	buf[9] = 0x02;			/* control data length */
-	if (action == CTL_READ) {
-		buf[10] = 0xff;
-		buf[11] = 0xff;
-	} else {
-		buf[10] = *value >> 8;
-		buf[11] = *value;
-	}
-
-	err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
-	if (err < 0)
-		goto error;
-	if (err < 12) {
-		dev_err(&oxfw->unit->device, "short FCP response\n");
-		err = -EIO;
-		goto error;
-	}
-	if (buf[0] != response_ok) {
-		dev_err(&oxfw->unit->device, "volume command failed\n");
-		err = -EIO;
-		goto error;
-	}
-	if (action == CTL_READ)
-		*value = (buf[10] << 8) | buf[11];
-
-	err = 0;
-
-error:
-	kfree(buf);
-
-	return err;
-}
-
-static int oxfw_mute_get(struct snd_kcontrol *control,
-			  struct snd_ctl_elem_value *value)
-{
-	struct snd_oxfw *oxfw = control->private_data;
-
-	value->value.integer.value[0] = !oxfw->mute;
-
-	return 0;
-}
-
-static int oxfw_mute_put(struct snd_kcontrol *control,
-			  struct snd_ctl_elem_value *value)
-{
-	struct snd_oxfw *oxfw = control->private_data;
-	bool mute;
-	int err;
-
-	mute = !value->value.integer.value[0];
-
-	if (mute == oxfw->mute)
-		return 0;
-
-	err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
-	if (err < 0)
-		return err;
-	oxfw->mute = mute;
-
-	return 1;
-}
-
-static int oxfw_volume_info(struct snd_kcontrol *control,
-			     struct snd_ctl_elem_info *info)
-{
-	struct snd_oxfw *oxfw = control->private_data;
-
-	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	info->count = oxfw->device_info->mixer_channels;
-	info->value.integer.min = oxfw->volume_min;
-	info->value.integer.max = oxfw->volume_max;
-
-	return 0;
-}
-
-static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
-
-static int oxfw_volume_get(struct snd_kcontrol *control,
-			    struct snd_ctl_elem_value *value)
-{
-	struct snd_oxfw *oxfw = control->private_data;
-	unsigned int i;
-
-	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
-		value->value.integer.value[channel_map[i]] = oxfw->volume[i];
-
-	return 0;
-}
-
-static int oxfw_volume_put(struct snd_kcontrol *control,
-			  struct snd_ctl_elem_value *value)
-{
-	struct snd_oxfw *oxfw = control->private_data;
-	unsigned int i, changed_channels;
-	bool equal_values = true;
-	s16 volume;
-	int err;
-
-	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
-		if (value->value.integer.value[i] < oxfw->volume_min ||
-		    value->value.integer.value[i] > oxfw->volume_max)
-			return -EINVAL;
-		if (value->value.integer.value[i] !=
-		    value->value.integer.value[0])
-			equal_values = false;
-	}
-
-	changed_channels = 0;
-	for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
-		if (value->value.integer.value[channel_map[i]] !=
-							oxfw->volume[i])
-			changed_channels |= 1 << (i + 1);
-
-	if (equal_values && changed_channels != 0)
-		changed_channels = 1 << 0;
-
-	for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
-		volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
-		if (changed_channels & (1 << i)) {
-			err = oxfw_volume_command(oxfw, &volume, i,
-						   CTL_CURRENT, CTL_WRITE);
-			if (err < 0)
-				return err;
-		}
-		if (i > 0)
-			oxfw->volume[i - 1] = volume;
-	}
-
-	return changed_channels != 0;
-}
-
-static int oxfw_create_mixer(struct snd_oxfw *oxfw)
-{
-	static const struct snd_kcontrol_new controls[] = {
-		{
-			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-			.name = "PCM Playback Switch",
-			.info = snd_ctl_boolean_mono_info,
-			.get = oxfw_mute_get,
-			.put = oxfw_mute_put,
-		},
-		{
-			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-			.name = "PCM Playback Volume",
-			.info = oxfw_volume_info,
-			.get = oxfw_volume_get,
-			.put = oxfw_volume_put,
-		},
-	};
-	unsigned int i, first_ch;
-	int err;
-
-	err = oxfw_volume_command(oxfw, &oxfw->volume_min,
-				   0, CTL_MIN, CTL_READ);
-	if (err < 0)
-		return err;
-	err = oxfw_volume_command(oxfw, &oxfw->volume_max,
-				   0, CTL_MAX, CTL_READ);
-	if (err < 0)
-		return err;
-
-	err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
-	if (err < 0)
-		return err;
-
-	first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
-	for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
-		err = oxfw_volume_command(oxfw, &oxfw->volume[i],
-					   first_ch + i, CTL_CURRENT, CTL_READ);
-		if (err < 0)
-			return err;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(controls); ++i) {
-		err = snd_ctl_add(oxfw->card,
-				  snd_ctl_new1(&controls[i], oxfw));
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-
 static u32 oxfw_read_firmware_version(struct fw_unit *unit)
 {
 	__be32 data;
@@ -353,7 +78,7 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
-	err = oxfw_create_mixer(oxfw);
+	err = snd_oxfw_create_mixer(oxfw);
 	if (err < 0)
 		goto error;
 
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 1196db8..6164bf3 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -58,3 +58,5 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
 int firewave_constraints(struct snd_pcm_runtime *runtime);
 int lacie_speakers_constraints(struct snd_pcm_runtime *runtime);
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 20/29] ALSA: oxfw: Change the way to name card
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (18 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 19/29] ALSA: oxfw: Split control " Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 21/29] ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation Takashi Sakamoto
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

The 'struct snd_card' has 4 members for name. They're 'driver', 'shortname',
'longname' and 'mixername'. This commit names these 4 members according to
two members in model-specific structure and reduce rest of members in the
structure.

The reason that alias names are still used is that currently supported devices,
Griffin FireWave and LaCie Firewire Speaker have too long names for model and
vendor field in their config rom to be over allocated buffer for the names.

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

diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 236a55e..08c55be 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -25,14 +25,34 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("snd-firewire-speakers");
 
-static u32 oxfw_read_firmware_version(struct fw_unit *unit)
+static int name_card(struct snd_oxfw *oxfw)
 {
-	__be32 data;
+	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+	const char *d, *v, *m;
+	u32 firmware;
 	int err;
 
-	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-				 OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0);
-	return err >= 0 ? be32_to_cpu(data) : 0;
+	err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
+				 OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
+	if (err < 0)
+		goto end;
+	be32_to_cpus(&firmware);
+
+	d = oxfw->device_info->driver_name;
+	v = oxfw->device_info->vendor_name;
+	m = oxfw->device_info->driver_name;
+
+	strcpy(oxfw->card->driver, d);
+	strcpy(oxfw->card->shortname, m);
+	strcpy(oxfw->card->mixername, m);
+
+	snprintf(oxfw->card->longname, sizeof(oxfw->card->longname),
+		 "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
+		 v, m, firmware >> 20, firmware & 0xffff,
+		 fw_dev->config_rom[3], fw_dev->config_rom[4],
+		 dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed);
+end:
+	return err;
 }
 
 static void oxfw_card_free(struct snd_card *card)
@@ -45,10 +65,8 @@ static void oxfw_card_free(struct snd_card *card)
 static int oxfw_probe(struct fw_unit *unit,
 		       const struct ieee1394_device_id *id)
 {
-	struct fw_device *fw_dev = fw_parent_device(unit);
 	struct snd_card *card;
 	struct snd_oxfw *oxfw;
-	u32 firmware;
 	int err;
 
 	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
@@ -63,16 +81,9 @@ static int oxfw_probe(struct fw_unit *unit,
 	oxfw->unit = unit;
 	oxfw->device_info = (const struct device_info *)id->driver_data;
 
-	strcpy(card->driver, oxfw->device_info->driver_name);
-	strcpy(card->shortname, oxfw->device_info->short_name);
-	firmware = oxfw_read_firmware_version(unit);
-	snprintf(card->longname, sizeof(card->longname),
-		 "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
-		 oxfw->device_info->long_name,
-		 firmware >> 20, firmware & 0xffff,
-		 fw_dev->config_rom[3], fw_dev->config_rom[4],
-		 dev_name(&unit->device), 100 << fw_dev->max_speed);
-	strcpy(card->mixername, "OXFW");
+	err = name_card(oxfw);
+	if (err < 0)
+		goto error;
 
 	err = snd_oxfw_create_pcm(oxfw);
 	if (err < 0)
@@ -118,9 +129,8 @@ static void oxfw_remove(struct fw_unit *unit)
 }
 
 static const struct device_info griffin_firewave = {
+	.vendor_name = "Griffin",
 	.driver_name = "FireWave",
-	.short_name  = "FireWave",
-	.long_name   = "Griffin FireWave Surround",
 	.pcm_constraints = firewave_constraints,
 	.mixer_channels = 6,
 	.mute_fb_id   = 0x01,
@@ -128,9 +138,8 @@ static const struct device_info griffin_firewave = {
 };
 
 static const struct device_info lacie_speakers = {
+	.vendor_name = "LaCie",
 	.driver_name = "FWSpeakers",
-	.short_name  = "FireWire Speakers",
-	.long_name   = "LaCie FireWire Speakers",
 	.pcm_constraints = lacie_speakers_constraints,
 	.mixer_channels = 1,
 	.mute_fb_id   = 0x01,
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 6164bf3..b60e91d 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -27,9 +27,8 @@
 #include "../cmp.h"
 
 struct device_info {
+	const char *vendor_name;
 	const char *driver_name;
-	const char *short_name;
-	const char *long_name;
 	int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
 	unsigned int mixer_channels;
 	u8 mute_fb_id;
-- 
1.9.1

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

* [PATCH 21/29] ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (19 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 20/29] ALSA: oxfw: Change the way to name card Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 22/29] ALSA: oxfw: Change the way to make PCM rules/constraints Takashi Sakamoto
                   ` (10 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

OXFW970/971 may supports AV/C Stream Format Information Specification 1.1
Working Draft (Apr 2005, 1394TA). By using this command, drivers can get to know
stream formations which device supports.

This commit adds 'EXTENDED STREAM FORMAT INFORMATION' command. This command
has two subfunctions, 'SINGLE' and 'LIST'. Drivers can use 'SINGLE' subfunction
to know/set current formation of AMDTP stream, Drivers can use 'LIST'
subfunction to know an available formation of AMDTP stream in a certain sampling
rate.

But some devices don't implement the 'LIST' subfunction. So this commit uses
an assumption that 'if they don't implement it, they don't change stream
formation depending on current each sampling rate'. With this assumption, this
driver generates formations for such devices by:
 1.getting current formation by SINGLE subfunction
 2.getting supported sampling rates
 3.applying current formation for all of supported sampling rates

Followed commit implements a parser of this format information.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/Makefile       |   2 +-
 sound/firewire/oxfw/oxfw-command.c | 153 +++++++++++++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw.h         |  33 ++++++++
 3 files changed, 187 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/oxfw/oxfw-command.c

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 0cf48fd..b107134 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,2 +1,2 @@
-snd-oxfw-objs := oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o
+snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c
new file mode 100644
index 0000000..8c603c5
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -0,0 +1,153 @@
+/*
+ * oxfw_command.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+			  unsigned int pid, u8 *format, unsigned int len)
+{
+	u8 *buf;
+	int err;
+
+	buf = kmalloc(len + 10, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = 0x00;		/* CONTROL */
+	buf[1] = 0xff;		/* UNIT */
+	buf[2] = 0xbf;		/* EXTENDED STREAM FORMAT INFORMATION */
+	buf[3] = 0xc0;		/* SINGLE subfunction */
+	buf[4] = dir;		/* Plug Direction */
+	buf[5] = 0x00;		/* UNIT */
+	buf[6] = 0x00;		/* PCR (Isochronous Plug) */
+	buf[7] = 0xff & pid;	/* Plug ID */
+	buf[8] = 0xff;		/* Padding */
+	buf[9] = 0xff;		/* Support status in response */
+	memcpy(buf + 10, format, len);
+
+	/* do transaction and check buf[1-7] are the same against command */
+	err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7) | BIT(8));
+	if ((err > 0) && (err < len + 10))
+		err = -EIO;
+	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+		err = -ENOSYS;
+	else if (buf[0] == 0x0a) /* REJECTED */
+		err = -EINVAL;
+	else
+		err = 0;
+
+	kfree(buf);
+
+	return err;
+}
+
+int avc_stream_get_format(struct fw_unit *unit,
+			  enum avc_general_plug_dir dir, unsigned int pid,
+			  u8 *buf, unsigned int *len, unsigned int eid)
+{
+	unsigned int subfunc;
+	int err;
+
+	if (eid == 0xff)
+		subfunc = 0xc0;	/* SINGLE */
+	else
+		subfunc = 0xc1;	/* LIST */
+
+	buf[0] = 0x01;		/* STATUS */
+	buf[1] = 0xff;		/* UNIT */
+	buf[2] = 0xbf;		/* EXTENDED STREAM FORMAT INFORMATION */
+	buf[3] = subfunc;	/* SINGLE or LIST */
+	buf[4] = dir;		/* Plug Direction */
+	buf[5] = 0x00;		/* Unit */
+	buf[6] = 0x00;		/* PCR (Isochronous Plug) */
+	buf[7] = 0xff & pid;	/* Plug ID */
+	buf[8] = 0xff;		/* Padding */
+	buf[9] = 0xff;		/* support status in response */
+	buf[10] = 0xff & eid;	/* entry ID for LIST subfunction */
+	buf[11] = 0xff;		/* padding */
+
+	/* do transaction and check buf[1-7] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7));
+	if ((err > 0) && (err < 10))
+		err = -EIO;
+	else if (buf[0] == 0x08)	/* NOT IMPLEMENTED */
+		err = -ENOSYS;
+	else if (buf[0] == 0x0a)	/* REJECTED */
+		err = -EINVAL;
+	else if (buf[0] == 0x0b)	/* IN TRANSITION */
+		err = -EAGAIN;
+	/* LIST subfunction has entry ID */
+	else if ((subfunc == 0xc1) && (buf[10] != eid))
+		err = -EIO;
+	if (err < 0)
+		goto end;
+
+	/* keep just stream format information */
+	if (subfunc == 0xc0) {
+		memmove(buf, buf + 10, err - 10);
+		*len = err - 10;
+	} else {
+		memmove(buf, buf + 11, err - 11);
+		*len = err - 11;
+	}
+
+	err = 0;
+end:
+	return err;
+}
+
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+				enum avc_general_plug_dir dir,
+				unsigned short pid)
+{
+	unsigned int sfc;
+	u8 *buf;
+	int err;
+
+	for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+		if (amdtp_rate_table[sfc] == rate)
+			break;
+	}
+	if (sfc == CIP_SFC_COUNT)
+		return -EINVAL;
+
+	buf = kzalloc(8, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = 0x02;		/* SPECIFIC INQUIRY */
+	buf[1] = 0xff;		/* UNIT */
+	if (dir == AVC_GENERAL_PLUG_DIR_IN)
+		buf[2] = 0x19;	/* INPUT PLUG SIGNAL FORMAT */
+	else
+		buf[2] = 0x18;	/* OUTPUT PLUG SIGNAL FORMAT */
+	buf[3] = 0xff & pid;	/* plug id */
+	buf[4] = 0x90;		/* EOH_1, Form_1, FMT. AM824 */
+	buf[5] = 0x07 & sfc;	/* FDF-hi. AM824, frequency */
+	buf[6] = 0xff;		/* FDF-mid. AM824, SYT hi (not used) */
+	buf[7] = 0xff;		/* FDF-low. AM824, SYT lo (not used) */
+
+	/* do transaction and check buf[1-5] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+	if ((err > 0) && (err < 8))
+		err = -EIO;
+	else if (buf[0] == 0x08)	/* NOT IMPLEMENTED */
+		err = -ENOSYS;
+	if (err < 0)
+		goto end;
+
+	err = 0;
+end:
+	kfree(buf);
+	return err;
+}
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index b60e91d..583ee7b 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -48,6 +48,39 @@ struct snd_oxfw {
 	s16 volume_max;
 };
 
+/*
+ * AV/C Stream Format Information Specification 1.1 Working Draft
+ * (Apr 2005, 1394TA)
+ */
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+			  unsigned int pid, u8 *format, unsigned int len);
+int avc_stream_get_format(struct fw_unit *unit,
+			  enum avc_general_plug_dir dir, unsigned int pid,
+			  u8 *buf, unsigned int *len, unsigned int eid);
+static inline int
+avc_stream_get_format_single(struct fw_unit *unit,
+			     enum avc_general_plug_dir dir, unsigned int pid,
+			     u8 *buf, unsigned int *len)
+{
+	return avc_stream_get_format(unit, dir, pid, buf, len, 0xff);
+}
+static inline int
+avc_stream_get_format_list(struct fw_unit *unit,
+			   enum avc_general_plug_dir dir, unsigned int pid,
+			   u8 *buf, unsigned int *len,
+			   unsigned int eid)
+{
+	return avc_stream_get_format(unit, dir, pid, buf, len, eid);
+}
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+				enum avc_general_plug_dir dir,
+				unsigned short pid);
+
 int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw);
 int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 22/29] ALSA: oxfw: Change the way to make PCM rules/constraints
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (20 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 21/29] ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 23/29] ALSA: oxfw: Add proc interface for debugging purpose Takashi Sakamoto
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In previous commit, this driver can get to know stream formations at
each supported sampling rates. This commit uses it to make PCM
rules/constraints and obsoletes hard-coded rules/constraints.

For this purpose, this commit adds 'struct snd_oxfw_stream_formation' and
snd_oxfw_stream_parse_format() to parse data channel formation of data
block.

According to datasheet of OXFW970/971, they support 32.0kHz to 196.0kHz.

As long as developers investigate, some devices are confirmed to have
several formats for the same sampling rate.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/oxfw-pcm.c    | 197 ++++++++++++++++------------
 sound/firewire/oxfw/oxfw-stream.c | 268 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw.c        |  42 +++---
 sound/firewire/oxfw/oxfw.h        |  21 ++-
 4 files changed, 426 insertions(+), 102 deletions(-)

diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 1fd76ce..727d281 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -7,117 +7,152 @@
 
 #include "oxfw.h"
 
-static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
-				    struct snd_pcm_hw_rule *rule)
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+			struct snd_pcm_hw_rule *rule)
 {
-	static unsigned int stereo_rates[] = { 48000, 96000 };
-	struct snd_interval *channels =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	struct snd_interval *rate =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-
-	/* two channels work only at 48/96 kHz */
-	if (snd_interval_max(channels) < 6)
-		return snd_interval_list(rate, 2, stereo_rates, 0);
-	return 0;
+	u8 **formats = rule->private;
+	struct snd_interval *r =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	const struct snd_interval *c =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval t = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	struct snd_oxfw_stream_formation formation;
+	unsigned int i, err;
+
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		if (formats[i] == NULL)
+			continue;
+
+		err = snd_oxfw_stream_parse_format(formats[i], &formation);
+		if (err < 0)
+			continue;
+		if (!snd_interval_test(c, formation.pcm))
+			continue;
+
+		t.min = min(t.min, formation.rate);
+		t.max = max(t.max, formation.rate);
+
+	}
+	return snd_interval_refine(r, &t);
 }
 
-static int firewave_channels_constraint(struct snd_pcm_hw_params *params,
-					struct snd_pcm_hw_rule *rule)
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+			    struct snd_pcm_hw_rule *rule)
 {
-	static const struct snd_interval all_channels = { .min = 6, .max = 6 };
-	struct snd_interval *rate =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *channels =
-			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-
-	/* 32/44.1 kHz work only with all six channels */
-	if (snd_interval_max(rate) < 48000)
-		return snd_interval_refine(channels, &all_channels);
-	return 0;
+	u8 **formats = rule->private;
+	struct snd_interval *c =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	const struct snd_interval *r =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_oxfw_stream_formation formation;
+	unsigned int i, j, err;
+	unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
+
+	count = 0;
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		if (formats[i] == NULL)
+			break;
+
+		err = snd_oxfw_stream_parse_format(formats[i], &formation);
+		if (err < 0)
+			continue;
+		if (!snd_interval_test(r, formation.rate))
+			continue;
+		if (list[count] == formation.pcm)
+			continue;
+
+		for (j = 0; j < ARRAY_SIZE(list); j++) {
+			if (list[j] == formation.pcm)
+				break;
+		}
+		if (j == ARRAY_SIZE(list)) {
+			list[count] = formation.pcm;
+			if (++count == ARRAY_SIZE(list))
+				break;
+		}
+	}
+
+	return snd_interval_list(c, count, list, 0);
 }
 
-int firewave_constraints(struct snd_pcm_runtime *runtime)
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
 {
-	static unsigned int channels_list[] = { 2, 6 };
-	static struct snd_pcm_hw_constraint_list channels_list_constraint = {
-		.count = 2,
-		.list = channels_list,
-	};
-	int err;
+	struct snd_oxfw_stream_formation formation;
+	unsigned int i, err;
 
-	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
-			    SNDRV_PCM_RATE_44100 |
-			    SNDRV_PCM_RATE_48000 |
-			    SNDRV_PCM_RATE_96000;
-	runtime->hw.channels_max = 6;
+	hw->channels_min = UINT_MAX;
+	hw->channels_max = 0;
 
-	err = snd_pcm_hw_constraint_list(runtime, 0,
-					 SNDRV_PCM_HW_PARAM_CHANNELS,
-					 &channels_list_constraint);
-	if (err < 0)
-		return err;
-	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-				  firewave_rate_constraint, NULL,
-				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-	if (err < 0)
-		return err;
-	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-				  firewave_channels_constraint, NULL,
-				  SNDRV_PCM_HW_PARAM_RATE, -1);
-	if (err < 0)
-		return err;
+	hw->rate_min = UINT_MAX;
+	hw->rate_max = 0;
+	hw->rates = 0;
 
-	return 0;
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		if (formats[i] == NULL)
+			break;
+
+		err = snd_oxfw_stream_parse_format(formats[i], &formation);
+		if (err < 0)
+			continue;
+
+		hw->channels_min = min(hw->channels_min, formation.pcm);
+		hw->channels_max = max(hw->channels_max, formation.pcm);
+
+		hw->rate_min = min(hw->rate_min, formation.rate);
+		hw->rate_max = max(hw->rate_max, formation.rate);
+		hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
+	}
 }
 
-int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
 {
-	runtime->hw.rates = SNDRV_PCM_RATE_32000 |
-			    SNDRV_PCM_RATE_44100 |
-			    SNDRV_PCM_RATE_48000 |
-			    SNDRV_PCM_RATE_88200 |
-			    SNDRV_PCM_RATE_96000;
+	hw->periods_min = 2;		/* SNDRV_PCM_INFO_BATCH */
+	hw->periods_max = UINT_MAX;
 
-	return 0;
+	hw->period_bytes_min = 4 * hw->channels_max;	/* bytes for a frame */
+
+	/* Just to prevent from allocating much pages. */
+	hw->period_bytes_max = hw->period_bytes_min * 2048;
+	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
 }
 
 static int pcm_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,
-		.channels_min = 2,
-		.channels_max = 2,
-		.buffer_bytes_max = 4 * 1024 * 1024,
-		.period_bytes_min = 1,
-		.period_bytes_max = UINT_MAX,
-		.periods_min = 1,
-		.periods_max = UINT_MAX,
-	};
 	struct snd_oxfw *oxfw = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	bool used;
+	u8 **formats;
 	int err;
 
-	err = cmp_connection_check_used(&oxfw->in_conn, &used);
-	if ((err < 0) || used)
-		goto end;
+	formats = oxfw->rx_stream_formats;
+
+	runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+			   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			   SNDRV_PCM_INFO_INTERLEAVED |
+			   SNDRV_PCM_INFO_MMAP |
+			   SNDRV_PCM_INFO_MMAP_VALID;
 
-	runtime->hw = hardware;
+	limit_channels_and_rates(&runtime->hw, formats);
+	limit_period_and_buffer(&runtime->hw);
 
-	err = oxfw->device_info->pcm_constraints(runtime);
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  hw_rule_channels, formats,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
 	if (err < 0)
 		goto end;
-	err = snd_pcm_limit_hw_rates(runtime);
+
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  hw_rule_rate, formats,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 	if (err < 0)
 		goto end;
 
 	err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime);
+	if (err < 0)
+		goto end;
+
+	snd_pcm_set_sync(substream);
 end:
 	return err;
 }
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index c262b56..f0b2c9c 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -8,6 +8,35 @@
 
 #include "oxfw.h"
 
+#define AVC_GENERIC_FRAME_MAXIMUM_BYTES	512
+
+/*
+ * According to datasheet of Oxford Semiconductor:
+ *  OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
+ *  OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
+ */
+static const unsigned int oxfw_rate_table[] = {
+	[0] = 32000,
+	[1] = 44100,
+	[2] = 48000,
+	[3] = 88200,
+	[4] = 96000,
+	[5] = 192000,
+};
+
+/*
+ * See Table 5.7 – Sampling frequency for Multi-bit Audio
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ */
+static const unsigned int avc_stream_rate_table[] = {
+	[0] = 0x02,
+	[1] = 0x03,
+	[2] = 0x04,
+	[3] = 0x0a,
+	[4] = 0x05,
+	[5] = 0x07,
+};
+
 int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw)
 {
 	int err;
@@ -90,3 +119,242 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw)
 		amdtp_stream_update(&oxfw->rx_stream);
 	}
 }
+
+/*
+ * See Table 6.16 - AM824 Stream Format
+ *     Figure 6.19 - format_information field for AM824 Compound
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+int snd_oxfw_stream_parse_format(u8 *format,
+				 struct snd_oxfw_stream_formation *formation)
+{
+	unsigned int i, e, channels, type;
+
+	memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
+
+	/*
+	 * this module can support a hierarchy combination that:
+	 *  Root:	Audio and Music (0x90)
+	 *  Level 1:	AM824 Compound  (0x40)
+	 */
+	if ((format[0] != 0x90) || (format[1] != 0x40))
+		return -ENOSYS;
+
+	/* check the sampling rate */
+	for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
+		if (format[2] == avc_stream_rate_table[i])
+			break;
+	}
+	if (i == ARRAY_SIZE(avc_stream_rate_table))
+		return -ENOSYS;
+
+	formation->rate = oxfw_rate_table[i];
+
+	for (e = 0; e < format[4]; e++) {
+		channels = format[5 + e * 2];
+		type = format[6 + e * 2];
+
+		switch (type) {
+		/* IEC 60958 Conformant, currently handled as MBLA */
+		case 0x00:
+		/* Multi Bit Linear Audio (Raw) */
+		case 0x06:
+			formation->pcm += channels;
+			break;
+		/* MIDI Conformant */
+		case 0x0d:
+			formation->midi = channels;
+			break;
+		/* IEC 61937-3 to 7 */
+		case 0x01:
+		case 0x02:
+		case 0x03:
+		case 0x04:
+		case 0x05:
+		/* Multi Bit Linear Audio */
+		case 0x07:	/* DVD-Audio */
+		case 0x0c:	/* High Precision */
+		/* One Bit Audio */
+		case 0x08:	/* (Plain) Raw */
+		case 0x09:	/* (Plain) SACD */
+		case 0x0a:	/* (Encoded) Raw */
+		case 0x0b:	/* (Encoded) SACD */
+		/* SMPTE Time-Code conformant */
+		case 0x0e:
+		/* Sample Count */
+		case 0x0f:
+		/* Anciliary Data */
+		case 0x10:
+		/* Synchronization Stream (Stereo Raw audio) */
+		case 0x40:
+		/* Don't care */
+		case 0xff:
+		default:
+			return -ENOSYS;	/* not supported */
+		}
+	}
+
+	if (formation->pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
+	    formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+		return -ENOSYS;
+
+	return 0;
+}
+
+static int
+assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
+		      unsigned int pid, u8 *buf, unsigned int *len,
+		      u8 **formats)
+{
+	struct snd_oxfw_stream_formation formation;
+	unsigned int i, eid;
+	int err;
+
+	/* get format at current sampling rate */
+	err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+	if (err < 0) {
+		dev_err(&oxfw->unit->device,
+		"fail to get current stream format for isoc %s plug %d:%d\n",
+			(dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+			pid, err);
+		goto end;
+	}
+
+	/* parse and set stream format */
+	eid = 0;
+	err = snd_oxfw_stream_parse_format(buf, &formation);
+	if (err < 0)
+		goto end;
+
+	formats[eid] = kmalloc(*len, GFP_KERNEL);
+	if (formats[eid] == NULL) {
+		err = -ENOMEM;
+		goto end;
+	}
+	memcpy(formats[eid], buf, *len);
+
+	/* apply the format for each available sampling rate */
+	for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
+		if (formation.rate == oxfw_rate_table[i])
+			continue;
+
+		err = avc_general_inquiry_sig_fmt(oxfw->unit,
+						  oxfw_rate_table[i],
+						  dir, pid);
+		if (err < 0)
+			continue;
+
+		eid++;
+		formats[eid] = kmalloc(*len, GFP_KERNEL);
+		if (formats[eid] == NULL) {
+			err = -ENOMEM;
+			goto end;
+		}
+		memcpy(formats[eid], buf, *len);
+		formats[eid][2] = avc_stream_rate_table[i];
+	}
+
+	err = 0;
+	oxfw->assumed = true;
+end:
+	return err;
+}
+
+static int fill_stream_formats(struct snd_oxfw *oxfw,
+			       enum avc_general_plug_dir dir,
+			       unsigned short pid)
+{
+	u8 *buf, **formats;
+	unsigned int len, eid = 0;
+	struct snd_oxfw_stream_formation dummy;
+	int err;
+
+	buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	formats = oxfw->rx_stream_formats;
+
+	/* get first entry */
+	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+	err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
+	if (err == -ENOSYS) {
+		/* LIST subfunction is not implemented */
+		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+		err = assume_stream_formats(oxfw, dir, pid, buf, &len,
+					    formats);
+		goto end;
+	} else if (err < 0) {
+		dev_err(&oxfw->unit->device,
+			"fail to get stream format %d for isoc %s plug %d:%d\n",
+			eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+			pid, err);
+		goto end;
+	}
+
+	/* LIST subfunction is implemented */
+	while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
+		/* The format is too short. */
+		if (len < 3) {
+			err = -EIO;
+			break;
+		}
+
+		/* parse and set stream format */
+		err = snd_oxfw_stream_parse_format(buf, &dummy);
+		if (err < 0)
+			break;
+
+		formats[eid] = kmalloc(len, GFP_KERNEL);
+		if (formats[eid] == NULL) {
+			err = -ENOMEM;
+			break;
+		}
+		memcpy(formats[eid], buf, len);
+
+		/* get next entry */
+		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+		err = avc_stream_get_format_list(oxfw->unit, dir, 0,
+						 buf, &len, ++eid);
+		/* No entries remained. */
+		if (err == -EINVAL) {
+			err = 0;
+			break;
+		} else if (err < 0) {
+			dev_err(&oxfw->unit->device,
+			"fail to get stream format %d for isoc %s plug %d:%d\n",
+				eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
+									"out",
+				pid, err);
+			break;
+		}
+	}
+end:
+	kfree(buf);
+	return err;
+}
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
+{
+	u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+	int err;
+
+	/* the number of plugs for isoc in/out, ext in/out  */
+	err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
+	if (err < 0) {
+		dev_err(&oxfw->unit->device,
+		"fail to get info for isoc/external in/out plugs: %d\n",
+			err);
+		goto end;
+	} else if (plugs[0] == 0) {
+		err = -ENOSYS;
+		goto end;
+	}
+
+	/* use iPCR[0] if exists */
+	if (plugs[0] > 0)
+		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+	return err;
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 08c55be..88ab913 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -25,6 +25,22 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("snd-firewire-speakers");
 
+static const struct device_info griffin_firewave = {
+	.vendor_name = "Griffin",
+	.driver_name = "FireWave",
+	.mixer_channels = 6,
+	.mute_fb_id   = 0x01,
+	.volume_fb_id = 0x02,
+};
+
+static const struct device_info lacie_speakers = {
+	.vendor_name = "LaCie",
+	.driver_name = "FWSpeakers",
+	.mixer_channels = 1,
+	.mute_fb_id   = 0x01,
+	.volume_fb_id = 0x01,
+};
+
 static int name_card(struct snd_oxfw *oxfw)
 {
 	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@@ -58,6 +74,10 @@ end:
 static void oxfw_card_free(struct snd_card *card)
 {
 	struct snd_oxfw *oxfw = card->private_data;
+	unsigned int i;
+
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++)
+		kfree(oxfw->rx_stream_formats[i]);
 
 	mutex_destroy(&oxfw->mutex);
 }
@@ -81,6 +101,10 @@ static int oxfw_probe(struct fw_unit *unit,
 	oxfw->unit = unit;
 	oxfw->device_info = (const struct device_info *)id->driver_data;
 
+	err = snd_oxfw_stream_discover(oxfw);
+	if (err < 0)
+		goto error;
+
 	err = name_card(oxfw);
 	if (err < 0)
 		goto error;
@@ -128,24 +152,6 @@ static void oxfw_remove(struct fw_unit *unit)
 	snd_card_free_when_closed(oxfw->card);
 }
 
-static const struct device_info griffin_firewave = {
-	.vendor_name = "Griffin",
-	.driver_name = "FireWave",
-	.pcm_constraints = firewave_constraints,
-	.mixer_channels = 6,
-	.mute_fb_id   = 0x01,
-	.volume_fb_id = 0x02,
-};
-
-static const struct device_info lacie_speakers = {
-	.vendor_name = "LaCie",
-	.driver_name = "FWSpeakers",
-	.pcm_constraints = lacie_speakers_constraints,
-	.mixer_channels = 1,
-	.mute_fb_id   = 0x01,
-	.volume_fb_id = 0x01,
-};
-
 static const struct ieee1394_device_id oxfw_id_table[] = {
 	{
 		.match_flags  = IEEE1394_MATCH_VENDOR_ID |
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 583ee7b..c4c1acd 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -29,19 +29,24 @@
 struct device_info {
 	const char *vendor_name;
 	const char *driver_name;
-	int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
 	unsigned int mixer_channels;
 	u8 mute_fb_id;
 	u8 volume_fb_id;
 };
 
+/* This is an arbitrary number for convinience. */
+#define	SND_OXFW_STREAM_FORMAT_ENTRIES	10
 struct snd_oxfw {
 	struct snd_card *card;
 	struct fw_unit *unit;
 	const struct device_info *device_info;
 	struct mutex mutex;
+
+	u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+	bool assumed;
 	struct cmp_connection in_conn;
 	struct amdtp_stream rx_stream;
+
 	bool mute;
 	s16 volume[6];
 	s16 volume_min;
@@ -87,8 +92,18 @@ void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
 
-int firewave_constraints(struct snd_pcm_runtime *runtime);
-int lacie_speakers_constraints(struct snd_pcm_runtime *runtime);
+struct snd_oxfw_stream_formation {
+	unsigned int rate;
+	unsigned int pcm;
+	unsigned int midi;
+};
+int snd_oxfw_stream_parse_format(u8 *format,
+				 struct snd_oxfw_stream_formation *formation);
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+				enum avc_general_plug_dir dir,
+				struct snd_oxfw_stream_formation *formation);
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
+
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
 
 int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
-- 
1.9.1

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

* [PATCH 23/29] ALSA: oxfw: Add proc interface for debugging purpose
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (21 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 22/29] ALSA: oxfw: Change the way to make PCM rules/constraints Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 24/29] ALSA: oxfw: Change the way to start stream Takashi Sakamoto
                   ` (8 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds proc interface to get information about stream
formation. This commit also adds snd_oxfw_stream_get_current_formation()
to get current stream formation.

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

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index b107134..e9297c6 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,2 +1,3 @@
-snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o
+snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
+		 oxfw-proc.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c
new file mode 100644
index 0000000..18e0305
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -0,0 +1,84 @@
+/*
+ * oxfw_proc.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./oxfw.h"
+
+static void proc_read_formation(struct snd_info_entry *entry,
+				struct snd_info_buffer *buffer)
+{
+	struct snd_oxfw *oxfw = entry->private_data;
+	struct snd_oxfw_stream_formation formation, curr;
+	u8 *format;
+	char flag;
+	unsigned int i, err;
+
+	/* Show input. */
+	err = snd_oxfw_stream_get_current_formation(oxfw,
+						    AVC_GENERAL_PLUG_DIR_IN,
+						    &curr);
+	if (err < 0)
+		return;
+
+	snd_iprintf(buffer, "Input Stream to device:\n");
+	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		format = oxfw->rx_stream_formats[i];
+		if (format == NULL)
+			continue;
+
+		err = snd_oxfw_stream_parse_format(format, &formation);
+		if (err < 0)
+			continue;
+
+		if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+			flag = '*';
+		else
+			flag = ' ';
+
+		snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+			    formation.rate, formation.pcm, formation.midi);
+	}
+
+}
+
+static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
+		     const char *name,
+		     void (*op)(struct snd_info_entry *e,
+				struct snd_info_buffer *b))
+{
+	struct snd_info_entry *entry;
+
+	entry = snd_info_create_card_entry(oxfw->card, name, root);
+	if (entry == NULL)
+		return;
+
+	snd_info_set_text_ops(entry, oxfw, op);
+	if (snd_info_register(entry) < 0)
+		snd_info_free_entry(entry);
+}
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
+{
+	struct snd_info_entry *root;
+
+	/*
+	 * All nodes are automatically removed at snd_card_disconnect(),
+	 * by following to link list.
+	 */
+	root = snd_info_create_card_entry(oxfw->card, "firewire",
+					  oxfw->card->proc_root);
+	if (root == NULL)
+		return;
+	root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	if (snd_info_register(root) < 0) {
+		snd_info_free_entry(root);
+		return;
+	}
+
+	add_node(oxfw, root, "formation", proc_read_formation);
+}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index f0b2c9c..dd905fe 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -120,6 +120,33 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw)
 	}
 }
 
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+				enum avc_general_plug_dir dir,
+				struct snd_oxfw_stream_formation *formation)
+{
+	u8 *format;
+	unsigned int len;
+	int err;
+
+	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+	format = kmalloc(len, GFP_KERNEL);
+	if (format == NULL)
+		return -ENOMEM;
+
+	err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+	if (err < 0)
+		goto end;
+	if (len < 3) {
+		err = -EIO;
+		goto end;
+	}
+
+	err = snd_oxfw_stream_parse_format(format, formation);
+end:
+	kfree(format);
+	return err;
+}
+
 /*
  * See Table 6.16 - AM824 Stream Format
  *     Figure 6.19 - format_information field for AM824 Compound
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 88ab913..b85d80a 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -117,6 +117,8 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	snd_oxfw_proc_init(oxfw);
+
 	err = snd_oxfw_stream_init_simplex(oxfw);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index c4c1acd..6bc2ac3 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -18,6 +18,7 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/info.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -107,3 +108,5 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
 
 int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 24/29] ALSA: oxfw: Change the way to start stream
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (22 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 23/29] ALSA: oxfw: Add proc interface for debugging purpose Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices Takashi Sakamoto
                   ` (7 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In past commit, this driver can keep stream formations for each sampling
rate. So its stream functionality can decide stream formations with given
some parameters.

This commit moves related codes from PCM functionality to stream
functionality. Furthermore, to set stream format correctly, this commit
uses AV/C Stream Format Information command instead of AV/C Input/Output
Plug Signal Format command.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/oxfw-pcm.c    |  38 ++---------
 sound/firewire/oxfw/oxfw-stream.c | 139 +++++++++++++++++++++++++++++++++++---
 sound/firewire/oxfw/oxfw.h        |   3 +-
 3 files changed, 137 insertions(+), 43 deletions(-)

diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 727d281..ab5991a 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -166,36 +166,10 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
-	int err;
-
-	snd_oxfw_stream_stop_simplex(oxfw);
-
-	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-					       params_buffer_bytes(hw_params));
-	if (err < 0)
-		goto error;
-
-	amdtp_stream_set_parameters(&oxfw->rx_stream,
-				    params_rate(hw_params),
-				    params_channels(hw_params),
-				    0);
 
-	amdtp_stream_set_pcm_format(&oxfw->rx_stream,
-				    params_format(hw_params));
-
-	err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params),
-				      AVC_GENERAL_PLUG_DIR_IN, 0);
-	if (err < 0) {
-		dev_err(&oxfw->unit->device, "failed to set sample rate\n");
-		goto err_buffer;
-	}
-
-	return 0;
-
-err_buffer:
-	snd_pcm_lib_free_vmalloc_buffer(substream);
-error:
-	return err;
+	amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
 }
 
 static int pcm_hw_free(struct snd_pcm_substream *substream)
@@ -210,11 +184,11 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
 static int pcm_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
-	snd_oxfw_stream_stop_simplex(oxfw);
-
-	err = snd_oxfw_stream_start_simplex(oxfw);
+	err = snd_oxfw_stream_start_simplex(oxfw, runtime->rate,
+					    runtime->channels);
 	if (err < 0)
 		goto end;
 
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index dd905fe..e3ba398 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -7,8 +7,10 @@
  */
 
 #include "oxfw.h"
+#include <linux/delay.h>
 
 #define AVC_GENERIC_FRAME_MAXIMUM_BYTES	512
+#define CALLBACK_TIMEOUT	200
 
 /*
  * According to datasheet of Oxford Semiconductor:
@@ -37,6 +39,47 @@ static const unsigned int avc_stream_rate_table[] = {
 	[5] = 0x07,
 };
 
+static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
+			     unsigned int rate, unsigned int pcm_channels)
+{
+	u8 **formats;
+	struct snd_oxfw_stream_formation formation;
+	enum avc_general_plug_dir dir;
+	unsigned int i, err, len;
+
+	formats = oxfw->rx_stream_formats;
+	dir = AVC_GENERAL_PLUG_DIR_IN;
+
+	/* Seek stream format for requirements. */
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		err = snd_oxfw_stream_parse_format(formats[i], &formation);
+		if (err < 0)
+			return err;
+
+		if ((formation.rate == rate) && (formation.pcm == pcm_channels))
+			break;
+	}
+	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+		return -EINVAL;
+
+	/* If assumed, just change rate. */
+	if (oxfw->assumed)
+		return avc_general_set_sig_fmt(oxfw->unit, rate,
+					       AVC_GENERAL_PLUG_DIR_IN, 0);
+
+	/* Calculate format length. */
+	len = 5 + formats[i][4] * 2;
+
+	err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
+	if (err < 0)
+		return err;
+
+	/* Some immediate requests just after changing format causes freezing. */
+	msleep(100);
+
+	return 0;
+}
+
 int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw)
 {
 	int err;
@@ -63,28 +106,104 @@ static void stop_stream(struct snd_oxfw *oxfw)
 	cmp_connection_break(&oxfw->in_conn);
 }
 
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw)
+static int start_stream(struct snd_oxfw *oxfw, unsigned int rate,
+			unsigned int pcm_channels)
 {
+	u8 **formats;
+	struct cmp_connection *conn;
+	struct snd_oxfw_stream_formation formation;
+	unsigned int i, midi_ports;
+	struct amdtp_stream *stream;
+	int err;
+
+	stream = &oxfw->rx_stream;
+	formats = oxfw->rx_stream_formats;
+	conn = &oxfw->in_conn;
+
+	/* Get stream formation */
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		if (formats[i] == NULL)
+			break;
+
+		err = snd_oxfw_stream_parse_format(formats[i], &formation);
+		if (err < 0)
+			goto end;
+		if (rate != formation.rate)
+			continue;
+		if (pcm_channels == 0 ||  pcm_channels == formation.pcm)
+			break;
+	}
+	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
+		err = -EINVAL;
+		goto end;
+	}
+
+	pcm_channels = formation.pcm;
+	midi_ports = DIV_ROUND_UP(formation.midi, 8);
+
+	/* The stream should have one pcm channels at least */
+	if (pcm_channels == 0) {
+		err = -EINVAL;
+		goto end;
+	}
+	amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+
+	err = cmp_connection_establish(conn,
+				       amdtp_stream_get_max_payload(stream));
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_start(stream,
+				 conn->resources.channel,
+				 conn->speed);
+	if (err < 0) {
+		cmp_connection_break(conn);
+		goto end;
+	}
+
+	/* Wait first packet */
+	err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
+	if (err < 0)
+		stop_stream(oxfw);
+end:
+	return err;
+}
+
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, unsigned int rate,
+				  unsigned int pcm_channels)
+{
+	struct snd_oxfw_stream_formation formation;
 	int err = 0;
 
 	mutex_lock(&oxfw->mutex);
 
+	/* packet queueing error */
 	if (amdtp_streaming_error(&oxfw->rx_stream))
 		stop_stream(oxfw);
 
-	if (amdtp_stream_running(&oxfw->rx_stream))
-		goto end;
-
-	err = cmp_connection_establish(&oxfw->in_conn,
-			amdtp_stream_get_max_payload(&oxfw->rx_stream));
+	err = snd_oxfw_stream_get_current_formation(oxfw,
+						    AVC_GENERAL_PLUG_DIR_IN,
+						    &formation);
 	if (err < 0)
 		goto end;
 
-	err = amdtp_stream_start(&oxfw->rx_stream,
-				 oxfw->in_conn.resources.channel,
-				 oxfw->in_conn.speed);
-	if (err < 0)
+	if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
 		stop_stream(oxfw);
+
+		/* arrange sampling rate */
+		err = set_stream_format(oxfw, &oxfw->rx_stream, rate,
+					pcm_channels);
+		if (err < 0) {
+			dev_err(&oxfw->unit->device,
+				"fail to set stream format: %d\n", err);
+			goto end;
+		}
+	}
+
+	err = start_stream(oxfw, rate, pcm_channels);
+	if (err < 0)
+		dev_err(&oxfw->unit->device,
+			"fail to start stream: %d\n", err);
 end:
 	mutex_unlock(&oxfw->mutex);
 	return err;
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 6bc2ac3..433e0c0 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -88,7 +88,8 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
 				unsigned short pid);
 
 int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw);
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, unsigned int rate,
+				  unsigned int pcm_channels);
 void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw);
 void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (23 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 24/29] ALSA: oxfw: Change the way to start stream Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-11-16 20:57   ` Clemens Ladisch
  2014-10-26 13:03 ` [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream Takashi Sakamoto
                   ` (6 subsequent siblings)
  31 siblings, 1 reply; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Some devices produced by Behringer/Mackie are based on OXFW970/971. This
commit adds support for them. Additionally, this commit changes the way to
name card with some information in config rom.

Ids of some Mackie(Loud) models are not identified, therefore this commit
applies name detection for these models.

The devices support capture/playback of PCM-samples and some of them
supports capture/playback of MIDI messages. These functionalities are
implemented by followed commits.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig     |  6 +++
 sound/firewire/oxfw/oxfw.c | 92 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 0932860..6364e5b 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -31,6 +31,12 @@ config SND_OXFW
 	  Oxford Semiconductor FW970/971 chipset.
 	   * Griffin Firewave
 	   * LaCie Firewire Speakers
+	   * Behringer F-Control Audio 202
+	   * Mackie(Loud) Onyx-i series (former models)
+	   * Mackie(Loud) Onyx Satellite
+	   * Mackie(Loud) Tapco Link.Firewire
+	   * Mackie(Loud) d.2 pro/d.4 pro
+	   * Mackie(Loud) U.420/U.420d
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-oxfw.
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index b85d80a..397cdeb 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -14,7 +14,9 @@
 #define OXFORD_HARDWARE_ID_OXFW970	0x39443841
 #define OXFORD_HARDWARE_ID_OXFW971	0x39373100
 
+#define VENDOR_LOUD		0x000ff2
 #define VENDOR_GRIFFIN		0x001292
+#define VENDOR_BEHRINGER	0x001564
 #define VENDOR_LACIE		0x00d04b
 
 #define SPECIFIER_1394TA	0x00a02d
@@ -41,22 +43,70 @@ static const struct device_info lacie_speakers = {
 	.volume_fb_id = 0x01,
 };
 
+static bool detect_loud_models(struct fw_unit *unit)
+{
+	const char *const models[] = {
+		"Onyxi",
+		"Onyx-i",
+		"d.Pro",
+		"Mackie Onyx Satellite",
+		"Tapco LINK.firewire 4x6",
+		"U.420"};
+	char model[32] = {0};
+	unsigned int i;
+	int err;
+
+	err = fw_csr_string(unit->directory, CSR_MODEL,
+			    model, sizeof(model));
+	if (err < 0)
+		return err;
+
+	model[31] = '\0';
+	for (i = 0; i < ARRAY_SIZE(models); i++) {
+		if (strncmp(models[i], model, strlen(model) == 0))
+			break;
+	}
+
+	return (i < ARRAY_SIZE(models));
+}
+
 static int name_card(struct snd_oxfw *oxfw)
 {
 	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+	char vendor[24] = {0};
+	char model[32] = {0};
 	const char *d, *v, *m;
 	u32 firmware;
 	int err;
 
+	/* get vendor name from root directory */
+	err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+			    vendor, sizeof(vendor));
+	if (err < 0)
+		goto end;
+
+	/* get model name from unit directory */
+	err = fw_csr_string(oxfw->unit->directory, CSR_MODEL,
+			    model, sizeof(model));
+	if (err < 0)
+		goto end;
+
 	err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
 				 OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
 	if (err < 0)
 		goto end;
 	be32_to_cpus(&firmware);
 
-	d = oxfw->device_info->driver_name;
-	v = oxfw->device_info->vendor_name;
-	m = oxfw->device_info->driver_name;
+	/* to apply card definitions */
+	if (oxfw->device_info) {
+		d = oxfw->device_info->driver_name;
+		v = oxfw->device_info->vendor_name;
+		m = oxfw->device_info->driver_name;
+	} else {
+		d = "OXFW";
+		v = vendor;
+		m = model;
+	}
 
 	strcpy(oxfw->card->driver, d);
 	strcpy(oxfw->card->shortname, m);
@@ -89,6 +139,9 @@ static int oxfw_probe(struct fw_unit *unit,
 	struct snd_oxfw *oxfw;
 	int err;
 
+	if ((id->vendor_id == VENDOR_LOUD) && !detect_loud_models(unit))
+		return -ENODEV;
+
 	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
 			   sizeof(*oxfw), &card);
 	if (err < 0)
@@ -113,9 +166,11 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
-	err = snd_oxfw_create_mixer(oxfw);
-	if (err < 0)
-		goto error;
+	if (oxfw->device_info) {
+		err = snd_oxfw_create_mixer(oxfw);
+		if (err < 0)
+			goto error;
+	}
 
 	snd_oxfw_proc_init(oxfw);
 
@@ -177,6 +232,31 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
 		.version      = VERSION_AVC,
 		.driver_data  = (kernel_ulong_t)&lacie_speakers,
 	},
+	/* Behringer,F-Control Audio 202 */
+	{
+		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
+				  IEEE1394_MATCH_MODEL_ID,
+		.vendor_id	= VENDOR_BEHRINGER,
+		.model_id	= 0x00fc22,
+	},
+	/*
+	 * Any Mackie(Loud) models (name string/model id):
+	 *  Onyx-i series (former models):	0x081216
+	 *  Mackie Onyx Satellite:		0x00200f
+	 *  Tapco LINK.firewire 4x6:		0x000460
+	 *  d.2 pro:				Unknown
+	 *  d.4 pro:				Unknown
+	 *  U.420:				Unknown
+	 *  U.420d:				Unknown
+	 */
+	{
+		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
+				  IEEE1394_MATCH_SPECIFIER_ID |
+				  IEEE1394_MATCH_VERSION,
+		.vendor_id	= VENDOR_LOUD,
+		.specifier_id	= SPECIFIER_1394TA,
+		.version	= VERSION_AVC,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
-- 
1.9.1

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

* [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (24 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-11-16 21:21   ` Clemens Ladisch
  2014-10-26 13:03 ` [PATCH 27/29] ALSA: oxfw: add support for capturing PCM samples Takashi Sakamoto
                   ` (5 subsequent siblings)
  31 siblings, 1 reply; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

Previous commit adds support for some devices which can capture PCM samples.
These devices transmit AMDTP stream in non-blocking mode. This commit adds
functionality to handle AMDTP incoming stream.

OXFW seems to have two quirks:
 - Transmits packets with non-zero dbc in its beginning
 - Transmits packets with wrong values in syt field

For the first quirk, this commit adds CIP_SKIP_INIT_DBC_CHECK flag for
incoming stream to skip first check of dbc.

For the second quirk, this commit doesn't add duplex stream which
Fireworks/BeBoB drivers use. So OXFW driver generates syt value for outgoing
stream.

Here are examples of a sequence of packets transmitted by Behringer F-Control
Audio 202. There are differences between sequences of syt value when OXFW
driver transfers outgoing stream or not.

When driver gives no outgoing stream:
Index   Payload CIP_Header_0    CIP_Header_1
38      14      00020092        900103D1
39      12      00020098        900102FF
40      12      0002009D        9001027F
41      14      000200A2        90010396
42      14      000200A8        900102E8
43      12      000200AE        90010219
44      14      000200B3        90010331
45      12      000200B9        9001025F
46      14      000200BE        90010376
47      12      000200C4        900102A1
00      12      000200C9        9001023E
01      14      000200CE        90010358
02      12      000200D4        90010289
03      16      000200D9        900103A3
04      12      000200E0        900102DD
05      14      000200E5        900103F1
06      12      000200EB        90010335
07      12      000200F0        90010263
08      14      000200F5        9001037C
09      12      000200FB        900102AE

When driver gives outgoing stream:
Index   Payload CIP_Header_0    CIP_Header_1
38      12      000200BD        900104A8
39      14      000200C2        900104A8
40      12      000200C8        900104AC
41      14      000200CD        900104A9
42      12      000200D3        900104B1
43      14      000200D8        900104A8
44      12      000200DE        900104AA
45      14      000200E3        900104A9
46      14      000200E9        900104AE
47      12      000200EF        900104A8
00      14      000200F4        900104AD
01      12      000200FA        900104A7
02      14      000200FF        900104A9
03      12      00020005        900104A9
04      14      0002000A        900104B1
05      12      00020010        900104AA
06      14      00020015        900104AD
07      12      0002001B        900104A7
08      14      00020020        900104AC
09      12      00020026        900104A7

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/oxfw-pcm.c    |   6 +-
 sound/firewire/oxfw/oxfw-proc.c   |  29 ++++
 sound/firewire/oxfw/oxfw-stream.c | 277 +++++++++++++++++++++++++++++---------
 sound/firewire/oxfw/oxfw.c        |  23 +++-
 sound/firewire/oxfw/oxfw.h        |  24 +++-
 5 files changed, 283 insertions(+), 76 deletions(-)

diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index ab5991a..b2fd930 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -176,7 +176,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	snd_oxfw_stream_stop_simplex(oxfw);
+	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -187,8 +187,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
-	err = snd_oxfw_stream_start_simplex(oxfw, runtime->rate,
-					    runtime->channels);
+	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
+					    runtime->rate, runtime->channels);
 	if (err < 0)
 		goto end;
 
diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c
index 18e0305..604808e 100644
--- a/sound/firewire/oxfw/oxfw-proc.c
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -44,6 +44,35 @@ static void proc_read_formation(struct snd_info_entry *entry,
 			    formation.rate, formation.pcm, formation.midi);
 	}
 
+	if (!oxfw->has_output)
+		return;
+
+	/* Show output. */
+	err = snd_oxfw_stream_get_current_formation(oxfw,
+						    AVC_GENERAL_PLUG_DIR_OUT,
+						    &curr);
+	if (err < 0)
+		return;
+
+	snd_iprintf(buffer, "Output Stream from device:\n");
+	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		format = oxfw->tx_stream_formats[i];
+		if (format == NULL)
+			continue;
+
+		err = snd_oxfw_stream_parse_format(format, &formation);
+		if (err < 0)
+			continue;
+
+		if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+			flag = '*';
+		else
+			flag = ' ';
+
+		snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+			    formation.rate, formation.pcm, formation.midi);
+	}
 }
 
 static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index e3ba398..aa7dbda 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -39,6 +39,22 @@ static const unsigned int avc_stream_rate_table[] = {
 	[5] = 0x07,
 };
 
+static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
+{
+	int err;
+
+	err = avc_general_set_sig_fmt(oxfw->unit, rate,
+				      AVC_GENERAL_PLUG_DIR_IN, 0);
+	if (err < 0)
+		goto end;
+
+	if (oxfw->has_output)
+		err = avc_general_set_sig_fmt(oxfw->unit, rate,
+					      AVC_GENERAL_PLUG_DIR_OUT, 0);
+end:
+	return err;
+}
+
 static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
 			     unsigned int rate, unsigned int pcm_channels)
 {
@@ -47,8 +63,13 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
 	enum avc_general_plug_dir dir;
 	unsigned int i, err, len;
 
-	formats = oxfw->rx_stream_formats;
-	dir = AVC_GENERAL_PLUG_DIR_IN;
+	if (s == &oxfw->tx_stream) {
+		formats = oxfw->tx_stream_formats;
+		dir = AVC_GENERAL_PLUG_DIR_OUT;
+	} else {
+		formats = oxfw->rx_stream_formats;
+		dir = AVC_GENERAL_PLUG_DIR_IN;
+	}
 
 	/* Seek stream format for requirements. */
 	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
@@ -64,8 +85,7 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
 
 	/* If assumed, just change rate. */
 	if (oxfw->assumed)
-		return avc_general_set_sig_fmt(oxfw->unit, rate,
-					       AVC_GENERAL_PLUG_DIR_IN, 0);
+		return set_rate(oxfw, rate);
 
 	/* Calculate format length. */
 	len = 5 + formats[i][4] * 2;
@@ -74,53 +94,41 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
 	if (err < 0)
 		return err;
 
-	/* Some immediate requests just after changing format causes freezing. */
+	/* Some immediate requests just after changing format cause freezing. */
 	msleep(100);
 
 	return 0;
 }
 
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw)
+static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
 {
-	int err;
-
-	err = cmp_connection_init(&oxfw->in_conn, oxfw->unit,
-				  CMP_INPUT, 0);
-	if (err < 0)
-		goto end;
+	amdtp_stream_pcm_abort(stream);
+	amdtp_stream_stop(stream);
 
-	err = amdtp_stream_init(&oxfw->rx_stream, oxfw->unit,
-				AMDTP_OUT_STREAM, CIP_NONBLOCKING);
-	if (err < 0) {
-		amdtp_stream_destroy(&oxfw->rx_stream);
-		cmp_connection_destroy(&oxfw->in_conn);
-	}
-end:
-	return err;
-}
-
-static void stop_stream(struct snd_oxfw *oxfw)
-{
-	amdtp_stream_pcm_abort(&oxfw->rx_stream);
-	amdtp_stream_stop(&oxfw->rx_stream);
-	cmp_connection_break(&oxfw->in_conn);
+	if (stream == &oxfw->tx_stream)
+		cmp_connection_break(&oxfw->out_conn);
+	else
+		cmp_connection_break(&oxfw->in_conn);
 }
 
-static int start_stream(struct snd_oxfw *oxfw, unsigned int rate,
-			unsigned int pcm_channels)
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
+			unsigned int rate, unsigned int pcm_channels)
 {
 	u8 **formats;
 	struct cmp_connection *conn;
 	struct snd_oxfw_stream_formation formation;
 	unsigned int i, midi_ports;
-	struct amdtp_stream *stream;
 	int err;
 
-	stream = &oxfw->rx_stream;
-	formats = oxfw->rx_stream_formats;
-	conn = &oxfw->in_conn;
+	if (stream == &oxfw->rx_stream) {
+		formats = oxfw->rx_stream_formats;
+		conn = &oxfw->in_conn;
+	} else {
+		formats = oxfw->tx_stream_formats;
+		conn = &oxfw->out_conn;
+	}
 
-	/* Get stream formation */
+	/* Get stream format */
 	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
 		if (formats[i] == NULL)
 			break;
@@ -164,78 +172,212 @@ static int start_stream(struct snd_oxfw *oxfw, unsigned int rate,
 	/* Wait first packet */
 	err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
 	if (err < 0)
-		stop_stream(oxfw);
+		stop_stream(oxfw, stream);
 end:
 	return err;
 }
 
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, unsigned int rate,
-				  unsigned int pcm_channels)
+static int check_connection_used_by_others(struct snd_oxfw *oxfw,
+					   struct amdtp_stream *stream)
+{
+	struct cmp_connection *conn;
+	bool used;
+	int err;
+
+	if (stream == &oxfw->tx_stream)
+		conn = &oxfw->out_conn;
+	else
+		conn = &oxfw->in_conn;
+
+	err = cmp_connection_check_used(conn, &used);
+	if ((err >= 0) && used && !amdtp_stream_running(stream)) {
+		dev_err(&oxfw->unit->device,
+			"Connection established by others: %cPCR[%d]\n",
+			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+			conn->pcr_index);
+		err = -EBUSY;
+	}
+
+	return err;
+}
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
+				 struct amdtp_stream *stream)
 {
+	struct cmp_connection *conn;
+	enum cmp_direction c_dir;
+	enum amdtp_stream_direction s_dir;
+	int err;
+
+	if (stream == &oxfw->tx_stream) {
+		conn = &oxfw->out_conn;
+		c_dir = CMP_OUTPUT;
+		s_dir = AMDTP_IN_STREAM;
+	} else {
+		conn = &oxfw->in_conn;
+		c_dir = CMP_INPUT;
+		s_dir = AMDTP_OUT_STREAM;
+	}
+
+	mutex_lock(&oxfw->mutex);
+
+	err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+	if (err < 0) {
+		amdtp_stream_destroy(stream);
+		cmp_connection_destroy(conn);
+		goto end;
+	}
+
+	/* OXFW starts to transmit packets with non-zero dbc. */
+	if (stream == &oxfw->tx_stream)
+		oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+end:
+	mutex_unlock(&oxfw->mutex);
+	return err;
+}
+
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
+				  struct amdtp_stream *stream,
+				  unsigned int rate, unsigned int pcm_channels)
+{
+	struct amdtp_stream *opposite;
 	struct snd_oxfw_stream_formation formation;
+	enum avc_general_plug_dir dir;
+	atomic_t *substreams, *opposite_substreams;
 	int err = 0;
 
+	if (stream == &oxfw->tx_stream) {
+		substreams = &oxfw->capture_substreams;
+		opposite = &oxfw->rx_stream;
+		opposite_substreams = &oxfw->playback_substreams;
+		dir = AVC_GENERAL_PLUG_DIR_OUT;
+	} else {
+		substreams = &oxfw->playback_substreams;
+		opposite_substreams = &oxfw->capture_substreams;
+
+		if (oxfw->has_output)
+			opposite = &oxfw->rx_stream;
+		else
+			opposite = NULL;
+
+		dir = AVC_GENERAL_PLUG_DIR_IN;
+	}
+
+	if (atomic_read(substreams) == 0)
+		goto end;
+
 	mutex_lock(&oxfw->mutex);
 
+	/*
+	 * Considering JACK/FFADO streaming:
+	 * TODO: This can be removed hwdep functionality becomes popular.
+	 */
+	err = check_connection_used_by_others(oxfw, stream);
+	if (err < 0)
+		goto end;
+
 	/* packet queueing error */
-	if (amdtp_streaming_error(&oxfw->rx_stream))
-		stop_stream(oxfw);
+	if (amdtp_streaming_error(stream))
+		stop_stream(oxfw, stream);
 
-	err = snd_oxfw_stream_get_current_formation(oxfw,
-						    AVC_GENERAL_PLUG_DIR_IN,
-						    &formation);
+	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
 	if (err < 0)
 		goto end;
 
 	if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
-		stop_stream(oxfw);
+		if (opposite != NULL) {
+			err = check_connection_used_by_others(oxfw, opposite);
+			if (err < 0)
+				goto end;
+			stop_stream(oxfw, opposite);
+		}
+		stop_stream(oxfw, stream);
 
-		/* arrange sampling rate */
-		err = set_stream_format(oxfw, &oxfw->rx_stream, rate,
-					pcm_channels);
+		err = set_stream_format(oxfw, stream, rate, pcm_channels);
 		if (err < 0) {
 			dev_err(&oxfw->unit->device,
 				"fail to set stream format: %d\n", err);
 			goto end;
 		}
+
+		/* Start opposite stream if needed. */
+		if (opposite && !amdtp_stream_running(opposite) &&
+		    (atomic_read(opposite_substreams) > 0)) {
+			err = start_stream(oxfw, opposite, rate, 0);
+			if (err < 0) {
+				dev_err(&oxfw->unit->device,
+					"fail to restart stream: %d\n", err);
+				goto end;
+			}
+		}
 	}
 
-	err = start_stream(oxfw, rate, pcm_channels);
-	if (err < 0)
-		dev_err(&oxfw->unit->device,
-			"fail to start stream: %d\n", err);
+	/* Start requested stream. */
+	if (!amdtp_stream_running(stream)) {
+		err = start_stream(oxfw, stream, rate, pcm_channels);
+		if (err < 0)
+			dev_err(&oxfw->unit->device,
+				"fail to start stream: %d\n", err);
+	}
 end:
 	mutex_unlock(&oxfw->mutex);
 	return err;
 }
 
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
+				  struct amdtp_stream *stream)
 {
+	if (((stream == &oxfw->tx_stream) &&
+	     (atomic_read(&oxfw->capture_substreams) > 0)) ||
+	    ((stream == &oxfw->rx_stream) &&
+	     (atomic_read(&oxfw->playback_substreams) > 0)))
+		return;
+
 	mutex_lock(&oxfw->mutex);
-	stop_stream(oxfw);
+	stop_stream(oxfw, stream);
 	mutex_unlock(&oxfw->mutex);
 }
 
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
+				     struct amdtp_stream *stream)
 {
+	struct cmp_connection *conn;
+
+	if (stream == &oxfw->tx_stream)
+		conn = &oxfw->out_conn;
+	else
+		conn = &oxfw->in_conn;
+
 	mutex_lock(&oxfw->mutex);
 
-	stop_stream(oxfw);
+	stop_stream(oxfw, stream);
 
-	amdtp_stream_destroy(&oxfw->rx_stream);
-	cmp_connection_destroy(&oxfw->in_conn);
+	amdtp_stream_destroy(stream);
+	cmp_connection_destroy(conn);
 
 	mutex_unlock(&oxfw->mutex);
 }
 
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
+				    struct amdtp_stream *stream)
 {
-	if (cmp_connection_update(&oxfw->in_conn) < 0) {
+	struct cmp_connection *conn;
+
+	if (stream == &oxfw->tx_stream)
+		conn = &oxfw->out_conn;
+	else
+		conn = &oxfw->in_conn;
+
+	if (cmp_connection_update(conn) < 0) {
 		mutex_lock(&oxfw->mutex);
-		stop_stream(oxfw);
+		stop_stream(oxfw, stream);
 		mutex_unlock(&oxfw->mutex);
 	} else {
-		amdtp_stream_update(&oxfw->rx_stream);
+		amdtp_stream_update(stream);
 	}
 }
 
@@ -420,7 +562,10 @@ static int fill_stream_formats(struct snd_oxfw *oxfw,
 	if (buf == NULL)
 		return -ENOMEM;
 
-	formats = oxfw->rx_stream_formats;
+	if (dir == AVC_GENERAL_PLUG_DIR_OUT)
+		formats = oxfw->tx_stream_formats;
+	else
+		formats = oxfw->rx_stream_formats;
 
 	/* get first entry */
 	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
@@ -493,11 +638,19 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
 		"fail to get info for isoc/external in/out plugs: %d\n",
 			err);
 		goto end;
-	} else if (plugs[0] == 0) {
+	} else if ((plugs[0] == 0) && (plugs[1] == 0)) {
 		err = -ENOSYS;
 		goto end;
 	}
 
+	/* use oPCR[0] if exists */
+	if (plugs[1] > 0) {
+		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
+		if (err < 0)
+			goto end;
+		oxfw->has_output = true;
+	}
+
 	/* use iPCR[0] if exists */
 	if (plugs[0] > 0)
 		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 397cdeb..f029288 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -126,8 +126,10 @@ static void oxfw_card_free(struct snd_card *card)
 	struct snd_oxfw *oxfw = card->private_data;
 	unsigned int i;
 
-	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++)
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		kfree(oxfw->tx_stream_formats[i]);
 		kfree(oxfw->rx_stream_formats[i]);
+	}
 
 	mutex_destroy(&oxfw->mutex);
 }
@@ -174,13 +176,20 @@ static int oxfw_probe(struct fw_unit *unit,
 
 	snd_oxfw_proc_init(oxfw);
 
-	err = snd_oxfw_stream_init_simplex(oxfw);
+	err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
 	if (err < 0)
 		goto error;
+	if (oxfw->has_output) {
+		err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+		if (err < 0)
+			goto error;
+	}
 
 	err = snd_card_register(card);
 	if (err < 0) {
-		snd_oxfw_stream_destroy_simplex(oxfw);
+		snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+		if (oxfw->has_output)
+			snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
 		goto error;
 	}
 	dev_set_drvdata(&unit->device, oxfw);
@@ -196,14 +205,18 @@ static void oxfw_bus_reset(struct fw_unit *unit)
 	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
 	fcp_bus_reset(oxfw->unit);
-	snd_oxfw_stream_update_simplex(oxfw);
+	snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+	if (oxfw->has_output)
+		snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
 }
 
 static void oxfw_remove(struct fw_unit *unit)
 {
 	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
-	snd_oxfw_stream_destroy_simplex(oxfw);
+	snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+	if (oxfw->has_output)
+		snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
 
 	snd_card_disconnect(oxfw->card);
 	snd_card_free_when_closed(oxfw->card);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 433e0c0..fd01178 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -43,10 +43,16 @@ struct snd_oxfw {
 	const struct device_info *device_info;
 	struct mutex mutex;
 
+	bool has_output;
+	u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
 	u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
 	bool assumed;
+	struct cmp_connection out_conn;
 	struct cmp_connection in_conn;
+	struct amdtp_stream tx_stream;
 	struct amdtp_stream rx_stream;
+	atomic_t capture_substreams;
+	atomic_t playback_substreams;
 
 	bool mute;
 	s16 volume[6];
@@ -87,12 +93,17 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
 				enum avc_general_plug_dir dir,
 				unsigned short pid);
 
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw);
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, unsigned int rate,
-				  unsigned int pcm_channels);
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw);
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw);
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
+				 struct amdtp_stream *stream);
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
+				  struct amdtp_stream *stream,
+				  unsigned int rate, unsigned int pcm_channels);
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
+				  struct amdtp_stream *stream);
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
+				     struct amdtp_stream *stream);
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
+				    struct amdtp_stream *stream);
 
 struct snd_oxfw_stream_formation {
 	unsigned int rate;
@@ -104,6 +115,7 @@ int snd_oxfw_stream_parse_format(u8 *format,
 int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
 				enum avc_general_plug_dir dir,
 				struct snd_oxfw_stream_formation *formation);
+
 int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
 
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 27/29] ALSA: oxfw: add support for capturing PCM samples
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (25 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 28/29] ALSA: oxfw: Add support for capture/playback MIDI messages Takashi Sakamoto
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

In previous commit, a support for transmitted packets is added. This commit
add a support for capturing PCM samples.

When any streams are already started, this driver should not change sampling
rate of the device, thus this commit also adds a restriction of sampling rate
in this situation.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/oxfw-pcm.c | 181 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 161 insertions(+), 20 deletions(-)

diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index b2fd930..25921f0 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -118,21 +118,31 @@ static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
 	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
 }
 
-static int pcm_open(struct snd_pcm_substream *substream)
+static int init_hw_params(struct snd_oxfw *oxfw,
+			  struct snd_pcm_substream *substream)
 {
-	struct snd_oxfw *oxfw = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	u8 **formats;
+	struct amdtp_stream *stream;
 	int err;
 
-	formats = oxfw->rx_stream_formats;
-
 	runtime->hw.info = SNDRV_PCM_INFO_BATCH |
 			   SNDRV_PCM_INFO_BLOCK_TRANSFER |
 			   SNDRV_PCM_INFO_INTERLEAVED |
+			   SNDRV_PCM_INFO_JOINT_DUPLEX |
 			   SNDRV_PCM_INFO_MMAP |
 			   SNDRV_PCM_INFO_MMAP_VALID;
 
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+		stream = &oxfw->tx_stream;
+		formats = oxfw->tx_stream_formats;
+	} else {
+		runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+		stream = &oxfw->rx_stream;
+		formats = oxfw->rx_stream_formats;
+	}
+
 	limit_channels_and_rates(&runtime->hw, formats);
 	limit_period_and_buffer(&runtime->hw);
 
@@ -148,10 +158,55 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		goto end;
 
-	err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime);
+	err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+end:
+	return err;
+}
+
+static int limit_to_current_params(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+	struct snd_oxfw_stream_formation formation;
+	enum avc_general_plug_dir dir;
+	int err;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		dir = AVC_GENERAL_PLUG_DIR_OUT;
+	else
+		dir = AVC_GENERAL_PLUG_DIR_IN;
+
+	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+	if (err < 0)
+		goto end;
+
+	substream->runtime->hw.channels_min = formation.pcm;
+	substream->runtime->hw.channels_max = formation.pcm;
+	substream->runtime->hw.rate_min = formation.rate;
+	substream->runtime->hw.rate_max = formation.rate;
+end:
+	return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+	int err;
+
+	err = init_hw_params(oxfw, substream);
 	if (err < 0)
 		goto end;
 
+	/*
+	 * When any PCM streams are already running, the available sampling
+	 * rate is limited at current value.
+	 */
+	if (amdtp_stream_pcm_running(&oxfw->tx_stream) ||
+	    amdtp_stream_pcm_running(&oxfw->rx_stream)) {
+		err = limit_to_current_params(substream);
+		if (err < 0)
+			goto end;
+	}
+
 	snd_pcm_set_sync(substream);
 end:
 	return err;
@@ -162,26 +217,70 @@ static int pcm_close(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static int pcm_hw_params(struct snd_pcm_substream *substream,
-			 struct snd_pcm_hw_params *hw_params)
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		atomic_inc(&oxfw->capture_substreams);
+	amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		atomic_inc(&oxfw->playback_substreams);
 	amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
 						params_buffer_bytes(hw_params));
 }
 
-static int pcm_hw_free(struct snd_pcm_substream *substream)
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		atomic_dec(&oxfw->capture_substreams);
+
+	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		atomic_dec(&oxfw->playback_substreams);
+
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
 
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
-static int pcm_prepare(struct snd_pcm_substream *substream)
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
+					    runtime->rate, runtime->channels);
+	if (err < 0)
+		goto end;
+
+	amdtp_stream_pcm_prepare(&oxfw->tx_stream);
+end:
+	return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -197,7 +296,23 @@ end:
 	return err;
 }
 
-static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&oxfw->tx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&oxfw->tx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
@@ -214,35 +329,61 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	return 0;
 }
 
-static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
 {
-	struct snd_oxfw *oxfw = substream->private_data;
+	struct snd_oxfw *oxfw = sbstm->private_data;
+
+	return amdtp_stream_pcm_pointer(&oxfw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
+{
+	struct snd_oxfw *oxfw = sbstm->private_data;
 
 	return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
 }
 
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
 {
-	static struct snd_pcm_ops ops = {
+	static struct snd_pcm_ops capture_ops = {
+		.open      = pcm_open,
+		.close     = pcm_close,
+		.ioctl     = snd_pcm_lib_ioctl,
+		.hw_params = pcm_capture_hw_params,
+		.hw_free   = pcm_capture_hw_free,
+		.prepare   = pcm_capture_prepare,
+		.trigger   = pcm_capture_trigger,
+		.pointer   = pcm_capture_pointer,
+		.page      = snd_pcm_lib_get_vmalloc_page,
+		.mmap      = snd_pcm_lib_mmap_vmalloc,
+	};
+	static struct snd_pcm_ops playback_ops = {
 		.open      = pcm_open,
 		.close     = pcm_close,
 		.ioctl     = snd_pcm_lib_ioctl,
-		.hw_params = pcm_hw_params,
-		.hw_free   = pcm_hw_free,
-		.prepare   = pcm_prepare,
-		.trigger   = pcm_trigger,
-		.pointer   = pcm_pointer,
+		.hw_params = pcm_playback_hw_params,
+		.hw_free   = pcm_playback_hw_free,
+		.prepare   = pcm_playback_prepare,
+		.trigger   = pcm_playback_trigger,
+		.pointer   = pcm_playback_pointer,
 		.page      = snd_pcm_lib_get_vmalloc_page,
 		.mmap      = snd_pcm_lib_mmap_vmalloc,
 	};
 	struct snd_pcm *pcm;
+	unsigned int cap = 0;
 	int err;
 
-	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, 0, &pcm);
+	if (oxfw->has_output)
+		cap = 1;
+
+	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
 	if (err < 0)
 		return err;
+
 	pcm->private_data = oxfw;
 	strcpy(pcm->name, oxfw->card->shortname);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+	if (cap > 0)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+
 	return 0;
 }
-- 
1.9.1

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

* [PATCH 28/29] ALSA: oxfw: Add support for capture/playback MIDI messages
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (26 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 27/29] ALSA: oxfw: add support for capturing PCM samples Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:03 ` [PATCH 29/29] ALSA: oxfw: Add hwdep interface Takashi Sakamoto
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

This commit adds MIDI functionality with an assumption of 'if the device
has MIDI comformant data channels in its stream formation, the device has
one MIDI port'.

When no streams have already started, MIDI functionality starts stream
with current sampling rate.

When MIDI functionality has already starts some streams and PCM
functionality is going to start streams at different sampling rate,
this driver stops streams once and changes sampling rate, then restarts
streams for both PCM/MIDI substreams.

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

diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index e9297c6..3904a30 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
 snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
-		 oxfw-proc.o oxfw.o
+		 oxfw-proc.o oxfw-midi.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
new file mode 100644
index 0000000..5a0f447
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -0,0 +1,169 @@
+/*
+ * oxfw_midi.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+	atomic_inc(&oxfw->capture_substreams);
+	return snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+	atomic_inc(&oxfw->playback_substreams);
+	return snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+	atomic_dec(&oxfw->capture_substreams);
+	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+
+	return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+	atomic_dec(&oxfw->playback_substreams);
+	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+
+	return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&oxfw->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&oxfw->tx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&oxfw->tx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&oxfw->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&oxfw->rx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&oxfw->rx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+	.open		= midi_capture_open,
+	.close		= midi_capture_close,
+	.trigger	= midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+	.open		= midi_playback_open,
+	.close		= midi_playback_close,
+	.trigger	= midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_oxfw *oxfw,
+				     struct snd_rawmidi_str *str)
+{
+	struct snd_rawmidi_substream *subs;
+
+	list_for_each_entry(subs, &str->substreams, list) {
+		snprintf(subs->name, sizeof(subs->name),
+			 "%s MIDI %d",
+			 oxfw->card->shortname, subs->number + 1);
+	}
+}
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
+{
+	struct snd_oxfw_stream_formation formation;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *str;
+	u8 *format;
+	int i, err;
+
+	/* If its stream has MIDI conformant data channel, add one MIDI port */
+	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+		format = oxfw->tx_stream_formats[i];
+		if (format != NULL) {
+			err = snd_oxfw_stream_parse_format(format, &formation);
+			if (err >= 0 && formation.midi > 0)
+				oxfw->midi_input_ports = 1;
+		}
+
+		format = oxfw->rx_stream_formats[i];
+		if (format != NULL) {
+			err = snd_oxfw_stream_parse_format(format, &formation);
+			if (err >= 0 && formation.midi > 0)
+				oxfw->midi_output_ports = 1;
+		}
+	}
+	if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+		return 0;
+
+	/* create midi ports */
+	err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
+			      oxfw->midi_output_ports, oxfw->midi_input_ports,
+			      &rmidi);
+	if (err < 0)
+		return err;
+
+	snprintf(rmidi->name, sizeof(rmidi->name),
+		 "%s MIDI", oxfw->card->shortname);
+	rmidi->private_data = oxfw;
+
+	if (oxfw->midi_input_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+				    &midi_capture_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+		set_midi_substream_names(oxfw, str);
+	}
+
+	if (oxfw->midi_output_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+				    &midi_playback_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+		set_midi_substream_names(oxfw, str);
+	}
+
+	if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index aa7dbda..420eadf 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -287,6 +287,10 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
 	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
 	if (err < 0)
 		goto end;
+	if (rate == 0)
+		rate = formation.rate;
+	if (pcm_channels == 0)
+		pcm_channels = formation.pcm;
 
 	if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
 		if (opposite != NULL) {
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index f029288..fb86980 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -155,6 +155,7 @@ static int oxfw_probe(struct fw_unit *unit,
 	mutex_init(&oxfw->mutex);
 	oxfw->unit = unit;
 	oxfw->device_info = (const struct device_info *)id->driver_data;
+	spin_lock_init(&oxfw->lock);
 
 	err = snd_oxfw_stream_discover(oxfw);
 	if (err < 0)
@@ -176,6 +177,10 @@ static int oxfw_probe(struct fw_unit *unit,
 
 	snd_oxfw_proc_init(oxfw);
 
+	err = snd_oxfw_create_midi(oxfw);
+	if (err < 0)
+		goto error;
+
 	err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index fd01178..b42a5ae 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -19,6 +19,7 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/info.h>
+#include <sound/rawmidi.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -42,6 +43,7 @@ struct snd_oxfw {
 	struct fw_unit *unit;
 	const struct device_info *device_info;
 	struct mutex mutex;
+	spinlock_t lock;
 
 	bool has_output;
 	u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
@@ -54,6 +56,9 @@ struct snd_oxfw {
 	atomic_t capture_substreams;
 	atomic_t playback_substreams;
 
+	unsigned int midi_input_ports;
+	unsigned int midi_output_ports;
+
 	bool mute;
 	s16 volume[6];
 	s16 volume_min;
@@ -123,3 +128,5 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
 int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
 
 void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH 29/29] ALSA: oxfw: Add hwdep interface
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (27 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 28/29] ALSA: oxfw: Add support for capture/playback MIDI messages Takashi Sakamoto
@ 2014-10-26 13:03 ` Takashi Sakamoto
  2014-10-26 13:29 ` [PATCH] hwdep: add OXFW driver support Takashi Sakamoto
                   ` (2 subsequent siblings)
  31 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:03 UTC (permalink / raw)
  To: clemens, tiwai; +Cc: alsa-devel, ffado-devel

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

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/asound.h       |   3 +-
 include/uapi/sound/firewire.h     |   3 +-
 sound/firewire/Kconfig            |   1 +
 sound/firewire/oxfw/Makefile      |   2 +-
 sound/firewire/oxfw/oxfw-hwdep.c  | 190 ++++++++++++++++++++++++++++++++++++++
 sound/firewire/oxfw/oxfw-midi.c   |  24 ++++-
 sound/firewire/oxfw/oxfw-pcm.c    |  12 ++-
 sound/firewire/oxfw/oxfw-stream.c |  39 ++++++++
 sound/firewire/oxfw/oxfw.c        |   5 +
 sound/firewire/oxfw/oxfw.h        |  13 +++
 10 files changed, 286 insertions(+), 6 deletions(-)
 create mode 100644 sound/firewire/oxfw/oxfw-hwdep.c

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 6ee5867..c30a4d4 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -96,9 +96,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */
 	SNDRV_HWDEP_IFACE_FW_FIREWORKS,	/* Echo Audio Fireworks based device */
 	SNDRV_HWDEP_IFACE_FW_BEBOB,	/* BridgeCo BeBoB based device */
+	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index af4bd13..49122df 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -55,7 +55,8 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_TYPE_DICE	1
 #define SNDRV_FIREWIRE_TYPE_FIREWORKS	2
 #define SNDRV_FIREWIRE_TYPE_BEBOB	3
-/* AV/C, RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_OXFW	4
+/* RME, MOTU, ... */
 
 struct snd_firewire_get_info {
 	unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 6364e5b..ecec547 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -26,6 +26,7 @@ config SND_DICE
 config SND_OXFW
 	tristate "Oxford Semiconductor FW970/971 chipset support"
 	select SND_FIREWIRE_LIB
+	select SND_HWDEP
 	help
 	  Say Y here to include support for FireWire devices based on
 	  Oxford Semiconductor FW970/971 chipset.
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 3904a30..a926850 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
 snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
-		 oxfw-proc.o oxfw-midi.o oxfw.o
+		 oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
 obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c
new file mode 100644
index 0000000..ff2687a
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-hwdep.c
@@ -0,0 +1,190 @@
+/*
+ * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "oxfw.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+		       loff_t *offset)
+{
+	struct snd_oxfw *oxfw = hwdep->private_data;
+	DEFINE_WAIT(wait);
+	union snd_firewire_event event;
+
+	spin_lock_irq(&oxfw->lock);
+
+	while (!oxfw->dev_lock_changed) {
+		prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+		spin_unlock_irq(&oxfw->lock);
+		schedule();
+		finish_wait(&oxfw->hwdep_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		spin_lock_irq(&oxfw->lock);
+	}
+
+	memset(&event, 0, sizeof(event));
+	if (oxfw->dev_lock_changed) {
+		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+		event.lock_status.status = (oxfw->dev_lock_count > 0);
+		oxfw->dev_lock_changed = false;
+
+		count = min_t(long, count, sizeof(event.lock_status));
+	}
+
+	spin_unlock_irq(&oxfw->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+			       poll_table *wait)
+{
+	struct snd_oxfw *oxfw = hwdep->private_data;
+	unsigned int events;
+
+	poll_wait(file, &oxfw->hwdep_wait, wait);
+
+	spin_lock_irq(&oxfw->lock);
+	if (oxfw->dev_lock_changed)
+		events = POLLIN | POLLRDNORM;
+	else
+		events = 0;
+	spin_unlock_irq(&oxfw->lock);
+
+	return events;
+}
+
+static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
+{
+	struct fw_device *dev = fw_parent_device(oxfw->unit);
+	struct snd_firewire_get_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.type = SNDRV_FIREWIRE_TYPE_OXFW;
+	info.card = dev->card->index;
+	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+	strlcpy(info.device_name, dev_name(&dev->device),
+		sizeof(info.device_name));
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int hwdep_lock(struct snd_oxfw *oxfw)
+{
+	int err;
+
+	spin_lock_irq(&oxfw->lock);
+
+	if (oxfw->dev_lock_count == 0) {
+		oxfw->dev_lock_count = -1;
+		err = 0;
+	} else {
+		err = -EBUSY;
+	}
+
+	spin_unlock_irq(&oxfw->lock);
+
+	return err;
+}
+
+static int hwdep_unlock(struct snd_oxfw *oxfw)
+{
+	int err;
+
+	spin_lock_irq(&oxfw->lock);
+
+	if (oxfw->dev_lock_count == -1) {
+		oxfw->dev_lock_count = 0;
+		err = 0;
+	} else {
+		err = -EBADFD;
+	}
+
+	spin_unlock_irq(&oxfw->lock);
+
+	return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct snd_oxfw *oxfw = hwdep->private_data;
+
+	spin_lock_irq(&oxfw->lock);
+	if (oxfw->dev_lock_count == -1)
+		oxfw->dev_lock_count = 0;
+	spin_unlock_irq(&oxfw->lock);
+
+	return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct snd_oxfw *oxfw = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return hwdep_get_info(oxfw, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return hwdep_lock(oxfw);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return hwdep_unlock(oxfw);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+			      unsigned int cmd, unsigned long arg)
+{
+	return hwdep_ioctl(hwdep, file, cmd,
+			   (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
+{
+	static const struct snd_hwdep_ops hwdep_ops = {
+		.read		= hwdep_read,
+		.release	= hwdep_release,
+		.poll		= hwdep_poll,
+		.ioctl		= hwdep_ioctl,
+		.ioctl_compat	= hwdep_compat_ioctl,
+	};
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, oxfw->card->driver);
+	hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = oxfw;
+	hwdep->exclusive = true;
+end:
+	return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
index 5a0f447..94bc8d0 100644
--- a/sound/firewire/oxfw/oxfw-midi.c
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -11,17 +11,35 @@
 static int midi_capture_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+	int err;
+
+	err = snd_oxfw_stream_lock_try(oxfw);
+	if (err < 0)
+		goto end;
 
 	atomic_inc(&oxfw->capture_substreams);
-	return snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+	if (err < 0)
+		snd_oxfw_stream_lock_release(oxfw);
+end:
+	return err;
 }
 
 static int midi_playback_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
+	int err;
+
+	err = snd_oxfw_stream_lock_try(oxfw);
+	if (err < 0)
+		goto end;
 
 	atomic_inc(&oxfw->playback_substreams);
-	return snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+	if (err < 0)
+		snd_oxfw_stream_lock_release(oxfw);
+end:
+	return err;
 }
 
 static int midi_capture_close(struct snd_rawmidi_substream *substream)
@@ -31,6 +49,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 	atomic_dec(&oxfw->capture_substreams);
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
 
+	snd_oxfw_stream_lock_release(oxfw);
 	return 0;
 }
 
@@ -41,6 +60,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 	atomic_dec(&oxfw->playback_substreams);
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
 
+	snd_oxfw_stream_lock_release(oxfw);
 	return 0;
 }
 
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 25921f0..c508a1f 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -192,10 +192,14 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	struct snd_oxfw *oxfw = substream->private_data;
 	int err;
 
-	err = init_hw_params(oxfw, substream);
+	err = snd_oxfw_stream_lock_try(oxfw);
 	if (err < 0)
 		goto end;
 
+	err = init_hw_params(oxfw, substream);
+	if (err < 0)
+		goto err_locked;
+
 	/*
 	 * When any PCM streams are already running, the available sampling
 	 * rate is limited at current value.
@@ -210,10 +214,16 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	snd_pcm_set_sync(substream);
 end:
 	return err;
+err_locked:
+	snd_oxfw_stream_lock_release(oxfw);
+	return err;
 }
 
 static int pcm_close(struct snd_pcm_substream *substream)
 {
+	struct snd_oxfw *oxfw = substream->private_data;
+
+	snd_oxfw_stream_lock_release(oxfw);
 	return 0;
 }
 
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 420eadf..b1dcc68 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -661,3 +661,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
 end:
 	return err;
 }
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
+{
+	oxfw->dev_lock_changed = true;
+	wake_up(&oxfw->hwdep_wait);
+}
+
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
+{
+	int err;
+
+	spin_lock_irq(&oxfw->lock);
+
+	/* user land lock this */
+	if (oxfw->dev_lock_count < 0) {
+		err = -EBUSY;
+		goto end;
+	}
+
+	/* this is the first time */
+	if (oxfw->dev_lock_count++ == 0)
+		snd_oxfw_stream_lock_changed(oxfw);
+	err = 0;
+end:
+	spin_unlock_irq(&oxfw->lock);
+	return err;
+}
+
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
+{
+	spin_lock_irq(&oxfw->lock);
+
+	if (WARN_ON(oxfw->dev_lock_count <= 0))
+		goto end;
+	if (--oxfw->dev_lock_count == 0)
+		snd_oxfw_stream_lock_changed(oxfw);
+end:
+	spin_unlock_irq(&oxfw->lock);
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index fb86980..40cd2c0 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -156,6 +156,7 @@ static int oxfw_probe(struct fw_unit *unit,
 	oxfw->unit = unit;
 	oxfw->device_info = (const struct device_info *)id->driver_data;
 	spin_lock_init(&oxfw->lock);
+	init_waitqueue_head(&oxfw->hwdep_wait);
 
 	err = snd_oxfw_stream_discover(oxfw);
 	if (err < 0)
@@ -181,6 +182,10 @@ static int oxfw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_oxfw_create_hwdep(oxfw);
+	if (err < 0)
+		goto error;
+
 	err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index b42a5ae..d2f8ee5 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -12,6 +12,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/compat.h>
 
 #include <sound/control.h>
 #include <sound/core.h>
@@ -20,6 +21,8 @@
 #include <sound/pcm_params.h>
 #include <sound/info.h>
 #include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -63,6 +66,10 @@ struct snd_oxfw {
 	s16 volume[6];
 	s16 volume_min;
 	s16 volume_max;
+
+	int dev_lock_count;
+	bool dev_lock_changed;
+	wait_queue_head_t hwdep_wait;
 };
 
 /*
@@ -123,6 +130,10 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
 
 int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
 
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
+
 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
 
 int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
@@ -130,3 +141,5 @@ int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
 void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
 
 int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
-- 
1.9.1

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

* [PATCH] hwdep: add OXFW driver support
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (28 preceding siblings ...)
  2014-10-26 13:03 ` [PATCH 29/29] ALSA: oxfw: Add hwdep interface Takashi Sakamoto
@ 2014-10-26 13:29 ` Takashi Sakamoto
  2014-10-26 13:43   ` [PATCH alsa-lib] " Takashi Sakamoto
  2014-10-26 16:51 ` [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Stefan Richter
  2014-11-14 10:50 ` Takashi Iwai
  31 siblings, 1 reply; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:29 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel

Linux 3.19 newly support this driver. By hardware dependent interface,
userspace applications can get hardware information, lock/unlock kernel
streaming and receive lock status event.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/hwdep.h        | 3 ++-
 include/sound/asound.h | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/include/hwdep.h b/include/hwdep.h
index ad8bb49..3d3c31b 100644
--- a/include/hwdep.h
+++ b/include/hwdep.h
@@ -73,8 +73,9 @@ typedef enum _snd_hwdep_iface {
 	SND_HWDEP_IFACE_FW_DICE,	/**< TC DICE FireWire device */
 	SND_HWDEP_IFACE_FW_FIREWORKS,	/**< Echo Audio Fireworks based device */
 	SND_HWDEP_IFACE_FW_BEBOB,	/**< BridgeCo BeBoB based device */
+	SND_HWDEP_IFACE_FW_OXFW,	/**< Oxford OXFW970/971 based device */
 
-	SND_HWDEP_IFACE_LAST = SND_HWDEP_IFACE_FW_BEBOB  /**< last known hwdep interface */
+	SND_HWDEP_IFACE_LAST = SND_HWDEP_IFACE_FW_OXFW	/**< last known hwdep interface */
 } snd_hwdep_iface_t;
 
 /** open for reading */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 6ee5867..c30a4d4 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -96,9 +96,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */
 	SNDRV_HWDEP_IFACE_FW_FIREWORKS,	/* Echo Audio Fireworks based device */
 	SNDRV_HWDEP_IFACE_FW_BEBOB,	/* BridgeCo BeBoB based device */
+	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
 };
 
 struct snd_hwdep_info {
-- 
1.9.1

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

* Re: [PATCH alsa-lib] hwdep: add OXFW driver support
  2014-10-26 13:29 ` [PATCH] hwdep: add OXFW driver support Takashi Sakamoto
@ 2014-10-26 13:43   ` Takashi Sakamoto
  0 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-10-26 13:43 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel

On Oct 26 2014 22:29, Takashi Sakamoto wrote:
> Linux 3.19 newly support this driver. By hardware dependent interface,
> userspace applications can get hardware information, lock/unlock kernel
> streaming and receive lock status event.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> ---
>  include/hwdep.h        | 3 ++-
>  include/sound/asound.h | 3 ++-
>  2 files changed, 4 insertions(+), 2 deletions(-)

This patch is for alsa-lib. I forgot to address it...

> diff --git a/include/hwdep.h b/include/hwdep.h
> index ad8bb49..3d3c31b 100644
> --- a/include/hwdep.h
> +++ b/include/hwdep.h
> @@ -73,8 +73,9 @@ typedef enum _snd_hwdep_iface {
>  	SND_HWDEP_IFACE_FW_DICE,	/**< TC DICE FireWire device */
>  	SND_HWDEP_IFACE_FW_FIREWORKS,	/**< Echo Audio Fireworks based device */
>  	SND_HWDEP_IFACE_FW_BEBOB,	/**< BridgeCo BeBoB based device */
> +	SND_HWDEP_IFACE_FW_OXFW,	/**< Oxford OXFW970/971 based device */
>  
> -	SND_HWDEP_IFACE_LAST = SND_HWDEP_IFACE_FW_BEBOB  /**< last known hwdep interface */
> +	SND_HWDEP_IFACE_LAST = SND_HWDEP_IFACE_FW_OXFW	/**< last known hwdep interface */
>  } snd_hwdep_iface_t;
>  
>  /** open for reading */
> diff --git a/include/sound/asound.h b/include/sound/asound.h
> index 6ee5867..c30a4d4 100644
> --- a/include/sound/asound.h
> +++ b/include/sound/asound.h
> @@ -96,9 +96,10 @@ enum {
>  	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */
>  	SNDRV_HWDEP_IFACE_FW_FIREWORKS,	/* Echo Audio Fireworks based device */
>  	SNDRV_HWDEP_IFACE_FW_BEBOB,	/* BridgeCo BeBoB based device */
> +	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
>  
>  	/* Don't forget to change the following: */
> -	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
> +	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
>  };
>  
>  struct snd_hwdep_info {
> 

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

* Re: [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (29 preceding siblings ...)
  2014-10-26 13:29 ` [PATCH] hwdep: add OXFW driver support Takashi Sakamoto
@ 2014-10-26 16:51 ` Stefan Richter
  2014-11-14 10:50 ` Takashi Iwai
  31 siblings, 0 replies; 45+ messages in thread
From: Stefan Richter @ 2014-10-26 16:51 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, ffado-devel, alsa-devel, clemens

On Oct 26 Takashi Sakamoto wrote:
> This patchset is to enable ALSA Dice driver to handle more models, to
> improve ALSA firewire-speakers driver as OXFW driver. This patchset is
> for linux-next branch and all of my work for Linux 3.19.
[...]
> == Updates from my previous post:
> [alsa-devel] [PATCH 00/14] ALSA: Dice enhancement
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-October/081890.html
>  - Fix typo
>  - Minor code change
> 
> [alsa-devel] [RFC][PATCH 00/15 v4] OXFW driver, a succesor to firewire-speakers
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-May/076581.html
>  - Support more Mackie (Loud) models by name detection
>  - Fix a bug to handle FireWave
>  - Use AV/C Stream Format Information command to set sampling rate
[...]

I fetched this patch series from https://github.com/takaswie/sound
request_v5 branch, applied it on top of 3.17.1, and reran the tests
snd-oxfw with Lacie FireWire Speakers and Griffin FireWave which I descibed
in "Re: [RFC][PATCH 00/15 v4] OXFW driver, a succesor to firewire-speakers".
I got the same positive results.

Tested-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (Speakers, FireWave)

I am planning to test the snd-dice part of this patch series with a
Focusrite Saffire PRO 24 sometime later this week.  I can test PCM I/O but
am currently not prepared to test MIDI I/O.
-- 
Stefan Richter
-=====-====- =-=- ==-=-
http://arcgraph.de/sr/

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

* Re: [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers
  2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
                   ` (30 preceding siblings ...)
  2014-10-26 16:51 ` [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Stefan Richter
@ 2014-11-14 10:50 ` Takashi Iwai
  2014-11-14 12:08   ` Clemens Ladisch
  31 siblings, 1 reply; 45+ messages in thread
From: Takashi Iwai @ 2014-11-14 10:50 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: ffado-devel, alsa-devel, clemens

Hi,

this has been left unreviewed...  Can anyone actually review, and more
importantly test?  Otherwise it'll slip from 3.19.


thanks,

Takashi

At Sun, 26 Oct 2014 22:03:01 +0900,
Takashi Sakamoto wrote:
> 
> This patchset is to enable ALSA Dice driver to handle more models, to
> improve ALSA firewire-speakers driver as OXFW driver. This patchset is
> for linux-next branch and all of my work for Linux 3.19.
> 
> == Features:
> Currently, ALSA Dice driver is limited for playback-only devices. The aim
> of this patchset is to add supports for:
>  - Full-duplex streams with synchronization
>  - Both PCM capture and playback
>  - Both MIDI capture and playback
> 
> Currently, ALSA firewire-speakers driver is only for two playback-only models.
> And this driver has a bug for one of them. The aim of this patchset is to fix
> the bug and to add supports for more models with:
>  - Both PCM capture and playback
>  - Both MIDI capture and playback
> 
> The renew drivers doesn't support:
>  - Synchronization between multiple devices on the same bus
>    For this purpose, please join in user-space driver project (FFADO)
>  - Any control functionality for internal DSP
>    For this purpose, please use ffado-dbus-server/ffado-mixer,
>    or write your own application
> 
> 
> == Updates from my previous post:
> [alsa-devel] [PATCH 00/14] ALSA: Dice enhancement
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-October/081890.html
>  - Fix typo
>  - Minor code change
> 
> [alsa-devel] [RFC][PATCH 00/15 v4] OXFW driver, a succesor to firewire-speakers
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-May/076581.html
>  - Support more Mackie (Loud) models by name detection
>  - Fix a bug to handle FireWave
>  - Use AV/C Stream Format Information command to set sampling rate
> 
> 
> == Remarks:
>  - When the Dice driver is loaded, user-space driver fails to register
>    notification address, thus cannot handle device for streaming. See:
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081731.html
>  - OXFW chipset seems to transmit wrong values for timestamp, thus the
>    driver don't apply full-duplex streams with synchronization
> 
> 
> == Supported Dice-based models (as long as I know):
> TC Electronic
>  - Desktop Konnekt 6
>  - Impact Twin
>  - Konnekt 8/24D/Live
>  - Studio Konnekt 48
> PreSonus
>  - FireStudio 26x26/Mobile/Project/Tube/LightPipe
>  - StudioLive 1602/1642/2442
>  - FireStation
> Mackie
>  - Onyx Blackbird
>  - Onyx i-series (latter models)
> Focusrite
>  - Saffire Pro 14/24/24 DSP/26/40/56
> M-Audio
>  - Profire 610/2626
> Alessis
>  - IO 24/26
>  - MultiMix 12/16
> Weiss
>  - ADC2
>  - Vesta
>  - Mineva
>  - AFI 1
>  - TAG DAC 1
>  - INT 202
>  - DAC 202
>  - Flex 5000
> DnR
>  - Axus FireWire I/O card 16x16
> Lexicon
>  - I-ONIX FW 810S
> Allen and Heath
>  - Zed R16
> Midas
>  - Venice F32
> 
> == Supported OXFW-based models (as long as I know):
> Griffin:
>  - FireWave
> LaCie:
>  - Firewire Speakers
> Behringer:
>  - F-Control Audio 202
> Mackie (Loud):
>  - Onyx-i series (former models)
>  - Onyx Satellite
>  - Tapco Link.Firewire
>  - d.2 pro/d.4 pro
>  - U.420/U.420d
> 
> Takashi Sakamoto (29):
>   ALSA: dice: Rename structure and its members
>   ALSA: dice: Move file to its own directory
>   ALSA: dice: Split transaction functionality into a file
>   ALSA: dice: Split stream functionality into a file
>   ALSA: dice: Split PCM functionality into a file
>   ALSA: dice: Split hwdep functionality into a file
>   ALSA: dice: Split proc interface into a file
>   ALSA: dice: Add new functions for constraints of PCM parameters
>   ALSA: dice: Change the way to start stream
>   ALSA: dice: Add support for duplex streams with synchronization
>   ALSA: dice: Support for non SYT-Match sampling clock source mode
>   ALSA: dice: Add support for capturing PCM samples
>   ALSA: dice: Add support for MIDI capture/playback
>   ALSA: dice: remove experimental state
>   ALSA: speakers: Rename to oxfw and rename some members
>   ALSA: oxfw: Move to its own directory
>   ALSA: oxfw: Split stream functionality to a new file and add a header
>     file
>   ALSA: oxfw: Split PCM functionality to a new file
>   ALSA: oxfw: Split control functionality to a new file
>   ALSA: oxfw: Change the way to name card
>   ALSA: oxfw: Add support for AV/C stream format command to get/set
>     supported stream formation
>   ALSA: oxfw: Change the way to make PCM rules/constraints
>   ALSA: oxfw: Add proc interface for debugging purpose
>   ALSA: oxfw: Change the way to start stream
>   ALSA: oxfw: Add support for Behringer/Mackie devices
>   ALSA: oxfw: Add support AMDTP in-stream
>   ALSA: oxfw: add support for capturing PCM samples
>   ALSA: oxfw: Add support for capture/playback MIDI messages
>   ALSA: oxfw: Add hwdep interface
> 
>  include/uapi/sound/asound.h                |    3 +-
>  include/uapi/sound/firewire.h              |    3 +-
>  sound/firewire/Kconfig                     |   26 +-
>  sound/firewire/Makefile                    |    6 +-
>  sound/firewire/dice.c                      | 1511 ----------------------------
>  sound/firewire/dice/Makefile               |    3 +
>  sound/firewire/dice/dice-hwdep.c           |  190 ++++
>  sound/firewire/{ => dice}/dice-interface.h |    0
>  sound/firewire/dice/dice-midi.c            |  176 ++++
>  sound/firewire/dice/dice-pcm.c             |  404 ++++++++
>  sound/firewire/dice/dice-proc.c            |  252 +++++
>  sound/firewire/dice/dice-stream.c          |  423 ++++++++
>  sound/firewire/dice/dice-transaction.c     |  382 +++++++
>  sound/firewire/dice/dice.c                 |  359 +++++++
>  sound/firewire/dice/dice.h                 |  189 ++++
>  sound/firewire/oxfw/Makefile               |    3 +
>  sound/firewire/oxfw/oxfw-command.c         |  153 +++
>  sound/firewire/oxfw/oxfw-control.c         |  283 ++++++
>  sound/firewire/oxfw/oxfw-hwdep.c           |  190 ++++
>  sound/firewire/oxfw/oxfw-midi.c            |  189 ++++
>  sound/firewire/oxfw/oxfw-pcm.c             |  399 ++++++++
>  sound/firewire/oxfw/oxfw-proc.c            |  113 +++
>  sound/firewire/oxfw/oxfw-stream.c          |  702 +++++++++++++
>  sound/firewire/oxfw/oxfw.c                 |  310 ++++++
>  sound/firewire/oxfw/oxfw.h                 |  145 +++
>  sound/firewire/speakers.c                  |  792 ---------------
>  26 files changed, 4887 insertions(+), 2319 deletions(-)
>  delete mode 100644 sound/firewire/dice.c
>  create mode 100644 sound/firewire/dice/Makefile
>  create mode 100644 sound/firewire/dice/dice-hwdep.c
>  rename sound/firewire/{ => dice}/dice-interface.h (100%)
>  create mode 100644 sound/firewire/dice/dice-midi.c
>  create mode 100644 sound/firewire/dice/dice-pcm.c
>  create mode 100644 sound/firewire/dice/dice-proc.c
>  create mode 100644 sound/firewire/dice/dice-stream.c
>  create mode 100644 sound/firewire/dice/dice-transaction.c
>  create mode 100644 sound/firewire/dice/dice.c
>  create mode 100644 sound/firewire/dice/dice.h
>  create mode 100644 sound/firewire/oxfw/Makefile
>  create mode 100644 sound/firewire/oxfw/oxfw-command.c
>  create mode 100644 sound/firewire/oxfw/oxfw-control.c
>  create mode 100644 sound/firewire/oxfw/oxfw-hwdep.c
>  create mode 100644 sound/firewire/oxfw/oxfw-midi.c
>  create mode 100644 sound/firewire/oxfw/oxfw-pcm.c
>  create mode 100644 sound/firewire/oxfw/oxfw-proc.c
>  create mode 100644 sound/firewire/oxfw/oxfw-stream.c
>  create mode 100644 sound/firewire/oxfw/oxfw.c
>  create mode 100644 sound/firewire/oxfw/oxfw.h
>  delete mode 100644 sound/firewire/speakers.c
> 
> -- 
> 1.9.1
> 

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

* Re: [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers
  2014-11-14 10:50 ` Takashi Iwai
@ 2014-11-14 12:08   ` Clemens Ladisch
  0 siblings, 0 replies; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-14 12:08 UTC (permalink / raw)
  To: Takashi Iwai, Takashi Sakamoto; +Cc: alsa-devel, ffado-devel

Takashi Iwai wrote:
> this has been left unreviewed...  Can anyone actually review, and more
> importantly test?

This is quite near the top of my TODO list.  I might be able to find
some time this weekend.


Regards,
Clemens

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

* Re: [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices
  2014-10-26 13:03 ` [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices Takashi Sakamoto
@ 2014-11-16 20:57   ` Clemens Ladisch
  2014-11-18 15:24     ` Takashi Sakamoto
  0 siblings, 1 reply; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-16 20:57 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> +++ b/sound/firewire/oxfw/oxfw.c
> +static bool detect_loud_models(struct fw_unit *unit)
> +{
> +	const char *const models[] = {
> +		"Onyxi",
> +		"Onyx-i",
> +		"d.Pro",
> +		"Mackie Onyx Satellite",
> +		"Tapco LINK.firewire 4x6",
> +		"U.420"};
> +	char model[32] = {0};

This initialization is not needed.

> +	unsigned int i;
> +	int err;
> +
> +	err = fw_csr_string(unit->directory, CSR_MODEL,
> +			    model, sizeof(model));
> +	if (err < 0)
> +		return err;
> +
> +	model[31] = '\0';

The returned string is guaranteed to be zero-terminated.

> +	for (i = 0; i < ARRAY_SIZE(models); i++) {
> +		if (strncmp(models[i], model, strlen(model) == 0))

Why not simply strcmp()?

>  static int name_card(struct snd_oxfw *oxfw)
>  {
>  	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
> +	char vendor[24] = {0};
> +	char model[32] = {0};

Same unneeded initialization.


Regards,
Clemens

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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-10-26 13:03 ` [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream Takashi Sakamoto
@ 2014-11-16 21:21   ` Clemens Ladisch
  2014-11-20 10:32     ` Takashi Sakamoto
  0 siblings, 1 reply; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-16 21:21 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> +++ b/sound/firewire/oxfw/oxfw-stream.c
> +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
> +				  struct amdtp_stream *stream,
> +				  unsigned int rate, unsigned int pcm_channels)
> +{
> ...
> +	if (atomic_read(substreams) == 0)
> +		goto end;
> +
>  	mutex_lock(&oxfw->mutex);

What happens if hw_free is called between the calls to atomic_read() and
mutex_lock()?

And why are the substreams counters atomic?
Can't they just be normal variables accessed from inside the mutex?


Regards,
Clemens

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

* Re: [PATCH 02/29] ALSA: dice: Move file to its own directory
  2014-10-26 13:03 ` [PATCH 02/29] ALSA: dice: Move file to its own directory Takashi Sakamoto
@ 2014-11-18 12:57   ` Clemens Ladisch
  2014-11-18 15:29     ` Takashi Sakamoto
  0 siblings, 1 reply; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-18 12:57 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> In followed commits, dice driver is split into several files. For easily
> managing these files, this commit adds subdirectory and move file into
> the directory.

> -// TODO: rx index
> +/* TODO: rx index */

> -		event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
> +		event.dice_notification.type =
> +					SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;

> -		count = min(count, (long)sizeof(event.dice_notification));
> +		count = min_t(long, count, sizeof(event.dice_notification));

> -static void dice_bus_reset(struct fw_unit *unit)
> +static void dice_update(struct fw_unit *unit)

Please mention these changes in the commit message, or move them into
a separate patch.


Regards,
Clemens

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

* Re: [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices
  2014-11-16 20:57   ` Clemens Ladisch
@ 2014-11-18 15:24     ` Takashi Sakamoto
  0 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-11-18 15:24 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: tiwai, alsa-devel, ffado-devel

Hi Clemens,

On Nov 17 2014 05:57, Clemens Ladisch wrote:
> Takashi Sakamoto wrote:
>> +++ b/sound/firewire/oxfw/oxfw.c
>> +static bool detect_loud_models(struct fw_unit *unit)
>> +{
>> +	const char *const models[] = {
>> +		"Onyxi",
>> +		"Onyx-i",
>> +		"d.Pro",
>> +		"Mackie Onyx Satellite",
>> +		"Tapco LINK.firewire 4x6",
>> +		"U.420"};
>> +	char model[32] = {0};
> 
> This initialization is not needed.
> 
>> +	unsigned int i;
>> +	int err;
>> +
>> +	err = fw_csr_string(unit->directory, CSR_MODEL,
>> +			    model, sizeof(model));
>> +	if (err < 0)
>> +		return err;
>> +
>> +	model[31] = '\0';
> 
> The returned string is guaranteed to be zero-terminated.
> 
>> +	for (i = 0; i < ARRAY_SIZE(models); i++) {
>> +		if (strncmp(models[i], model, strlen(model) == 0))
> 
> Why not simply strcmp()?
> 
>>  static int name_card(struct snd_oxfw *oxfw)
>>  {
>>  	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
>> +	char vendor[24] = {0};
>> +	char model[32] = {0};
> 
> Same unneeded initialization.

I forgot that fw_csr_string() guarantees to return string with
null-terminated...


Thanks

Takashi Sakamoto

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

* Re: [PATCH 02/29] ALSA: dice: Move file to its own directory
  2014-11-18 12:57   ` Clemens Ladisch
@ 2014-11-18 15:29     ` Takashi Sakamoto
  0 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-11-18 15:29 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: tiwai, alsa-devel, ffado-devel

On Nov 18 2014 21:57, Clemens Ladisch wrote:
> Takashi Sakamoto wrote:
>> In followed commits, dice driver is split into several files. For easily
>> managing these files, this commit adds subdirectory and move file into
>> the directory.
> 
>> -// TODO: rx index
>> +/* TODO: rx index */
> 
>> -		event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
>> +		event.dice_notification.type =
>> +					SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
> 
>> -		count = min(count, (long)sizeof(event.dice_notification));
>> +		count = min_t(long, count, sizeof(event.dice_notification));
> 
>> -static void dice_bus_reset(struct fw_unit *unit)
>> +static void dice_update(struct fw_unit *unit)
> 
> Please mention these changes in the commit message, or move them into
> a separate patch.

OK. The breaking-line and replacing with min_t is due to checkpatch.pl.
The renaming is to apply the same way which I use on bebob/fireworks but
is unnecessary.

As you mentioned, it's better to add a separete patch as a fix for them.


Thanks

Takashi Sakamoto

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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-11-16 21:21   ` Clemens Ladisch
@ 2014-11-20 10:32     ` Takashi Sakamoto
  2014-11-24  1:03       ` Takashi Sakamoto
  2014-11-24 13:54       ` Clemens Ladisch
  0 siblings, 2 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-11-20 10:32 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: tiwai, alsa-devel, ffado-devel

[-- Attachment #1: Type: text/plain, Size: 1392 bytes --]

Hi Clemens,

On Nov 17 2014 06:21, Clemens Ladisch wrote:
> Takashi Sakamoto wrote:
>> +++ b/sound/firewire/oxfw/oxfw-stream.c
>> +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
>> +				  struct amdtp_stream *stream,
>> +				  unsigned int rate, unsigned int pcm_channels)
>> +{
>> ...
>> +	if (atomic_read(substreams) == 0)
>> +		goto end;
>> +
>>  	mutex_lock(&oxfw->mutex);
> 
> What happens if hw_free is called between the calls to atomic_read() and
> mutex_lock()?

In the worst case, the codes after the mutex_lock() manage to start
duplex streams with released resources, then it causes kernel panic.

This is a bug. I should have move atmic_read() to the critical section...

> And why are the substreams counters atomic?
> Can't they just be normal variables accessed from inside the mutex?

Just for my convinience. I didn't want to write many
mutex_lock()/mutex_unlock() because wrong coding of them causes-dead
lock, then push them inner stream.c. This idea is good except for
reference couters, so I added atomic_t.

An attached patch achieves your idea over my patchset. Handling
reference counter is easy to understand (because it's arithmetric
operation) but I don't like much mutex_lock()/mutex_unlock().

Can I request you to explain about the advantages of your idea?


Regards

Takashi Sakamoto
o-takashi@sakamocchi.jp

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-dice-use-mutex-only-instead-of-atomic_t-to-handle-cr.patch --]
[-- Type: text/x-diff; name="0001-dice-use-mutex-only-instead-of-atomic_t-to-handle-cr.patch", Size: 9361 bytes --]

From 3e9968a4063f7b5d2977970821f34d5df3b70c57 Mon Sep 17 00:00:00 2001
From: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Date: Thu, 20 Nov 2014 10:28:08 +0900
Subject: [PATCH] dice: use mutex only instead of atomic_t to handle critical
 section easily

Dice driver has some critical sections. Some of them are protected by atomic_t
and the others are protected by mutex. But they can be merged.

Originally, I added atomic_t for a reference counter to enclose
mutex_lock()/mutex_unlock() inner -stream.c But it's cumbersome that this
driver uses some mutual primitives for stream handling.

This commit obsoletes atomic_t and use mutex only.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/dice/dice-midi.c   | 16 ++++++++++++----
 sound/firewire/dice/dice-pcm.c    | 30 ++++++++++++++++++++++++------
 sound/firewire/dice/dice-stream.c | 29 +++++++----------------------
 sound/firewire/dice/dice.c        |  4 ++++
 sound/firewire/dice/dice.h        |  2 +-
 5 files changed, 48 insertions(+), 33 deletions(-)

diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index 4f4eae7..74cd3b3 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -16,8 +16,10 @@ static int capture_open(struct snd_rawmidi_substream *substream)
 	if (err < 0)
 		goto end;
 
-	atomic_inc(&dice->substreams_counter);
+	mutex_lock(&dice->mutex);
+	dice->substreams_counter++;
 	err = snd_dice_stream_start_duplex(dice, 0);
+	mutex_unlock(&dice->mutex);
 	if (err < 0)
 		snd_dice_stream_lock_release(dice);
 end:
@@ -33,8 +35,10 @@ static int playback_open(struct snd_rawmidi_substream *substream)
 	if (err < 0)
 		goto end;
 
-	atomic_inc(&dice->substreams_counter);
+	mutex_lock(&dice->mutex);
+	dice->substreams_counter++;
 	err = snd_dice_stream_start_duplex(dice, 0);
+	mutex_unlock(&dice->mutex);
 	if (err < 0)
 		snd_dice_stream_lock_release(dice);
 end:
@@ -45,8 +49,10 @@ static int capture_close(struct snd_rawmidi_substream *substream)
 {
 	struct snd_dice *dice = substream->rmidi->private_data;
 
-	atomic_dec(&dice->substreams_counter);
+	mutex_lock(&dice->mutex);
+	dice->substreams_counter--;
 	snd_dice_stream_stop_duplex(dice);
+	mutex_unlock(&dice->mutex);
 
 	snd_dice_stream_lock_release(dice);
 	return 0;
@@ -56,8 +62,10 @@ static int playback_close(struct snd_rawmidi_substream *substream)
 {
 	struct snd_dice *dice = substream->rmidi->private_data;
 
-	atomic_dec(&dice->substreams_counter);
+	mutex_lock(&dice->mutex);
+	dice->substreams_counter--;
 	snd_dice_stream_stop_duplex(dice);
+	mutex_unlock(&dice->mutex);
 
 	snd_dice_stream_lock_release(dice);
 	return 0;
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 3427a21..f777145 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -231,8 +231,11 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
 {
 	struct snd_dice *dice = substream->private_data;
 
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		atomic_inc(&dice->substreams_counter);
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&dice->mutex);
+		dice->substreams_counter++;
+		mutex_unlock(&dice->mutex);
+	}
 
 	amdtp_stream_set_pcm_format(&dice->tx_stream,
 				    params_format(hw_params));
@@ -245,8 +248,11 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
 {
 	struct snd_dice *dice = substream->private_data;
 
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		atomic_inc(&dice->substreams_counter);
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&dice->mutex);
+		dice->substreams_counter++;
+		mutex_unlock(&dice->mutex);
+	}
 
 	amdtp_stream_set_pcm_format(&dice->rx_stream,
 				    params_format(hw_params));
@@ -259,11 +265,15 @@ static int capture_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
+	mutex_lock(&dice->mutex);
+
 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		atomic_dec(&dice->substreams_counter);
+		dice->substreams_counter--;
 
 	snd_dice_stream_stop_duplex(dice);
 
+	mutex_unlock(&dice->mutex);
+
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
@@ -271,11 +281,15 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dice *dice = substream->private_data;
 
+	mutex_lock(&dice->mutex);
+
 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		atomic_dec(&dice->substreams_counter);
+		dice->substreams_counter--;
 
 	snd_dice_stream_stop_duplex(dice);
 
+	mutex_unlock(&dice->mutex);
+
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
@@ -284,7 +298,9 @@ static int capture_prepare(struct snd_pcm_substream *substream)
 	struct snd_dice *dice = substream->private_data;
 	int err;
 
+	mutex_lock(&dice->mutex);
 	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+	mutex_unlock(&dice->mutex);
 	if (err >= 0)
 		amdtp_stream_pcm_prepare(&dice->tx_stream);
 
@@ -295,7 +311,9 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 	struct snd_dice *dice = substream->private_data;
 	int err;
 
+	mutex_lock(&dice->mutex);
 	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+	mutex_unlock(&dice->mutex);
 	if (err >= 0)
 		amdtp_stream_pcm_prepare(&dice->rx_stream);
 
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index 37d1aab..edb6777 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -193,11 +193,9 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 	enum cip_flags sync_mode;
 	int err = 0;
 
-	if (atomic_read(&dice->substreams_counter) == 0)
+	if (dice->substreams_counter == 0)
 		goto end;
 
-	mutex_lock(&dice->mutex);
-
 	err = get_sync_mode(dice, &sync_mode);
 	if (err < 0)
 		goto end;
@@ -271,23 +269,18 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 		}
 	}
 end:
-	mutex_unlock(&dice->mutex);
 	return err;
 }
 
 void snd_dice_stream_stop_duplex(struct snd_dice *dice)
 {
-	if (atomic_read(&dice->substreams_counter) > 0)
+	if (dice->substreams_counter > 0)
 		return;
 
-	mutex_lock(&dice->mutex);
-
 	snd_dice_transaction_clear_enable(dice);
 
 	stop_stream(dice, &dice->tx_stream);
 	stop_stream(dice, &dice->rx_stream);
-
-	mutex_unlock(&dice->mutex);
 }
 
 static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
@@ -332,7 +325,7 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
 {
 	int err;
 
-	atomic_set(&dice->substreams_counter, 0);
+	dice->substreams_counter = 0;
 
 	err = init_stream(dice, &dice->tx_stream);
 	if (err < 0)
@@ -345,8 +338,6 @@ end:
 
 void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
 {
-	mutex_lock(&dice->mutex);
-
 	snd_dice_transaction_clear_enable(dice);
 
 	stop_stream(dice, &dice->tx_stream);
@@ -355,13 +346,14 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
 	stop_stream(dice, &dice->rx_stream);
 	destroy_stream(dice, &dice->rx_stream);
 
-	atomic_set(&dice->substreams_counter, 0);
-
-	mutex_unlock(&dice->mutex);
+	dice->substreams_counter = 0;
 }
 
 void snd_dice_stream_update_duplex(struct snd_dice *dice)
 {
+	/* The enable register becomes initialized, then streams are stopped. */
+	dice->global_enabled = false;
+
 	/*
 	 * On a bus reset, the DICE firmware disables streaming and then goes
 	 * off contemplating its own navel for hundreds of milliseconds before
@@ -370,18 +362,11 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
 	 * to stop so that the application can restart them in an orderly
 	 * manner.
 	 */
-	mutex_lock(&dice->mutex);
-
-	/* The enable register becomes initialized, then streams are stopped. */
-	dice->global_enabled = false;
-
 	stop_stream(dice, &dice->rx_stream);
 	stop_stream(dice, &dice->tx_stream);
 
 	fw_iso_resources_update(&dice->rx_resources);
 	fw_iso_resources_update(&dice->tx_resources);
-
-	mutex_unlock(&dice->mutex);
 }
 
 static void dice_lock_changed(struct snd_dice *dice)
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index b6b2c92..2a5605f 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -316,10 +316,14 @@ static void dice_update(struct fw_unit *unit)
 {
 	struct snd_dice *dice = dev_get_drvdata(&unit->device);
 
+	mutex_lock(&dice->mutex);
+
 	/* The handler address register becomes initialized. */
 	snd_dice_transaction_reinit(dice);
 
 	snd_dice_stream_update_duplex(dice);
+
+	mutex_unlock(&dice->mutex);
 }
 
 #define DICE_INTERFACE	0x000001
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 40552ca..d8b721c 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -77,7 +77,7 @@ struct snd_dice {
 	struct amdtp_stream rx_stream;
 	bool global_enabled;
 	struct completion clock_accepted;
-	atomic_t substreams_counter;
+	unsigned int substreams_counter;
 };
 
 enum snd_dice_addr_type {
-- 
2.1.0


[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-11-20 10:32     ` Takashi Sakamoto
@ 2014-11-24  1:03       ` Takashi Sakamoto
  2014-11-24 13:54       ` Clemens Ladisch
  1 sibling, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-11-24  1:03 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: tiwai, alsa-devel, ffado-devel

[-- Attachment #1: Type: text/plain, Size: 946 bytes --]

Clemens,

On Nov 20 2014 19:32, Takashi Sakamoto wrote:
>> And why are the substreams counters atomic?
>> Can't they just be normal variables accessed from inside the mutex?
> 
> Just for my convinience. I didn't want to write many
> mutex_lock()/mutex_unlock() because wrong coding of them causes-dead
> lock, then push them inner stream.c. This idea is good except for
> reference couters, so I added atomic_t.
> 
> An attached patch achieves your idea over my patchset. Handling
> reference counter is easy to understand (because it's arithmetric
> operation) but I don't like much mutex_lock()/mutex_unlock().
> 
> Can I request you to explain about the advantages of your idea?

Oops. I attached a patch for Dice driver...

But my strategy for programming is nearly the same. Would you please
give me some comments so as I can post new patchsets in this week?


Regards

Takashi Sakamoto
o-takashi@sakamocchi.jp


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-oxfw-use-mutex-only-instead-of-atomic_t-to-handle-cr.patch --]
[-- Type: text/x-diff; name="0001-oxfw-use-mutex-only-instead-of-atomic_t-to-handle-cr.patch", Size: 10855 bytes --]

From cecd70d492e343e802f71e9cca94b94fb46efa93 Mon Sep 17 00:00:00 2001
From: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Date: Thu, 20 Nov 2014 09:57:09 +0900
Subject: [PATCH] oxfw: use mutex only instead of atomic_t to handle critical
 section easily

OXFW driver has some critical sections. Some of them are protected by atomic_t
and the others are protected by mutex. But they can be merged.

Originally, I added atomic_t for a reference counter to enclose
mutex_lock()/mutex_unlock() inner -stream.c. But it's cumbersome that this
driver has some mutual primitives for stream handling.

This commit obsoletes atomic_t and use mutex only.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/oxfw/oxfw-midi.c   | 16 ++++++++++++----
 sound/firewire/oxfw/oxfw-pcm.c    | 34 ++++++++++++++++++++++++++--------
 sound/firewire/oxfw/oxfw-stream.c | 39 +++++++++++----------------------------
 sound/firewire/oxfw/oxfw.c        |  4 ++++
 sound/firewire/oxfw/oxfw.h        |  4 ++--
 5 files changed, 55 insertions(+), 42 deletions(-)

diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
index 94bc8d0..331838e 100644
--- a/sound/firewire/oxfw/oxfw-midi.c
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
 	if (err < 0)
 		goto end;
 
-	atomic_inc(&oxfw->capture_substreams);
+	mutex_lock(&oxfw->mutex);
+	oxfw->capture_substreams++;
 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+	mutex_unlock(&oxfw->mutex);
 	if (err < 0)
 		snd_oxfw_stream_lock_release(oxfw);
 end:
@@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
 	if (err < 0)
 		goto end;
 
-	atomic_inc(&oxfw->playback_substreams);
+	mutex_lock(&oxfw->mutex);
+	oxfw->playback_substreams++;
 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+	mutex_unlock(&oxfw->mutex);
 	if (err < 0)
 		snd_oxfw_stream_lock_release(oxfw);
 end:
@@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
 
-	atomic_dec(&oxfw->capture_substreams);
+	mutex_lock(&oxfw->mutex);
+	oxfw->capture_substreams--;
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+	mutex_unlock(&oxfw->mutex);
 
 	snd_oxfw_stream_lock_release(oxfw);
 	return 0;
@@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
 
-	atomic_dec(&oxfw->playback_substreams);
+	mutex_lock(&oxfw->mutex);
+	oxfw->playback_substreams--;
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+	mutex_unlock(&oxfw->mutex);
 
 	snd_oxfw_stream_lock_release(oxfw);
 	return 0;
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index c508a1f..475d1aa 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -232,8 +232,12 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		atomic_inc(&oxfw->capture_substreams);
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&oxfw->mutex);
+		oxfw->capture_substreams++;
+		mutex_unlock(&oxfw->mutex);
+	}
+
 	amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
 
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -244,8 +248,12 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		atomic_inc(&oxfw->playback_substreams);
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&oxfw->mutex);
+		oxfw->playback_substreams++;
+		mutex_unlock(&oxfw->mutex);
+	}
+
 	amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
 
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -256,8 +264,11 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		atomic_dec(&oxfw->capture_substreams);
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&oxfw->mutex);
+		oxfw->capture_substreams--;
+		mutex_unlock(&oxfw->mutex);
+	}
 
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
 
@@ -267,8 +278,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_oxfw *oxfw = substream->private_data;
 
-	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		atomic_dec(&oxfw->playback_substreams);
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+		mutex_lock(&oxfw->mutex);
+		oxfw->playback_substreams--;
+		mutex_unlock(&oxfw->mutex);
+	}
 
 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
 
@@ -281,8 +295,10 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
+	mutex_lock(&oxfw->mutex);
 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
 					    runtime->rate, runtime->channels);
+	mutex_unlock(&oxfw->mutex);
 	if (err < 0)
 		goto end;
 
@@ -296,8 +312,10 @@ static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
+	mutex_lock(&oxfw->mutex);
 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
 					    runtime->rate, runtime->channels);
+	mutex_unlock(&oxfw->mutex);
 	if (err < 0)
 		goto end;
 
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index b1dcc68..00870df 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -219,8 +219,6 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
 		s_dir = AMDTP_OUT_STREAM;
 	}
 
-	mutex_lock(&oxfw->mutex);
-
 	err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
 	if (err < 0)
 		goto end;
@@ -236,7 +234,6 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
 	if (stream == &oxfw->tx_stream)
 		oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
 end:
-	mutex_unlock(&oxfw->mutex);
 	return err;
 }
 
@@ -247,17 +244,17 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
 	struct amdtp_stream *opposite;
 	struct snd_oxfw_stream_formation formation;
 	enum avc_general_plug_dir dir;
-	atomic_t *substreams, *opposite_substreams;
+	unsigned int substreams, opposite_substreams;
 	int err = 0;
 
 	if (stream == &oxfw->tx_stream) {
-		substreams = &oxfw->capture_substreams;
+		substreams = oxfw->capture_substreams;
 		opposite = &oxfw->rx_stream;
-		opposite_substreams = &oxfw->playback_substreams;
+		opposite_substreams = oxfw->playback_substreams;
 		dir = AVC_GENERAL_PLUG_DIR_OUT;
 	} else {
-		substreams = &oxfw->playback_substreams;
-		opposite_substreams = &oxfw->capture_substreams;
+		substreams = oxfw->playback_substreams;
+		opposite_substreams = oxfw->capture_substreams;
 
 		if (oxfw->has_output)
 			opposite = &oxfw->rx_stream;
@@ -267,11 +264,9 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
 		dir = AVC_GENERAL_PLUG_DIR_IN;
 	}
 
-	if (atomic_read(substreams) == 0)
+	if (substreams == 0)
 		goto end;
 
-	mutex_lock(&oxfw->mutex);
-
 	/*
 	 * Considering JACK/FFADO streaming:
 	 * TODO: This can be removed hwdep functionality becomes popular.
@@ -310,7 +305,7 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
 
 		/* Start opposite stream if needed. */
 		if (opposite && !amdtp_stream_running(opposite) &&
-		    (atomic_read(opposite_substreams) > 0)) {
+		    (opposite_substreams > 0)) {
 			err = start_stream(oxfw, opposite, rate, 0);
 			if (err < 0) {
 				dev_err(&oxfw->unit->device,
@@ -328,22 +323,17 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
 				"fail to start stream: %d\n", err);
 	}
 end:
-	mutex_unlock(&oxfw->mutex);
 	return err;
 }
 
 void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
 				  struct amdtp_stream *stream)
 {
-	if (((stream == &oxfw->tx_stream) &&
-	     (atomic_read(&oxfw->capture_substreams) > 0)) ||
-	    ((stream == &oxfw->rx_stream) &&
-	     (atomic_read(&oxfw->playback_substreams) > 0)))
+	if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
+	    ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
 		return;
 
-	mutex_lock(&oxfw->mutex);
 	stop_stream(oxfw, stream);
-	mutex_unlock(&oxfw->mutex);
 }
 
 void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
@@ -356,14 +346,10 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
 	else
 		conn = &oxfw->in_conn;
 
-	mutex_lock(&oxfw->mutex);
-
 	stop_stream(oxfw, stream);
 
 	amdtp_stream_destroy(stream);
 	cmp_connection_destroy(conn);
-
-	mutex_unlock(&oxfw->mutex);
 }
 
 void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
@@ -376,13 +362,10 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
 	else
 		conn = &oxfw->in_conn;
 
-	if (cmp_connection_update(conn) < 0) {
-		mutex_lock(&oxfw->mutex);
+	if (cmp_connection_update(conn) < 0)
 		stop_stream(oxfw, stream);
-		mutex_unlock(&oxfw->mutex);
-	} else {
+	else
 		amdtp_stream_update(stream);
-	}
 }
 
 int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 40cd2c0..60c68ce 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -214,10 +214,14 @@ static void oxfw_bus_reset(struct fw_unit *unit)
 {
 	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
+	mutex_lock(&oxfw->mutex);
+
 	fcp_bus_reset(oxfw->unit);
 	snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
 	if (oxfw->has_output)
 		snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+
+	mutex_unlock(&oxfw->mutex);
 }
 
 static void oxfw_remove(struct fw_unit *unit)
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index d2f8ee5..65b95f2 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -56,8 +56,8 @@ struct snd_oxfw {
 	struct cmp_connection in_conn;
 	struct amdtp_stream tx_stream;
 	struct amdtp_stream rx_stream;
-	atomic_t capture_substreams;
-	atomic_t playback_substreams;
+	unsigned int capture_substreams;
+	unsigned int playback_substreams;
 
 	unsigned int midi_input_ports;
 	unsigned int midi_output_ports;
-- 
2.1.0


[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-11-20 10:32     ` Takashi Sakamoto
  2014-11-24  1:03       ` Takashi Sakamoto
@ 2014-11-24 13:54       ` Clemens Ladisch
       [not found]         ` <54746395.4070807@sakamocchi.jp>
  1 sibling, 1 reply; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-24 13:54 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> On Nov 17 2014 06:21, Clemens Ladisch wrote:
>> And why are the substreams counters atomic?
>> Can't they just be normal variables accessed from inside the mutex?
>
> Just for my convinience. I didn't want to write many
> mutex_lock()/mutex_unlock() because wrong coding of them causes-dead
> lock

Just use a kernel compiled with CONFIG_PROVE_LOCKING.

> An attached patch achieves your idea over my patchset. Handling
> reference counter is easy to understand (because it's arithmetric
> operation) but I don't like much mutex_lock()/mutex_unlock().
>
> Can I request you to explain about the advantages of your idea?

It makes the driver work correctly.

There's nothing wrong with atomic_t itself, but in this case, none of
the accesses would be valid outside of the lock, so it is not necessary
for them to be atomic.

In theory, it would be possible to use atomic_t to synchronize multiple
streams, but this requires that
1. two CPUs that start a stream at the same time do not both think they
   are the first, so you *must* use something like atomic_inc_and_test(),
   and
2. when one CPU is still starting a stream, code on all other CPUs must
   be able to work correctly, without synchronization.  This is not
   possible if the code requires to know that the DMA actually has
   started; consider the following code:

   void start_some_substream(...)
   {
       if (atomic_inc_and_test(substreams)) {
           mutex_lock();
           // lots of stuff to start DMA ...
           mutex_unlock();
       } else {
           // wait for the DMA to be started
       }
       ...
   }

   How could the 'else' branch ensure that the DMA engine is started?
   Just taking the mutex is not enough, because it could happen before
   the mutex_lock() of the first stream.  This is not possible without
   moving the inc_and_test into the locked region.


Regards,
Clemens

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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
       [not found]         ` <54746395.4070807@sakamocchi.jp>
@ 2014-11-25 12:04           ` Clemens Ladisch
  2014-11-25 22:41             ` Takashi Sakamoto
  0 siblings, 1 reply; 45+ messages in thread
From: Clemens Ladisch @ 2014-11-25 12:04 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> On Nov 24 2014 22:54, Clemens Ladisch wrote:
>> In theory, it would be possible to use atomic_t to synchronize multiple
>> streams, but this requires that
>> 1. two CPUs that start a stream at the same time do not both think they
>>    are the first, ... and
>> 2. when one CPU is still starting a stream, code on all other CPUs must
>>    be able to work correctly, without synchronization.  [...]
>
> If I rewrite your pseudo code, like this:
>
>>    void start_some_substream(...)
>>    {
>>        if (atomic_inc_and_test(substreams)) {
>>            mutex_lock();
>>            // lots of stuff to start DMA ...
>
>              if (wait_for_first_packet() == ETIMEDOUT) {
>                  stop_dma();
>                  release_streams();
>                  mutex_unlock();
>                  return ETIMEDOUT;
>              }
>>            mutex_unlock();
>>        }
>>        ...
>>    }

This is an even better example of why atomic_t is incorrect:  when the
startup fails, the second call to start_some_substream() at the same
time, which just sees that substreams>0, cannot notice the error, and
thinks its own substream has been started successfully.


Regards,
Clemens

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

* Re: [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream
  2014-11-25 12:04           ` Clemens Ladisch
@ 2014-11-25 22:41             ` Takashi Sakamoto
  0 siblings, 0 replies; 45+ messages in thread
From: Takashi Sakamoto @ 2014-11-25 22:41 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: tiwai, alsa-devel, ffado-devel

On Nov 15 2014 21:04, Clemens Ladisch wrote:
> Takashi Sakamoto wrote:
>> If I rewrite your pseudo code, like this:
>>
>>>    void start_some_substream(...)
>>>    {
>>>        if (atomic_inc_and_test(substreams)) {
>>>            mutex_lock();
>>>            // lots of stuff to start DMA ...
>>
>>              if (wait_for_first_packet() == ETIMEDOUT) {
>>                  stop_dma();
>>                  release_streams();
>>                  mutex_unlock();
>>                  return ETIMEDOUT;
>>              }
>>>            mutex_unlock();
>>>        }
>>>        ...
>>>    }
> 
> This is an even better example of why atomic_t is incorrect:  when the
> startup fails, the second call to start_some_substream() at the same
> time, which just sees that substreams>0, cannot notice the error, and
> thinks its own substream has been started successfully.

Yes. The reference counter is invalid in this pseudo code which I
showrf. I left your insistence about 'atomic_inc_and_test()' what it is.

More precisely, current implementations (bebob/fireworks/dice) are (or
should be for oxfw) like:

void start_some_substream(...)
{
    mutex_lock();

    if (atomic_read(substreams) == 0)
        goto end;

    if (streams_are_running())
        goto end;

    if (!start_isochronous_context_and_dma()) {
        release_isochronous_context_and_dma();
        release_streams();
        goto end;
    }

    if (wait_for_first_packet() == ETIMEDOUT) {
        release_isochronous_context_and_dma();
        release_streams();
    }
end:
    mutex_unlock();
    return err;
}

I believe that the reference counter with atomic_t is valid regardless
of inner/outer the lock (mutex). For this critical section, it's just a
importance that some substreams already subscribe the stream or not,
instead of the number of substreams.

If the reference counter is non-zero, it means that 'a stream is
requested to run for some substream, even if the substreams are not for
current caller'.

I don't show in the pseudo code; when the streams are already running
but the sampling transfer frequency is not the same as the one which the
caller requested, then the streams are stopped once and set sampling
transfer frequency and restart them.


Regards

Takashi Sakamoto
o-takashi@sakamocchi.jp

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

end of thread, other threads:[~2014-11-25 22:41 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-26 13:03 [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 01/29] ALSA: dice: Rename structure and its members Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 02/29] ALSA: dice: Move file to its own directory Takashi Sakamoto
2014-11-18 12:57   ` Clemens Ladisch
2014-11-18 15:29     ` Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 03/29] ALSA: dice: Split transaction functionality into a file Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 04/29] ALSA: dice: Split stream " Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 05/29] ALSA: dice: Split PCM " Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 06/29] ALSA: dice: Split hwdep " Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 07/29] ALSA: dice: Split proc interface " Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 08/29] ALSA: dice: Add new functions for constraints of PCM parameters Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 09/29] ALSA: dice: Change the way to start stream Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 10/29] ALSA: dice: Add support for duplex streams with synchronization Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 11/29] ALSA: dice: Support for non SYT-Match sampling clock source mode Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 12/29] ALSA: dice: Add support for capturing PCM samples Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 13/29] ALSA: dice: Add support for MIDI capture/playback Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 14/29] ALSA: dice: remove experimental state Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 15/29] ALSA: speakers: Rename to oxfw and rename some members Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 16/29] ALSA: oxfw: Move to its own directory Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 17/29] ALSA: oxfw: Split stream functionality to a new file and add a header file Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 18/29] ALSA: oxfw: Split PCM functionality to a new file Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 19/29] ALSA: oxfw: Split control " Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 20/29] ALSA: oxfw: Change the way to name card Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 21/29] ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 22/29] ALSA: oxfw: Change the way to make PCM rules/constraints Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 23/29] ALSA: oxfw: Add proc interface for debugging purpose Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 24/29] ALSA: oxfw: Change the way to start stream Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 25/29] ALSA: oxfw: Add support for Behringer/Mackie devices Takashi Sakamoto
2014-11-16 20:57   ` Clemens Ladisch
2014-11-18 15:24     ` Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 26/29] ALSA: oxfw: Add support AMDTP in-stream Takashi Sakamoto
2014-11-16 21:21   ` Clemens Ladisch
2014-11-20 10:32     ` Takashi Sakamoto
2014-11-24  1:03       ` Takashi Sakamoto
2014-11-24 13:54       ` Clemens Ladisch
     [not found]         ` <54746395.4070807@sakamocchi.jp>
2014-11-25 12:04           ` Clemens Ladisch
2014-11-25 22:41             ` Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 27/29] ALSA: oxfw: add support for capturing PCM samples Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 28/29] ALSA: oxfw: Add support for capture/playback MIDI messages Takashi Sakamoto
2014-10-26 13:03 ` [PATCH 29/29] ALSA: oxfw: Add hwdep interface Takashi Sakamoto
2014-10-26 13:29 ` [PATCH] hwdep: add OXFW driver support Takashi Sakamoto
2014-10-26 13:43   ` [PATCH alsa-lib] " Takashi Sakamoto
2014-10-26 16:51 ` [PATCH 00/29 v2] ALSA: Enhancement for existed FireWire drivers Stefan Richter
2014-11-14 10:50 ` Takashi Iwai
2014-11-14 12:08   ` Clemens Ladisch

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