2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-10-12 16:33:45 +04:00
/*
* poodle . c - - SoC audio for Poodle
*
* Copyright 2005 Wolfson Microelectronics PLC .
* Copyright 2005 Openedhand Ltd .
*
2008-10-12 16:17:36 +04:00
* Authors : Liam Girdwood < lrg @ slimlogic . co . uk >
2006-10-12 16:33:45 +04:00
* Richard Purdie < richard @ openedhand . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/timer.h>
2009-02-18 15:34:53 +03:00
# include <linux/i2c.h>
2006-10-12 16:33:45 +04:00
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <asm/mach-types.h>
# include <asm/hardware/locomo.h>
2008-08-05 19:14:15 +04:00
# include <mach/poodle.h>
# include <mach/audio.h>
2006-10-12 16:33:45 +04:00
# include "../codecs/wm8731.h"
2007-02-02 19:23:42 +03:00
# include "pxa2xx-i2s.h"
2006-10-12 16:33:45 +04:00
# define POODLE_HP 1
# define POODLE_HP_OFF 0
# define POODLE_SPK_ON 1
# define POODLE_SPK_OFF 0
/* audio clock in Hz - rounded from 12.235MHz */
# define POODLE_AUDIO_CLOCK 12288000
static int poodle_jack_func ;
static int poodle_spk_func ;
2012-02-03 21:43:09 +04:00
static void poodle_ext_control ( struct snd_soc_dapm_context * dapm )
2006-10-12 16:33:45 +04:00
{
/* set up jack connection */
if ( poodle_jack_func = = POODLE_HP ) {
/* set = unmute headphone */
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_L , 1 ) ;
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_R , 1 ) ;
2010-11-05 16:53:46 +03:00
snd_soc_dapm_enable_pin ( dapm , " Headphone Jack " ) ;
2006-10-12 16:33:45 +04:00
} else {
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_L , 0 ) ;
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_R , 0 ) ;
2010-11-05 16:53:46 +03:00
snd_soc_dapm_disable_pin ( dapm , " Headphone Jack " ) ;
2006-10-12 16:33:45 +04:00
}
/* set the enpoints to their new connetion states */
2008-07-07 16:35:17 +04:00
if ( poodle_spk_func = = POODLE_SPK_ON )
2010-11-05 16:53:46 +03:00
snd_soc_dapm_enable_pin ( dapm , " Ext Spk " ) ;
2008-07-07 16:35:17 +04:00
else
2010-11-05 16:53:46 +03:00
snd_soc_dapm_disable_pin ( dapm , " Ext Spk " ) ;
2006-10-12 16:33:45 +04:00
/* signal a DAPM event */
2010-11-05 16:53:46 +03:00
snd_soc_dapm_sync ( dapm ) ;
2006-10-12 16:33:45 +04:00
}
static int poodle_startup ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
/* check the jack status at stream startup */
2014-03-01 18:48:13 +04:00
poodle_ext_control ( & rtd - > card - > dapm ) ;
2010-11-05 20:50:48 +03:00
2006-10-12 16:33:45 +04:00
return 0 ;
}
/* we need to unmute the HP at shutdown as the mute burns power on poodle */
2008-07-29 14:42:22 +04:00
static void poodle_shutdown ( struct snd_pcm_substream * substream )
2006-10-12 16:33:45 +04:00
{
/* set = unmute headphone */
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_L , 1 ) ;
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_R , 1 ) ;
}
2007-02-02 19:23:42 +03:00
static int poodle_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 ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2007-02-02 19:23:42 +03:00
unsigned int clk = 0 ;
int ret = 0 ;
switch ( params_rate ( params ) ) {
case 8000 :
case 16000 :
case 48000 :
case 96000 :
clk = 12288000 ;
break ;
case 11025 :
case 22050 :
case 44100 :
clk = 11289600 ;
break ;
}
/* set the codec system clock for DAC and ADC */
2010-08-18 02:40:24 +04:00
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8731_SYSCLK_XTAL , clk ,
2007-02-02 19:23:42 +03:00
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
/* set the I2S system clock as input (unused) */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_sysclk ( cpu_dai , PXA2XX_I2S_SYSCLK , 0 ,
2007-02-02 19:23:42 +03:00
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2017-03-13 22:46:40 +03:00
static const struct snd_soc_ops poodle_ops = {
2006-10-12 16:33:45 +04:00
. startup = poodle_startup ,
2007-02-02 19:23:42 +03:00
. hw_params = poodle_hw_params ,
2006-10-12 16:33:45 +04:00
. shutdown = poodle_shutdown ,
} ;
static int poodle_get_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2016-02-29 19:23:51 +03:00
ucontrol - > value . enumerated . item [ 0 ] = poodle_jack_func ;
2006-10-12 16:33:45 +04:00
return 0 ;
}
static int poodle_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 ) ;
2006-10-12 16:33:45 +04:00
2016-02-29 19:23:51 +03:00
if ( poodle_jack_func = = ucontrol - > value . enumerated . item [ 0 ] )
2006-10-12 16:33:45 +04:00
return 0 ;
2016-02-29 19:23:51 +03:00
poodle_jack_func = ucontrol - > value . enumerated . item [ 0 ] ;
2012-02-03 21:43:09 +04:00
poodle_ext_control ( & card - > dapm ) ;
2006-10-12 16:33:45 +04:00
return 1 ;
}
static int poodle_get_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2016-02-29 19:23:51 +03:00
ucontrol - > value . enumerated . item [ 0 ] = poodle_spk_func ;
2006-10-12 16:33:45 +04:00
return 0 ;
}
static int poodle_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 ) ;
2006-10-12 16:33:45 +04:00
2016-02-29 19:23:51 +03:00
if ( poodle_spk_func = = ucontrol - > value . enumerated . item [ 0 ] )
2006-10-12 16:33:45 +04:00
return 0 ;
2016-02-29 19:23:51 +03:00
poodle_spk_func = ucontrol - > value . enumerated . item [ 0 ] ;
2012-02-03 21:43:09 +04:00
poodle_ext_control ( & card - > dapm ) ;
2006-10-12 16:33:45 +04:00
return 1 ;
}
2008-02-28 14:34:48 +03:00
static int poodle_amp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
2006-10-12 16:33:45 +04:00
{
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_AMP_ON , 0 ) ;
else
locomo_gpio_write ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_AMP_ON , 1 ) ;
return 0 ;
}
/* poodle machine dapm widgets */
static const struct snd_soc_dapm_widget wm8731_dapm_widgets [ ] = {
SND_SOC_DAPM_HP ( " Headphone Jack " , NULL ) ,
SND_SOC_DAPM_SPK ( " Ext Spk " , poodle_amp_event ) ,
2015-05-07 22:27:47 +03:00
SND_SOC_DAPM_MIC ( " Microphone " , NULL ) ,
2006-10-12 16:33:45 +04:00
} ;
2008-05-13 16:55:48 +04:00
/* Corgi machine connections to the codec pins */
2011-12-30 05:19:32 +04:00
static const struct snd_soc_dapm_route poodle_audio_map [ ] = {
2006-10-12 16:33:45 +04:00
/* headphone connected to LHPOUT1, RHPOUT1 */
{ " Headphone Jack " , NULL , " LHPOUT " } ,
{ " Headphone Jack " , NULL , " RHPOUT " } ,
/* speaker connected to LOUT, ROUT */
{ " Ext Spk " , NULL , " ROUT " } ,
{ " Ext Spk " , NULL , " LOUT " } ,
2015-05-07 22:27:47 +03:00
{ " MICIN " , NULL , " Microphone " } ,
2006-10-12 16:33:45 +04:00
} ;
2016-11-12 16:23:13 +03:00
static const char * const jack_function [ ] = { " Off " , " Headphone " } ;
static const char * const spk_function [ ] = { " Off " , " On " } ;
2006-10-12 16:33:45 +04:00
static const struct soc_enum poodle_enum [ ] = {
SOC_ENUM_SINGLE_EXT ( 2 , jack_function ) ,
SOC_ENUM_SINGLE_EXT ( 2 , spk_function ) ,
} ;
2008-07-29 14:42:22 +04:00
static const struct snd_kcontrol_new wm8731_poodle_controls [ ] = {
2006-10-12 16:33:45 +04:00
SOC_ENUM_EXT ( " Jack Function " , poodle_enum [ 0 ] , poodle_get_jack ,
poodle_set_jack ) ,
SOC_ENUM_EXT ( " Speaker Function " , poodle_enum [ 1 ] , poodle_get_spk ,
poodle_set_spk ) ,
} ;
/* poodle digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link poodle_dai = {
. name = " WM8731 " ,
. stream_name = " WM8731 " ,
2010-08-20 20:18:43 +04:00
. cpu_dai_name = " pxa2xx-i2s " ,
2010-08-18 02:35:31 +04:00
. codec_dai_name = " wm8731-hifi " ,
2010-03-17 23:15:21 +03:00
. platform_name = " pxa-pcm-audio " ,
2011-03-27 17:35:15 +04:00
. codec_name = " wm8731.0-001b " ,
2011-12-30 05:20:40 +04:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS ,
2007-02-02 19:23:42 +03:00
. ops = & poodle_ops ,
2006-10-12 16:33:45 +04:00
} ;
/* poodle audio machine driver */
2011-12-30 05:16:11 +04:00
static struct snd_soc_card poodle = {
2006-10-12 16:33:45 +04:00
. name = " Poodle " ,
. dai_link = & poodle_dai ,
. num_links = 1 ,
2010-03-17 23:15:21 +03:00
. owner = THIS_MODULE ,
2011-12-30 05:19:32 +04:00
. controls = wm8731_poodle_controls ,
. num_controls = ARRAY_SIZE ( wm8731_poodle_controls ) ,
. dapm_widgets = wm8731_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( wm8731_dapm_widgets ) ,
. dapm_routes = poodle_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( poodle_audio_map ) ,
2015-05-07 22:27:47 +03:00
. fully_routed = true ,
2006-10-12 16:33:45 +04:00
} ;
2012-12-07 18:26:17 +04:00
static int poodle_probe ( struct platform_device * pdev )
2006-10-12 16:33:45 +04:00
{
2011-12-30 05:16:11 +04:00
struct snd_soc_card * card = & poodle ;
2006-10-12 16:33:45 +04:00
int ret ;
locomo_gpio_set_dir ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_AMP_ON , 0 ) ;
/* should we mute HP at startup - burning power ?*/
locomo_gpio_set_dir ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_L , 0 ) ;
locomo_gpio_set_dir ( & poodle_locomo_device . dev ,
POODLE_LOCOMO_GPIO_MUTE_R , 0 ) ;
2011-12-30 05:16:11 +04:00
card - > dev = & pdev - > dev ;
2006-10-12 16:33:45 +04:00
2015-09-01 05:32:59 +03:00
ret = devm_snd_soc_register_card ( & pdev - > dev , card ) ;
2006-10-12 16:33:45 +04:00
if ( ret )
2011-12-30 05:16:11 +04:00
dev_err ( & pdev - > dev , " snd_soc_register_card() failed: %d \n " ,
ret ) ;
2006-10-12 16:33:45 +04:00
return ret ;
}
2011-12-30 05:16:11 +04:00
static struct platform_driver poodle_driver = {
. driver = {
. name = " poodle-audio " ,
2013-10-17 14:01:37 +04:00
. pm = & snd_soc_pm_ops ,
2011-12-30 05:16:11 +04:00
} ,
. probe = poodle_probe ,
} ;
module_platform_driver ( poodle_driver ) ;
2006-10-12 16:33:45 +04:00
/* Module information */
MODULE_AUTHOR ( " Richard Purdie " ) ;
MODULE_DESCRIPTION ( " ALSA SoC Poodle " ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-12-30 05:16:11 +04:00
MODULE_ALIAS ( " platform:poodle-audio " ) ;