// SPDX-License-Identifier: GPL-2.0-only /* * oxfw_stream.c - a part of driver for OXFW970/971 based devices * * Copyright (c) 2014 Takashi Sakamoto
*/
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++) {
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 set_rate(oxfw, rate);
/* Calculate format length. */
len = 5 + formats[i][4] * 2;
// 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;
err = start_stream(oxfw, &oxfw->rx_stream); if (err < 0) {
dev_err(&oxfw->unit->device, "fail to prepare rx stream: %d\n", err); goto error;
}
if (oxfw->has_output &&
!amdtp_stream_running(&oxfw->tx_stream)) {
err = start_stream(oxfw, &oxfw->tx_stream); if (err < 0) {
dev_err(&oxfw->unit->device, "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;
} elseif (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;
}
}
// 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;
int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
{ int err;
err = init_stream(oxfw, &oxfw->rx_stream); if (err < 0) return err;
if (oxfw->has_output) {
err = init_stream(oxfw, &oxfw->tx_stream); if (err < 0) {
destroy_stream(oxfw, &oxfw->rx_stream); return err;
}
}
err = amdtp_domain_init(&oxfw->domain); if (err < 0) {
destroy_stream(oxfw, &oxfw->rx_stream); if (oxfw->has_output)
destroy_stream(oxfw, &oxfw->tx_stream);
}
return err;
}
// This function should be called before starting the stream or after stopping // the streams. void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
{
amdtp_domain_destroy(&oxfw->domain);
destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
destroy_stream(oxfw, &oxfw->tx_stream);
}
if (oxfw->has_output) {
cmp_connection_break(&oxfw->out_conn);
amdtp_stream_pcm_abort(&oxfw->tx_stream);
}
}
int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, struct snd_oxfw_stream_formation *formation)
{ int err;
if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
u8 *format; unsignedint len;
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. unsignedint rate;
u8 *const *formats; int i;
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;
}
return err;
}
/* * 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(const u8 *format, struct snd_oxfw_stream_formation *formation)
{ unsignedint i, e, channels, type;
/* * 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 -ENXIO;
/* 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 -ENXIO;
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 -ENXIO; /* not supported */
}
}
if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) return -ENXIO;
// 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;
/* 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;
/* get first entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0); if (err == -ENXIO) { /* LIST subfunction is not implemented */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
err = assume_stream_formats(oxfw, dir, pid, buf, &len,
formats); goto end;
} elseif (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;
/* 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;
} elseif (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]; struct snd_oxfw_stream_formation formation;
u8 *format; unsignedint i; 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;
} elseif ((plugs[0] == 0) && (plugs[1] == 0)) {
err = -ENXIO; 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) { if (err != -ENXIO) return err;
// The oPCR is not available for isoc communication.
err = 0;
} else { 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;
/* Add one MIDI port. */ if (formation.midi > 0)
oxfw->midi_input_ports = 1;
}
oxfw->has_output = true;
}
}
/* use iPCR[0] if exists */ if (plugs[0] > 0) {
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); if (err < 0) { if (err != -ENXIO) return err;
// The iPCR is not available for isoc communication.
err = 0;
} else { 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;
/* Add one MIDI port. */ if (formation.midi > 0)
oxfw->midi_output_ports = 1;
}
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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.