2019-05-29 16:57:59 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-03-22 21:30:20 +09:00
/*
* motu - stream . c - a part of driver for MOTU FireWire series
*
* Copyright ( c ) 2015 - 2017 Takashi Sakamoto < o - takashi @ sakamocchi . jp >
*/
# include "motu.h"
# define CALLBACK_TIMEOUT 200
# define ISOC_COMM_CONTROL_OFFSET 0x0b00
# define ISOC_COMM_CONTROL_MASK 0xffff0000
# define CHANGE_RX_ISOC_COMM_STATE 0x80000000
# define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
# define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
# define RX_ISOC_COMM_CHANNEL_SHIFT 24
# define CHANGE_TX_ISOC_COMM_STATE 0x00800000
# define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
# define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
# define TX_ISOC_COMM_CHANNEL_SHIFT 16
# define PACKET_FORMAT_OFFSET 0x0b10
# define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
# define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
# define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
2019-06-17 17:15:03 +09:00
static int keep_resources ( struct snd_motu * motu , unsigned int rate ,
struct amdtp_stream * stream )
2017-03-22 21:30:20 +09:00
{
2019-06-17 17:15:03 +09:00
struct fw_iso_resources * resources ;
struct snd_motu_packet_format * packet_format ;
2017-03-22 21:30:23 +09:00
unsigned int midi_ports = 0 ;
2017-03-22 21:30:20 +09:00
int err ;
2019-06-17 17:15:03 +09:00
if ( stream = = & motu - > rx_stream ) {
resources = & motu - > rx_resources ;
packet_format = & motu - > rx_packet_formats ;
2017-03-22 21:30:23 +09:00
2019-06-17 17:15:03 +09:00
if ( ( motu - > spec - > flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q ) | |
( motu - > spec - > flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q ) )
midi_ports = 1 ;
} else {
resources = & motu - > tx_resources ;
packet_format = & motu - > tx_packet_formats ;
2017-03-22 21:30:20 +09:00
2019-06-17 17:15:03 +09:00
if ( ( motu - > spec - > flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q ) | |
( motu - > spec - > flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q ) )
midi_ports = 1 ;
}
2017-08-20 21:25:03 +09:00
2019-06-17 17:15:03 +09:00
err = amdtp_motu_set_parameters ( stream , rate , midi_ports ,
packet_format ) ;
2017-03-22 21:30:20 +09:00
if ( err < 0 )
return err ;
2019-06-17 17:15:03 +09:00
return fw_iso_resources_allocate ( resources ,
amdtp_stream_get_max_payload ( stream ) ,
2017-03-22 21:30:20 +09:00
fw_parent_device ( motu - > unit ) - > max_speed ) ;
2019-06-17 17:15:03 +09:00
}
2019-06-17 17:15:08 +09:00
static int begin_session ( struct snd_motu * motu )
2019-06-17 17:15:03 +09:00
{
__be32 reg ;
u32 data ;
int err ;
// Configure the unit to start isochronous communication.
2017-03-22 21:30:20 +09:00
err = snd_motu_transaction_read ( motu , ISOC_COMM_CONTROL_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
data = be32_to_cpu ( reg ) & ~ ISOC_COMM_CONTROL_MASK ;
data | = CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
( motu - > rx_resources . channel < < RX_ISOC_COMM_CHANNEL_SHIFT ) |
CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
( motu - > tx_resources . channel < < TX_ISOC_COMM_CHANNEL_SHIFT ) ;
reg = cpu_to_be32 ( data ) ;
return snd_motu_transaction_write ( motu , ISOC_COMM_CONTROL_OFFSET , & reg ,
sizeof ( reg ) ) ;
}
2019-06-17 17:15:05 +09:00
static void finish_session ( struct snd_motu * motu )
2017-03-22 21:30:20 +09:00
{
__be32 reg ;
u32 data ;
int err ;
2020-05-19 20:16:31 +09:00
err = snd_motu_protocol_switch_fetching_mode ( motu , false ) ;
2017-03-22 21:30:20 +09:00
if ( err < 0 )
return ;
err = snd_motu_transaction_read ( motu , ISOC_COMM_CONTROL_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return ;
data = be32_to_cpu ( reg ) ;
data & = ~ ( RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED ) ;
data | = CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE ;
reg = cpu_to_be32 ( data ) ;
snd_motu_transaction_write ( motu , ISOC_COMM_CONTROL_OFFSET , & reg ,
sizeof ( reg ) ) ;
}
2017-08-20 21:25:03 +09:00
int snd_motu_stream_cache_packet_formats ( struct snd_motu * motu )
{
int err ;
2020-05-19 20:16:31 +09:00
err = snd_motu_protocol_cache_packet_formats ( motu ) ;
2017-08-20 21:25:03 +09:00
if ( err < 0 )
return err ;
if ( motu - > spec - > flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q ) {
motu - > tx_packet_formats . midi_flag_offset = 4 ;
motu - > tx_packet_formats . midi_byte_offset = 6 ;
} else if ( motu - > spec - > flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q ) {
motu - > tx_packet_formats . midi_flag_offset = 8 ;
motu - > tx_packet_formats . midi_byte_offset = 7 ;
}
if ( motu - > spec - > flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q ) {
motu - > rx_packet_formats . midi_flag_offset = 4 ;
motu - > rx_packet_formats . midi_byte_offset = 6 ;
} else if ( motu - > spec - > flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q ) {
motu - > rx_packet_formats . midi_flag_offset = 8 ;
motu - > rx_packet_formats . midi_byte_offset = 7 ;
}
return 0 ;
}
2019-10-07 20:05:23 +09:00
int snd_motu_stream_reserve_duplex ( struct snd_motu * motu , unsigned int rate ,
2019-10-18 00:54:20 +09:00
unsigned int frames_per_period ,
unsigned int frames_per_buffer )
2019-06-17 17:15:08 +09:00
{
unsigned int curr_rate ;
int err ;
2020-05-19 20:16:31 +09:00
err = snd_motu_protocol_get_clock_rate ( motu , & curr_rate ) ;
2019-06-17 17:15:08 +09:00
if ( err < 0 )
return err ;
if ( rate = = 0 )
rate = curr_rate ;
if ( motu - > substreams_counter = = 0 | | curr_rate ! = rate ) {
2019-08-04 15:21:36 +09:00
amdtp_domain_stop ( & motu - > domain ) ;
2019-06-17 17:15:08 +09:00
finish_session ( motu ) ;
fw_iso_resources_free ( & motu - > tx_resources ) ;
fw_iso_resources_free ( & motu - > rx_resources ) ;
2020-05-19 20:16:31 +09:00
err = snd_motu_protocol_set_clock_rate ( motu , rate ) ;
2019-06-17 17:15:08 +09:00
if ( err < 0 ) {
dev_err ( & motu - > unit - > device ,
" fail to set sampling rate: %d \n " , err ) ;
return err ;
}
err = snd_motu_stream_cache_packet_formats ( motu ) ;
if ( err < 0 )
return err ;
err = keep_resources ( motu , rate , & motu - > tx_stream ) ;
if ( err < 0 )
return err ;
err = keep_resources ( motu , rate , & motu - > rx_stream ) ;
if ( err < 0 ) {
fw_iso_resources_free ( & motu - > tx_resources ) ;
return err ;
}
2019-10-07 20:05:23 +09:00
err = amdtp_domain_set_events_per_period ( & motu - > domain ,
2019-10-18 00:54:20 +09:00
frames_per_period , frames_per_buffer ) ;
2019-10-07 20:05:23 +09:00
if ( err < 0 ) {
fw_iso_resources_free ( & motu - > tx_resources ) ;
fw_iso_resources_free ( & motu - > rx_resources ) ;
return err ;
}
2019-06-17 17:15:08 +09:00
}
return 0 ;
}
2017-03-22 21:30:20 +09:00
static int ensure_packet_formats ( struct snd_motu * motu )
{
__be32 reg ;
u32 data ;
int err ;
err = snd_motu_transaction_read ( motu , PACKET_FORMAT_OFFSET , & reg ,
sizeof ( reg ) ) ;
if ( err < 0 )
return err ;
data = be32_to_cpu ( reg ) ;
data & = ~ ( TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
TX_PACKET_TRANSMISSION_SPEED_MASK ) ;
2020-05-19 20:16:37 +09:00
if ( motu - > spec - > tx_fixed_pcm_chunks [ 0 ] = = motu - > tx_packet_formats . pcm_chunks [ 0 ] )
2017-03-22 21:30:20 +09:00
data | = TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS ;
2020-05-19 20:16:37 +09:00
if ( motu - > spec - > rx_fixed_pcm_chunks [ 0 ] = = motu - > rx_packet_formats . pcm_chunks [ 0 ] )
2017-03-22 21:30:20 +09:00
data | = RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS ;
data | = fw_parent_device ( motu - > unit ) - > max_speed ;
reg = cpu_to_be32 ( data ) ;
return snd_motu_transaction_write ( motu , PACKET_FORMAT_OFFSET , & reg ,
sizeof ( reg ) ) ;
}
2019-06-17 17:15:08 +09:00
int snd_motu_stream_start_duplex ( struct snd_motu * motu )
2017-03-22 21:30:20 +09:00
{
2019-06-17 17:15:09 +09:00
unsigned int generation = motu - > rx_resources . generation ;
2017-03-22 21:30:20 +09:00
int err = 0 ;
2019-06-17 17:15:01 +09:00
if ( motu - > substreams_counter = = 0 )
2017-03-22 21:30:20 +09:00
return 0 ;
2019-06-17 17:15:08 +09:00
if ( amdtp_streaming_error ( & motu - > rx_stream ) | |
2019-08-04 15:21:36 +09:00
amdtp_streaming_error ( & motu - > tx_stream ) ) {
amdtp_domain_stop ( & motu - > domain ) ;
2019-06-17 17:15:05 +09:00
finish_session ( motu ) ;
2019-08-04 15:21:36 +09:00
}
2017-03-22 21:30:20 +09:00
2019-06-17 17:15:09 +09:00
if ( generation ! = fw_parent_device ( motu - > unit ) - > card - > generation ) {
err = fw_iso_resources_update ( & motu - > rx_resources ) ;
if ( err < 0 )
return err ;
err = fw_iso_resources_update ( & motu - > tx_resources ) ;
if ( err < 0 )
return err ;
}
2017-03-22 21:30:20 +09:00
if ( ! amdtp_stream_running ( & motu - > rx_stream ) ) {
2019-08-04 15:21:36 +09:00
int spd = fw_parent_device ( motu - > unit ) - > max_speed ;
2017-03-22 21:30:20 +09:00
err = ensure_packet_formats ( motu ) ;
if ( err < 0 )
return err ;
2019-06-17 17:15:08 +09:00
err = begin_session ( motu ) ;
2017-03-22 21:30:20 +09:00
if ( err < 0 ) {
dev_err ( & motu - > unit - > device ,
" fail to start isochronous comm: %d \n " , err ) ;
2017-09-06 13:30:14 +02:00
goto stop_streams ;
2017-03-22 21:30:20 +09:00
}
2019-08-04 15:21:36 +09:00
err = amdtp_domain_add_stream ( & motu - > domain , & motu - > tx_stream ,
motu - > tx_resources . channel , spd ) ;
if ( err < 0 )
2017-09-06 13:30:14 +02:00
goto stop_streams ;
2017-03-22 21:30:20 +09:00
2019-08-04 15:21:36 +09:00
err = amdtp_domain_add_stream ( & motu - > domain , & motu - > rx_stream ,
motu - > rx_resources . channel , spd ) ;
if ( err < 0 )
goto stop_streams ;
2019-10-18 15:19:11 +09:00
err = amdtp_domain_start ( & motu - > domain , 0 ) ;
2019-08-04 15:21:36 +09:00
if ( err < 0 )
goto stop_streams ;
if ( ! amdtp_stream_wait_callback ( & motu - > tx_stream ,
CALLBACK_TIMEOUT ) | |
! amdtp_stream_wait_callback ( & motu - > rx_stream ,
CALLBACK_TIMEOUT ) ) {
err = - ETIMEDOUT ;
2017-09-06 13:30:14 +02:00
goto stop_streams ;
2017-03-22 21:30:20 +09:00
}
2020-05-19 20:16:31 +09:00
err = snd_motu_protocol_switch_fetching_mode ( motu , true ) ;
2017-03-22 21:30:20 +09:00
if ( err < 0 ) {
dev_err ( & motu - > unit - > device ,
2019-08-04 15:21:36 +09:00
" fail to enable frame fetching: %d \n " , err ) ;
2017-09-06 13:30:14 +02:00
goto stop_streams ;
2017-03-22 21:30:20 +09:00
}
}
return 0 ;
2017-09-06 13:30:14 +02:00
stop_streams :
2019-08-04 15:21:36 +09:00
amdtp_domain_stop ( & motu - > domain ) ;
2019-06-17 17:15:05 +09:00
finish_session ( motu ) ;
2017-09-06 13:30:14 +02:00
return err ;
2017-03-22 21:30:20 +09:00
}
void snd_motu_stream_stop_duplex ( struct snd_motu * motu )
{
2019-06-18 22:26:17 +09:00
if ( motu - > substreams_counter = = 0 ) {
2019-08-04 15:21:36 +09:00
amdtp_domain_stop ( & motu - > domain ) ;
2019-06-17 17:15:07 +09:00
finish_session ( motu ) ;
2019-06-18 22:26:17 +09:00
fw_iso_resources_free ( & motu - > tx_resources ) ;
fw_iso_resources_free ( & motu - > rx_resources ) ;
}
2017-03-22 21:30:20 +09:00
}
2019-08-04 15:21:28 +09:00
static int init_stream ( struct snd_motu * motu , struct amdtp_stream * s )
2017-03-22 21:30:20 +09:00
{
struct fw_iso_resources * resources ;
2019-08-04 15:21:28 +09:00
enum amdtp_stream_direction dir ;
int err ;
2017-03-22 21:30:20 +09:00
2019-08-04 15:21:28 +09:00
if ( s = = & motu - > tx_stream ) {
2017-03-22 21:30:20 +09:00
resources = & motu - > tx_resources ;
2019-08-04 15:21:28 +09:00
dir = AMDTP_IN_STREAM ;
2017-03-22 21:30:20 +09:00
} else {
resources = & motu - > rx_resources ;
2019-08-04 15:21:28 +09:00
dir = AMDTP_OUT_STREAM ;
2017-03-22 21:30:20 +09:00
}
err = fw_iso_resources_init ( resources , motu - > unit ) ;
if ( err < 0 )
return err ;
2020-05-19 20:16:30 +09:00
err = amdtp_motu_init ( s , motu - > unit , dir , motu - > spec ) ;
2019-08-04 15:21:28 +09:00
if ( err < 0 )
2017-03-22 21:30:20 +09:00
fw_iso_resources_destroy ( resources ) ;
return err ;
}
2019-08-04 15:21:28 +09:00
static void destroy_stream ( struct snd_motu * motu , struct amdtp_stream * s )
2017-03-22 21:30:20 +09:00
{
2019-08-04 15:21:28 +09:00
amdtp_stream_destroy ( s ) ;
2017-03-22 21:30:20 +09:00
2019-08-04 15:21:28 +09:00
if ( s = = & motu - > tx_stream )
fw_iso_resources_destroy ( & motu - > tx_resources ) ;
else
fw_iso_resources_destroy ( & motu - > rx_resources ) ;
2017-03-22 21:30:20 +09:00
}
int snd_motu_stream_init_duplex ( struct snd_motu * motu )
{
int err ;
2019-08-04 15:21:28 +09:00
err = init_stream ( motu , & motu - > tx_stream ) ;
2017-03-22 21:30:20 +09:00
if ( err < 0 )
return err ;
2019-08-04 15:21:28 +09:00
err = init_stream ( motu , & motu - > rx_stream ) ;
2019-08-04 15:21:36 +09:00
if ( err < 0 ) {
2019-08-04 15:21:28 +09:00
destroy_stream ( motu , & motu - > tx_stream ) ;
2019-08-04 15:21:36 +09:00
return err ;
}
err = amdtp_domain_init ( & motu - > domain ) ;
if ( err < 0 ) {
destroy_stream ( motu , & motu - > tx_stream ) ;
destroy_stream ( motu , & motu - > rx_stream ) ;
}
2017-03-22 21:30:20 +09:00
return err ;
}
2019-08-04 15:21:36 +09:00
// This function should be called before starting streams or after stopping
// streams.
2017-03-22 21:30:20 +09:00
void snd_motu_stream_destroy_duplex ( struct snd_motu * motu )
{
2019-08-04 15:21:36 +09:00
amdtp_domain_destroy ( & motu - > domain ) ;
2019-08-04 15:21:28 +09:00
destroy_stream ( motu , & motu - > rx_stream ) ;
destroy_stream ( motu , & motu - > tx_stream ) ;
2017-03-22 21:30:20 +09:00
2019-06-17 17:15:01 +09:00
motu - > substreams_counter = 0 ;
2017-03-22 21:30:20 +09:00
}
2017-03-22 21:30:24 +09:00
static void motu_lock_changed ( struct snd_motu * motu )
{
motu - > dev_lock_changed = true ;
wake_up ( & motu - > hwdep_wait ) ;
}
int snd_motu_stream_lock_try ( struct snd_motu * motu )
{
int err ;
spin_lock_irq ( & motu - > lock ) ;
if ( motu - > dev_lock_count < 0 ) {
err = - EBUSY ;
goto out ;
}
if ( motu - > dev_lock_count + + = = 0 )
motu_lock_changed ( motu ) ;
err = 0 ;
out :
spin_unlock_irq ( & motu - > lock ) ;
return err ;
}
void snd_motu_stream_lock_release ( struct snd_motu * motu )
{
spin_lock_irq ( & motu - > lock ) ;
if ( WARN_ON ( motu - > dev_lock_count < = 0 ) )
goto out ;
if ( - - motu - > dev_lock_count = = 0 )
motu_lock_changed ( motu ) ;
out :
spin_unlock_irq ( & motu - > lock ) ;
}