2015-07-15 07:17:36 +00:00
/*
* mix . c
*
* Copyright ( c ) 2015 Kuninori Morimoto < kuninori . morimoto . gx @ renesas . com >
*
* 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 .
*/
2017-10-01 23:49:18 +00:00
/*
* CTUn MIXn
* + - - - - - - + + - - - - - - +
* [ SRC3 / SRC6 ] - > | CTU n0 | - > [ MIX n0 | - >
* [ SRC4 / SRC9 ] - > | CTU n1 | - > [ MIX n1 | - >
* [ SRC0 / SRC1 ] - > | CTU n2 | - > [ MIX n2 | - >
* [ SRC2 / SRC5 ] - > | CTU n3 | - > [ MIX n3 | - >
* + - - - - - - + + - - - - - - +
*
* ex )
* DAI0 : playback = < & src0 & ctu02 & mix0 & dvc0 & ssi0 > ;
* DAI1 : playback = < & src2 & ctu03 & mix0 & dvc0 & ssi0 > ;
*
* MIX Volume
* amixer set " MIX " , 0 100 % // DAI0 Volume
* amixer set " MIX " , 1 100 % // DAI1 Volume
*
* Volume Ramp
* amixer set " MIX Ramp Up Rate " " 0.125 dB/1 step "
* amixer set " MIX Ramp Down Rate " " 4 dB/1 step "
* amixer set " MIX Ramp " on
* aplay xxx . wav &
* amixer set " MIX " , 0 80 % // DAI0 Volume Down
* amixer set " MIX " , 1 100 % // DAI1 Volume Up
*/
2015-07-15 07:17:36 +00:00
# include "rsnd.h"
# define MIX_NAME_SIZE 16
# define MIX_NAME "mix"
struct rsnd_mix {
struct rsnd_mod mod ;
2017-10-01 23:49:18 +00:00
struct rsnd_kctrl_cfg_s volumeA ; /* MDBAR */
struct rsnd_kctrl_cfg_s volumeB ; /* MDBBR */
struct rsnd_kctrl_cfg_s volumeC ; /* MDBCR */
struct rsnd_kctrl_cfg_s volumeD ; /* MDBDR */
struct rsnd_kctrl_cfg_s ren ; /* Ramp Enable */
struct rsnd_kctrl_cfg_s rup ; /* Ramp Rate Up */
struct rsnd_kctrl_cfg_s rdw ; /* Ramp Rate Down */
u32 flags ;
2015-07-15 07:17:36 +00:00
} ;
2017-10-01 23:49:18 +00:00
# define ONCE_KCTRL_INITIALIZED (1 << 0)
# define HAS_VOLA (1 << 1)
# define HAS_VOLB (1 << 2)
# define HAS_VOLC (1 << 3)
# define HAS_VOLD (1 << 4)
# define VOL_MAX 0x3ff
# define rsnd_mod_to_mix(_mod) \
container_of ( ( _mod ) , struct rsnd_mix , mod )
2015-11-10 05:12:32 +00:00
# define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
2015-07-15 07:17:36 +00:00
# define rsnd_mix_nr(priv) ((priv)->mix_nr)
# define for_each_rsnd_mix(pos, priv, i) \
for ( ( i ) = 0 ; \
( ( i ) < rsnd_mix_nr ( priv ) ) & & \
( ( pos ) = ( struct rsnd_mix * ) ( priv ) - > mix + i ) ; \
i + + )
2015-11-30 08:50:51 +00:00
static void rsnd_mix_activation ( struct rsnd_mod * mod )
2015-07-15 07:17:36 +00:00
{
rsnd_mod_write ( mod , MIX_SWRSR , 0 ) ;
rsnd_mod_write ( mod , MIX_SWRSR , 1 ) ;
}
2015-11-30 08:51:52 +00:00
static void rsnd_mix_halt ( struct rsnd_mod * mod )
{
rsnd_mod_write ( mod , MIX_MIXIR , 1 ) ;
rsnd_mod_write ( mod , MIX_SWRSR , 0 ) ;
}
2017-10-01 23:49:18 +00:00
# define rsnd_mix_get_vol(mix, X) \
rsnd_flags_has ( mix , HAS_VOL # # X ) ? \
2017-10-11 04:42:34 +00:00
( VOL_MAX - rsnd_kctrl_vals ( mix - > volume # # X ) ) : 0
2015-11-04 08:44:32 +00:00
static void rsnd_mix_volume_parameter ( struct rsnd_dai_stream * io ,
struct rsnd_mod * mod )
2015-07-15 07:17:36 +00:00
{
2017-10-01 23:49:18 +00:00
struct rsnd_priv * priv = rsnd_mod_to_priv ( mod ) ;
struct device * dev = rsnd_priv_to_dev ( priv ) ;
struct rsnd_mix * mix = rsnd_mod_to_mix ( mod ) ;
u32 volA = rsnd_mix_get_vol ( mix , A ) ;
u32 volB = rsnd_mix_get_vol ( mix , B ) ;
u32 volC = rsnd_mix_get_vol ( mix , C ) ;
u32 volD = rsnd_mix_get_vol ( mix , D ) ;
dev_dbg ( dev , " MIX A/B/C/D = %02x/%02x/%02x/%02x \n " ,
volA , volB , volC , volD ) ;
rsnd_mod_write ( mod , MIX_MDBAR , volA ) ;
rsnd_mod_write ( mod , MIX_MDBBR , volB ) ;
rsnd_mod_write ( mod , MIX_MDBCR , volC ) ;
rsnd_mod_write ( mod , MIX_MDBDR , volD ) ;
2015-11-04 08:44:32 +00:00
}
static void rsnd_mix_volume_init ( struct rsnd_dai_stream * io ,
struct rsnd_mod * mod )
{
2017-10-01 23:49:18 +00:00
struct rsnd_mix * mix = rsnd_mod_to_mix ( mod ) ;
2015-11-04 08:44:32 +00:00
rsnd_mod_write ( mod , MIX_MIXIR , 1 ) ;
/* General Information */
2016-02-25 05:54:58 +00:00
rsnd_mod_write ( mod , MIX_ADINR , rsnd_runtime_channel_after_ctu ( io ) ) ;
2015-11-04 08:44:32 +00:00
/* volume step */
2017-10-11 04:42:34 +00:00
rsnd_mod_write ( mod , MIX_MIXMR , rsnd_kctrl_vals ( mix - > ren ) ) ;
rsnd_mod_write ( mod , MIX_MVPDR , rsnd_kctrl_vals ( mix - > rup ) < < 8 |
rsnd_kctrl_vals ( mix - > rdw ) ) ;
2015-11-04 08:44:32 +00:00
/* common volume parameter */
rsnd_mix_volume_parameter ( io , mod ) ;
rsnd_mod_write ( mod , MIX_MIXIR , 0 ) ;
2015-07-15 07:17:36 +00:00
}
static void rsnd_mix_volume_update ( struct rsnd_dai_stream * io ,
struct rsnd_mod * mod )
{
/* Disable MIX dB setting */
rsnd_mod_write ( mod , MIX_MDBER , 0 ) ;
2015-11-04 08:44:32 +00:00
/* common volume parameter */
rsnd_mix_volume_parameter ( io , mod ) ;
2015-07-15 07:17:36 +00:00
/* Enable MIX dB setting */
rsnd_mod_write ( mod , MIX_MDBER , 1 ) ;
}
2015-10-26 08:43:21 +00:00
static int rsnd_mix_probe_ ( struct rsnd_mod * mod ,
struct rsnd_dai_stream * io ,
struct rsnd_priv * priv )
{
return rsnd_cmd_attach ( io , rsnd_mod_id ( mod ) ) ;
}
2015-07-15 07:17:36 +00:00
static int rsnd_mix_init ( struct rsnd_mod * mod ,
struct rsnd_dai_stream * io ,
struct rsnd_priv * priv )
{
2015-10-22 03:15:04 +00:00
rsnd_mod_power_on ( mod ) ;
2015-07-15 07:17:36 +00:00
2015-11-30 08:50:51 +00:00
rsnd_mix_activation ( mod ) ;
2015-07-15 07:17:36 +00:00
2015-11-04 08:44:32 +00:00
rsnd_mix_volume_init ( io , mod ) ;
2015-07-15 07:17:36 +00:00
rsnd_mix_volume_update ( io , mod ) ;
return 0 ;
}
static int rsnd_mix_quit ( struct rsnd_mod * mod ,
struct rsnd_dai_stream * io ,
struct rsnd_priv * priv )
{
2015-11-30 08:51:52 +00:00
rsnd_mix_halt ( mod ) ;
2015-10-22 03:15:04 +00:00
rsnd_mod_power_off ( mod ) ;
2015-07-15 07:17:36 +00:00
return 0 ;
}
2017-10-01 23:49:18 +00:00
static int rsnd_mix_pcm_new ( struct rsnd_mod * mod ,
struct rsnd_dai_stream * io ,
struct snd_soc_pcm_runtime * rtd )
{
struct rsnd_priv * priv = rsnd_mod_to_priv ( mod ) ;
struct device * dev = rsnd_priv_to_dev ( priv ) ;
struct rsnd_mix * mix = rsnd_mod_to_mix ( mod ) ;
struct rsnd_mod * src_mod = rsnd_io_to_mod_src ( io ) ;
struct rsnd_kctrl_cfg_s * volume ;
int ret ;
switch ( rsnd_mod_id ( src_mod ) ) {
case 3 :
case 6 : /* MDBAR */
volume = & mix - > volumeA ;
rsnd_flags_set ( mix , HAS_VOLA ) ;
break ;
case 4 :
case 9 : /* MDBBR */
volume = & mix - > volumeB ;
rsnd_flags_set ( mix , HAS_VOLB ) ;
break ;
case 0 :
case 1 : /* MDBCR */
volume = & mix - > volumeC ;
rsnd_flags_set ( mix , HAS_VOLC ) ;
break ;
case 2 :
case 5 : /* MDBDR */
volume = & mix - > volumeD ;
rsnd_flags_set ( mix , HAS_VOLD ) ;
break ;
default :
dev_err ( dev , " unknown SRC is connected \n " ) ;
return - EINVAL ;
}
/* Volume */
ret = rsnd_kctrl_new_s ( mod , io , rtd ,
" MIX Playback Volume " ,
rsnd_kctrl_accept_anytime ,
rsnd_mix_volume_update ,
volume , VOL_MAX ) ;
if ( ret < 0 )
return ret ;
2017-10-11 04:42:34 +00:00
rsnd_kctrl_vals ( * volume ) = VOL_MAX ;
2017-10-01 23:49:18 +00:00
if ( rsnd_flags_has ( mix , ONCE_KCTRL_INITIALIZED ) )
return ret ;
/* Ramp */
ret = rsnd_kctrl_new_s ( mod , io , rtd ,
" MIX Ramp Switch " ,
rsnd_kctrl_accept_anytime ,
rsnd_mix_volume_update ,
& mix - > ren , 1 ) ;
if ( ret < 0 )
return ret ;
ret = rsnd_kctrl_new_e ( mod , io , rtd ,
" MIX Ramp Up Rate " ,
rsnd_kctrl_accept_anytime ,
rsnd_mix_volume_update ,
& mix - > rup ,
volume_ramp_rate ,
VOLUME_RAMP_MAX_MIX ) ;
if ( ret < 0 )
return ret ;
ret = rsnd_kctrl_new_e ( mod , io , rtd ,
" MIX Ramp Down Rate " ,
rsnd_kctrl_accept_anytime ,
rsnd_mix_volume_update ,
& mix - > rdw ,
volume_ramp_rate ,
VOLUME_RAMP_MAX_MIX ) ;
rsnd_flags_set ( mix , ONCE_KCTRL_INITIALIZED ) ;
return ret ;
}
2015-07-15 07:17:36 +00:00
static struct rsnd_mod_ops rsnd_mix_ops = {
. name = MIX_NAME ,
2015-10-26 08:43:21 +00:00
. probe = rsnd_mix_probe_ ,
2015-07-15 07:17:36 +00:00
. init = rsnd_mix_init ,
. quit = rsnd_mix_quit ,
2017-10-01 23:49:18 +00:00
. pcm_new = rsnd_mix_pcm_new ,
2015-07-15 07:17:36 +00:00
} ;
struct rsnd_mod * rsnd_mix_mod_get ( struct rsnd_priv * priv , int id )
{
if ( WARN_ON ( id < 0 | | id > = rsnd_mix_nr ( priv ) ) )
id = 0 ;
2015-11-10 05:12:32 +00:00
return rsnd_mod_get ( rsnd_mix_get ( priv , id ) ) ;
2015-07-15 07:17:36 +00:00
}
2015-11-10 05:14:12 +00:00
int rsnd_mix_probe ( struct rsnd_priv * priv )
2015-07-15 07:17:36 +00:00
{
2015-11-10 05:12:32 +00:00
struct device_node * node ;
struct device_node * np ;
2015-07-15 07:17:36 +00:00
struct device * dev = rsnd_priv_to_dev ( priv ) ;
struct rsnd_mix * mix ;
struct clk * clk ;
char name [ MIX_NAME_SIZE ] ;
int i , nr , ret ;
/* This driver doesn't support Gen1 at this point */
2015-10-15 03:25:28 +00:00
if ( rsnd_is_gen1 ( priv ) )
return 0 ;
2015-07-15 07:17:36 +00:00
2015-11-10 05:12:32 +00:00
node = rsnd_mix_of_node ( priv ) ;
if ( ! node )
return 0 ; /* not used is not error */
2015-07-15 07:17:36 +00:00
2015-11-10 05:12:32 +00:00
nr = of_get_child_count ( node ) ;
if ( ! nr ) {
ret = - EINVAL ;
goto rsnd_mix_probe_done ;
}
2015-07-15 07:17:36 +00:00
mix = devm_kzalloc ( dev , sizeof ( * mix ) * nr , GFP_KERNEL ) ;
2015-11-10 05:12:32 +00:00
if ( ! mix ) {
ret = - ENOMEM ;
goto rsnd_mix_probe_done ;
}
2015-07-15 07:17:36 +00:00
priv - > mix_nr = nr ;
priv - > mix = mix ;
2015-11-10 05:12:32 +00:00
i = 0 ;
2015-12-14 12:05:28 +00:00
ret = 0 ;
2015-11-10 05:12:32 +00:00
for_each_child_of_node ( node , np ) {
mix = rsnd_mix_get ( priv , i ) ;
2015-07-15 07:17:36 +00:00
snprintf ( name , MIX_NAME_SIZE , " %s.%d " ,
MIX_NAME , i ) ;
clk = devm_clk_get ( dev , name ) ;
2015-11-10 05:12:32 +00:00
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
2017-07-15 09:19:07 +02:00
of_node_put ( np ) ;
2015-11-10 05:12:32 +00:00
goto rsnd_mix_probe_done ;
}
2015-07-15 07:17:36 +00:00
2015-09-10 07:02:21 +00:00
ret = rsnd_mod_init ( priv , rsnd_mod_get ( mix ) , & rsnd_mix_ops ,
2016-01-21 01:58:07 +00:00
clk , rsnd_mod_get_status , RSND_MOD_MIX , i ) ;
2017-07-15 09:19:07 +02:00
if ( ret ) {
of_node_put ( np ) ;
2015-11-10 05:12:32 +00:00
goto rsnd_mix_probe_done ;
2017-07-15 09:19:07 +02:00
}
2015-11-10 05:12:32 +00:00
i + + ;
2015-07-15 07:17:36 +00:00
}
2015-11-10 05:12:32 +00:00
rsnd_mix_probe_done :
of_node_put ( node ) ;
return ret ;
2015-07-15 07:17:36 +00:00
}
2015-11-10 05:14:12 +00:00
void rsnd_mix_remove ( struct rsnd_priv * priv )
2015-07-15 07:17:36 +00:00
{
struct rsnd_mix * mix ;
int i ;
for_each_rsnd_mix ( mix , priv , i ) {
2015-09-10 07:02:21 +00:00
rsnd_mod_quit ( rsnd_mod_get ( mix ) ) ;
2015-07-15 07:17:36 +00:00
}
}