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 .
*/
# include "rsnd.h"
# define MIX_NAME_SIZE 16
# define MIX_NAME "mix"
struct rsnd_mix {
struct rsnd_mod 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 ) ;
}
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
{
2015-11-04 08:44:32 +00:00
rsnd_mod_write ( mod , MIX_MDBAR , 0 ) ;
rsnd_mod_write ( mod , MIX_MDBBR , 0 ) ;
rsnd_mod_write ( mod , MIX_MDBCR , 0 ) ;
rsnd_mod_write ( mod , MIX_MDBDR , 0 ) ;
}
static void rsnd_mix_volume_init ( struct rsnd_dai_stream * io ,
struct rsnd_mod * mod )
{
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 */
rsnd_mod_write ( mod , MIX_MIXMR , 0 ) ;
rsnd_mod_write ( mod , MIX_MVPDR , 0 ) ;
/* 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 ;
}
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 ,
} ;
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 ) ;
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 ) ;
2015-07-15 07:17:36 +00:00
if ( ret )
2015-11-10 05:12:32 +00:00
goto rsnd_mix_probe_done ;
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
}
}