2012-04-08 21:17:50 -07:00
/*
* ASoC simple sound card support
*
* Copyright ( C ) 2012 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 .
*/
2013-11-20 15:25:02 +09:00
# include <linux/clk.h>
2012-04-08 21:17:50 -07:00
# include <linux/module.h>
2013-11-20 15:25:02 +09:00
# include <linux/of.h>
2012-04-08 21:17:50 -07:00
# include <linux/platform_device.h>
2014-01-14 12:35:32 +08:00
# include <linux/string.h>
2012-04-08 21:17:50 -07:00
# include <sound/simple_card.h>
2013-01-10 16:49:11 -08:00
static int __asoc_simple_card_dai_init ( struct snd_soc_dai * dai ,
struct asoc_simple_dai * set ,
unsigned int daifmt )
{
int ret = 0 ;
daifmt | = set - > fmt ;
2013-12-23 13:24:59 +08:00
if ( daifmt )
2013-01-10 16:49:11 -08:00
ret = snd_soc_dai_set_fmt ( dai , daifmt ) ;
2013-10-17 22:46:49 -07:00
if ( ret = = - ENOTSUPP ) {
dev_dbg ( dai - > dev , " ASoC: set_fmt is not supported \n " ) ;
ret = 0 ;
}
2013-01-10 16:49:11 -08:00
if ( ! ret & & set - > sysclk )
ret = snd_soc_dai_set_sysclk ( dai , 0 , set - > sysclk , 0 ) ;
return ret ;
}
2012-04-08 21:17:50 -07:00
static int asoc_simple_card_dai_init ( struct snd_soc_pcm_runtime * rtd )
{
2014-01-13 17:08:08 +08:00
struct asoc_simple_card_info * info =
snd_soc_card_get_drvdata ( rtd - > card ) ;
2012-04-08 21:17:50 -07:00
struct snd_soc_dai * codec = rtd - > codec_dai ;
struct snd_soc_dai * cpu = rtd - > cpu_dai ;
2013-01-10 16:49:11 -08:00
unsigned int daifmt = info - > daifmt ;
2012-04-08 21:17:50 -07:00
int ret ;
2013-01-10 16:49:11 -08:00
ret = __asoc_simple_card_dai_init ( codec , & info - > codec_dai , daifmt ) ;
if ( ret < 0 )
return ret ;
2012-04-08 21:17:50 -07:00
2013-01-10 16:49:11 -08:00
ret = __asoc_simple_card_dai_init ( cpu , & info - > cpu_dai , daifmt ) ;
if ( ret < 0 )
return ret ;
2012-04-08 21:17:50 -07:00
return 0 ;
}
2013-11-20 15:25:02 +09:00
static int
asoc_simple_card_sub_parse_of ( struct device_node * np ,
struct asoc_simple_dai * dai ,
2014-01-15 16:51:41 +01:00
const struct device_node * * p_node )
2013-11-20 15:25:02 +09:00
{
2014-01-15 16:51:41 +01:00
struct device_node * node ;
2013-11-20 15:25:02 +09:00
struct clk * clk ;
int ret ;
/*
* get node via " sound-dai = <&phandle port> "
* it will be used as xxx_of_node on soc_bind_dai_link ( )
*/
2014-01-15 16:51:41 +01:00
node = of_parse_phandle ( np , " sound-dai " , 0 ) ;
if ( ! node )
2013-11-20 15:25:02 +09:00
return - ENODEV ;
2014-01-15 16:51:41 +01:00
* p_node = node ;
2013-11-20 15:25:02 +09:00
/* get dai->name */
ret = snd_soc_of_get_dai_name ( np , & dai - > name ) ;
if ( ret < 0 )
goto parse_error ;
/*
* bitclock - inversion , frame - inversion
* bitclock - master , frame - master
* and specific " format " if it has
*/
dai - > fmt = snd_soc_of_parse_daifmt ( np , NULL ) ;
/*
* dai - > sysclk come from
* " clocks = <&xxx> " ( if system has common clock )
* or " system-clock-frequency = <xxx> "
2013-12-23 15:25:38 +08:00
* or device ' s module clock .
2013-11-20 15:25:02 +09:00
*/
2013-12-23 15:25:38 +08:00
if ( of_property_read_bool ( np , " clocks " ) ) {
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
goto parse_error ;
}
dai - > sysclk = clk_get_rate ( clk ) ;
} else if ( of_property_read_bool ( np , " system-clock-frequency " ) ) {
2013-11-20 15:25:02 +09:00
of_property_read_u32 ( np ,
" system-clock-frequency " ,
& dai - > sysclk ) ;
2013-12-23 15:25:38 +08:00
} else {
2014-01-15 16:51:41 +01:00
clk = of_clk_get ( node , 0 ) ;
2014-01-06 12:34:36 +08:00
if ( ! IS_ERR ( clk ) )
dai - > sysclk = clk_get_rate ( clk ) ;
2013-12-23 15:25:38 +08:00
}
2013-11-20 15:25:02 +09:00
ret = 0 ;
parse_error :
2014-01-15 16:51:41 +01:00
of_node_put ( node ) ;
2013-11-20 15:25:02 +09:00
return ret ;
}
static int asoc_simple_card_parse_of ( struct device_node * node ,
struct asoc_simple_card_info * info ,
2014-01-15 16:51:41 +01:00
struct device * dev )
2013-11-20 15:25:02 +09:00
{
struct device_node * np ;
char * name ;
2013-12-23 12:57:01 +08:00
int ret ;
2013-11-20 15:25:02 +09:00
/* get CPU/CODEC common format via simple-audio-card,format */
info - > daifmt = snd_soc_of_parse_daifmt ( node , " simple-audio-card, " ) &
( SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK ) ;
2013-12-23 12:57:01 +08:00
/* DAPM routes */
2014-01-07 09:15:16 +08:00
if ( of_property_read_bool ( node , " simple-audio-card,routing " ) ) {
2014-01-07 09:13:42 +08:00
ret = snd_soc_of_parse_audio_routing ( & info - > snd_card ,
2014-01-07 09:15:16 +08:00
" simple-audio-card,routing " ) ;
2014-01-07 09:13:42 +08:00
if ( ret )
return ret ;
}
2013-12-23 12:57:01 +08:00
2013-11-20 15:25:02 +09:00
/* CPU sub-node */
ret = - EINVAL ;
np = of_get_child_by_name ( node , " simple-audio-card,cpu " ) ;
if ( np )
ret = asoc_simple_card_sub_parse_of ( np ,
& info - > cpu_dai ,
2014-01-15 16:51:41 +01:00
& info - > snd_link . cpu_of_node ) ;
2013-11-20 15:25:02 +09:00
if ( ret < 0 )
return ret ;
/* CODEC sub-node */
ret = - EINVAL ;
np = of_get_child_by_name ( node , " simple-audio-card,codec " ) ;
if ( np )
ret = asoc_simple_card_sub_parse_of ( np ,
& info - > codec_dai ,
2014-01-15 16:51:41 +01:00
& info - > snd_link . codec_of_node ) ;
2013-11-20 15:25:02 +09:00
if ( ret < 0 )
return ret ;
2013-12-20 14:39:50 +08:00
if ( ! info - > cpu_dai . name | | ! info - > codec_dai . name )
return - EINVAL ;
2013-11-20 15:25:02 +09:00
/* card name is created from CPU/CODEC dai name */
name = devm_kzalloc ( dev ,
strlen ( info - > cpu_dai . name ) +
strlen ( info - > codec_dai . name ) + 2 ,
GFP_KERNEL ) ;
sprintf ( name , " %s-%s " , info - > cpu_dai . name , info - > codec_dai . name ) ;
2014-01-15 16:51:37 +01:00
info - > snd_card . name = name ;
info - > snd_link . name = info - > snd_link . stream_name = name ;
2013-11-20 15:25:02 +09:00
/* simple-card assumes platform == cpu */
2014-01-15 16:51:41 +01:00
info - > snd_link . platform_of_node = info - > snd_link . cpu_of_node ;
2013-11-20 15:25:02 +09:00
2014-01-15 16:51:37 +01:00
dev_dbg ( dev , " card-name : %s \n " , name ) ;
2013-11-20 15:25:02 +09:00
dev_dbg ( dev , " platform : %04x \n " , info - > daifmt ) ;
dev_dbg ( dev , " cpu : %s / %04x / %d \n " ,
info - > cpu_dai . name ,
info - > cpu_dai . fmt ,
info - > cpu_dai . sysclk ) ;
dev_dbg ( dev , " codec : %s / %04x / %d \n " ,
info - > codec_dai . name ,
info - > codec_dai . fmt ,
info - > codec_dai . sysclk ) ;
return 0 ;
}
2012-04-08 21:17:50 -07:00
static int asoc_simple_card_probe ( struct platform_device * pdev )
{
2013-11-20 15:25:02 +09:00
struct asoc_simple_card_info * cinfo ;
struct device_node * np = pdev - > dev . of_node ;
2012-12-25 22:52:33 -08:00
struct device * dev = & pdev - > dev ;
2014-01-14 12:35:32 +08:00
int ret ;
2012-04-08 21:17:50 -07:00
2014-01-14 12:35:32 +08:00
cinfo = devm_kzalloc ( dev , sizeof ( * cinfo ) , GFP_KERNEL ) ;
if ( ! cinfo )
return - ENOMEM ;
2014-01-15 16:51:41 +01:00
/*
* init snd_soc_card
*/
cinfo - > snd_card . owner = THIS_MODULE ;
cinfo - > snd_card . dev = dev ;
cinfo - > snd_card . dai_link = & cinfo - > snd_link ;
cinfo - > snd_card . num_links = 1 ;
2013-11-20 15:25:02 +09:00
if ( np & & of_device_is_available ( np ) ) {
2014-01-14 12:35:32 +08:00
2014-01-15 16:51:41 +01:00
ret = asoc_simple_card_parse_of ( np , cinfo , dev ) ;
2014-01-14 12:35:32 +08:00
if ( ret < 0 ) {
if ( ret ! = - EPROBE_DEFER )
dev_err ( dev , " parse error %d \n " , ret ) ;
return ret ;
2013-11-20 15:25:02 +09:00
}
} else {
2014-01-14 12:35:32 +08:00
if ( ! dev - > platform_data ) {
2014-01-09 17:49:40 +08:00
dev_err ( dev , " no info for asoc-simple-card \n " ) ;
return - EINVAL ;
}
2013-11-20 15:25:02 +09:00
2014-01-14 12:35:32 +08:00
memcpy ( cinfo , dev - > platform_data , sizeof ( * cinfo ) ) ;
2014-01-15 16:51:33 +01:00
if ( ! cinfo - > name | |
! cinfo - > card | |
! cinfo - > codec_dai . name | |
! cinfo - > codec | |
! cinfo - > platform | |
! cinfo - > cpu_dai . name ) {
dev_err ( dev , " insufficient asoc_simple_card_info settings \n " ) ;
return - EINVAL ;
}
2014-01-15 16:51:37 +01:00
cinfo - > snd_card . name = cinfo - > card ;
cinfo - > snd_link . name = cinfo - > name ;
cinfo - > snd_link . stream_name = cinfo - > name ;
cinfo - > snd_link . platform_name = cinfo - > platform ;
cinfo - > snd_link . codec_name = cinfo - > codec ;
2012-04-08 21:17:50 -07:00
}
/*
* init snd_soc_dai_link
*/
2013-01-10 16:49:11 -08:00
cinfo - > snd_link . cpu_dai_name = cinfo - > cpu_dai . name ;
cinfo - > snd_link . codec_dai_name = cinfo - > codec_dai . name ;
cinfo - > snd_link . init = asoc_simple_card_dai_init ;
2012-04-08 21:17:50 -07:00
2014-01-13 17:08:08 +08:00
snd_soc_card_set_drvdata ( & cinfo - > snd_card , cinfo ) ;
2012-04-08 21:17:50 -07:00
2013-12-19 11:59:54 +08:00
return devm_snd_soc_register_card ( & pdev - > dev , & cinfo - > snd_card ) ;
2012-04-08 21:17:50 -07:00
}
2013-11-20 15:25:02 +09:00
static const struct of_device_id asoc_simple_of_match [ ] = {
{ . compatible = " simple-audio-card " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , asoc_simple_of_match ) ;
2012-04-08 21:17:50 -07:00
static struct platform_driver asoc_simple_card = {
. driver = {
. name = " asoc-simple-card " ,
2013-08-23 14:35:17 -03:00
. owner = THIS_MODULE ,
2013-11-20 15:25:02 +09:00
. of_match_table = asoc_simple_of_match ,
2012-04-08 21:17:50 -07:00
} ,
. probe = asoc_simple_card_probe ,
} ;
module_platform_driver ( asoc_simple_card ) ;
2013-08-23 14:35:17 -03:00
MODULE_ALIAS ( " platform:asoc-simple-card " ) ;
2012-04-08 21:17:50 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " ASoC Simple Sound Card " ) ;
MODULE_AUTHOR ( " Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> " ) ;