2014-11-29 00:59:28 +09:00
/*
* oxfw_pcm . c - a part of driver for OXFW970 / 971 based devices
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
* Licensed under the terms of the GNU General Public License , version 2.
*/
# include "oxfw.h"
2014-12-09 00:10:42 +09:00
static int hw_rule_rate ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2014-11-29 00:59:28 +09:00
{
2014-12-09 00:10:42 +09:00
u8 * * formats = rule - > private ;
struct snd_interval * r =
hw_param_interval ( params , SNDRV_PCM_HW_PARAM_RATE ) ;
const struct snd_interval * c =
hw_param_interval_c ( params , SNDRV_PCM_HW_PARAM_CHANNELS ) ;
struct snd_interval t = {
. min = UINT_MAX , . max = 0 , . integer = 1
} ;
struct snd_oxfw_stream_formation formation ;
unsigned int i , err ;
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
if ( formats [ i ] = = NULL )
continue ;
err = snd_oxfw_stream_parse_format ( formats [ i ] , & formation ) ;
if ( err < 0 )
continue ;
if ( ! snd_interval_test ( c , formation . pcm ) )
continue ;
t . min = min ( t . min , formation . rate ) ;
t . max = max ( t . max , formation . rate ) ;
}
return snd_interval_refine ( r , & t ) ;
2014-11-29 00:59:28 +09:00
}
2014-12-09 00:10:42 +09:00
static int hw_rule_channels ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
2014-11-29 00:59:28 +09:00
{
2014-12-09 00:10:42 +09:00
u8 * * formats = rule - > private ;
struct snd_interval * c =
hw_param_interval ( params , SNDRV_PCM_HW_PARAM_CHANNELS ) ;
const struct snd_interval * r =
hw_param_interval_c ( params , SNDRV_PCM_HW_PARAM_RATE ) ;
struct snd_oxfw_stream_formation formation ;
unsigned int i , j , err ;
unsigned int count , list [ SND_OXFW_STREAM_FORMAT_ENTRIES ] = { 0 } ;
count = 0 ;
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
if ( formats [ i ] = = NULL )
break ;
err = snd_oxfw_stream_parse_format ( formats [ i ] , & formation ) ;
if ( err < 0 )
continue ;
if ( ! snd_interval_test ( r , formation . rate ) )
continue ;
if ( list [ count ] = = formation . pcm )
continue ;
for ( j = 0 ; j < ARRAY_SIZE ( list ) ; j + + ) {
if ( list [ j ] = = formation . pcm )
break ;
}
if ( j = = ARRAY_SIZE ( list ) ) {
list [ count ] = formation . pcm ;
if ( + + count = = ARRAY_SIZE ( list ) )
break ;
}
}
return snd_interval_list ( c , count , list , 0 ) ;
2014-11-29 00:59:28 +09:00
}
2014-12-09 00:10:42 +09:00
static void limit_channels_and_rates ( struct snd_pcm_hardware * hw , u8 * * formats )
2014-11-29 00:59:28 +09:00
{
2014-12-09 00:10:42 +09:00
struct snd_oxfw_stream_formation formation ;
unsigned int i , err ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:42 +09:00
hw - > channels_min = UINT_MAX ;
hw - > channels_max = 0 ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:42 +09:00
hw - > rate_min = UINT_MAX ;
hw - > rate_max = 0 ;
hw - > rates = 0 ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:42 +09:00
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
if ( formats [ i ] = = NULL )
break ;
err = snd_oxfw_stream_parse_format ( formats [ i ] , & formation ) ;
if ( err < 0 )
continue ;
hw - > channels_min = min ( hw - > channels_min , formation . pcm ) ;
hw - > channels_max = max ( hw - > channels_max , formation . pcm ) ;
hw - > rate_min = min ( hw - > rate_min , formation . rate ) ;
hw - > rate_max = max ( hw - > rate_max , formation . rate ) ;
hw - > rates | = snd_pcm_rate_to_rate_bit ( formation . rate ) ;
}
2014-11-29 00:59:28 +09:00
}
2014-12-09 00:10:42 +09:00
static void limit_period_and_buffer ( struct snd_pcm_hardware * hw )
2014-11-29 00:59:28 +09:00
{
2014-12-09 00:10:42 +09:00
hw - > periods_min = 2 ; /* SNDRV_PCM_INFO_BATCH */
hw - > periods_max = UINT_MAX ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:42 +09:00
hw - > period_bytes_min = 4 * hw - > channels_max ; /* bytes for a frame */
/* Just to prevent from allocating much pages. */
hw - > period_bytes_max = hw - > period_bytes_min * 2048 ;
hw - > buffer_bytes_max = hw - > period_bytes_max * hw - > periods_min ;
2014-11-29 00:59:28 +09:00
}
2014-12-09 00:10:47 +09:00
static int init_hw_params ( struct snd_oxfw * oxfw ,
struct snd_pcm_substream * substream )
2014-11-29 00:59:28 +09:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2014-12-09 00:10:42 +09:00
u8 * * formats ;
2014-12-09 00:10:47 +09:00
struct amdtp_stream * stream ;
2014-11-29 00:59:28 +09:00
int err ;
2014-12-09 00:10:42 +09:00
runtime - > hw . info = SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_INTERLEAVED |
2014-12-09 00:10:47 +09:00
SNDRV_PCM_INFO_JOINT_DUPLEX |
2014-12-09 00:10:42 +09:00
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:47 +09:00
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
runtime - > hw . formats = AMDTP_IN_PCM_FORMAT_BITS ;
stream = & oxfw - > tx_stream ;
formats = oxfw - > tx_stream_formats ;
} else {
runtime - > hw . formats = AMDTP_OUT_PCM_FORMAT_BITS ;
stream = & oxfw - > rx_stream ;
formats = oxfw - > rx_stream_formats ;
}
2014-12-09 00:10:42 +09:00
limit_channels_and_rates ( & runtime - > hw , formats ) ;
limit_period_and_buffer ( & runtime - > hw ) ;
2014-11-29 00:59:28 +09:00
2014-12-09 00:10:42 +09:00
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_CHANNELS ,
hw_rule_channels , formats ,
SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
2014-11-29 00:59:28 +09:00
if ( err < 0 )
goto end ;
2014-12-09 00:10:42 +09:00
err = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
hw_rule_rate , formats ,
SNDRV_PCM_HW_PARAM_CHANNELS , - 1 ) ;
2014-11-29 00:59:28 +09:00
if ( err < 0 )
goto end ;
2014-12-09 00:10:47 +09:00
err = amdtp_stream_add_pcm_hw_constraints ( stream , runtime ) ;
end :
return err ;
}
static int limit_to_current_params ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
struct snd_oxfw_stream_formation formation ;
enum avc_general_plug_dir dir ;
int err ;
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
dir = AVC_GENERAL_PLUG_DIR_OUT ;
else
dir = AVC_GENERAL_PLUG_DIR_IN ;
err = snd_oxfw_stream_get_current_formation ( oxfw , dir , & formation ) ;
if ( err < 0 )
goto end ;
substream - > runtime - > hw . channels_min = formation . pcm ;
substream - > runtime - > hw . channels_max = formation . pcm ;
substream - > runtime - > hw . rate_min = formation . rate ;
substream - > runtime - > hw . rate_max = formation . rate ;
end :
return err ;
}
static int pcm_open ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
int err ;
err = init_hw_params ( oxfw , substream ) ;
2014-12-09 00:10:42 +09:00
if ( err < 0 )
goto end ;
2014-12-09 00:10:47 +09:00
/*
* When any PCM streams are already running , the available sampling
* rate is limited at current value .
*/
if ( amdtp_stream_pcm_running ( & oxfw - > tx_stream ) | |
amdtp_stream_pcm_running ( & oxfw - > rx_stream ) ) {
err = limit_to_current_params ( substream ) ;
if ( err < 0 )
goto end ;
}
2014-12-09 00:10:42 +09:00
snd_pcm_set_sync ( substream ) ;
2014-11-29 00:59:28 +09:00
end :
return err ;
}
static int pcm_close ( struct snd_pcm_substream * substream )
{
return 0 ;
}
2014-12-09 00:10:47 +09:00
static int pcm_capture_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
2014-11-29 00:59:28 +09:00
{
struct snd_oxfw * oxfw = substream - > private_data ;
2014-12-09 00:10:47 +09:00
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
mutex_lock ( & oxfw - > mutex ) ;
oxfw - > capture_substreams + + ;
mutex_unlock ( & oxfw - > mutex ) ;
}
amdtp_stream_set_pcm_format ( & oxfw - > tx_stream , params_format ( hw_params ) ) ;
return snd_pcm_lib_alloc_vmalloc_buffer ( substream ,
params_buffer_bytes ( hw_params ) ) ;
}
static int pcm_playback_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct snd_oxfw * oxfw = substream - > private_data ;
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
mutex_lock ( & oxfw - > mutex ) ;
oxfw - > playback_substreams + + ;
mutex_unlock ( & oxfw - > mutex ) ;
}
2014-12-09 00:10:44 +09:00
amdtp_stream_set_pcm_format ( & oxfw - > rx_stream , params_format ( hw_params ) ) ;
2014-12-09 00:10:47 +09:00
2014-12-09 00:10:44 +09:00
return snd_pcm_lib_alloc_vmalloc_buffer ( substream ,
params_buffer_bytes ( hw_params ) ) ;
2014-11-29 00:59:28 +09:00
}
2014-12-09 00:10:47 +09:00
static int pcm_capture_hw_free ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
mutex_lock ( & oxfw - > mutex ) ;
if ( substream - > runtime - > status - > state ! = SNDRV_PCM_STATE_OPEN )
oxfw - > capture_substreams - - ;
snd_oxfw_stream_stop_simplex ( oxfw , & oxfw - > tx_stream ) ;
mutex_unlock ( & oxfw - > mutex ) ;
return snd_pcm_lib_free_vmalloc_buffer ( substream ) ;
}
static int pcm_playback_hw_free ( struct snd_pcm_substream * substream )
2014-11-29 00:59:28 +09:00
{
struct snd_oxfw * oxfw = substream - > private_data ;
mutex_lock ( & oxfw - > mutex ) ;
2014-12-09 00:10:47 +09:00
if ( substream - > runtime - > status - > state ! = SNDRV_PCM_STATE_OPEN )
oxfw - > playback_substreams - - ;
2014-12-09 00:10:46 +09:00
snd_oxfw_stream_stop_simplex ( oxfw , & oxfw - > rx_stream ) ;
2014-12-09 00:10:47 +09:00
2014-11-29 00:59:28 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
return snd_pcm_lib_free_vmalloc_buffer ( substream ) ;
}
2014-12-09 00:10:47 +09:00
static int pcm_capture_prepare ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
int err ;
mutex_lock ( & oxfw - > mutex ) ;
err = snd_oxfw_stream_start_simplex ( oxfw , & oxfw - > tx_stream ,
runtime - > rate , runtime - > channels ) ;
mutex_unlock ( & oxfw - > mutex ) ;
if ( err < 0 )
goto end ;
amdtp_stream_pcm_prepare ( & oxfw - > tx_stream ) ;
end :
return err ;
}
static int pcm_playback_prepare ( struct snd_pcm_substream * substream )
2014-11-29 00:59:28 +09:00
{
struct snd_oxfw * oxfw = substream - > private_data ;
2014-12-09 00:10:44 +09:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2014-11-29 00:59:28 +09:00
int err ;
mutex_lock ( & oxfw - > mutex ) ;
2014-12-09 00:10:46 +09:00
err = snd_oxfw_stream_start_simplex ( oxfw , & oxfw - > rx_stream ,
runtime - > rate , runtime - > channels ) ;
2014-12-09 00:10:44 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
2014-11-29 00:59:28 +09:00
if ( err < 0 )
goto end ;
amdtp_stream_pcm_prepare ( & oxfw - > rx_stream ) ;
end :
return err ;
}
2014-12-09 00:10:47 +09:00
static int pcm_capture_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct snd_oxfw * oxfw = substream - > private_data ;
struct snd_pcm_substream * pcm ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
pcm = substream ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
pcm = NULL ;
break ;
default :
return - EINVAL ;
}
amdtp_stream_pcm_trigger ( & oxfw - > tx_stream , pcm ) ;
return 0 ;
}
static int pcm_playback_trigger ( struct snd_pcm_substream * substream , int cmd )
2014-11-29 00:59:28 +09:00
{
struct snd_oxfw * oxfw = substream - > private_data ;
struct snd_pcm_substream * pcm ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
pcm = substream ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
pcm = NULL ;
break ;
default :
return - EINVAL ;
}
amdtp_stream_pcm_trigger ( & oxfw - > rx_stream , pcm ) ;
return 0 ;
}
2014-12-09 00:10:47 +09:00
static snd_pcm_uframes_t pcm_capture_pointer ( struct snd_pcm_substream * sbstm )
2014-11-29 00:59:28 +09:00
{
2014-12-09 00:10:47 +09:00
struct snd_oxfw * oxfw = sbstm - > private_data ;
return amdtp_stream_pcm_pointer ( & oxfw - > tx_stream ) ;
}
static snd_pcm_uframes_t pcm_playback_pointer ( struct snd_pcm_substream * sbstm )
{
struct snd_oxfw * oxfw = sbstm - > private_data ;
2014-11-29 00:59:28 +09:00
return amdtp_stream_pcm_pointer ( & oxfw - > rx_stream ) ;
}
int snd_oxfw_create_pcm ( struct snd_oxfw * oxfw )
{
2014-12-09 00:10:47 +09:00
static struct snd_pcm_ops capture_ops = {
. open = pcm_open ,
. close = pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = pcm_capture_hw_params ,
. hw_free = pcm_capture_hw_free ,
. prepare = pcm_capture_prepare ,
. trigger = pcm_capture_trigger ,
. pointer = pcm_capture_pointer ,
. page = snd_pcm_lib_get_vmalloc_page ,
. mmap = snd_pcm_lib_mmap_vmalloc ,
} ;
static struct snd_pcm_ops playback_ops = {
2014-11-29 00:59:28 +09:00
. open = pcm_open ,
. close = pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
2014-12-09 00:10:47 +09:00
. hw_params = pcm_playback_hw_params ,
. hw_free = pcm_playback_hw_free ,
. prepare = pcm_playback_prepare ,
. trigger = pcm_playback_trigger ,
. pointer = pcm_playback_pointer ,
2014-11-29 00:59:28 +09:00
. page = snd_pcm_lib_get_vmalloc_page ,
. mmap = snd_pcm_lib_mmap_vmalloc ,
} ;
struct snd_pcm * pcm ;
2014-12-09 00:10:47 +09:00
unsigned int cap = 0 ;
2014-11-29 00:59:28 +09:00
int err ;
2014-12-09 00:10:47 +09:00
if ( oxfw - > has_output )
cap = 1 ;
err = snd_pcm_new ( oxfw - > card , oxfw - > card - > driver , 0 , 1 , cap , & pcm ) ;
2014-11-29 00:59:28 +09:00
if ( err < 0 )
return err ;
2014-12-09 00:10:47 +09:00
2014-11-29 00:59:28 +09:00
pcm - > private_data = oxfw ;
strcpy ( pcm - > name , oxfw - > card - > shortname ) ;
2014-12-09 00:10:47 +09:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & playback_ops ) ;
if ( cap > 0 )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & capture_ops ) ;
2014-11-29 00:59:28 +09:00
return 0 ;
}