alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
From: Takashi Sakamoto <o-takashi@sakamocchi.jp>
To: clemens@ladisch.de, tiwai@suse.de, perex@perex.cz
Cc: alsa-devel@alsa-project.org, ffado-devel@lists.sf.net
Subject: [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture
Date: Fri, 28 Feb 2014 12:27:18 +0900	[thread overview]
Message-ID: <1393558072-25926-6-git-send-email-o-takashi@sakamocchi.jp> (raw)
In-Reply-To: <1393558072-25926-1-git-send-email-o-takashi@sakamocchi.jp>

For capturing PCM, this commit adds the functionality to handle in-stream.
This is also applied for dual-wire mode.

Currently, capturing 32bit samples are supported.

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

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index ca79cb4..4ebfd67 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -39,6 +39,7 @@
  * only "Clock-based rate control mode" is supported
  */
 #define AMDTP_FDF_AM824		(0 << (CIP_FDF_SFC_SHIFT + 3))
+#define AMDTP_FDF_NO_DATA	0xff
 #define AMDTP_DBS_MASK		0x00ff0000
 #define AMDTP_DBS_SHIFT		16
 #define AMDTP_DBC_MASK		0x000000ff
@@ -47,6 +48,7 @@
 #define INTERRUPT_INTERVAL	16
 #define QUEUE_LENGTH		48
 
+#define IN_PACKET_HEADER_SIZE	4
 #define OUT_PACKET_HEADER_SIZE	0
 
 static void pcm_period_tasklet(unsigned long data);
@@ -179,6 +181,12 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32(struct amdtp_stream *s,
+			   struct snd_pcm_substream *pcm,
+			   __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
+				    struct snd_pcm_substream *pcm,
+				    __be32 *buffer, unsigned int frames);
 
 /**
  * amdtp_stream_set_pcm_format - set the PCM format
@@ -200,16 +208,26 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
 		WARN_ON(1);
 		/* fall through */
 	case SNDRV_PCM_FORMAT_S16:
-		if (s->dual_wire)
-			s->transfer_samples = amdtp_write_s16_dualwire;
-		else
-			s->transfer_samples = amdtp_write_s16;
-		break;
+		if (s->direction == AMDTP_OUT_STREAM) {
+			if (s->dual_wire)
+				s->transfer_samples = amdtp_write_s16_dualwire;
+			else
+				s->transfer_samples = amdtp_write_s16;
+			break;
+		}
+		WARN_ON(1);
+		/* fall through */
 	case SNDRV_PCM_FORMAT_S32:
-		if (s->dual_wire)
-			s->transfer_samples = amdtp_write_s32_dualwire;
-		else
-			s->transfer_samples = amdtp_write_s32;
+		if (s->direction == AMDTP_OUT_STREAM) {
+			if (s->dual_wire)
+				s->transfer_samples = amdtp_write_s32_dualwire;
+			else
+				s->transfer_samples = amdtp_write_s32;
+		} else if (s->dual_wire) {
+			s->transfer_samples = amdtp_read_s32_dualwire;
+		} else {
+			s->transfer_samples = amdtp_read_s32;
+		}
 		break;
 	}
 }
@@ -420,6 +438,58 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 	}
 }
 
+static void amdtp_read_s32(struct amdtp_stream *s,
+			   struct snd_pcm_substream *pcm,
+			   __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int remaining_frames, i, c;
+	u32 *dst;
+
+	dst  = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < s->pcm_channels; ++c) {
+			*dst = be32_to_cpu(buffer[c]) << 8;
+			dst++;
+		}
+		buffer += s->data_block_quadlets;
+		if (--remaining_frames == 0)
+			dst = (void *)runtime->dma_area;
+	}
+}
+
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
+				    struct snd_pcm_substream *pcm,
+				    __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int channels, remaining_frames, i, c;
+	u32 *dst;
+
+	dst = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+	channels = s->pcm_channels / 2;
+
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < channels; ++c) {
+			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			dst++;
+		}
+		buffer += 1;
+		for (c = 0; c < channels; ++c) {
+			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			dst++;
+		}
+		buffer += s->data_block_quadlets - 1;
+		if (--remaining_frames == 0)
+			dst = (void *)runtime->dma_area;
+	}
+}
+
 static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
 				   __be32 *buffer, unsigned int frames)
 {
@@ -505,6 +575,12 @@ static inline int queue_out_packet(struct amdtp_stream *s,
 			    payload_length, skip);
 }
 
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+	return queue_packet(s, IN_PACKET_HEADER_SIZE,
+			    amdtp_stream_get_max_payload(s), false);
+}
+
 static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
 {
 	__be32 *buffer;
@@ -552,6 +628,67 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
 		update_pcm_pointers(s, pcm, data_blocks);
 }
 
+static void handle_in_packet(struct amdtp_stream *s,
+			     unsigned int payload_quadlets,
+			     __be32 *buffer)
+{
+	u32 cip_header[2];
+	unsigned int data_block_quadlets, data_blocks, data_block_counter;
+	struct snd_pcm_substream *pcm;
+
+	cip_header[0] = be32_to_cpu(buffer[0]);
+	cip_header[1] = be32_to_cpu(buffer[1]);
+
+	/*
+	 * This module supports 'Two-quadlet CIP header with SYT field'.
+	 * For convinience, also check FMT field is AM824 or not.
+	 */
+	if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+	    ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
+	    ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+		dev_info_ratelimited(&s->unit->device,
+				"Invalid CIP header for AMDTP: %08X:%08X\n",
+				cip_header[0], cip_header[1]);
+		return;
+	}
+
+	/* ignore empty CIP packet or NO-DATA AMDTP packet */
+	if ((payload_quadlets < 3) ||
+	    (((cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SFC_SHIFT) ==
+							AMDTP_FDF_NO_DATA))
+		return;
+
+	data_block_quadlets =
+			(cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+	/* avoid division by zero */
+	if (data_block_quadlets == 0) {
+		dev_info_ratelimited(&s->unit->device,
+				 "Detect invalid value in dbs field: %08X\n",
+				 data_block_quadlets);
+		return;
+	}
+
+	data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+	data_block_counter = cip_header[1] & AMDTP_DBC_MASK;
+
+	/* check packet continuity */
+	s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+	if (s->data_block_counter != data_block_counter) {
+		dev_info_ratelimited(&s->unit->device,
+				     "Detect uncontinuity of CIP packets\n");
+		s->data_block_counter = data_block_counter;
+		return;
+	}
+
+	buffer += 2;
+
+	pcm = ACCESS_ONCE(s->pcm);
+	if (pcm) {
+		s->transfer_samples(s, pcm, buffer, data_blocks);
+		update_pcm_pointers(s, pcm, data_blocks);
+	}
+}
+
 static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 				size_t header_length, void *header,
 				void *private_data)
@@ -571,6 +708,35 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 	fw_iso_context_queue_flush(s->context);
 }
 
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+			       size_t header_length, void *header,
+			       void *private_data)
+{
+	struct amdtp_stream *s = private_data;
+	unsigned int p, packets, data_quadlets;
+	__be32 *buffer, *headers = header;
+
+	/* The number of packets in buffer */
+	packets = header_length / IN_PACKET_HEADER_SIZE;
+
+	for (p = 0; p < packets; p++) {
+		buffer = s->buffer.packets[s->packet_index].buffer;
+
+		/* The number of quadlets in this packet */
+		data_quadlets =
+			(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+		/* handle each packet data */
+		handle_in_packet(s, data_quadlets, buffer);
+
+		if (queue_in_packet(s) < 0) {
+			amdtp_stream_pcm_abort(s);
+			return;
+		}
+	}
+
+	fw_iso_context_queue_flush(s->context);
+}
+
 /**
  * amdtp_stream_start - start transferring packets
  * @s: the AMDTP stream to start
@@ -595,7 +761,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 		[CIP_SFC_88200]  = {  0,   67 },
 		[CIP_SFC_176400] = {  0,   67 },
 	};
-	int err;
+	unsigned int header_size;
+	enum dma_data_direction dir;
+	fw_iso_callback_t cb;
+	int type, err;
 
 	mutex_lock(&s->mutex);
 
@@ -609,16 +778,26 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	s->syt_offset_state = initial_state[s->sfc].syt_offset;
 	s->last_syt_offset = TICKS_PER_CYCLE;
 
+	/* initialize packet buffer */
+	if (s->direction == AMDTP_IN_STREAM) {
+		dir = DMA_FROM_DEVICE;
+		type = FW_ISO_CONTEXT_RECEIVE;
+		header_size = IN_PACKET_HEADER_SIZE;
+		cb = in_stream_callback;
+	} else {
+		dir = DMA_TO_DEVICE;
+		type = FW_ISO_CONTEXT_TRANSMIT;
+		header_size = OUT_PACKET_HEADER_SIZE;
+		cb = out_stream_callback;
+	}
 	err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-				      amdtp_stream_get_max_payload(s),
-				      DMA_TO_DEVICE);
+				      amdtp_stream_get_max_payload(s), dir);
 	if (err < 0)
 		goto err_unlock;
 
 	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-					   FW_ISO_CONTEXT_TRANSMIT,
-					   channel, speed, 0,
-					   out_stream_callback, s);
+					   type, channel, speed, header_size,
+					   cb, s);
 	if (IS_ERR(s->context)) {
 		err = PTR_ERR(s->context);
 		if (err == -EBUSY)
@@ -631,13 +810,18 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
 	s->packet_index = 0;
 	do {
-		err = queue_out_packet(s, 0, true);
+		if (s->direction == AMDTP_IN_STREAM)
+			err = queue_in_packet(s);
+		else
+			err = queue_out_packet(s, 0, true);
 		if (err < 0)
 			goto err_context;
 	} while (s->packet_index > 0);
 
+	/* NOTE: TAG1 matches CIP. This just affects in stream */
 	s->data_block_counter = 0;
-	err = fw_iso_context_start(s->context, -1, 0, 0);
+	err = fw_iso_context_start(s->context, -1, 0,
+				   FW_ISO_CONTEXT_MATCH_TAG1);
 	if (err < 0)
 		goto err_context;
 
-- 
1.8.3.2

  parent reply	other threads:[~2014-02-28  3:28 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 02/39] firewire-lib: Add macros instead of fixed value " Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 03/39] firewire-lib: Add 'direction' member to 'amdtp_stream' structure Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 04/39] firewire-lib: Split some codes into functions to reuse for both streams Takashi Sakamoto
2014-02-28  3:27 ` Takashi Sakamoto [this message]
2014-02-28  3:27 ` [PATCH 06/39] firewire-lib: Add support for MIDI capture/playback Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 07/39] firewire-lib: Give syt value as parameter to handle_out_packet() Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 08/39] firewire-lib: Add support for duplex streams synchronization in blocking mode Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 09/39] firewire-lib: Add sort function for transmitted packet Takashi Sakamoto
2014-02-28  6:40   ` Takashi Iwai
2014-02-28 14:31     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 10/39] firewire-lib: Add transfer delay to synchronized duplex streams Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 11/39] firewire-lib: Add support for channel mapping Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 12/39] firewire-lib: Rename macros, variables and functions for CMP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 13/39] firewire-lib: Add 'direction' member to 'cmp_connection' structure Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 15/39] firewire-lib: Add a new function to check others' connection Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 16/39] firewire-lib: Add some AV/C general commands Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 17/39] firewire-lib: Add quirks for Fireworks Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
2014-02-28 20:25   ` Stefan Richter
2014-02-28 20:39     ` Stefan Richter
2014-03-01  3:18     ` Takashi Sakamoto
2014-03-01 10:10       ` Stefan Richter
2014-03-01 12:22         ` Takashi Sakamoto
2014-03-01 14:20           ` Stefan Richter
2014-03-04  1:35             ` [FFADO-devel] " Jonathan Woithe
2014-03-04  8:33               ` Stefan Richter
2014-03-04  9:28                 ` Stefan Richter
2014-03-01 10:34   ` Stefan Richter
2014-03-01 12:23     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 19/39] fireworks: Add skelton for Fireworks based devices Takashi Sakamoto
2014-02-28  6:51   ` Takashi Iwai
2014-02-28 15:05     ` Takashi Sakamoto
2014-02-28 15:10       ` Takashi Iwai
2014-02-28 15:36         ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 20/39] fireworks: Add transaction and some commands Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 21/39] fireworks: Add connection and stream management Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 22/39] fireworks: Add proc interface for debugging purpose Takashi Sakamoto
2014-02-28  6:54   ` Takashi Iwai
2014-03-01 13:17     ` Takashi Sakamoto
2014-03-03  9:01       ` Takashi Iwai
2014-03-04 14:10         ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 23/39] fireworks: Add MIDI interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 24/39] fireworks: Add PCM interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 25/39] fireworks: Add hwdep interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 26/39] fireworks: Add command/response functionality into " Takashi Sakamoto
2014-02-28  6:58   ` Takashi Iwai
2014-03-01 13:18     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 27/39] bebob: Add skelton for BeBoB based devices Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 28/39] bebob: Add commands and connections/streams management Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 29/39] bebob: Add proc interface for debugging purpose Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 30/39] bebob: Add MIDI interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 31/39] bebob: Add PCM interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 32/39] bebob: Add hwdep interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 33/39] bebob: Prepare for device specific operations Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 34/39] bebob: Add support for Terratec PHASE, EWS series and Aureon Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 35/39] bebob: Add support for Yamaha GO series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 36/39] bebob: Add support for Focusrite Saffire/SaffirePro series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 37/39] bebob: Add support for M-Audio usual Firewire series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 38/39] bebob: Send a cue to load firmware for M-Audio " Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 39/39] bebob: Add support for M-Audio special " Takashi Sakamoto
2014-03-01 10:53   ` Stefan Richter
2014-03-01 13:06     ` Takashi Sakamoto
2014-03-01 14:32       ` Stefan Richter
2014-02-28  6:36 ` [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Iwai
2014-03-01 13:14   ` Takashi Sakamoto
     [not found] <5316963F.1000206@sakamocchi.jp>
2014-03-05 10:47 ` [GIT PULL][PATCH 00/39 v2] " Takashi Sakamoto
2014-03-05 10:47   ` [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture Takashi Sakamoto
2014-03-09 20:37     ` Clemens Ladisch
2014-03-10  3:55       ` Takashi Sakamoto
2014-03-12  2:00       ` Takashi Sakamoto
2014-03-14 13:50       ` Takashi Sakamoto

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1393558072-25926-6-git-send-email-o-takashi@sakamocchi.jp \
    --to=o-takashi@sakamocchi.jp \
    --cc=alsa-devel@alsa-project.org \
    --cc=clemens@ladisch.de \
    --cc=ffado-devel@lists.sf.net \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).