2019-04-19 13:21:55 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// smdk_spdif.c - S/PDIF audio for SMDK
//
// Copyright (C) 2010 Samsung Electronics Co., Ltd.
2010-10-12 15:58:52 +04:00
# 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 )
{
2023-09-12 02:49:45 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_dai * cpu_dai = snd_soc_rtd_to_cpu ( rtd , 0 ) ;
2010-10-12 15:58:52 +04:00
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 ;
}
2017-08-16 19:59:29 +03:00
static const struct snd_soc_ops smdk_spdif_ops = {
2010-10-12 15:58:52 +04:00
. hw_params = smdk_hw_params ,
} ;
2019-06-06 07:09:19 +03:00
SND_SOC_DAILINK_DEFS ( spdif ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " samsung-spdif " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " spdif-dit " , " dit-hifi " ) ) ,
DAILINK_COMP_ARRAY ( COMP_PLATFORM ( " samsung-spdif " ) ) ) ;
2010-10-12 15:58:52 +04:00
static struct snd_soc_dai_link smdk_dai = {
. name = " S/PDIF " ,
. stream_name = " S/PDIF PCM Playback " ,
. ops = & smdk_spdif_ops ,
2019-06-06 07:09:19 +03:00
SND_SOC_DAILINK_REG ( spdif ) ,
2010-10-12 15:58:52 +04:00
} ;
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 " ) ;