2019-05-29 16:57:59 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-03-22 21:30:26 +09:00
/*
* motu - protocol - v2 . c - a part of driver for MOTU FireWire series
*
* Copyright ( c ) 2015 - 2017 Takashi Sakamoto < o - takashi @ sakamocchi . jp >
*/
# include "motu.h"
# define V2_CLOCK_STATUS_OFFSET 0x0b14
# define V2_CLOCK_RATE_MASK 0x00000038
# define V2_CLOCK_RATE_SHIFT 3
# define V2_CLOCK_SRC_MASK 0x00000007
# define V2_CLOCK_SRC_SHIFT 0
2019-10-30 17:06:43 +09:00
# define V2_CLOCK_FETCH_ENABLE 0x02000000
# define V2_CLOCK_MODEL_SPECIFIC 0x04000000
2017-03-22 21:30:26 +09:00
# define V2_IN_OUT_CONF_OFFSET 0x0c04
# define V2_OPT_OUT_IFACE_MASK 0x00000c00
# define V2_OPT_OUT_IFACE_SHIFT 10
# define V2_OPT_IN_IFACE_MASK 0x00000300
# define V2_OPT_IN_IFACE_SHIFT 8
# define V2_OPT_IFACE_MODE_NONE 0
# define V2_OPT_IFACE_MODE_ADAT 1
# define V2_OPT_IFACE_MODE_SPDIF 2
2019-10-30 17:06:42 +09:00
static int get_clock_rate ( u32 data , unsigned int * rate )
{
unsigned int index = ( data & V2_CLOCK_RATE_MASK ) > > V2_CLOCK_RATE_SHIFT ;
if ( index > = ARRAY_SIZE ( snd_motu_clock_rates ) )
return - EIO ;
* rate = snd_motu_clock_rates [ index ] ;
return 0 ;
}
2020-05-19 20:16:31 +09:00
int snd_motu_protocol_v2_get_clock_rate ( struct snd_motu * motu ,
unsigned int * rate )
2017-03-22 21:30:26 +09:00
{
__be32 reg ;
int err ;
err = snd_motu_transaction_read ( motu , V2_CLOCK_STATUS_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
2019-10-30 17:06:42 +09:00
return get_clock_rate ( be32_to_cpu ( reg ) , rate ) ;
2017-03-22 21:30:26 +09:00
}
2020-05-19 20:16:31 +09:00
int snd_motu_protocol_v2_set_clock_rate ( struct snd_motu * motu ,
unsigned int rate )
2017-03-22 21:30:26 +09:00
{
__be32 reg ;
u32 data ;
int i ;
int err ;
for ( i = 0 ; i < ARRAY_SIZE ( snd_motu_clock_rates ) ; + + i ) {
if ( snd_motu_clock_rates [ i ] = = rate )
break ;
}
if ( i = = ARRAY_SIZE ( snd_motu_clock_rates ) )
return - EINVAL ;
err = snd_motu_transaction_read ( motu , V2_CLOCK_STATUS_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
data = be32_to_cpu ( reg ) ;
data & = ~ V2_CLOCK_RATE_MASK ;
data | = i < < V2_CLOCK_RATE_SHIFT ;
reg = cpu_to_be32 ( data ) ;
return snd_motu_transaction_write ( motu , V2_CLOCK_STATUS_OFFSET , & reg ,
sizeof ( reg ) ) ;
}
2020-05-19 20:16:39 +09:00
static int detect_clock_source_optical_model ( struct snd_motu * motu , u32 data ,
enum snd_motu_clock_source * src )
2017-03-22 21:30:26 +09:00
{
2020-05-19 20:16:39 +09:00
switch ( data ) {
2017-03-22 21:30:26 +09:00
case 0 :
* src = SND_MOTU_CLOCK_SOURCE_INTERNAL ;
break ;
case 1 :
2019-10-30 17:06:42 +09:00
{
__be32 reg ;
// To check the configuration of optical interface.
int err = snd_motu_transaction_read ( motu , V2_IN_OUT_CONF_OFFSET ,
& reg , sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
2017-03-22 21:30:26 +09:00
if ( be32_to_cpu ( reg ) & 0x00000200 )
* src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT ;
else
* src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT ;
break ;
2019-10-30 17:06:42 +09:00
}
2017-03-22 21:30:26 +09:00
case 2 :
* src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX ;
break ;
2019-10-30 17:06:41 +09:00
case 3 :
* src = SND_MOTU_CLOCK_SOURCE_SPH ;
break ;
2017-03-22 21:30:26 +09:00
case 4 :
* src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC ;
break ;
case 5 :
* src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB ;
break ;
default :
2020-05-19 20:16:39 +09:00
* src = SND_MOTU_CLOCK_SOURCE_UNKNOWN ;
break ;
2017-03-22 21:30:26 +09:00
}
return 0 ;
}
2020-05-19 20:16:39 +09:00
static int v2_detect_clock_source ( struct snd_motu * motu , u32 data ,
enum snd_motu_clock_source * src )
{
switch ( data ) {
case 0 :
* src = SND_MOTU_CLOCK_SOURCE_INTERNAL ;
break ;
case 2 :
* src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX ;
break ;
case 3 :
* src = SND_MOTU_CLOCK_SOURCE_SPH ;
break ;
case 4 :
* src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC ;
break ;
default :
* src = SND_MOTU_CLOCK_SOURCE_UNKNOWN ;
break ;
}
return 0 ;
}
static int get_clock_source ( struct snd_motu * motu , u32 data ,
enum snd_motu_clock_source * src )
{
data & = V2_CLOCK_SRC_MASK ;
if ( motu - > spec = = & snd_motu_spec_828mk2 | |
motu - > spec = = & snd_motu_spec_traveler )
return detect_clock_source_optical_model ( motu , data , src ) ;
else
return v2_detect_clock_source ( motu , data , src ) ;
}
2020-05-19 20:16:31 +09:00
int snd_motu_protocol_v2_get_clock_source ( struct snd_motu * motu ,
enum snd_motu_clock_source * src )
2019-10-30 17:06:42 +09:00
{
__be32 reg ;
int err ;
err = snd_motu_transaction_read ( motu , V2_CLOCK_STATUS_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
return get_clock_source ( motu , be32_to_cpu ( reg ) , src ) ;
}
2020-05-19 20:16:41 +09:00
// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3.
static int switch_fetching_mode_cyclone ( struct snd_motu * motu , u32 * data ,
bool enable )
2017-03-22 21:30:26 +09:00
{
2020-05-19 20:16:41 +09:00
* data | = V2_CLOCK_MODEL_SPECIFIC ;
ALSA: firewire-motu: add support for Motu Traveler
This commit adds support for MOTU Traveler, launched in 2005, discontinued
quite before. As a result, transmission of PCM frame and MIDI messages is
available via ALSA PCM and RawMIDI/Sequencer interfaces.
This model supports sampling transmission frequency up to 192.0 kHz, and
AES/EBU on XLR interface and ADAT on optical interface. Unlike
Motu 828MkII, Windows driver can switch fetching mode for DSP, like
mute/unmute feature.
Although this commit enables high sampling transmission frequency, actual
sound from this model is not good. As long as I tested, it's silence at
176.4 kHz, and it includes hissing noise at 192.0 kHz. In my opinion, as I
reported at 3526ce7f9ba7 ('ALSA: firewire-motu: add MOTU specific protocol
layer'), timestamping on source packet header (SPH) may not still be good
for this model as well.
$ python2 crpp < /sys/bus/firewire/devices/fw1/config_rom
ROM header and bus information block
-----------------------------------------------------------------
400 04106505 bus_info_length 4, crc_length 16, crc 25861
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 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
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 00035955 directory_length 3, crc 22869
42c 120001f2 specifier id
430 13000009 version
434 17107800 model
eui-64 leaf at 438
-----------------------------------------------------------------
438 000206b2 leaf_length 2, crc 1714
43c 0001f200 company_id 0001f2 |
440 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-06-18 21:07:55 +09:00
2020-05-19 20:16:41 +09:00
return 0 ;
}
2019-10-30 17:06:43 +09:00
2020-05-19 20:16:41 +09:00
// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
static int switch_fetching_mode_spartan ( struct snd_motu * motu , u32 * data ,
bool enable )
{
unsigned int rate ;
enum snd_motu_clock_source src ;
int err ;
err = get_clock_source ( motu , * data , & src ) ;
2019-10-30 17:06:43 +09:00
if ( err < 0 )
return err ;
2020-05-19 20:16:41 +09:00
err = get_clock_rate ( * data , & rate ) ;
2019-10-30 17:06:43 +09:00
if ( err < 0 )
return err ;
2020-05-19 20:16:41 +09:00
if ( src = = SND_MOTU_CLOCK_SOURCE_SPH & & rate > 48000 )
* data | = V2_CLOCK_MODEL_SPECIFIC ;
return 0 ;
}
2019-10-30 17:06:43 +09:00
2020-05-19 20:16:41 +09:00
int snd_motu_protocol_v2_switch_fetching_mode ( struct snd_motu * motu ,
bool enable )
{
if ( motu - > spec = = & snd_motu_spec_828mk2 ) {
// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
return 0 ;
2019-10-30 17:06:43 +09:00
} else {
2020-05-19 20:16:41 +09:00
__be32 reg ;
u32 data ;
int err ;
2019-10-30 17:06:43 +09:00
2020-05-19 20:16:41 +09:00
err = snd_motu_transaction_read ( motu , V2_CLOCK_STATUS_OFFSET ,
& reg , sizeof ( reg ) ) ;
ALSA: firewire-motu: add support for Motu Traveler
This commit adds support for MOTU Traveler, launched in 2005, discontinued
quite before. As a result, transmission of PCM frame and MIDI messages is
available via ALSA PCM and RawMIDI/Sequencer interfaces.
This model supports sampling transmission frequency up to 192.0 kHz, and
AES/EBU on XLR interface and ADAT on optical interface. Unlike
Motu 828MkII, Windows driver can switch fetching mode for DSP, like
mute/unmute feature.
Although this commit enables high sampling transmission frequency, actual
sound from this model is not good. As long as I tested, it's silence at
176.4 kHz, and it includes hissing noise at 192.0 kHz. In my opinion, as I
reported at 3526ce7f9ba7 ('ALSA: firewire-motu: add MOTU specific protocol
layer'), timestamping on source packet header (SPH) may not still be good
for this model as well.
$ python2 crpp < /sys/bus/firewire/devices/fw1/config_rom
ROM header and bus information block
-----------------------------------------------------------------
400 04106505 bus_info_length 4, crc_length 16, crc 25861
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 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
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 00035955 directory_length 3, crc 22869
42c 120001f2 specifier id
430 13000009 version
434 17107800 model
eui-64 leaf at 438
-----------------------------------------------------------------
438 000206b2 leaf_length 2, crc 1714
43c 0001f200 company_id 0001f2 |
440 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-06-18 21:07:55 +09:00
if ( err < 0 )
return err ;
2020-05-19 20:16:41 +09:00
data = be32_to_cpu ( reg ) ;
2019-10-30 17:06:43 +09:00
2020-05-19 20:16:41 +09:00
data & = ~ ( V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC ) ;
if ( enable )
data | = V2_CLOCK_FETCH_ENABLE ;
ALSA: firewire-motu: add support for Motu Traveler
This commit adds support for MOTU Traveler, launched in 2005, discontinued
quite before. As a result, transmission of PCM frame and MIDI messages is
available via ALSA PCM and RawMIDI/Sequencer interfaces.
This model supports sampling transmission frequency up to 192.0 kHz, and
AES/EBU on XLR interface and ADAT on optical interface. Unlike
Motu 828MkII, Windows driver can switch fetching mode for DSP, like
mute/unmute feature.
Although this commit enables high sampling transmission frequency, actual
sound from this model is not good. As long as I tested, it's silence at
176.4 kHz, and it includes hissing noise at 192.0 kHz. In my opinion, as I
reported at 3526ce7f9ba7 ('ALSA: firewire-motu: add MOTU specific protocol
layer'), timestamping on source packet header (SPH) may not still be good
for this model as well.
$ python2 crpp < /sys/bus/firewire/devices/fw1/config_rom
ROM header and bus information block
-----------------------------------------------------------------
400 04106505 bus_info_length 4, crc_length 16, crc 25861
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 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
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 00035955 directory_length 3, crc 22869
42c 120001f2 specifier id
430 13000009 version
434 17107800 model
eui-64 leaf at 438
-----------------------------------------------------------------
438 000206b2 leaf_length 2, crc 1714
43c 0001f200 company_id 0001f2 |
440 0001f32f device_id 000001f32f | EUI-64 0001f2000001f32f
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-06-18 21:07:55 +09:00
2020-05-19 20:16:41 +09:00
if ( motu - > spec = = & snd_motu_spec_traveler )
err = switch_fetching_mode_cyclone ( motu , & data , enable ) ;
else
err = switch_fetching_mode_spartan ( motu , & data , enable ) ;
if ( err < 0 )
return err ;
reg = cpu_to_be32 ( data ) ;
return snd_motu_transaction_write ( motu , V2_CLOCK_STATUS_OFFSET ,
& reg , sizeof ( reg ) ) ;
}
2017-03-22 21:30:26 +09:00
}
2020-05-19 20:16:34 +09:00
static int detect_packet_formats_828mk2 ( struct snd_motu * motu , u32 data )
{
if ( ( ( data & V2_OPT_IN_IFACE_MASK ) > > V2_OPT_IN_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > tx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > tx_packet_formats . pcm_chunks [ 1 ] + = 4 ;
}
if ( ( ( data & V2_OPT_OUT_IFACE_MASK ) > > V2_OPT_OUT_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > rx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > rx_packet_formats . pcm_chunks [ 1 ] + = 4 ;
}
return 0 ;
}
static int detect_packet_formats_traveler ( struct snd_motu * motu , u32 data )
{
if ( ( ( data & V2_OPT_IN_IFACE_MASK ) > > V2_OPT_IN_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > tx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > tx_packet_formats . pcm_chunks [ 1 ] + = 4 ;
}
if ( ( ( data & V2_OPT_OUT_IFACE_MASK ) > > V2_OPT_OUT_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > rx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > rx_packet_formats . pcm_chunks [ 1 ] + = 4 ;
}
return 0 ;
}
static int detect_packet_formats_8pre ( struct snd_motu * motu , u32 data )
{
if ( ( ( data & V2_OPT_IN_IFACE_MASK ) > > V2_OPT_IN_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > tx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > tx_packet_formats . pcm_chunks [ 1 ] + = 8 ;
}
if ( ( ( data & V2_OPT_OUT_IFACE_MASK ) > > V2_OPT_OUT_IFACE_SHIFT ) = =
V2_OPT_IFACE_MODE_ADAT ) {
motu - > rx_packet_formats . pcm_chunks [ 0 ] + = 8 ;
motu - > rx_packet_formats . pcm_chunks [ 1 ] + = 8 ;
}
return 0 ;
}
2020-05-19 20:16:31 +09:00
int snd_motu_protocol_v2_cache_packet_formats ( struct snd_motu * motu )
2017-03-22 21:30:26 +09:00
{
__be32 reg ;
u32 data ;
int err ;
2020-05-19 20:16:34 +09:00
motu - > tx_packet_formats . pcm_byte_offset = 10 ;
motu - > rx_packet_formats . pcm_byte_offset = 10 ;
motu - > tx_packet_formats . msg_chunks = 2 ;
motu - > rx_packet_formats . msg_chunks = 2 ;
2017-03-22 21:30:26 +09:00
err = snd_motu_transaction_read ( motu , V2_IN_OUT_CONF_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
data = be32_to_cpu ( reg ) ;
2020-05-19 20:16:35 +09:00
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 ) ) ;
2017-03-22 21:30:26 +09:00
2020-05-19 20:16:34 +09:00
if ( motu - > spec = = & snd_motu_spec_828mk2 )
return detect_packet_formats_828mk2 ( motu , data ) ;
else if ( motu - > spec = = & snd_motu_spec_traveler )
return detect_packet_formats_traveler ( motu , data ) ;
else if ( motu - > spec = = & snd_motu_spec_8pre )
return detect_packet_formats_8pre ( motu , data ) ;
else
return 0 ;
2017-03-22 21:30:26 +09:00
}
2020-05-19 20:16:28 +09:00
const struct snd_motu_spec snd_motu_spec_828mk2 = {
. name = " 828mk2 " ,
2020-05-19 20:16:30 +09:00
. protocol_version = SND_MOTU_PROTOCOL_V2 ,
2020-05-19 20:16:38 +09:00
. flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
2020-05-19 20:16:28 +09:00
SND_MOTU_SPEC_TX_MIDI_2ND_Q ,
2020-05-19 20:16:33 +09:00
. tx_fixed_pcm_chunks = { 14 , 14 , 0 } ,
. rx_fixed_pcm_chunks = { 14 , 14 , 0 } ,
2020-05-19 20:16:28 +09:00
} ;
const struct snd_motu_spec snd_motu_spec_traveler = {
. name = " Traveler " ,
2020-05-19 20:16:30 +09:00
. protocol_version = SND_MOTU_PROTOCOL_V2 ,
2020-05-19 20:16:38 +09:00
. flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
2020-05-19 20:16:28 +09:00
SND_MOTU_SPEC_TX_MIDI_2ND_Q ,
2020-05-19 20:16:33 +09:00
. tx_fixed_pcm_chunks = { 14 , 14 , 8 } ,
. rx_fixed_pcm_chunks = { 14 , 14 , 8 } ,
2020-05-19 20:16:28 +09:00
} ;
const struct snd_motu_spec snd_motu_spec_ultralite = {
. name = " UltraLite " ,
2020-05-19 20:16:30 +09:00
. protocol_version = SND_MOTU_PROTOCOL_V2 ,
2020-05-19 20:16:38 +09:00
. flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
SND_MOTU_SPEC_TX_MIDI_2ND_Q ,
2020-05-19 20:16:33 +09:00
. tx_fixed_pcm_chunks = { 14 , 14 , 0 } ,
. rx_fixed_pcm_chunks = { 14 , 14 , 0 } ,
2020-05-19 20:16:28 +09:00
} ;
const struct snd_motu_spec snd_motu_spec_8pre = {
. name = " 8pre " ,
2020-05-19 20:16:30 +09:00
. protocol_version = SND_MOTU_PROTOCOL_V2 ,
2020-05-19 20:16:38 +09:00
. flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
2020-05-19 20:16:28 +09:00
SND_MOTU_SPEC_TX_MIDI_2ND_Q ,
2020-05-19 20:16:33 +09:00
. tx_fixed_pcm_chunks = { 10 , 6 , 0 } ,
. rx_fixed_pcm_chunks = { 10 , 6 , 0 } ,
2020-05-19 20:16:28 +09:00
} ;