2011-01-08 08:36:15 +03:00
/*
* tegra_asoc_utils . c - Harmony machine ASoC driver
*
* Author : Stephen Warren < swarren @ nvidia . com >
2012-04-06 21:15:55 +04:00
* Copyright ( C ) 2010 , 2012 - NVIDIA , Inc .
2011-01-08 08:36:15 +03:00
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/clk.h>
2011-01-29 00:26:40 +03:00
# include <linux/device.h>
2011-01-08 08:36:15 +03:00
# include <linux/err.h>
# include <linux/kernel.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2012-04-06 21:15:55 +04:00
# include <linux/of.h>
2011-01-08 08:36:15 +03:00
# include "tegra_asoc_utils.h"
2011-01-29 00:26:40 +03:00
int tegra_asoc_utils_set_rate ( struct tegra_asoc_utils_data * data , int srate ,
2011-04-20 01:25:09 +04:00
int mclk )
2011-01-08 08:36:15 +03:00
{
int new_baseclock ;
2011-04-20 01:25:09 +04:00
bool clk_change ;
2011-01-08 08:36:15 +03:00
int err ;
switch ( srate ) {
case 11025 :
case 22050 :
case 44100 :
case 88200 :
2012-04-06 21:15:55 +04:00
if ( data - > soc = = TEGRA_ASOC_UTILS_SOC_TEGRA20 )
new_baseclock = 56448000 ;
else
new_baseclock = 564480000 ;
2011-01-08 08:36:15 +03:00
break ;
case 8000 :
case 16000 :
case 32000 :
case 48000 :
case 64000 :
case 96000 :
2012-04-06 21:15:55 +04:00
if ( data - > soc = = TEGRA_ASOC_UTILS_SOC_TEGRA20 )
new_baseclock = 73728000 ;
else
new_baseclock = 552960000 ;
2011-01-08 08:36:15 +03:00
break ;
default :
return - EINVAL ;
}
2011-04-20 01:25:09 +04:00
clk_change = ( ( new_baseclock ! = data - > set_baseclock ) | |
2011-01-29 00:26:40 +03:00
( mclk ! = data - > set_mclk ) ) ;
2011-04-20 01:25:09 +04:00
if ( ! clk_change )
return 0 ;
2011-01-08 08:36:15 +03:00
2011-01-29 00:26:40 +03:00
data - > set_baseclock = 0 ;
data - > set_mclk = 0 ;
2011-01-08 08:36:15 +03:00
2012-06-05 08:29:42 +04:00
clk_disable_unprepare ( data - > clk_cdev1 ) ;
clk_disable_unprepare ( data - > clk_pll_a_out0 ) ;
clk_disable_unprepare ( data - > clk_pll_a ) ;
2011-01-08 08:36:15 +03:00
2011-01-29 00:26:40 +03:00
err = clk_set_rate ( data - > clk_pll_a , new_baseclock ) ;
2011-01-08 08:36:15 +03:00
if ( err ) {
2011-01-29 00:26:40 +03:00
dev_err ( data - > dev , " Can't set pll_a rate: %d \n " , err ) ;
2011-01-08 08:36:15 +03:00
return err ;
}
2011-01-29 00:26:40 +03:00
err = clk_set_rate ( data - > clk_pll_a_out0 , mclk ) ;
2011-01-08 08:36:15 +03:00
if ( err ) {
2011-01-29 00:26:40 +03:00
dev_err ( data - > dev , " Can't set pll_a_out0 rate: %d \n " , err ) ;
2011-01-08 08:36:15 +03:00
return err ;
}
2012-04-06 21:15:55 +04:00
/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
2011-01-08 08:36:15 +03:00
2012-06-05 08:29:42 +04:00
err = clk_prepare_enable ( data - > clk_pll_a ) ;
2011-01-08 08:36:15 +03:00
if ( err ) {
2011-01-29 00:26:40 +03:00
dev_err ( data - > dev , " Can't enable pll_a: %d \n " , err ) ;
2011-01-08 08:36:15 +03:00
return err ;
}
2012-06-05 08:29:42 +04:00
err = clk_prepare_enable ( data - > clk_pll_a_out0 ) ;
2011-01-08 08:36:15 +03:00
if ( err ) {
2011-01-29 00:26:40 +03:00
dev_err ( data - > dev , " Can't enable pll_a_out0: %d \n " , err ) ;
2011-01-08 08:36:15 +03:00
return err ;
}
2012-06-05 08:29:42 +04:00
err = clk_prepare_enable ( data - > clk_cdev1 ) ;
2011-01-08 08:36:15 +03:00
if ( err ) {
2011-01-29 00:26:40 +03:00
dev_err ( data - > dev , " Can't enable cdev1: %d \n " , err ) ;
2011-01-08 08:36:15 +03:00
return err ;
}
2011-01-29 00:26:40 +03:00
data - > set_baseclock = new_baseclock ;
data - > set_mclk = mclk ;
2011-01-08 08:36:15 +03:00
return 0 ;
}
2011-02-23 03:23:56 +03:00
EXPORT_SYMBOL_GPL ( tegra_asoc_utils_set_rate ) ;
2011-01-08 08:36:15 +03:00
2011-01-29 00:26:40 +03:00
int tegra_asoc_utils_init ( struct tegra_asoc_utils_data * data ,
struct device * dev )
2011-01-08 08:36:15 +03:00
{
int ret ;
2011-01-29 00:26:40 +03:00
data - > dev = dev ;
2012-04-10 23:11:17 +04:00
if ( of_machine_is_compatible ( " nvidia,tegra20 " ) )
2012-04-06 21:15:55 +04:00
data - > soc = TEGRA_ASOC_UTILS_SOC_TEGRA20 ;
else if ( of_machine_is_compatible ( " nvidia,tegra30 " ) )
data - > soc = TEGRA_ASOC_UTILS_SOC_TEGRA30 ;
2012-04-10 23:11:17 +04:00
else if ( ! dev - > of_node )
/* non-DT is always Tegra20 */
data - > soc = TEGRA_ASOC_UTILS_SOC_TEGRA20 ;
2012-04-06 21:15:55 +04:00
else
2012-04-10 23:11:17 +04:00
/* DT boot, but unknown SoC */
2012-04-06 21:15:55 +04:00
return - EINVAL ;
2011-01-29 00:26:40 +03:00
data - > clk_pll_a = clk_get_sys ( NULL , " pll_a " ) ;
if ( IS_ERR ( data - > clk_pll_a ) ) {
dev_err ( data - > dev , " Can't retrieve clk pll_a \n " ) ;
ret = PTR_ERR ( data - > clk_pll_a ) ;
2011-01-08 08:36:15 +03:00
goto err ;
}
2011-01-29 00:26:40 +03:00
data - > clk_pll_a_out0 = clk_get_sys ( NULL , " pll_a_out0 " ) ;
if ( IS_ERR ( data - > clk_pll_a_out0 ) ) {
dev_err ( data - > dev , " Can't retrieve clk pll_a_out0 \n " ) ;
ret = PTR_ERR ( data - > clk_pll_a_out0 ) ;
2011-01-11 22:48:53 +03:00
goto err_put_pll_a ;
2011-01-08 08:36:15 +03:00
}
2012-04-06 21:15:55 +04:00
if ( data - > soc = = TEGRA_ASOC_UTILS_SOC_TEGRA20 )
data - > clk_cdev1 = clk_get_sys ( NULL , " cdev1 " ) ;
else
data - > clk_cdev1 = clk_get_sys ( " extern1 " , NULL ) ;
2011-01-29 00:26:40 +03:00
if ( IS_ERR ( data - > clk_cdev1 ) ) {
dev_err ( data - > dev , " Can't retrieve clk cdev1 \n " ) ;
ret = PTR_ERR ( data - > clk_cdev1 ) ;
2011-01-11 22:48:53 +03:00
goto err_put_pll_a_out0 ;
2011-01-08 08:36:15 +03:00
}
2012-04-06 21:18:16 +04:00
ret = tegra_asoc_utils_set_rate ( data , 44100 , 256 * 44100 ) ;
if ( ret )
goto err_put_cdev1 ;
2011-01-08 08:36:15 +03:00
return 0 ;
2012-04-06 21:18:16 +04:00
err_put_cdev1 :
clk_put ( data - > clk_cdev1 ) ;
2011-01-11 22:48:53 +03:00
err_put_pll_a_out0 :
2011-01-29 00:26:40 +03:00
clk_put ( data - > clk_pll_a_out0 ) ;
2011-01-11 22:48:53 +03:00
err_put_pll_a :
2011-01-29 00:26:40 +03:00
clk_put ( data - > clk_pll_a ) ;
2011-01-08 08:36:15 +03:00
err :
return ret ;
}
2011-02-23 03:23:56 +03:00
EXPORT_SYMBOL_GPL ( tegra_asoc_utils_init ) ;
2011-01-08 08:36:15 +03:00
2011-01-29 00:26:40 +03:00
void tegra_asoc_utils_fini ( struct tegra_asoc_utils_data * data )
2011-01-08 08:36:15 +03:00
{
2011-01-29 00:26:40 +03:00
clk_put ( data - > clk_cdev1 ) ;
clk_put ( data - > clk_pll_a_out0 ) ;
clk_put ( data - > clk_pll_a ) ;
2011-01-08 08:36:15 +03:00
}
2011-02-23 03:23:56 +03:00
EXPORT_SYMBOL_GPL ( tegra_asoc_utils_fini ) ;
2011-01-08 08:36:15 +03:00
2011-02-23 03:23:56 +03:00
MODULE_AUTHOR ( " Stephen Warren <swarren@nvidia.com> " ) ;
MODULE_DESCRIPTION ( " Tegra ASoC utility code " ) ;
MODULE_LICENSE ( " GPL " ) ;