2014-11-29 00:59:14 +09:00
/*
* dice_stream . c - a part of driver for DICE based devices
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
* Copyright ( c ) 2014 Takashi Sakamoto < o - takashi @ sakamocchi . jp >
*
* Licensed under the terms of the GNU General Public License , version 2.
*/
# include "dice.h"
2014-12-09 00:10:35 +09:00
# define CALLBACK_TIMEOUT 200
2014-11-29 00:59:14 +09:00
const unsigned int snd_dice_rates [ SND_DICE_RATES_COUNT ] = {
/* mode 0 */
[ 0 ] = 32000 ,
[ 1 ] = 44100 ,
[ 2 ] = 48000 ,
/* mode 1 */
[ 3 ] = 88200 ,
[ 4 ] = 96000 ,
/* mode 2 */
[ 5 ] = 176400 ,
[ 6 ] = 192000 ,
} ;
int snd_dice_stream_get_rate_mode ( struct snd_dice * dice , unsigned int rate ,
unsigned int * mode )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( snd_dice_rates ) ; i + + ) {
if ( ! ( dice - > clock_caps & BIT ( i ) ) )
continue ;
if ( snd_dice_rates [ i ] ! = rate )
continue ;
* mode = ( i - 1 ) / 2 ;
return 0 ;
}
return - EINVAL ;
}
2014-12-09 00:10:36 +09:00
static void release_resources ( struct snd_dice * dice ,
struct fw_iso_resources * resources )
2014-11-29 00:59:14 +09:00
{
2014-12-09 00:10:35 +09:00
unsigned int channel ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:35 +09:00
/* Reset channel number */
channel = cpu_to_be32 ( ( u32 ) - 1 ) ;
2014-12-09 00:10:36 +09:00
if ( resources = = & dice - > tx_resources )
snd_dice_transaction_write_tx ( dice , TX_ISOCHRONOUS ,
& channel , 4 ) ;
else
snd_dice_transaction_write_rx ( dice , RX_ISOCHRONOUS ,
& channel , 4 ) ;
fw_iso_resources_free ( resources ) ;
2014-11-29 00:59:14 +09:00
}
2014-12-09 00:10:36 +09:00
static int keep_resources ( struct snd_dice * dice ,
struct fw_iso_resources * resources ,
unsigned int max_payload_bytes )
2014-11-29 00:59:14 +09:00
{
2014-12-09 00:10:35 +09:00
unsigned int channel ;
2014-11-29 00:59:14 +09:00
int err ;
2014-12-09 00:10:36 +09:00
err = fw_iso_resources_allocate ( resources , max_payload_bytes ,
2014-11-29 00:59:14 +09:00
fw_parent_device ( dice - > unit ) - > max_speed ) ;
if ( err < 0 )
2014-12-09 00:10:35 +09:00
goto end ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:35 +09:00
/* Set channel number */
2014-12-09 00:10:36 +09:00
channel = cpu_to_be32 ( resources - > channel ) ;
if ( resources = = & dice - > tx_resources )
err = snd_dice_transaction_write_tx ( dice , TX_ISOCHRONOUS ,
& channel , 4 ) ;
else
err = snd_dice_transaction_write_rx ( dice , RX_ISOCHRONOUS ,
& channel , 4 ) ;
2014-12-09 00:10:35 +09:00
if ( err < 0 )
2014-12-09 00:10:36 +09:00
release_resources ( dice , resources ) ;
2014-12-09 00:10:35 +09:00
end :
2014-11-29 00:59:14 +09:00
return err ;
}
2014-12-09 00:10:36 +09:00
static void stop_stream ( struct snd_dice * dice , struct amdtp_stream * stream )
2014-11-29 00:59:14 +09:00
{
2014-12-09 00:10:36 +09:00
amdtp_stream_pcm_abort ( stream ) ;
amdtp_stream_stop ( stream ) ;
2014-11-29 00:59:15 +09:00
2014-12-09 00:10:36 +09:00
if ( stream = = & dice - > tx_stream )
release_resources ( dice , & dice - > tx_resources ) ;
else
release_resources ( dice , & dice - > rx_resources ) ;
2014-11-29 00:59:14 +09:00
}
2014-12-09 00:10:36 +09:00
static int start_stream ( struct snd_dice * dice , struct amdtp_stream * stream ,
unsigned int rate )
2014-11-29 00:59:14 +09:00
{
2014-12-09 00:10:36 +09:00
struct fw_iso_resources * resources ;
2014-12-09 00:10:35 +09:00
unsigned int i , mode , pcm_chs , midi_ports ;
int err ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:35 +09:00
err = snd_dice_stream_get_rate_mode ( dice , rate , & mode ) ;
if ( err < 0 )
goto end ;
2014-12-09 00:10:36 +09:00
if ( stream = = & dice - > tx_stream ) {
resources = & dice - > tx_resources ;
pcm_chs = dice - > tx_channels [ mode ] ;
midi_ports = dice - > tx_midi_ports [ mode ] ;
} else {
resources = & dice - > rx_resources ;
pcm_chs = dice - > rx_channels [ mode ] ;
midi_ports = dice - > rx_midi_ports [ mode ] ;
}
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:35 +09:00
/*
* At 176.4 / 192.0 kHz , Dice has a quirk to transfer two PCM frames in
* one data block of AMDTP packet . Thus sampling transfer frequency is
* a half of PCM sampling frequency , i . e . PCM frames at 192.0 kHz are
* transferred on AMDTP packets at 96 kHz . Two successive samples of a
* channel are stored consecutively in the packet . This quirk is called
* as ' Dual Wire ' .
* For this quirk , blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL .
*/
if ( mode > 1 ) {
rate / = 2 ;
pcm_chs * = 2 ;
2014-12-09 00:10:36 +09:00
stream - > double_pcm_frames = true ;
2014-12-09 00:10:35 +09:00
} else {
2014-12-09 00:10:36 +09:00
stream - > double_pcm_frames = false ;
2014-12-09 00:10:35 +09:00
}
2014-11-29 00:59:14 +09:00
2015-09-19 11:21:49 +09:00
err = amdtp_stream_set_parameters ( stream , rate , pcm_chs , midi_ports ) ;
if ( err < 0 )
goto end ;
2014-12-09 00:10:35 +09:00
if ( mode > 1 ) {
pcm_chs / = 2 ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:35 +09:00
for ( i = 0 ; i < pcm_chs ; i + + ) {
2014-12-09 00:10:36 +09:00
stream - > pcm_positions [ i ] = i * 2 ;
stream - > pcm_positions [ i + pcm_chs ] = i * 2 + 1 ;
2014-12-09 00:10:35 +09:00
}
}
2014-12-09 00:10:36 +09:00
err = keep_resources ( dice , resources ,
amdtp_stream_get_max_payload ( stream ) ) ;
2014-12-09 00:10:35 +09:00
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
" fail to keep isochronous resources \n " ) ;
goto end ;
}
2014-12-09 00:10:36 +09:00
err = amdtp_stream_start ( stream , resources - > channel ,
2014-12-09 00:10:35 +09:00
fw_parent_device ( dice - > unit ) - > max_speed ) ;
if ( err < 0 )
2014-12-09 00:10:36 +09:00
release_resources ( dice , resources ) ;
2014-12-09 00:10:35 +09:00
end :
return err ;
}
2014-12-09 00:10:36 +09:00
static int get_sync_mode ( struct snd_dice * dice , enum cip_flags * sync_mode )
{
2014-12-09 00:10:37 +09:00
u32 source ;
int err ;
err = snd_dice_transaction_get_clock_source ( dice , & source ) ;
if ( err < 0 )
goto end ;
switch ( source ) {
/* So-called 'SYT Match' modes, sync_to_syt value of packets received */
case CLOCK_SOURCE_ARX4 : /* in 4th stream */
case CLOCK_SOURCE_ARX3 : /* in 3rd stream */
case CLOCK_SOURCE_ARX2 : /* in 2nd stream */
err = - ENOSYS ;
break ;
case CLOCK_SOURCE_ARX1 : /* in 1st stream, which this driver uses */
* sync_mode = 0 ;
break ;
default :
* sync_mode = CIP_SYNC_TO_DEVICE ;
break ;
}
end :
return err ;
2014-12-09 00:10:36 +09:00
}
int snd_dice_stream_start_duplex ( struct snd_dice * dice , unsigned int rate )
2014-12-09 00:10:35 +09:00
{
2014-12-09 00:10:36 +09:00
struct amdtp_stream * master , * slave ;
2014-12-09 00:10:35 +09:00
unsigned int curr_rate ;
2014-12-09 00:10:36 +09:00
enum cip_flags sync_mode ;
int err = 0 ;
if ( dice - > substreams_counter = = 0 )
goto end ;
err = get_sync_mode ( dice , & sync_mode ) ;
if ( err < 0 )
goto end ;
if ( sync_mode = = CIP_SYNC_TO_DEVICE ) {
master = & dice - > tx_stream ;
slave = & dice - > rx_stream ;
} else {
master = & dice - > rx_stream ;
slave = & dice - > tx_stream ;
}
2014-12-09 00:10:35 +09:00
/* Some packet queueing errors. */
2014-12-09 00:10:36 +09:00
if ( amdtp_streaming_error ( master ) | | amdtp_streaming_error ( slave ) )
stop_stream ( dice , master ) ;
2014-12-09 00:10:35 +09:00
/* Stop stream if rate is different. */
err = snd_dice_transaction_get_rate ( dice , & curr_rate ) ;
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
" fail to get sampling rate \n " ) ;
goto end ;
}
2014-12-09 00:10:39 +09:00
if ( rate = = 0 )
rate = curr_rate ;
2014-12-09 00:10:35 +09:00
if ( rate ! = curr_rate )
2014-12-09 00:10:36 +09:00
stop_stream ( dice , master ) ;
2014-12-09 00:10:35 +09:00
2014-12-09 00:10:36 +09:00
if ( ! amdtp_stream_running ( master ) ) {
stop_stream ( dice , slave ) ;
2014-12-09 00:10:35 +09:00
snd_dice_transaction_clear_enable ( dice ) ;
2014-12-09 00:10:36 +09:00
amdtp_stream_set_sync ( sync_mode , master , slave ) ;
2014-12-09 00:10:35 +09:00
err = snd_dice_transaction_set_rate ( dice , rate ) ;
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
" fail to set sampling rate \n " ) ;
goto end ;
}
2014-12-09 00:10:36 +09:00
/* Start both streams. */
err = start_stream ( dice , master , rate ) ;
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
" fail to start AMDTP master stream \n " ) ;
goto end ;
}
err = start_stream ( dice , slave , rate ) ;
2014-12-09 00:10:35 +09:00
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
2014-12-09 00:10:36 +09:00
" fail to start AMDTP slave stream \n " ) ;
stop_stream ( dice , master ) ;
2014-12-09 00:10:35 +09:00
goto end ;
}
err = snd_dice_transaction_set_enable ( dice ) ;
if ( err < 0 ) {
dev_err ( & dice - > unit - > device ,
" fail to enable interface \n " ) ;
2014-12-09 00:10:36 +09:00
stop_stream ( dice , master ) ;
stop_stream ( dice , slave ) ;
2014-12-09 00:10:35 +09:00
goto end ;
}
2014-12-09 00:10:36 +09:00
/* Wait first callbacks */
if ( ! amdtp_stream_wait_callback ( master , CALLBACK_TIMEOUT ) | |
! amdtp_stream_wait_callback ( slave , CALLBACK_TIMEOUT ) ) {
2014-12-09 00:10:35 +09:00
snd_dice_transaction_clear_enable ( dice ) ;
2014-12-09 00:10:36 +09:00
stop_stream ( dice , master ) ;
stop_stream ( dice , slave ) ;
2014-12-09 00:10:35 +09:00
err = - ETIMEDOUT ;
}
}
end :
return err ;
}
2014-12-09 00:10:36 +09:00
void snd_dice_stream_stop_duplex ( struct snd_dice * dice )
2014-12-09 00:10:35 +09:00
{
2014-12-09 00:10:36 +09:00
if ( dice - > substreams_counter > 0 )
return ;
2014-12-09 00:10:35 +09:00
snd_dice_transaction_clear_enable ( dice ) ;
2014-12-09 00:10:36 +09:00
stop_stream ( dice , & dice - > tx_stream ) ;
stop_stream ( dice , & dice - > rx_stream ) ;
2014-11-29 00:59:14 +09:00
}
2014-12-09 00:10:36 +09:00
static int init_stream ( struct snd_dice * dice , struct amdtp_stream * stream )
2014-11-29 00:59:14 +09:00
{
int err ;
2014-12-09 00:10:36 +09:00
struct fw_iso_resources * resources ;
enum amdtp_stream_direction dir ;
if ( stream = = & dice - > tx_stream ) {
resources = & dice - > tx_resources ;
dir = AMDTP_IN_STREAM ;
} else {
resources = & dice - > rx_resources ;
dir = AMDTP_OUT_STREAM ;
}
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:36 +09:00
err = fw_iso_resources_init ( resources , dice - > unit ) ;
2014-11-29 00:59:14 +09:00
if ( err < 0 )
goto end ;
2014-12-09 00:10:36 +09:00
resources - > channels_mask = 0x00000000ffffffffuLL ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:36 +09:00
err = amdtp_stream_init ( stream , dice - > unit , dir , CIP_BLOCKING ) ;
if ( err < 0 ) {
amdtp_stream_destroy ( stream ) ;
fw_iso_resources_destroy ( resources ) ;
}
end :
return err ;
}
2015-02-21 23:54:59 +09:00
/*
* This function should be called before starting streams or after stopping
* streams .
*/
2014-12-09 00:10:36 +09:00
static void destroy_stream ( struct snd_dice * dice , struct amdtp_stream * stream )
{
2015-02-21 23:54:59 +09:00
struct fw_iso_resources * resources ;
2014-12-09 00:10:36 +09:00
if ( stream = = & dice - > tx_stream )
2015-02-21 23:54:59 +09:00
resources = & dice - > tx_resources ;
2014-12-09 00:10:36 +09:00
else
2015-02-21 23:54:59 +09:00
resources = & dice - > rx_resources ;
amdtp_stream_destroy ( stream ) ;
fw_iso_resources_destroy ( resources ) ;
2014-12-09 00:10:36 +09:00
}
int snd_dice_stream_init_duplex ( struct snd_dice * dice )
{
int err ;
dice - > substreams_counter = 0 ;
err = init_stream ( dice , & dice - > tx_stream ) ;
2014-11-29 00:59:14 +09:00
if ( err < 0 )
2014-12-09 00:10:36 +09:00
goto end ;
2014-11-29 00:59:14 +09:00
2014-12-09 00:10:36 +09:00
err = init_stream ( dice , & dice - > rx_stream ) ;
2015-02-21 23:54:59 +09:00
if ( err < 0 )
destroy_stream ( dice , & dice - > tx_stream ) ;
2014-11-29 00:59:14 +09:00
end :
return err ;
}
2014-12-09 00:10:36 +09:00
void snd_dice_stream_destroy_duplex ( struct snd_dice * dice )
2014-11-29 00:59:14 +09:00
{
2014-12-09 00:10:35 +09:00
snd_dice_transaction_clear_enable ( dice ) ;
2014-12-09 00:10:36 +09:00
destroy_stream ( dice , & dice - > tx_stream ) ;
destroy_stream ( dice , & dice - > rx_stream ) ;
dice - > substreams_counter = 0 ;
2014-11-29 00:59:14 +09:00
}
2014-12-09 00:10:36 +09:00
void snd_dice_stream_update_duplex ( struct snd_dice * dice )
2014-11-29 00:59:14 +09:00
{
/*
* On a bus reset , the DICE firmware disables streaming and then goes
* off contemplating its own navel for hundreds of milliseconds before
* it can react to any of our attempts to reenable streaming . This
* means that we lose synchronization anyway , so we force our streams
* to stop so that the application can restart them in an orderly
* manner .
*/
dice - > global_enabled = false ;
2014-12-09 00:10:36 +09:00
stop_stream ( dice , & dice - > rx_stream ) ;
stop_stream ( dice , & dice - > tx_stream ) ;
2014-12-09 00:10:35 +09:00
2014-11-29 00:59:14 +09:00
fw_iso_resources_update ( & dice - > rx_resources ) ;
2014-12-09 00:10:36 +09:00
fw_iso_resources_update ( & dice - > tx_resources ) ;
2014-11-29 00:59:14 +09:00
}
static void dice_lock_changed ( struct snd_dice * dice )
{
dice - > dev_lock_changed = true ;
wake_up ( & dice - > hwdep_wait ) ;
}
int snd_dice_stream_lock_try ( struct snd_dice * dice )
{
int err ;
spin_lock_irq ( & dice - > lock ) ;
if ( dice - > dev_lock_count < 0 ) {
err = - EBUSY ;
goto out ;
}
if ( dice - > dev_lock_count + + = = 0 )
dice_lock_changed ( dice ) ;
err = 0 ;
out :
spin_unlock_irq ( & dice - > lock ) ;
return err ;
}
void snd_dice_stream_lock_release ( struct snd_dice * dice )
{
spin_lock_irq ( & dice - > lock ) ;
if ( WARN_ON ( dice - > dev_lock_count < = 0 ) )
goto out ;
if ( - - dice - > dev_lock_count = = 0 )
dice_lock_changed ( dice ) ;
out :
spin_unlock_irq ( & dice - > lock ) ;
}