2019-04-19 13:21:53 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// Modifications by Christian Pellegrin <chripell@evolware.org>
//
// s3c24xx_uda134x.c - S3C24XX_UDA134X ALSA SoC Audio board driver
//
// Copyright 2007 Dension Audio Systems Ltd.
// Author: Zoltan Devai
2008-11-15 10:58:32 +03:00
# include <linux/clk.h>
# include <linux/gpio.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2011-01-11 01:26:06 +03:00
2008-11-15 10:58:32 +03:00
# include <sound/soc.h>
# include <sound/s3c24xx_uda134x.h>
2013-04-11 21:08:42 +04:00
# include "regs-iis.h"
2008-11-15 10:58:32 +03:00
# include "s3c24xx-i2s.h"
2016-10-25 13:57:57 +03:00
struct s3c24xx_uda134x {
struct clk * xtal ;
struct clk * pclk ;
struct mutex clk_lock ;
int clk_users ;
} ;
2008-11-15 10:58:32 +03:00
/* #define ENFORCE_RATES 1 */
/*
Unfortunately the S3C24XX in master mode has a limited capacity of
generating the clock for the codec . If you define this only rates
that are really available will be enforced . But be careful , most
user level application just want the usual sampling frequencies ( 8 ,
11.025 , 22.050 , 44.1 kHz ) and anyway resampling is a costly
operation for embedded systems . So if you aren ' t very lucky or your
hardware engineer wasn ' t very forward - looking it ' s better to leave
this undefined . If you do so an approximate value for the requested
sampling rate in the range - / + 5 % will be chosen . If this in not
possible an error will be returned .
*/
static unsigned int rates [ 33 * 2 ] ;
# ifdef ENFORCE_RATES
2017-06-09 00:37:25 +03:00
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
2008-11-15 10:58:32 +03:00
. count = ARRAY_SIZE ( rates ) ,
. list = rates ,
. mask = 0 ,
} ;
# endif
2008-11-19 00:57:17 +03:00
static int s3c24xx_uda134x_startup ( struct snd_pcm_substream * substream )
2008-11-15 10:58:32 +03:00
{
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2016-10-25 13:57:57 +03:00
struct s3c24xx_uda134x * priv = snd_soc_card_get_drvdata ( rtd - > card ) ;
2020-03-23 08:20:20 +03:00
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
2016-08-04 12:51:25 +03:00
int ret = 0 ;
2008-11-15 10:58:32 +03:00
2016-10-25 13:57:57 +03:00
mutex_lock ( & priv - > clk_lock ) ;
2016-08-05 12:47:09 +03:00
2016-10-25 13:57:57 +03:00
if ( priv - > clk_users = = 0 ) {
priv - > xtal = clk_get ( rtd - > dev , " xtal " ) ;
if ( IS_ERR ( priv - > xtal ) ) {
2016-08-05 12:47:09 +03:00
dev_err ( rtd - > dev , " %s cannot get xtal \n " , __func__ ) ;
2016-10-25 13:57:57 +03:00
ret = PTR_ERR ( priv - > xtal ) ;
2008-11-15 10:58:32 +03:00
} else {
2016-10-25 13:57:57 +03:00
priv - > pclk = clk_get ( cpu_dai - > dev , " iis " ) ;
if ( IS_ERR ( priv - > pclk ) ) {
2016-08-05 12:47:09 +03:00
dev_err ( rtd - > dev , " %s cannot get pclk \n " ,
__func__ ) ;
2016-10-25 13:57:57 +03:00
clk_put ( priv - > xtal ) ;
ret = PTR_ERR ( priv - > pclk ) ;
2008-11-15 10:58:32 +03:00
}
}
if ( ! ret ) {
int i , j ;
for ( i = 0 ; i < 2 ; i + + ) {
int fs = i ? 256 : 384 ;
2016-10-25 13:57:57 +03:00
rates [ i * 33 ] = clk_get_rate ( priv - > xtal ) / fs ;
2008-11-15 10:58:32 +03:00
for ( j = 1 ; j < 33 ; j + + )
2016-10-25 13:57:57 +03:00
rates [ i * 33 + j ] = clk_get_rate ( priv - > pclk ) /
2008-11-15 10:58:32 +03:00
( j * fs ) ;
}
}
}
2016-10-25 13:57:57 +03:00
priv - > clk_users + = 1 ;
mutex_unlock ( & priv - > clk_lock ) ;
2008-11-15 10:58:32 +03:00
if ( ! ret ) {
# ifdef ENFORCE_RATES
2016-10-25 13:57:57 +03:00
ret = snd_pcm_hw_constraint_list ( substream - > runtime , 0 ,
2008-11-15 10:58:32 +03:00
SNDRV_PCM_HW_PARAM_RATE ,
& hw_constraints_rates ) ;
if ( ret < 0 )
2016-08-05 12:47:09 +03:00
dev_err ( rtd - > dev , " %s cannot set constraints \n " ,
__func__ ) ;
2008-11-15 10:58:32 +03:00
# endif
}
return ret ;
}
2008-11-19 00:57:17 +03:00
static void s3c24xx_uda134x_shutdown ( struct snd_pcm_substream * substream )
2008-11-15 10:58:32 +03:00
{
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2016-10-25 13:57:57 +03:00
struct s3c24xx_uda134x * priv = snd_soc_card_get_drvdata ( rtd - > card ) ;
mutex_lock ( & priv - > clk_lock ) ;
priv - > clk_users - = 1 ;
if ( priv - > clk_users = = 0 ) {
clk_put ( priv - > xtal ) ;
priv - > xtal = NULL ;
clk_put ( priv - > pclk ) ;
priv - > pclk = NULL ;
2008-11-15 10:58:32 +03:00
}
2016-10-25 13:57:57 +03:00
mutex_unlock ( & priv - > clk_lock ) ;
2008-11-15 10:58:32 +03:00
}
static int s3c24xx_uda134x_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2020-03-23 08:20:20 +03:00
struct snd_soc_dai * codec_dai = asoc_rtd_to_codec ( rtd , 0 ) ;
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
2008-11-15 10:58:32 +03:00
unsigned int clk = 0 ;
int ret = 0 ;
int clk_source , fs_mode ;
unsigned long rate = params_rate ( params ) ;
long err , cerr ;
unsigned int div ;
int i , bi ;
err = 999999 ;
bi = 0 ;
for ( i = 0 ; i < 2 * 33 ; i + + ) {
cerr = rates [ i ] - rate ;
if ( cerr < 0 )
cerr = - cerr ;
if ( cerr < err ) {
err = cerr ;
bi = i ;
}
}
if ( bi / 33 = = 1 )
fs_mode = S3C2410_IISMOD_256FS ;
else
fs_mode = S3C2410_IISMOD_384FS ;
if ( bi % 33 = = 0 ) {
clk_source = S3C24XX_CLKSRC_MPLL ;
div = 1 ;
} else {
clk_source = S3C24XX_CLKSRC_PCLK ;
div = bi % 33 ;
}
2016-08-05 12:47:09 +03:00
dev_dbg ( rtd - > dev , " %s desired rate %lu, %d \n " , __func__ , rate , bi ) ;
2008-11-15 10:58:32 +03:00
clk = ( fs_mode = = S3C2410_IISMOD_384FS ? 384 : 256 ) * rate ;
2016-08-05 12:47:09 +03:00
dev_dbg ( rtd - > dev , " %s will use: %s %s %d sysclk %d err %ld \n " , __func__ ,
fs_mode = = S3C2410_IISMOD_384FS ? " 384FS " : " 256FS " ,
clk_source = = S3C24XX_CLKSRC_MPLL ? " MPLLin " : " PCLK " ,
div , clk , err ) ;
2008-11-15 10:58:32 +03:00
if ( ( err * 100 / rate ) > 5 ) {
2016-08-05 12:47:09 +03:00
dev_err ( rtd - > dev , " effective frequency too different "
" from desired (%ld%%) \n " , err * 100 / rate ) ;
2008-11-15 10:58:32 +03:00
return - EINVAL ;
}
2008-11-19 00:57:17 +03:00
ret = snd_soc_dai_set_sysclk ( cpu_dai , clk_source , clk ,
SND_SOC_CLOCK_IN ) ;
2008-11-15 10:58:32 +03:00
if ( ret < 0 )
return ret ;
2008-11-19 00:57:17 +03:00
ret = snd_soc_dai_set_clkdiv ( cpu_dai , S3C24XX_DIV_MCLK , fs_mode ) ;
2008-11-15 10:58:32 +03:00
if ( ret < 0 )
return ret ;
2008-11-19 00:57:17 +03:00
ret = snd_soc_dai_set_clkdiv ( cpu_dai , S3C24XX_DIV_BCLK ,
S3C2410_IISMOD_32FS ) ;
2008-11-15 10:58:32 +03:00
if ( ret < 0 )
return ret ;
2008-11-19 00:57:17 +03:00
ret = snd_soc_dai_set_clkdiv ( cpu_dai , S3C24XX_DIV_PRESCALER ,
S3C24XX_PRESCALE ( div , div ) ) ;
2008-11-15 10:58:32 +03:00
if ( ret < 0 )
return ret ;
/* set the codec system clock for DAC and ADC */
2008-11-19 00:57:17 +03:00
ret = snd_soc_dai_set_sysclk ( codec_dai , 0 , clk ,
SND_SOC_CLOCK_OUT ) ;
2008-11-15 10:58:32 +03:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2017-08-16 19:59:29 +03:00
static const struct snd_soc_ops s3c24xx_uda134x_ops = {
2008-11-15 10:58:32 +03:00
. startup = s3c24xx_uda134x_startup ,
. shutdown = s3c24xx_uda134x_shutdown ,
. hw_params = s3c24xx_uda134x_hw_params ,
} ;
2019-06-06 07:10:06 +03:00
SND_SOC_DAILINK_DEFS ( uda134x ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " s3c24xx-iis " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " uda134x-codec " , " uda134x-hifi " ) ) ,
DAILINK_COMP_ARRAY ( COMP_PLATFORM ( " s3c24xx-iis " ) ) ) ;
2008-11-15 10:58:32 +03:00
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
. name = " UDA134X " ,
. stream_name = " UDA134X " ,
2015-01-01 19:16:25 +03:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS ,
2008-11-15 10:58:32 +03:00
. ops = & s3c24xx_uda134x_ops ,
2019-06-06 07:10:06 +03:00
SND_SOC_DAILINK_REG ( uda134x ) ,
2008-11-15 10:58:32 +03:00
} ;
2008-11-18 23:50:34 +03:00
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
2008-11-15 10:58:32 +03:00
. name = " S3C24XX_UDA134X " ,
2011-12-22 06:53:15 +04:00
. owner = THIS_MODULE ,
2008-11-15 10:58:32 +03:00
. dai_link = & s3c24xx_uda134x_dai_link ,
. num_links = 1 ,
} ;
static int s3c24xx_uda134x_probe ( struct platform_device * pdev )
{
2016-08-04 16:38:46 +03:00
struct snd_soc_card * card = & snd_soc_s3c24xx_uda134x ;
2016-10-25 13:57:57 +03:00
struct s3c24xx_uda134x * priv ;
2008-11-15 10:58:32 +03:00
int ret ;
2016-10-25 13:57:57 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
mutex_init ( & priv - > clk_lock ) ;
2016-08-04 16:38:46 +03:00
card - > dev = & pdev - > dev ;
2016-10-25 13:57:57 +03:00
snd_soc_card_set_drvdata ( card , priv ) ;
2008-11-15 10:58:32 +03:00
2016-08-04 16:38:46 +03:00
ret = devm_snd_soc_register_card ( & pdev - > dev , card ) ;
if ( ret )
dev_err ( & pdev - > dev , " failed to register card: %d \n " , ret ) ;
2008-11-15 10:58:32 +03:00
return ret ;
}
static struct platform_driver s3c24xx_uda134x_driver = {
. probe = s3c24xx_uda134x_probe ,
. driver = {
. name = " s3c24xx_uda134x " ,
} ,
} ;
2011-11-23 19:20:13 +04:00
module_platform_driver ( s3c24xx_uda134x_driver ) ;
2008-11-15 10:58:32 +03:00
MODULE_AUTHOR ( " Zoltan Devai, Christian Pellegrin <chripell@evolware.org> " ) ;
MODULE_DESCRIPTION ( " S3C24XX_UDA134X ALSA SoC audio driver " ) ;
MODULE_LICENSE ( " GPL " ) ;