2019-05-29 16:57:59 -07:00
// SPDX-License-Identifier: GPL-2.0-only
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 >
*/
# 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 ;
2014-12-12 22:27:03 +03:00
int i , err ;
2014-12-09 00:10:42 +09:00
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 ;
2014-12-12 22:27:03 +03:00
int i , j , err ;
2014-12-09 00:10:42 +09:00
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 ;
2014-12-12 22:27:03 +03:00
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: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:47 +09:00
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
2015-09-19 11:22:01 +09:00
runtime - > hw . formats = AM824_IN_PCM_FORMAT_BITS ;
2014-12-09 00:10:47 +09:00
stream = & oxfw - > tx_stream ;
formats = oxfw - > tx_stream_formats ;
} else {
2015-09-19 11:22:01 +09:00
runtime - > hw . formats = AM824_OUT_PCM_FORMAT_BITS ;
2014-12-09 00:10:47 +09:00
stream = & oxfw - > rx_stream ;
formats = oxfw - > rx_stream_formats ;
}
2014-12-09 00:10:42 +09:00
limit_channels_and_rates ( & runtime - > hw , formats ) ;
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 ;
2015-09-19 11:21:57 +09:00
err = amdtp_am824_add_pcm_hw_constraints ( stream , runtime ) ;
2014-12-09 00:10:47 +09:00
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 ;
2019-10-07 20:05:27 +09:00
struct amdtp_domain * d = & oxfw - > domain ;
2014-12-09 00:10:47 +09:00
int err ;
2014-12-09 00:10:49 +09:00
err = snd_oxfw_stream_lock_try ( oxfw ) ;
2014-12-09 00:10:42 +09:00
if ( err < 0 )
2019-10-07 20:05:27 +09:00
return err ;
2014-12-09 00:10:42 +09:00
2014-12-09 00:10:49 +09:00
err = init_hw_params ( oxfw , substream ) ;
if ( err < 0 )
goto err_locked ;
2019-10-07 20:05:27 +09:00
mutex_lock ( & oxfw - > mutex ) ;
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if ( oxfw - > substreams_count > 0 & & d - > events_per_period > 0 ) {
unsigned int frames_per_period = d - > events_per_period ;
2019-10-18 00:54:16 +09:00
unsigned int frames_per_buffer = d - > events_per_buffer ;
2019-10-07 20:05:27 +09:00
2014-12-09 00:10:47 +09:00
err = limit_to_current_params ( substream ) ;
2019-10-07 20:05:27 +09:00
if ( err < 0 ) {
mutex_unlock ( & oxfw - > mutex ) ;
goto err_locked ;
}
if ( frames_per_period > 0 ) {
err = snd_pcm_hw_constraint_minmax ( substream - > runtime ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE ,
frames_per_period , frames_per_period ) ;
if ( err < 0 ) {
mutex_unlock ( & oxfw - > mutex ) ;
goto err_locked ;
}
2019-10-18 00:54:16 +09:00
err = snd_pcm_hw_constraint_minmax ( substream - > runtime ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
frames_per_buffer , frames_per_buffer ) ;
if ( err < 0 ) {
mutex_unlock ( & oxfw - > mutex ) ;
goto err_locked ;
}
2019-10-07 20:05:27 +09:00
}
2014-12-09 00:10:47 +09:00
}
2019-10-07 20:05:27 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
2014-12-09 00:10:42 +09:00
snd_pcm_set_sync ( substream ) ;
2019-10-07 20:05:27 +09:00
return 0 ;
2014-12-09 00:10:49 +09:00
err_locked :
snd_oxfw_stream_lock_release ( oxfw ) ;
return err ;
2014-11-29 00:59:28 +09:00
}
static int pcm_close ( struct snd_pcm_substream * substream )
{
2014-12-09 00:10:49 +09:00
struct snd_oxfw * oxfw = substream - > private_data ;
snd_oxfw_stream_lock_release ( oxfw ) ;
2014-11-29 00:59:28 +09:00
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 ;
2019-12-09 10:48:41 +01:00
int err = 0 ;
2014-12-09 00:10:47 +09:00
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
2019-06-12 17:44:21 +09:00
unsigned int rate = params_rate ( hw_params ) ;
unsigned int channels = params_channels ( hw_params ) ;
2019-10-07 20:05:19 +09:00
unsigned int frames_per_period = params_period_size ( hw_params ) ;
2019-10-18 00:54:16 +09:00
unsigned int frames_per_buffer = params_buffer_size ( hw_params ) ;
2019-06-12 17:44:21 +09:00
2014-12-09 00:10:47 +09:00
mutex_lock ( & oxfw - > mutex ) ;
2019-06-12 17:44:21 +09:00
err = snd_oxfw_stream_reserve_duplex ( oxfw , & oxfw - > tx_stream ,
2019-10-18 00:54:16 +09:00
rate , channels , frames_per_period ,
frames_per_buffer ) ;
2019-06-12 17:44:21 +09:00
if ( err > = 0 )
+ + oxfw - > substreams_count ;
2014-12-09 00:10:47 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
}
2019-06-12 17:44:21 +09:00
return err ;
2014-12-09 00:10:47 +09:00
}
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 ;
2019-12-09 10:48:41 +01:00
int err = 0 ;
2014-12-09 00:10:47 +09:00
if ( substream - > runtime - > status - > state = = SNDRV_PCM_STATE_OPEN ) {
2019-06-12 17:44:21 +09:00
unsigned int rate = params_rate ( hw_params ) ;
unsigned int channels = params_channels ( hw_params ) ;
2019-10-07 20:05:19 +09:00
unsigned int frames_per_period = params_period_size ( hw_params ) ;
2019-10-18 00:54:16 +09:00
unsigned int frames_per_buffer = params_buffer_size ( hw_params ) ;
2019-06-12 17:44:21 +09:00
2014-12-09 00:10:47 +09:00
mutex_lock ( & oxfw - > mutex ) ;
2019-08-26 22:55:15 +09:00
err = snd_oxfw_stream_reserve_duplex ( oxfw , & oxfw - > rx_stream ,
2019-10-18 00:54:16 +09:00
rate , channels , frames_per_period ,
frames_per_buffer ) ;
2019-06-12 17:44:21 +09:00
if ( err > = 0 )
+ + oxfw - > substreams_count ;
2014-12-09 00:10:47 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
}
2019-12-10 00:03:04 +09:00
return err ;
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 )
2019-06-12 17:44:20 +09:00
- - oxfw - > substreams_count ;
2014-12-09 00:10:47 +09:00
2019-06-12 17:44:19 +09:00
snd_oxfw_stream_stop_duplex ( oxfw ) ;
2014-12-09 00:10:47 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
2019-12-09 10:48:41 +01:00
return 0 ;
2014-12-09 00:10:47 +09:00
}
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 )
2019-06-12 17:44:20 +09:00
- - oxfw - > substreams_count ;
2014-12-09 00:10:47 +09:00
2019-06-12 17:44:19 +09:00
snd_oxfw_stream_stop_duplex ( oxfw ) ;
2014-12-09 00:10:47 +09:00
2014-11-29 00:59:28 +09:00
mutex_unlock ( & oxfw - > mutex ) ;
2019-12-09 10:48:41 +01:00
return 0 ;
2014-11-29 00:59:28 +09:00
}
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 ;
int err ;
mutex_lock ( & oxfw - > mutex ) ;
2019-06-12 17:44:21 +09:00
err = snd_oxfw_stream_start_duplex ( oxfw ) ;
2014-12-09 00:10:47 +09:00
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 ;
int err ;
mutex_lock ( & oxfw - > mutex ) ;
2019-06-12 17:44:21 +09:00
err = snd_oxfw_stream_start_duplex ( oxfw ) ;
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 ;
2019-10-18 15:19:07 +09:00
return amdtp_domain_stream_pcm_pointer ( & oxfw - > domain , & oxfw - > tx_stream ) ;
2014-12-09 00:10:47 +09:00
}
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
2019-10-18 15:19:07 +09:00
return amdtp_domain_stream_pcm_pointer ( & oxfw - > domain , & oxfw - > rx_stream ) ;
2014-11-29 00:59:28 +09:00
}
2017-06-07 09:38:05 +09:00
static int pcm_capture_ack ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
2019-10-18 15:19:08 +09:00
return amdtp_domain_stream_pcm_ack ( & oxfw - > domain , & oxfw - > tx_stream ) ;
2017-06-07 09:38:05 +09:00
}
static int pcm_playback_ack ( struct snd_pcm_substream * substream )
{
struct snd_oxfw * oxfw = substream - > private_data ;
2019-10-18 15:19:08 +09:00
return amdtp_domain_stream_pcm_ack ( & oxfw - > domain , & oxfw - > rx_stream ) ;
2017-06-07 09:38:05 +09:00
}
2014-11-29 00:59:28 +09:00
int snd_oxfw_create_pcm ( struct snd_oxfw * oxfw )
{
2016-09-02 00:13:11 +02:00
static const struct snd_pcm_ops capture_ops = {
2014-12-09 00:10:47 +09:00
. open = pcm_open ,
. close = pcm_close ,
. hw_params = pcm_capture_hw_params ,
. hw_free = pcm_capture_hw_free ,
. prepare = pcm_capture_prepare ,
. trigger = pcm_capture_trigger ,
. pointer = pcm_capture_pointer ,
2017-06-07 09:38:05 +09:00
. ack = pcm_capture_ack ,
2014-12-09 00:10:47 +09:00
} ;
2016-09-02 00:13:11 +02:00
static const struct snd_pcm_ops playback_ops = {
2014-11-29 00:59:28 +09:00
. open = pcm_open ,
. close = pcm_close ,
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 ,
2017-06-07 09:38:05 +09:00
. ack = pcm_playback_ack ,
2014-11-29 00:59:28 +09:00
} ;
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 ) ;
2019-12-09 10:48:41 +01:00
snd_pcm_set_managed_buffer_all ( pcm , SNDRV_DMA_TYPE_VMALLOC , NULL , 0 , 0 ) ;
2014-12-09 00:10:47 +09:00
2014-11-29 00:59:28 +09:00
return 0 ;
}