summaryrefslogtreecommitdiff
path: root/sound/firewire/oxfw/oxfw-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/oxfw/oxfw-stream.c')
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c188
1 files changed, 123 insertions, 65 deletions
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 80c9dc13f1b5..5e36d7153a7b 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
-#define CALLBACK_TIMEOUT 200
+#define READY_TIMEOUT_MS 600
/*
* According to datasheet of Oxford Semiconductor:
@@ -153,12 +153,32 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
struct cmp_connection *conn;
enum cmp_direction c_dir;
enum amdtp_stream_direction s_dir;
+ unsigned int flags = 0;
int err;
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_BLOCKING_TRANSMISSION))
+ flags |= CIP_NONBLOCKING;
+ else
+ flags |= CIP_BLOCKING;
+
+ // OXFW 970/971 has no function to generate playback timing according to the sequence
+ // of value in syt field, thus the packet should include NO_INFO value in the field.
+ // However, some models just ignore data blocks in packet with NO_INFO for audio data
+ // processing.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET))
+ flags |= CIP_UNAWARE_SYT;
+
if (stream == &oxfw->tx_stream) {
conn = &oxfw->out_conn;
c_dir = CMP_OUTPUT;
s_dir = AMDTP_IN_STREAM;
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD)
+ flags |= CIP_JUMBO_PAYLOAD;
+ if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS)
+ flags |= CIP_WRONG_DBS;
+ if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS)
+ flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS;
} else {
conn = &oxfw->in_conn;
c_dir = CMP_INPUT;
@@ -169,24 +189,12 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
if (err < 0)
return err;
- err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, flags);
if (err < 0) {
cmp_connection_destroy(conn);
return err;
}
- /*
- * OXFW starts to transmit packets with non-zero dbc.
- * OXFW postpone transferring packets till handling any asynchronous
- * packets. As a result, next isochronous packet includes more data
- * blocks than IEC 61883-6 defines.
- */
- if (stream == &oxfw->tx_stream) {
- oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
- if (oxfw->wrong_dbs)
- oxfw->tx_stream.flags |= CIP_WRONG_DBS;
- }
-
return 0;
}
@@ -338,6 +346,9 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
}
if (!amdtp_stream_running(&oxfw->rx_stream)) {
+ unsigned int tx_init_skip_cycles = 0;
+ bool replay_seq = false;
+
err = start_stream(oxfw, &oxfw->rx_stream);
if (err < 0) {
dev_err(&oxfw->unit->device,
@@ -353,26 +364,32 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
"fail to prepare tx stream: %d\n", err);
goto error;
}
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD) {
+ // Just after changing sampling transfer frequency, many cycles are
+ // skipped for packet transmission.
+ tx_init_skip_cycles = 400;
+ } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) {
+ // It takes a bit time for target device to adjust event frequency
+ // according to nominal event frequency in isochronous packets from
+ // ALSA oxfw driver.
+ tx_init_skip_cycles = 4000;
+ } else {
+ replay_seq = true;
+ }
}
- err = amdtp_domain_start(&oxfw->domain, 0);
+ // NOTE: The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&oxfw->domain, tx_init_skip_cycles, replay_seq, false);
if (err < 0)
goto error;
- // Wait first packet.
- if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
- CALLBACK_TIMEOUT)) {
+ if (!amdtp_domain_wait_ready(&oxfw->domain, READY_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
-
- if (oxfw->has_output) {
- if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
- CALLBACK_TIMEOUT)) {
- err = -ETIMEDOUT;
- goto error;
- }
- }
}
return 0;
@@ -471,26 +488,57 @@ 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;
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ u8 *format;
+ unsigned int len;
- err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
- if (err < 0)
- goto end;
- if (len < 3) {
- err = -EIO;
- goto end;
+ 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) {
+ if (len < 3)
+ err = -EIO;
+ else
+ err = snd_oxfw_stream_parse_format(format, formation);
+ }
+
+ kfree(format);
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the duplicated hard-coded format, instead.
+ unsigned int rate;
+ u8 *const *formats;
+ int i;
+
+ err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0);
+ if (err < 0)
+ return err;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ formats = oxfw->rx_stream_formats;
+ else
+ formats = oxfw->tx_stream_formats;
+
+ for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) {
+ if (!formats[i])
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], formation);
+ if (err < 0)
+ continue;
+
+ if (formation->rate == rate)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EIO;
}
- err = snd_oxfw_stream_parse_format(format, formation);
-end:
- kfree(format);
return err;
}
@@ -500,7 +548,7 @@ end:
* 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,
+int snd_oxfw_stream_parse_format(const u8 *format,
struct snd_oxfw_stream_formation *formation)
{
unsigned int i, e, channels, type;
@@ -585,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
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;
+ // get format at current sampling rate.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ 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;
+ }
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the hard-coded format, instead.
+ buf[0] = 0x90;
+ buf[1] = 0x40;
+ buf[2] = avc_stream_rate_table[0];
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[5] = 0x08;
+ else
+ buf[5] = 0x02;
+
+ buf[6] = 0x06;
+
+ *len = 7;
}
/* parse and set stream format */
@@ -799,33 +866,24 @@ void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
{
- int err;
-
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
/* user land lock this */
- if (oxfw->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (oxfw->dev_lock_count < 0)
+ return -EBUSY;
/* 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;
+ return 0;
}
void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
{
- spin_lock_irq(&oxfw->lock);
+ guard(spinlock_irq)(&oxfw->lock);
if (WARN_ON(oxfw->dev_lock_count <= 0))
- goto end;
+ return;
if (--oxfw->dev_lock_count == 0)
snd_oxfw_stream_lock_changed(oxfw);
-end:
- spin_unlock_irq(&oxfw->lock);
}