2014-05-08 17:44:49 -07:00
/*
* Renesas R - Car DVC support
*
* Copyright ( C ) 2014 Renesas Solutions Corp .
* 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 RSND_DVC_NAME_SIZE 16
2014-10-21 18:13:56 -07:00
# define RSND_DVC_CHANNELS 2
2014-05-22 23:25:43 -07:00
# define DVC_NAME "dvc"
2014-10-21 18:14:14 -07:00
struct rsnd_dvc_cfg {
unsigned int max ;
2014-11-04 20:27:46 -08:00
unsigned int size ;
u32 * val ;
} ;
struct rsnd_dvc_cfg_m {
struct rsnd_dvc_cfg cfg ;
2014-10-21 18:14:14 -07:00
u32 val [ RSND_DVC_CHANNELS ] ;
} ;
2014-11-04 20:28:10 -08:00
struct rsnd_dvc_cfg_s {
struct rsnd_dvc_cfg cfg ;
u32 val ;
} ;
2014-05-08 17:44:49 -07:00
struct rsnd_dvc {
struct rsnd_dvc_platform_info * info ; /* rcar_snd.h */
struct rsnd_mod mod ;
struct clk * clk ;
2014-11-04 20:27:46 -08:00
struct rsnd_dvc_cfg_m volume ;
struct rsnd_dvc_cfg_m mute ;
2014-05-08 17:44:49 -07:00
} ;
# define rsnd_mod_to_dvc(_mod) \
container_of ( ( _mod ) , struct rsnd_dvc , mod )
# define for_each_rsnd_dvc(pos, priv, i) \
for ( ( i ) = 0 ; \
( ( i ) < rsnd_dvc_nr ( priv ) ) & & \
( ( pos ) = ( struct rsnd_dvc * ) ( priv ) - > dvc + i ) ; \
i + + )
static void rsnd_dvc_volume_update ( struct rsnd_mod * mod )
{
struct rsnd_dvc * dvc = rsnd_mod_to_dvc ( mod ) ;
2014-11-04 20:26:53 -08:00
u32 dvucr = 0 ;
2014-08-01 03:10:55 -07:00
u32 mute = 0 ;
2014-05-08 17:44:49 -07:00
int i ;
2014-11-04 20:27:46 -08:00
for ( i = 0 ; i < dvc - > mute . cfg . size ; i + + )
mute | = ( ! ! dvc - > mute . cfg . val [ i ] ) < < i ;
2014-05-08 17:44:49 -07:00
ASoC: rsnd: move DVC_DVUER settings under rsnd_dvc_volume_update()
We need to Enable/Disable DVC_DVUER register if we set
DVCp_ZCMCR, DVCp_VRCTR, DVCp_VRPDR, DVCp_VRDBR,
DVCp_VOL0R, DVCp_VOL1R, DVCp_VOL2R, DVCp_VOL3R,
DVCp_VOL4R, DVCp_VOL5R, DVCp_VOL6R, DVCp_VOL7R
and, these are controlled under rsnd_dvc_volume_update().
This patch moves DVC_DVUER settings to it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2014-11-04 20:27:18 -08:00
/* Disable DVC Register access */
rsnd_mod_write ( mod , DVC_DVUER , 0 ) ;
2014-11-04 20:26:53 -08:00
/* Enable Digital Volume */
dvucr = 0x100 ;
2014-10-21 18:14:14 -07:00
rsnd_mod_write ( mod , DVC_VOL0R , dvc - > volume . val [ 0 ] ) ;
rsnd_mod_write ( mod , DVC_VOL1R , dvc - > volume . val [ 1 ] ) ;
2014-08-01 03:10:55 -07:00
2014-11-04 20:26:53 -08:00
/* Enable Mute */
if ( mute ) {
dvucr | = 0x1 ;
rsnd_mod_write ( mod , DVC_ZCMCR , mute ) ;
}
rsnd_mod_write ( mod , DVC_DVUCR , dvucr ) ;
ASoC: rsnd: move DVC_DVUER settings under rsnd_dvc_volume_update()
We need to Enable/Disable DVC_DVUER register if we set
DVCp_ZCMCR, DVCp_VRCTR, DVCp_VRPDR, DVCp_VRDBR,
DVCp_VOL0R, DVCp_VOL1R, DVCp_VOL2R, DVCp_VOL3R,
DVCp_VOL4R, DVCp_VOL5R, DVCp_VOL6R, DVCp_VOL7R
and, these are controlled under rsnd_dvc_volume_update().
This patch moves DVC_DVUER settings to it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2014-11-04 20:27:18 -08:00
/* Enable DVC Register access */
rsnd_mod_write ( mod , DVC_DVUER , 1 ) ;
2014-05-08 17:44:49 -07:00
}
2014-05-22 23:25:43 -07:00
static int rsnd_dvc_probe_gen2 ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai )
{
struct rsnd_priv * priv = rsnd_mod_to_priv ( mod ) ;
struct device * dev = rsnd_priv_to_dev ( priv ) ;
dev_dbg ( dev , " %s (Gen2) is probed \n " , rsnd_mod_name ( mod ) ) ;
return 0 ;
}
2014-05-08 17:44:49 -07:00
static int rsnd_dvc_init ( struct rsnd_mod * dvc_mod ,
struct rsnd_dai * rdai )
{
struct rsnd_dvc * dvc = rsnd_mod_to_dvc ( dvc_mod ) ;
struct rsnd_dai_stream * io = rsnd_mod_to_io ( dvc_mod ) ;
struct rsnd_priv * priv = rsnd_mod_to_priv ( dvc_mod ) ;
struct rsnd_mod * src_mod = rsnd_io_to_mod_src ( io ) ;
struct device * dev = rsnd_priv_to_dev ( priv ) ;
int dvc_id = rsnd_mod_id ( dvc_mod ) ;
int src_id = rsnd_mod_id ( src_mod ) ;
u32 route [ ] = {
[ 0 ] = 0x30000 ,
[ 1 ] = 0x30001 ,
[ 2 ] = 0x40000 ,
[ 3 ] = 0x10000 ,
[ 4 ] = 0x20000 ,
[ 5 ] = 0x40100
} ;
if ( src_id > = ARRAY_SIZE ( route ) ) {
dev_err ( dev , " DVC%d isn't connected to SRC%d \n " , dvc_id , src_id ) ;
return - EINVAL ;
}
clk_prepare_enable ( dvc - > clk ) ;
/*
* fixme
* it doesn ' t support CTU / MIX
*/
rsnd_mod_write ( dvc_mod , CMD_ROUTE_SLCT , route [ src_id ] ) ;
rsnd_mod_write ( dvc_mod , DVC_SWRSR , 0 ) ;
rsnd_mod_write ( dvc_mod , DVC_SWRSR , 1 ) ;
rsnd_mod_write ( dvc_mod , DVC_DVUIR , 1 ) ;
rsnd_mod_write ( dvc_mod , DVC_ADINR , rsnd_get_adinr ( dvc_mod ) ) ;
/* ch0/ch1 Volume */
rsnd_dvc_volume_update ( dvc_mod ) ;
rsnd_mod_write ( dvc_mod , DVC_DVUIR , 0 ) ;
rsnd_adg_set_cmd_timsel_gen2 ( rdai , dvc_mod , io ) ;
return 0 ;
}
static int rsnd_dvc_quit ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai )
{
struct rsnd_dvc * dvc = rsnd_mod_to_dvc ( mod ) ;
clk_disable_unprepare ( dvc - > clk ) ;
return 0 ;
}
static int rsnd_dvc_start ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai )
{
rsnd_mod_write ( mod , CMD_CTRL , 0x10 ) ;
return 0 ;
}
static int rsnd_dvc_stop ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai )
{
rsnd_mod_write ( mod , CMD_CTRL , 0 ) ;
return 0 ;
}
static int rsnd_dvc_volume_info ( struct snd_kcontrol * kctrl ,
struct snd_ctl_elem_info * uinfo )
{
2014-10-21 18:14:14 -07:00
struct rsnd_dvc_cfg * cfg = ( struct rsnd_dvc_cfg * ) kctrl - > private_value ;
2014-08-01 03:10:55 -07:00
2014-11-04 20:27:46 -08:00
uinfo - > count = cfg - > size ;
2014-05-08 17:44:49 -07:00
uinfo - > value . integer . min = 0 ;
2014-10-21 18:14:14 -07:00
uinfo - > value . integer . max = cfg - > max ;
2014-08-01 03:10:55 -07:00
2014-10-21 18:14:14 -07:00
if ( cfg - > max = = 1 )
2014-08-01 03:10:55 -07:00
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
2014-10-21 18:14:14 -07:00
else
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
2014-05-08 17:44:49 -07:00
return 0 ;
}
static int rsnd_dvc_volume_get ( struct snd_kcontrol * kctrl ,
struct snd_ctl_elem_value * ucontrol )
{
2014-10-21 18:14:14 -07:00
struct rsnd_dvc_cfg * cfg = ( struct rsnd_dvc_cfg * ) kctrl - > private_value ;
2014-05-08 17:44:49 -07:00
int i ;
2014-11-04 20:27:46 -08:00
for ( i = 0 ; i < cfg - > size ; i + + )
2014-10-21 18:14:14 -07:00
ucontrol - > value . integer . value [ i ] = cfg - > val [ i ] ;
2014-05-08 17:44:49 -07:00
return 0 ;
}
static int rsnd_dvc_volume_put ( struct snd_kcontrol * kctrl ,
struct snd_ctl_elem_value * ucontrol )
{
struct rsnd_mod * mod = snd_kcontrol_chip ( kctrl ) ;
2014-10-21 18:14:14 -07:00
struct rsnd_dvc_cfg * cfg = ( struct rsnd_dvc_cfg * ) kctrl - > private_value ;
2014-05-08 17:44:49 -07:00
int i , change = 0 ;
2014-11-04 20:27:46 -08:00
for ( i = 0 ; i < cfg - > size ; i + + ) {
2014-10-21 18:14:14 -07:00
change | = ( ucontrol - > value . integer . value [ i ] ! = cfg - > val [ i ] ) ;
cfg - > val [ i ] = ucontrol - > value . integer . value [ i ] ;
2014-05-08 17:44:49 -07:00
}
2014-08-01 03:10:47 -07:00
if ( change )
2014-05-08 17:44:49 -07:00
rsnd_dvc_volume_update ( mod ) ;
return change ;
}
2014-08-01 03:10:47 -07:00
static int __rsnd_dvc_pcm_new ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct snd_soc_pcm_runtime * rtd ,
const unsigned char * name ,
2014-10-21 18:14:14 -07:00
struct rsnd_dvc_cfg * private )
2014-05-08 17:44:49 -07:00
{
struct snd_card * card = rtd - > card - > snd_card ;
struct snd_kcontrol * kctrl ;
2014-08-01 03:10:47 -07:00
struct snd_kcontrol_new knew = {
2014-05-08 17:44:49 -07:00
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2014-08-01 03:10:47 -07:00
. name = name ,
2014-05-08 17:44:49 -07:00
. info = rsnd_dvc_volume_info ,
. get = rsnd_dvc_volume_get ,
. put = rsnd_dvc_volume_put ,
2014-08-01 03:10:47 -07:00
. private_value = ( unsigned long ) private ,
2014-05-08 17:44:49 -07:00
} ;
int ret ;
kctrl = snd_ctl_new1 ( & knew , mod ) ;
if ( ! kctrl )
return - ENOMEM ;
ret = snd_ctl_add ( card , kctrl ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2014-11-04 20:27:46 -08:00
static int _rsnd_dvc_pcm_new_m ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct snd_soc_pcm_runtime * rtd ,
const unsigned char * name ,
struct rsnd_dvc_cfg_m * private ,
u32 max )
{
private - > cfg . max = max ;
private - > cfg . size = RSND_DVC_CHANNELS ;
private - > cfg . val = private - > val ;
return __rsnd_dvc_pcm_new ( mod , rdai , rtd , name , & private - > cfg ) ;
}
2014-11-04 20:28:10 -08:00
static int _rsnd_dvc_pcm_new_s ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct snd_soc_pcm_runtime * rtd ,
const unsigned char * name ,
struct rsnd_dvc_cfg_s * private ,
u32 max )
{
private - > cfg . max = max ;
private - > cfg . size = 1 ;
private - > cfg . val = & private - > val ;
return __rsnd_dvc_pcm_new ( mod , rdai , rtd , name , & private - > cfg ) ;
}
2014-08-01 03:10:47 -07:00
static int rsnd_dvc_pcm_new ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct snd_soc_pcm_runtime * rtd )
{
struct rsnd_dai_stream * io = rsnd_mod_to_io ( mod ) ;
struct rsnd_dvc * dvc = rsnd_mod_to_dvc ( mod ) ;
int ret ;
/* Volume */
2014-11-04 20:27:46 -08:00
ret = _rsnd_dvc_pcm_new_m ( mod , rdai , rtd ,
2014-08-01 03:10:47 -07:00
rsnd_dai_is_play ( rdai , io ) ?
" DVC Out Playback Volume " : " DVC In Capture Volume " ,
2014-11-04 20:27:46 -08:00
& dvc - > volume , 0x00800000 - 1 ) ;
2014-08-01 03:10:47 -07:00
if ( ret < 0 )
return ret ;
2014-08-01 03:10:55 -07:00
/* Mute */
2014-11-04 20:27:46 -08:00
ret = _rsnd_dvc_pcm_new_m ( mod , rdai , rtd ,
2014-08-01 03:10:55 -07:00
rsnd_dai_is_play ( rdai , io ) ?
" DVC Out Mute Switch " : " DVC In Mute Switch " ,
2014-11-04 20:27:46 -08:00
& dvc - > mute , 1 ) ;
2014-08-01 03:10:55 -07:00
if ( ret < 0 )
return ret ;
2014-08-01 03:10:47 -07:00
return 0 ;
}
2014-05-08 17:44:49 -07:00
static struct rsnd_mod_ops rsnd_dvc_ops = {
2014-05-22 23:25:43 -07:00
. name = DVC_NAME ,
. probe = rsnd_dvc_probe_gen2 ,
2014-05-08 17:44:49 -07:00
. init = rsnd_dvc_init ,
. quit = rsnd_dvc_quit ,
. start = rsnd_dvc_start ,
. stop = rsnd_dvc_stop ,
. pcm_new = rsnd_dvc_pcm_new ,
} ;
struct rsnd_mod * rsnd_dvc_mod_get ( struct rsnd_priv * priv , int id )
{
if ( WARN_ON ( id < 0 | | id > = rsnd_dvc_nr ( priv ) ) )
id = 0 ;
return & ( ( struct rsnd_dvc * ) ( priv - > dvc ) + id ) - > mod ;
}
2014-06-22 17:59:28 -07:00
static void rsnd_of_parse_dvc ( struct platform_device * pdev ,
const struct rsnd_of_data * of_data ,
struct rsnd_priv * priv )
{
struct device_node * node ;
struct rsnd_dvc_platform_info * dvc_info ;
struct rcar_snd_info * info = rsnd_priv_to_info ( priv ) ;
struct device * dev = & pdev - > dev ;
int nr ;
if ( ! of_data )
return ;
node = of_get_child_by_name ( dev - > of_node , " rcar_sound,dvc " ) ;
if ( ! node )
return ;
nr = of_get_child_count ( node ) ;
if ( ! nr )
goto rsnd_of_parse_dvc_end ;
dvc_info = devm_kzalloc ( dev ,
sizeof ( struct rsnd_dvc_platform_info ) * nr ,
GFP_KERNEL ) ;
if ( ! dvc_info ) {
dev_err ( dev , " dvc info allocation error \n " ) ;
goto rsnd_of_parse_dvc_end ;
}
info - > dvc_info = dvc_info ;
info - > dvc_info_nr = nr ;
rsnd_of_parse_dvc_end :
of_node_put ( node ) ;
}
2014-05-08 17:44:49 -07:00
int rsnd_dvc_probe ( struct platform_device * pdev ,
const struct rsnd_of_data * of_data ,
struct rsnd_priv * priv )
{
struct rcar_snd_info * info = rsnd_priv_to_info ( priv ) ;
struct device * dev = rsnd_priv_to_dev ( priv ) ;
struct rsnd_dvc * dvc ;
struct clk * clk ;
char name [ RSND_DVC_NAME_SIZE ] ;
int i , nr ;
2014-06-22 17:59:28 -07:00
rsnd_of_parse_dvc ( pdev , of_data , priv ) ;
2014-05-08 17:44:49 -07:00
nr = info - > dvc_info_nr ;
if ( ! nr )
return 0 ;
/* This driver doesn't support Gen1 at this point */
if ( rsnd_is_gen1 ( priv ) ) {
dev_warn ( dev , " CMD is not supported on Gen1 \n " ) ;
return - EINVAL ;
}
dvc = devm_kzalloc ( dev , sizeof ( * dvc ) * nr , GFP_KERNEL ) ;
if ( ! dvc ) {
dev_err ( dev , " CMD allocate failed \n " ) ;
return - ENOMEM ;
}
priv - > dvc_nr = nr ;
priv - > dvc = dvc ;
for_each_rsnd_dvc ( dvc , priv , i ) {
2014-05-22 23:25:43 -07:00
snprintf ( name , RSND_DVC_NAME_SIZE , " %s.%d " ,
DVC_NAME , i ) ;
2014-05-08 17:44:49 -07:00
clk = devm_clk_get ( dev , name ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
dvc - > info = & info - > dvc_info [ i ] ;
dvc - > clk = clk ;
rsnd_mod_init ( priv , & dvc - > mod , & rsnd_dvc_ops , RSND_MOD_DVC , i ) ;
dev_dbg ( dev , " CMD%d probed \n " , i ) ;
}
return 0 ;
}