-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ALSA: firewire-motu: add support for MOTU 828
MOTU 828 is a first model in MOTU FireWire series, produced in 2001. This model consists of three chips: * Texas Instruments TSB41AB1 (Physical layer for IEEE 1394 bus) * Philips Semiconductors 1394L21BE (Link layer for IEEE 1394 bus and packet processing layer) * QuickLogic QuickRAM QL4016 (Data block processing layer and digital signal processing) This commit adds a support for this model, with its unique protocol as version 1. The features of this protocol are: * no MIDI support. * Rx packets have no data chunks for control and status messages. * Tx packets have 2 data chunks for control and status messages in the end of each data block. The chunks consist of data block counter (4 byte) and message (2 byte). * All of settings are represented in bit flag in one quadlet address (0x'ffff'f000'0b00). * When optical interface is configured as S/PDIF, signals of the interface is multiplexed for packets, instead of signals of coaxial interface. * The internal multiplexer is not configured by software. I note that the device has a quirk to mute output voluntarily during receiving batch of packets in the beginning of packet streaming. The operation to unmute should be done by software enough after the device shifts the state, however it's not deterministic. Furthermore, just after switching rate of sampling clock, the device keeps the state longer. This patch manages to sleep 100 msec before unmute operation, but it may fail to release the mute in the case that the rate is changed. As a workaround, users can restart packet streaming at the same rate, or write to specific register from userspace. $ python3 crpp < /sys/bus/firewire/devices/fw1/config_rom ROM header and bus information block ----------------------------------------------------------------- 400 04105c54 bus_info_length 4, crc_length 16, crc 23636 404 31333934 bus_name "1394" 408 20001000 irmc 0, cmc 0, isc 1, bmc 0, cyc_clk_acc 0, max_rec 1 (4) 40c 0001f200 company_id 0001f2 | 410 00005015 device_id 0000005015 | EUI-64 0001f20000005015 root directory ----------------------------------------------------------------- 414 0004c65c directory_length 4, crc 50780 418 030001f2 vendor 41c 0c0083c0 node capabilities per IEEE 1394 420 8d000006 --> eui-64 leaf at 438 424 d1000001 --> unit directory at 428 unit directory at 428 ----------------------------------------------------------------- 428 00035052 directory_length 3, crc 20562 42c 120001f2 specifier id 430 13000001 version 434 17101800 model eui-64 leaf at 438 ----------------------------------------------------------------- 438 0002eeb6 leaf_length 2, crc 61110 43c 0001f200 company_id 0001f2 | 440 00005015 device_id 0000005015 | EUI-64 0001f20000005015 Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Link: https://lore.kernel.org/r/20210616082847.124688-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai <tiwai@suse.de>
- Loading branch information
Takashi Sakamoto
authored and
Takashi Iwai
committed
Jun 17, 2021
1 parent
36d1a67
commit d13d6b2
Showing
5 changed files
with
252 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
|
||
// motu-protocol-v1.c - a part of driver for MOTU FireWire series | ||
// | ||
// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp> | ||
// | ||
// Licensed under the terms of the GNU General Public License, version 2. | ||
|
||
#include "motu.h" | ||
|
||
#include <linux/delay.h> | ||
|
||
// Status register for MOTU 828 (0x'ffff'f000'0b00). | ||
// | ||
// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c. | ||
// 0x00008000: mode of optical input interface. | ||
// 0x00008000: for S/PDIF signal. | ||
// 0x00000000: disabled or for ADAT signal. | ||
// 0x00004000: mode of optical output interface. | ||
// 0x00004000: for S/PDIF signal. | ||
// 0x00000000: disabled or for ADAT signal. | ||
// 0x00003f40: monitor input mode. | ||
// 0x00000800: analog-1/2 | ||
// 0x00001a00: analog-3/4 | ||
// 0x00002c00: analog-5/6 | ||
// 0x00003e00: analog-7/8 | ||
// 0x00000000: analog-1 | ||
// 0x00000900: analog-2 | ||
// 0x00001200: analog-3 | ||
// 0x00001b00: analog-4 | ||
// 0x00002400: analog-5 | ||
// 0x00002d00: analog-6 | ||
// 0x00003600: analog-7 | ||
// 0x00003f00: analog-8 | ||
// 0x00000040: disabled | ||
// 0x00000004: rate of sampling clock. | ||
// 0x00000004: 48.0 kHz | ||
// 0x00000000: 44.1 kHz | ||
// 0x00000023: source of sampling clock. | ||
// 0x00000002: S/PDIF on optical/coaxial interface. | ||
// 0x00000021: ADAT on optical interface | ||
// 0x00000001: ADAT on Dsub 9pin | ||
// 0x00000000: internal or SMPTE | ||
|
||
#define CLK_828_STATUS_OFFSET 0x0b00 | ||
#define CLK_828_STATUS_MASK 0x0000ffff | ||
#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000 | ||
#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000 | ||
#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080 | ||
#define CLK_828_STATUS_FLAG_SRC_IS_NOT_FROM_ADAT_DSUB 0x00000020 | ||
#define CLK_828_STATUS_FLAG_OUTPUT_MUTE 0x00000008 | ||
#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004 | ||
#define CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX 0x00000002 | ||
#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB 0x00000001 | ||
|
||
static void parse_clock_rate_828(u32 data, unsigned int *rate) | ||
{ | ||
if (data & CLK_828_STATUS_FLAG_RATE_48000) | ||
*rate = 48000; | ||
else | ||
*rate = 44100; | ||
} | ||
|
||
static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate) | ||
{ | ||
__be32 reg; | ||
int err; | ||
|
||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
if (err < 0) | ||
return err; | ||
parse_clock_rate_828(be32_to_cpu(reg), rate); | ||
|
||
return 0; | ||
} | ||
|
||
int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate) | ||
{ | ||
if (motu->spec == &snd_motu_spec_828) | ||
return get_clock_rate_828(motu, rate); | ||
else | ||
return -ENXIO; | ||
} | ||
|
||
static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate) | ||
{ | ||
__be32 reg; | ||
u32 data; | ||
int err; | ||
|
||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
if (err < 0) | ||
return err; | ||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; | ||
|
||
data &= ~CLK_828_STATUS_FLAG_RATE_48000; | ||
if (rate == 48000) | ||
data |= CLK_828_STATUS_FLAG_RATE_48000; | ||
|
||
reg = cpu_to_be32(data); | ||
return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
} | ||
|
||
int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate) | ||
{ | ||
if (motu->spec == &snd_motu_spec_828) | ||
return set_clock_rate_828(motu, rate); | ||
else | ||
return -ENXIO; | ||
} | ||
|
||
static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src) | ||
{ | ||
__be32 reg; | ||
u32 data; | ||
int err; | ||
|
||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
if (err < 0) | ||
return err; | ||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; | ||
|
||
if (data & CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB) { | ||
if (data & CLK_828_STATUS_FLAG_SRC_IS_NOT_FROM_ADAT_DSUB) | ||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; | ||
else | ||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; | ||
} else if (data & CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX) { | ||
if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF) | ||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; | ||
else | ||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; | ||
} else { | ||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src) | ||
{ | ||
if (motu->spec == &snd_motu_spec_828) | ||
return get_clock_source_828(motu, src); | ||
else | ||
return -ENXIO; | ||
} | ||
|
||
static int switch_fetching_mode_828(struct snd_motu *motu, bool enable) | ||
{ | ||
__be32 reg; | ||
u32 data; | ||
int err; | ||
|
||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
if (err < 0) | ||
return err; | ||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; | ||
|
||
data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE); | ||
if (enable) { | ||
// This transaction should be initiated after the device receives batch of packets | ||
// since the device voluntarily mutes outputs. As a workaround, yield processor over | ||
// 100 msec. | ||
msleep(100); | ||
data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE; | ||
} | ||
|
||
reg = cpu_to_be32(data); | ||
return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
} | ||
|
||
int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable) | ||
{ | ||
if (motu->spec == &snd_motu_spec_828) | ||
return switch_fetching_mode_828(motu, enable); | ||
else | ||
return -ENXIO; | ||
} | ||
|
||
static int detect_packet_formats_828(struct snd_motu *motu) | ||
{ | ||
__be32 reg; | ||
u32 data; | ||
int err; | ||
|
||
motu->tx_packet_formats.pcm_byte_offset = 4; | ||
motu->tx_packet_formats.msg_chunks = 2; | ||
|
||
motu->rx_packet_formats.pcm_byte_offset = 4; | ||
motu->rx_packet_formats.msg_chunks = 0; | ||
|
||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); | ||
if (err < 0) | ||
return err; | ||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; | ||
|
||
// The number of chunks is just reduced when SPDIF is activated. | ||
if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)) | ||
motu->tx_packet_formats.pcm_chunks[0] += 8; | ||
|
||
if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF)) | ||
motu->rx_packet_formats.pcm_chunks[0] += 8; | ||
|
||
return 0; | ||
} | ||
|
||
int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu) | ||
{ | ||
memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks, | ||
sizeof(motu->tx_packet_formats.pcm_chunks)); | ||
memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks, | ||
sizeof(motu->rx_packet_formats.pcm_chunks)); | ||
|
||
if (motu->spec == &snd_motu_spec_828) | ||
return detect_packet_formats_828(motu); | ||
else | ||
return 0; | ||
} | ||
|
||
const struct snd_motu_spec snd_motu_spec_828 = { | ||
.name = "828", | ||
.protocol_version = SND_MOTU_PROTOCOL_V1, | ||
.tx_fixed_pcm_chunks = {10, 0, 0}, | ||
.rx_fixed_pcm_chunks = {10, 0, 0}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters