2015-05-09 13:26:42 +03:00
/*
* PCM DRM helpers
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/export.h>
# include <drm/drm_edid.h>
# include <sound/pcm.h>
# include <sound/pcm_drm_eld.h>
static const unsigned int eld_rates [ ] = {
32000 ,
44100 ,
48000 ,
88200 ,
96000 ,
176400 ,
192000 ,
} ;
static unsigned int sad_max_channels ( const u8 * sad )
{
return 1 + ( sad [ 0 ] & 7 ) ;
}
static int eld_limit_rates ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
{
struct snd_interval * r = hw_param_interval ( params , rule - > var ) ;
2017-05-17 02:48:20 +03:00
const struct snd_interval * c ;
2015-05-09 13:26:42 +03:00
unsigned int rate_mask = 7 , i ;
const u8 * sad , * eld = rule - > private ;
sad = drm_eld_sad ( eld ) ;
if ( sad ) {
2017-05-17 02:48:20 +03:00
c = hw_param_interval_c ( params , SNDRV_PCM_HW_PARAM_CHANNELS ) ;
2015-05-09 13:26:42 +03:00
for ( i = drm_eld_sad_count ( eld ) ; i > 0 ; i - - , sad + = 3 ) {
unsigned max_channels = sad_max_channels ( sad ) ;
/*
* Exclude SADs which do not include the
* requested number of channels .
*/
if ( c - > min < = max_channels )
rate_mask | = sad [ 1 ] ;
}
}
return snd_interval_list ( r , ARRAY_SIZE ( eld_rates ) , eld_rates ,
rate_mask ) ;
}
static int eld_limit_channels ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
{
struct snd_interval * c = hw_param_interval ( params , rule - > var ) ;
2017-05-17 02:48:20 +03:00
const struct snd_interval * r ;
2015-05-09 13:26:42 +03:00
struct snd_interval t = { . min = 1 , . max = 2 , . integer = 1 , } ;
unsigned int i ;
const u8 * sad , * eld = rule - > private ;
sad = drm_eld_sad ( eld ) ;
if ( sad ) {
unsigned int rate_mask = 0 ;
/* Convert the rate interval to a mask */
2017-05-17 02:48:20 +03:00
r = hw_param_interval_c ( params , SNDRV_PCM_HW_PARAM_RATE ) ;
2015-05-09 13:26:42 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( eld_rates ) ; i + + )
if ( r - > min < = eld_rates [ i ] & & r - > max > = eld_rates [ i ] )
rate_mask | = BIT ( i ) ;
for ( i = drm_eld_sad_count ( eld ) ; i > 0 ; i - - , sad + = 3 )
if ( rate_mask & sad [ 1 ] )
t . max = max ( t . max , sad_max_channels ( sad ) ) ;
}
return snd_interval_refine ( c , & t ) ;
}
int snd_pcm_hw_constraint_eld ( struct snd_pcm_runtime * runtime , void * eld )
{
int ret ;
ret = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
eld_limit_rates , eld ,
SNDRV_PCM_HW_PARAM_CHANNELS , - 1 ) ;
if ( ret < 0 )
return ret ;
ret = snd_pcm_hw_rule_add ( runtime , 0 , SNDRV_PCM_HW_PARAM_CHANNELS ,
eld_limit_channels , eld ,
SNDRV_PCM_HW_PARAM_RATE , - 1 ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( snd_pcm_hw_constraint_eld ) ;