2008-04-25 15:55:19 +04:00
/*
* n810 . c - - SoC audio for Nokia N810
*
* Copyright ( C ) 2008 Nokia Corporation
*
2011-08-11 16:44:57 +04:00
* Contact : Jarkko Nikula < jarkko . nikula @ bitmer . com >
2008-04-25 15:55:19 +04: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>
2009-08-21 01:50:42 +04:00
# include <linux/i2c.h>
2008-04-25 15:55:19 +04:00
# include <linux/platform_device.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <asm/mach-types.h>
2008-05-15 13:01:36 +04:00
# include <linux/gpio.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2012-08-24 17:21:06 +04:00
# include <linux/platform_data/asoc-ti-mcbsp.h>
2008-04-25 15:55:19 +04:00
# include "omap-mcbsp.h"
2008-05-15 13:01:36 +04:00
# define N810_HEADSET_AMP_GPIO 10
# define N810_SPEAKER_AMP_GPIO 101
2008-04-25 15:55:19 +04:00
2009-03-18 17:46:54 +03:00
enum {
N810_JACK_DISABLED ,
N810_JACK_HP ,
N810_JACK_HS ,
N810_JACK_MIC ,
} ;
2008-04-25 15:55:19 +04:00
static struct clk * sys_clkout2 ;
static struct clk * sys_clkout2_src ;
static struct clk * func96m_clk ;
static int n810_spk_func ;
static int n810_jack_func ;
2008-06-25 15:58:47 +04:00
static int n810_dmic_func ;
2008-04-25 15:55:19 +04:00
2012-02-03 21:43:09 +04:00
static void n810_ext_control ( struct snd_soc_dapm_context * dapm )
2008-04-25 15:55:19 +04:00
{
2009-03-18 17:46:54 +03:00
int hp = 0 , line1l = 0 ;
switch ( n810_jack_func ) {
case N810_JACK_HS :
line1l = 1 ;
case N810_JACK_HP :
hp = 1 ;
break ;
case N810_JACK_MIC :
line1l = 1 ;
break ;
}
2014-02-18 19:22:23 +04:00
snd_soc_dapm_mutex_lock ( dapm ) ;
2008-07-07 16:35:17 +04:00
if ( n810_spk_func )
2014-02-18 19:22:23 +04:00
snd_soc_dapm_enable_pin_unlocked ( dapm , " Ext Spk " ) ;
2008-07-07 16:35:17 +04:00
else
2014-02-18 19:22:23 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " Ext Spk " ) ;
2008-07-07 16:35:17 +04:00
2009-03-18 17:46:54 +03:00
if ( hp )
2014-02-18 19:22:23 +04:00
snd_soc_dapm_enable_pin_unlocked ( dapm , " Headphone Jack " ) ;
2008-07-07 16:35:17 +04:00
else
2014-02-18 19:22:23 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " Headphone Jack " ) ;
2009-03-18 17:46:54 +03:00
if ( line1l )
2014-02-18 19:22:23 +04:00
snd_soc_dapm_enable_pin_unlocked ( dapm , " LINE1L " ) ;
2009-03-18 17:46:54 +03:00
else
2014-02-18 19:22:23 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " LINE1L " ) ;
2008-07-07 16:35:17 +04:00
if ( n810_dmic_func )
2014-02-18 19:22:23 +04:00
snd_soc_dapm_enable_pin_unlocked ( dapm , " DMic " ) ;
2008-07-07 16:35:17 +04:00
else
2014-02-18 19:22:23 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " DMic " ) ;
snd_soc_dapm_sync_unlocked ( dapm ) ;
2008-04-25 15:55:19 +04:00
2014-02-18 19:22:23 +04:00
snd_soc_dapm_mutex_unlock ( dapm ) ;
2008-04-25 15:55:19 +04:00
}
static int n810_startup ( struct snd_pcm_substream * substream )
{
2008-11-25 13:45:08 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2008-04-25 15:55:19 +04:00
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2015-10-18 16:39:26 +03:00
snd_pcm_hw_constraint_single ( runtime , SNDRV_PCM_HW_PARAM_CHANNELS , 2 ) ;
2008-11-25 13:45:08 +03:00
2015-04-11 11:47:57 +03:00
n810_ext_control ( & rtd - > card - > dapm ) ;
2013-11-30 20:05:28 +04:00
return clk_prepare_enable ( sys_clkout2 ) ;
2008-04-25 15:55:19 +04:00
}
static void n810_shutdown ( struct snd_pcm_substream * substream )
{
2013-11-30 20:05:28 +04:00
clk_disable_unprepare ( sys_clkout2 ) ;
2008-04-25 15:55:19 +04:00
}
static int n810_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
2008-04-25 15:55:19 +04:00
int err ;
/* Set the codec system clock for DAC and ADC */
2008-07-08 16:19:18 +04:00
err = snd_soc_dai_set_sysclk ( codec_dai , 0 , 12000000 ,
2008-04-25 15:55:19 +04:00
SND_SOC_CLOCK_IN ) ;
return err ;
}
static struct snd_soc_ops n810_ops = {
. startup = n810_startup ,
. hw_params = n810_hw_params ,
. shutdown = n810_shutdown ,
} ;
static int n810_get_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = n810_spk_func ;
return 0 ;
}
static int n810_set_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2012-02-03 21:43:09 +04:00
struct snd_soc_card * card = snd_kcontrol_chip ( kcontrol ) ;
2008-04-25 15:55:19 +04:00
if ( n810_spk_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
n810_spk_func = ucontrol - > value . integer . value [ 0 ] ;
2012-02-03 21:43:09 +04:00
n810_ext_control ( & card - > dapm ) ;
2008-04-25 15:55:19 +04:00
return 1 ;
}
static int n810_get_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = n810_jack_func ;
return 0 ;
}
static int n810_set_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2012-02-03 21:43:09 +04:00
struct snd_soc_card * card = snd_kcontrol_chip ( kcontrol ) ;
2008-04-25 15:55:19 +04:00
if ( n810_jack_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
n810_jack_func = ucontrol - > value . integer . value [ 0 ] ;
2012-02-03 21:43:09 +04:00
n810_ext_control ( & card - > dapm ) ;
2008-04-25 15:55:19 +04:00
return 1 ;
}
2008-06-25 15:58:47 +04:00
static int n810_get_input ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = n810_dmic_func ;
return 0 ;
}
static int n810_set_input ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2012-02-03 21:43:09 +04:00
struct snd_soc_card * card = snd_kcontrol_chip ( kcontrol ) ;
2008-06-25 15:58:47 +04:00
if ( n810_dmic_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
n810_dmic_func = ucontrol - > value . integer . value [ 0 ] ;
2012-02-10 10:54:37 +04:00
n810_ext_control ( & card - > dapm ) ;
2008-06-25 15:58:47 +04:00
return 1 ;
}
2008-04-25 15:55:19 +04:00
static int n810_spk_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
2008-05-15 13:01:36 +04:00
gpio_set_value ( N810_SPEAKER_AMP_GPIO , 1 ) ;
2008-04-25 15:55:19 +04:00
else
2008-05-15 13:01:36 +04:00
gpio_set_value ( N810_SPEAKER_AMP_GPIO , 0 ) ;
2008-04-25 15:55:19 +04:00
return 0 ;
}
static int n810_jack_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
2008-05-15 13:01:36 +04:00
gpio_set_value ( N810_HEADSET_AMP_GPIO , 1 ) ;
2008-04-25 15:55:19 +04:00
else
2008-05-15 13:01:36 +04:00
gpio_set_value ( N810_HEADSET_AMP_GPIO , 0 ) ;
2008-04-25 15:55:19 +04:00
return 0 ;
}
static const struct snd_soc_dapm_widget aic33_dapm_widgets [ ] = {
SND_SOC_DAPM_SPK ( " Ext Spk " , n810_spk_event ) ,
SND_SOC_DAPM_HP ( " Headphone Jack " , n810_jack_event ) ,
2008-06-25 15:58:47 +04:00
SND_SOC_DAPM_MIC ( " DMic " , NULL ) ,
2008-04-25 15:55:19 +04:00
} ;
2008-05-13 16:58:57 +04:00
static const struct snd_soc_dapm_route audio_map [ ] = {
2008-04-25 15:55:19 +04:00
{ " Headphone Jack " , NULL , " HPLOUT " } ,
{ " Headphone Jack " , NULL , " HPROUT " } ,
{ " Ext Spk " , NULL , " LLOUT " } ,
{ " Ext Spk " , NULL , " RLOUT " } ,
2008-06-25 15:58:47 +04:00
2013-01-31 16:53:04 +04:00
{ " DMic Rate 64 " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " DMic " } ,
2008-04-25 15:55:19 +04:00
} ;
static const char * spk_function [ ] = { " Off " , " On " } ;
2009-03-18 17:46:54 +03:00
static const char * jack_function [ ] = { " Off " , " Headphone " , " Headset " , " Mic " } ;
2008-06-25 15:58:47 +04:00
static const char * input_function [ ] = { " ADC " , " Digital Mic " } ;
2008-04-25 15:55:19 +04:00
static const struct soc_enum n810_enum [ ] = {
2008-05-13 18:02:04 +04:00
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( spk_function ) , spk_function ) ,
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( jack_function ) , jack_function ) ,
2008-06-25 15:58:47 +04:00
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( input_function ) , input_function ) ,
2008-04-25 15:55:19 +04:00
} ;
static const struct snd_kcontrol_new aic33_n810_controls [ ] = {
SOC_ENUM_EXT ( " Speaker Function " , n810_enum [ 0 ] ,
n810_get_spk , n810_set_spk ) ,
SOC_ENUM_EXT ( " Jack Function " , n810_enum [ 1 ] ,
n810_get_jack , n810_set_jack ) ,
2008-06-25 15:58:47 +04:00
SOC_ENUM_EXT ( " Input Select " , n810_enum [ 2 ] ,
n810_get_input , n810_set_input ) ,
2008-04-25 15:55:19 +04:00
} ;
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link n810_dai = {
. name = " TLV320AIC33 " ,
. stream_name = " AIC33 " ,
2012-02-14 20:20:58 +04:00
. cpu_dai_name = " omap-mcbsp.2 " ,
2014-04-16 16:46:25 +04:00
. platform_name = " omap-mcbsp.2 " ,
2010-03-17 23:15:21 +03:00
. codec_name = " tlv320aic3x-codec.2-0018 " ,
. codec_dai_name = " tlv320aic3x-hifi " ,
2011-09-30 17:07:45 +04:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
2008-04-25 15:55:19 +04:00
. ops = & n810_ops ,
} ;
/* Audio machine driver */
2008-11-18 23:50:34 +03:00
static struct snd_soc_card snd_soc_n810 = {
2008-04-25 15:55:19 +04:00
. name = " N810 " ,
2011-12-22 07:08:59 +04:00
. owner = THIS_MODULE ,
2008-04-25 15:55:19 +04:00
. dai_link = & n810_dai ,
. num_links = 1 ,
2011-10-10 16:34:10 +04:00
. controls = aic33_n810_controls ,
. num_controls = ARRAY_SIZE ( aic33_n810_controls ) ,
. dapm_widgets = aic33_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( aic33_dapm_widgets ) ,
. dapm_routes = audio_map ,
. num_dapm_routes = ARRAY_SIZE ( audio_map ) ,
2015-04-11 11:47:58 +03:00
. fully_routed = true ,
2008-04-25 15:55:19 +04:00
} ;
static struct platform_device * n810_snd_device ;
static int __init n810_soc_init ( void )
{
int err ;
struct device * dev ;
2014-03-04 02:45:18 +04:00
if ( ! of_have_populated_dt ( ) | |
( ! of_machine_is_compatible ( " nokia,n810 " ) & &
! of_machine_is_compatible ( " nokia,n810-wimax " ) ) )
2008-04-25 15:55:19 +04:00
return - ENODEV ;
n810_snd_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! n810_snd_device )
return - ENOMEM ;
2010-03-17 23:15:21 +03:00
platform_set_drvdata ( n810_snd_device , & snd_soc_n810 ) ;
2008-04-25 15:55:19 +04:00
err = platform_device_add ( n810_snd_device ) ;
if ( err )
goto err1 ;
dev = & n810_snd_device - > dev ;
sys_clkout2_src = clk_get ( dev , " sys_clkout2_src " ) ;
if ( IS_ERR ( sys_clkout2_src ) ) {
dev_err ( dev , " Could not get sys_clkout2_src clock \n " ) ;
2008-08-26 14:32:57 +04:00
err = PTR_ERR ( sys_clkout2_src ) ;
goto err2 ;
2008-04-25 15:55:19 +04:00
}
sys_clkout2 = clk_get ( dev , " sys_clkout2 " ) ;
if ( IS_ERR ( sys_clkout2 ) ) {
dev_err ( dev , " Could not get sys_clkout2 \n " ) ;
2008-08-26 14:32:57 +04:00
err = PTR_ERR ( sys_clkout2 ) ;
goto err3 ;
2008-04-25 15:55:19 +04:00
}
/*
* Configure 12 MHz output on SYS_CLKOUT2 . Therefore we must use
* 96 MHz as its parent in order to get 12 MHz
*/
func96m_clk = clk_get ( dev , " func_96m_ck " ) ;
if ( IS_ERR ( func96m_clk ) ) {
dev_err ( dev , " Could not get func 96M clock \n " ) ;
2008-08-26 14:32:57 +04:00
err = PTR_ERR ( func96m_clk ) ;
goto err4 ;
2008-04-25 15:55:19 +04:00
}
clk_set_parent ( sys_clkout2_src , func96m_clk ) ;
clk_set_rate ( sys_clkout2 , 12000000 ) ;
2013-11-05 21:40:02 +04:00
if ( WARN_ON ( ( gpio_request ( N810_HEADSET_AMP_GPIO , " hs_amp " ) < 0 ) | |
( gpio_request ( N810_SPEAKER_AMP_GPIO , " spk_amp " ) < 0 ) ) ) {
err = - EINVAL ;
goto err4 ;
}
2009-04-12 05:04:43 +04:00
2008-05-15 13:01:36 +04:00
gpio_direction_output ( N810_HEADSET_AMP_GPIO , 0 ) ;
gpio_direction_output ( N810_SPEAKER_AMP_GPIO , 0 ) ;
2008-04-25 15:55:19 +04:00
return 0 ;
2008-08-26 14:32:57 +04:00
err4 :
2008-04-25 15:55:19 +04:00
clk_put ( sys_clkout2 ) ;
2008-08-26 14:32:57 +04:00
err3 :
clk_put ( sys_clkout2_src ) ;
err2 :
2008-04-25 15:55:19 +04:00
platform_device_del ( n810_snd_device ) ;
err1 :
platform_device_put ( n810_snd_device ) ;
return err ;
}
static void __exit n810_soc_exit ( void )
{
2008-05-15 13:01:36 +04:00
gpio_free ( N810_SPEAKER_AMP_GPIO ) ;
gpio_free ( N810_HEADSET_AMP_GPIO ) ;
2008-08-26 14:32:57 +04:00
clk_put ( sys_clkout2_src ) ;
clk_put ( sys_clkout2 ) ;
clk_put ( func96m_clk ) ;
2008-05-15 13:01:36 +04:00
2008-04-25 15:55:19 +04:00
platform_device_unregister ( n810_snd_device ) ;
}
module_init ( n810_soc_init ) ;
module_exit ( n810_soc_exit ) ;
2011-08-11 16:44:57 +04:00
MODULE_AUTHOR ( " Jarkko Nikula <jarkko.nikula@bitmer.com> " ) ;
2008-04-25 15:55:19 +04:00
MODULE_DESCRIPTION ( " ALSA SoC Nokia N810 " ) ;
MODULE_LICENSE ( " GPL " ) ;