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"
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 ;
}
int snd_dice_stream_start_packets ( struct snd_dice * dice )
{
int err ;
if ( amdtp_stream_running ( & dice - > rx_stream ) )
return 0 ;
err = amdtp_stream_start ( & dice - > rx_stream , dice - > rx_resources . channel ,
fw_parent_device ( dice - > unit ) - > max_speed ) ;
if ( err < 0 )
return err ;
err = snd_dice_transaction_set_enable ( dice ) ;
if ( err < 0 ) {
amdtp_stream_stop ( & dice - > rx_stream ) ;
return err ;
}
return 0 ;
}
int snd_dice_stream_start ( struct snd_dice * dice )
{
__be32 channel ;
int err ;
if ( ! dice - > rx_resources . allocated ) {
err = fw_iso_resources_allocate ( & dice - > rx_resources ,
amdtp_stream_get_max_payload ( & dice - > rx_stream ) ,
fw_parent_device ( dice - > unit ) - > max_speed ) ;
if ( err < 0 )
goto error ;
channel = cpu_to_be32 ( dice - > rx_resources . channel ) ;
err = snd_dice_transaction_write_tx ( dice , RX_ISOCHRONOUS ,
& channel , 4 ) ;
if ( err < 0 )
goto err_resources ;
}
err = snd_dice_stream_start_packets ( dice ) ;
if ( err < 0 )
goto err_rx_channel ;
return 0 ;
err_rx_channel :
channel = cpu_to_be32 ( ( u32 ) - 1 ) ;
snd_dice_transaction_write_rx ( dice , RX_ISOCHRONOUS , & channel , 4 ) ;
err_resources :
fw_iso_resources_free ( & dice - > rx_resources ) ;
error :
return err ;
}
void snd_dice_stream_stop_packets ( struct snd_dice * dice )
{
2014-11-29 00:59:15 +09:00
if ( ! amdtp_stream_running ( & dice - > rx_stream ) )
return ;
snd_dice_transaction_clear_enable ( dice ) ;
amdtp_stream_stop ( & dice - > rx_stream ) ;
2014-11-29 00:59:14 +09:00
}
void snd_dice_stream_stop ( struct snd_dice * dice )
{
__be32 channel ;
snd_dice_stream_stop_packets ( dice ) ;
if ( ! dice - > rx_resources . allocated )
return ;
channel = cpu_to_be32 ( ( u32 ) - 1 ) ;
snd_dice_transaction_write_rx ( dice , RX_ISOCHRONOUS , & channel , 4 ) ;
fw_iso_resources_free ( & dice - > rx_resources ) ;
}
int snd_dice_stream_init ( struct snd_dice * dice )
{
int err ;
err = fw_iso_resources_init ( & dice - > rx_resources , dice - > unit ) ;
if ( err < 0 )
goto end ;
dice - > rx_resources . channels_mask = 0x00000000ffffffffuLL ;
err = amdtp_stream_init ( & dice - > rx_stream , dice - > unit , AMDTP_OUT_STREAM ,
CIP_BLOCKING ) ;
if ( err < 0 )
goto error ;
err = snd_dice_transaction_set_clock_source ( dice , CLOCK_SOURCE_ARX1 ) ;
if ( err < 0 )
goto error ;
end :
return err ;
error :
amdtp_stream_destroy ( & dice - > rx_stream ) ;
fw_iso_resources_destroy ( & dice - > rx_resources ) ;
return err ;
}
void snd_dice_stream_destroy ( struct snd_dice * dice )
{
amdtp_stream_pcm_abort ( & dice - > rx_stream ) ;
snd_dice_stream_stop ( dice ) ;
amdtp_stream_destroy ( & dice - > rx_stream ) ;
fw_iso_resources_destroy ( & dice - > rx_resources ) ;
}
void snd_dice_stream_update ( struct snd_dice * dice )
{
/*
* 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 ;
amdtp_stream_pcm_abort ( & dice - > rx_stream ) ;
snd_dice_stream_stop_packets ( dice ) ;
fw_iso_resources_update ( & dice - > rx_resources ) ;
}
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 ) ;
}