2010-10-12 15:58:52 +04:00
/*
* smdk_spdif . c - - S / PDIF audio for SMDK
*
* Copyright 2010 Samsung Electronics Co . Ltd .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of the
* License , or ( at your option ) any later version .
*
*/
# include <linux/clk.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2010-10-12 15:58:52 +04:00
# include <sound/soc.h>
# include "spdif.h"
/* Audio clock settings are belonged to board specific part. Every
* board can set audio source clock setting which is matched with H / W
* like this function - ' set_audio_clock_heirachy ' .
*/
static int set_audio_clock_heirachy ( struct platform_device * pdev )
{
struct clk * fout_epll , * mout_epll , * sclk_audio0 , * sclk_spdif ;
2010-12-06 01:56:59 +03:00
int ret = 0 ;
2010-10-12 15:58:52 +04:00
fout_epll = clk_get ( NULL , " fout_epll " ) ;
if ( IS_ERR ( fout_epll ) ) {
printk ( KERN_WARNING " %s: Cannot find fout_epll. \n " ,
__func__ ) ;
return - EINVAL ;
}
mout_epll = clk_get ( NULL , " mout_epll " ) ;
2010-11-21 20:40:21 +03:00
if ( IS_ERR ( mout_epll ) ) {
2010-10-12 15:58:52 +04:00
printk ( KERN_WARNING " %s: Cannot find mout_epll. \n " ,
__func__ ) ;
ret = - EINVAL ;
goto out1 ;
}
sclk_audio0 = clk_get ( & pdev - > dev , " sclk_audio " ) ;
if ( IS_ERR ( sclk_audio0 ) ) {
printk ( KERN_WARNING " %s: Cannot find sclk_audio. \n " ,
__func__ ) ;
ret = - EINVAL ;
goto out2 ;
}
sclk_spdif = clk_get ( NULL , " sclk_spdif " ) ;
2010-11-21 20:40:21 +03:00
if ( IS_ERR ( sclk_spdif ) ) {
2010-10-12 15:58:52 +04:00
printk ( KERN_WARNING " %s: Cannot find sclk_spdif. \n " ,
__func__ ) ;
ret = - EINVAL ;
goto out3 ;
}
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
/* Set audio clock hierarchy for S/PDIF */
2010-10-12 15:58:52 +04:00
clk_set_parent ( mout_epll , fout_epll ) ;
clk_set_parent ( sclk_audio0 , mout_epll ) ;
clk_set_parent ( sclk_spdif , sclk_audio0 ) ;
clk_put ( sclk_spdif ) ;
out3 :
clk_put ( sclk_audio0 ) ;
out2 :
clk_put ( mout_epll ) ;
out1 :
clk_put ( fout_epll ) ;
return ret ;
}
/* We should haved to set clock directly on this part because of clock
* scheme of Samsudng SoCs did not support to set rates from abstrct
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
* clock of it ' s hierarchy .
2010-10-12 15:58:52 +04:00
*/
static int set_audio_clock_rate ( unsigned long epll_rate ,
unsigned long audio_rate )
{
struct clk * fout_epll , * sclk_spdif ;
fout_epll = clk_get ( NULL , " fout_epll " ) ;
if ( IS_ERR ( fout_epll ) ) {
printk ( KERN_ERR " %s: failed to get fout_epll \n " , __func__ ) ;
return - ENOENT ;
}
clk_set_rate ( fout_epll , epll_rate ) ;
clk_put ( fout_epll ) ;
sclk_spdif = clk_get ( NULL , " sclk_spdif " ) ;
if ( IS_ERR ( sclk_spdif ) ) {
printk ( KERN_ERR " %s: failed to get sclk_spdif \n " , __func__ ) ;
return - ENOENT ;
}
clk_set_rate ( sclk_spdif , audio_rate ) ;
clk_put ( sclk_spdif ) ;
return 0 ;
}
static int smdk_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
unsigned long pll_out , rclk_rate ;
int ret , ratio ;
switch ( params_rate ( params ) ) {
case 44100 :
pll_out = 45158400 ;
break ;
case 32000 :
case 48000 :
case 96000 :
pll_out = 49152000 ;
break ;
default :
return - EINVAL ;
}
/* Setting ratio to 512fs helps to use S/PDIF with HDMI without
* modify S / PDIF ASoC machine driver .
*/
ratio = 512 ;
rclk_rate = params_rate ( params ) * ratio ;
/* Set audio source clock rates */
ret = set_audio_clock_rate ( pll_out , rclk_rate ) ;
if ( ret < 0 )
return ret ;
/* Set S/PDIF uses internal source clock */
ret = snd_soc_dai_set_sysclk ( cpu_dai , SND_SOC_SPDIF_INT_MCLK ,
rclk_rate , SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
return ret ;
}
static struct snd_soc_ops smdk_spdif_ops = {
. hw_params = smdk_hw_params ,
} ;
static struct snd_soc_dai_link smdk_dai = {
. name = " S/PDIF " ,
. stream_name = " S/PDIF PCM Playback " ,
2010-11-22 09:35:50 +03:00
. platform_name = " samsung-audio " ,
2010-10-12 15:58:52 +04:00
. cpu_dai_name = " samsung-spdif " ,
. codec_dai_name = " dit-hifi " ,
. codec_name = " spdif-dit " ,
. ops = & smdk_spdif_ops ,
} ;
static struct snd_soc_card smdk = {
. name = " SMDK-S/PDIF " ,
2011-12-22 06:53:15 +04:00
. owner = THIS_MODULE ,
2010-10-12 15:58:52 +04:00
. dai_link = & smdk_dai ,
. num_links = 1 ,
} ;
static struct platform_device * smdk_snd_spdif_dit_device ;
static struct platform_device * smdk_snd_spdif_device ;
static int __init smdk_init ( void )
{
int ret ;
smdk_snd_spdif_dit_device = platform_device_alloc ( " spdif-dit " , - 1 ) ;
if ( ! smdk_snd_spdif_dit_device )
return - ENOMEM ;
ret = platform_device_add ( smdk_snd_spdif_dit_device ) ;
if ( ret )
2010-11-26 09:54:42 +03:00
goto err1 ;
2010-10-12 15:58:52 +04:00
smdk_snd_spdif_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! smdk_snd_spdif_device ) {
ret = - ENOMEM ;
goto err2 ;
}
platform_set_drvdata ( smdk_snd_spdif_device , & smdk ) ;
ret = platform_device_add ( smdk_snd_spdif_device ) ;
if ( ret )
2010-11-26 09:54:42 +03:00
goto err3 ;
2010-10-12 15:58:52 +04:00
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
/* Set audio clock hierarchy manually */
2010-10-12 15:58:52 +04:00
ret = set_audio_clock_heirachy ( smdk_snd_spdif_device ) ;
if ( ret )
2010-11-26 09:54:42 +03:00
goto err4 ;
2010-10-12 15:58:52 +04:00
return 0 ;
2010-11-26 09:54:42 +03:00
err4 :
platform_device_del ( smdk_snd_spdif_device ) ;
err3 :
2010-10-12 15:58:52 +04:00
platform_device_put ( smdk_snd_spdif_device ) ;
err2 :
2010-11-26 09:54:42 +03:00
platform_device_del ( smdk_snd_spdif_dit_device ) ;
err1 :
2010-10-12 15:58:52 +04:00
platform_device_put ( smdk_snd_spdif_dit_device ) ;
return ret ;
}
static void __exit smdk_exit ( void )
{
platform_device_unregister ( smdk_snd_spdif_device ) ;
2010-11-26 09:54:42 +03:00
platform_device_unregister ( smdk_snd_spdif_dit_device ) ;
2010-10-12 15:58:52 +04:00
}
module_init ( smdk_init ) ;
module_exit ( smdk_exit ) ;
MODULE_AUTHOR ( " Seungwhan Youn, <sw.youn@samsung.com> " ) ;
MODULE_DESCRIPTION ( " ALSA SoC SMDK+S/PDIF " ) ;
MODULE_LICENSE ( " GPL " ) ;