2006-10-12 16:32:13 +04:00
/*
* spitz . c - - SoC audio for Sharp SL - Cxx00 models Spitz , Borzoi and Akita
*
* 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:32:13 +04:00
* Richard Purdie < richard @ openedhand . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/timer.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
2008-09-05 18:15:23 +04:00
# include <linux/gpio.h>
2006-10-12 16:32:13 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <asm/mach-types.h>
2008-08-05 19:14:15 +04:00
# include <mach/spitz.h>
2006-10-12 16:32:13 +04:00
# include "../codecs/wm8750.h"
# include "pxa2xx-pcm.h"
2007-02-02 19:22:46 +03:00
# include "pxa2xx-i2s.h"
2006-10-12 16:32:13 +04:00
# define SPITZ_HP 0
# define SPITZ_MIC 1
# define SPITZ_LINE 2
# define SPITZ_HEADSET 3
# define SPITZ_HP_OFF 4
# define SPITZ_SPK_ON 0
# define SPITZ_SPK_OFF 1
/* audio clock in Hz - rounded from 12.235MHz */
# define SPITZ_AUDIO_CLOCK 12288000
static int spitz_jack_func ;
static int spitz_spk_func ;
static void spitz_ext_control ( struct snd_soc_codec * codec )
{
if ( spitz_spk_func = = SPITZ_SPK_ON )
2008-07-07 16:35:17 +04:00
snd_soc_dapm_enable_pin ( codec , " Ext Spk " ) ;
2006-10-12 16:32:13 +04:00
else
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Ext Spk " ) ;
2006-10-12 16:32:13 +04:00
/* set up jack connection */
switch ( spitz_jack_func ) {
case SPITZ_HP :
/* enable and unmute hp jack, disable mic bias */
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Headset Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Mic Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Line Jack " ) ;
snd_soc_dapm_enable_pin ( codec , " Headphone Jack " ) ;
2008-09-05 18:15:23 +04:00
gpio_set_value ( SPITZ_GPIO_MUTE_L , 1 ) ;
gpio_set_value ( SPITZ_GPIO_MUTE_R , 1 ) ;
2006-10-12 16:32:13 +04:00
break ;
case SPITZ_MIC :
/* enable mic jack and bias, mute hp */
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Headphone Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Line Jack " ) ;
snd_soc_dapm_enable_pin ( codec , " Mic Jack " ) ;
2008-09-05 18:15:23 +04:00
gpio_set_value ( SPITZ_GPIO_MUTE_L , 0 ) ;
gpio_set_value ( SPITZ_GPIO_MUTE_R , 0 ) ;
2006-10-12 16:32:13 +04:00
break ;
case SPITZ_LINE :
/* enable line jack, disable mic bias and mute hp */
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Headphone Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Mic Jack " ) ;
snd_soc_dapm_enable_pin ( codec , " Line Jack " ) ;
2008-09-05 18:15:23 +04:00
gpio_set_value ( SPITZ_GPIO_MUTE_L , 0 ) ;
gpio_set_value ( SPITZ_GPIO_MUTE_R , 0 ) ;
2006-10-12 16:32:13 +04:00
break ;
case SPITZ_HEADSET :
/* enable and unmute headset jack enable mic bias, mute L hp */
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Headphone Jack " ) ;
snd_soc_dapm_enable_pin ( codec , " Mic Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Line Jack " ) ;
snd_soc_dapm_enable_pin ( codec , " Headset Jack " ) ;
2008-09-05 18:15:23 +04:00
gpio_set_value ( SPITZ_GPIO_MUTE_L , 0 ) ;
gpio_set_value ( SPITZ_GPIO_MUTE_R , 1 ) ;
2006-10-12 16:32:13 +04:00
break ;
case SPITZ_HP_OFF :
/* jack removed, everything off */
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Headphone Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Mic Jack " ) ;
snd_soc_dapm_disable_pin ( codec , " Line Jack " ) ;
2008-09-05 18:15:23 +04:00
gpio_set_value ( SPITZ_GPIO_MUTE_L , 0 ) ;
gpio_set_value ( SPITZ_GPIO_MUTE_R , 0 ) ;
2006-10-12 16:32:13 +04:00
break ;
}
2008-07-07 16:35:17 +04:00
snd_soc_dapm_sync ( codec ) ;
2006-10-12 16:32:13 +04:00
}
static int spitz_startup ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_codec * codec = rtd - > socdev - > codec ;
/* check the jack status at stream startup */
spitz_ext_control ( codec ) ;
return 0 ;
}
2007-02-02 19:22:46 +03:00
static int spitz_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-07-07 19:08:11 +04:00
struct snd_soc_dai * codec_dai = rtd - > dai - > codec_dai ;
struct snd_soc_dai * cpu_dai = rtd - > dai - > cpu_dai ;
2007-02-02 19:22:46 +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 codec DAI configuration */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_fmt ( codec_dai , SND_SOC_DAIFMT_I2S |
2007-02-02 19:22:46 +03:00
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS ) ;
if ( ret < 0 )
return ret ;
/* set cpu DAI configuration */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_fmt ( cpu_dai , SND_SOC_DAIFMT_I2S |
2007-02-02 19:22:46 +03:00
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS ) ;
if ( ret < 0 )
return ret ;
/* set the codec system clock for DAC and ADC */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8750_SYSCLK , clk ,
2007-02-02 19:22:46 +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:22:46 +03:00
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2006-10-12 16:32:13 +04:00
static struct snd_soc_ops spitz_ops = {
. startup = spitz_startup ,
2007-02-02 19:22:46 +03:00
. hw_params = spitz_hw_params ,
2006-10-12 16:32:13 +04:00
} ;
static int spitz_get_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = spitz_jack_func ;
return 0 ;
}
static int spitz_set_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( spitz_jack_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
spitz_jack_func = ucontrol - > value . integer . value [ 0 ] ;
spitz_ext_control ( codec ) ;
return 1 ;
}
static int spitz_get_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = spitz_spk_func ;
return 0 ;
}
static int spitz_set_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( spitz_spk_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
spitz_spk_func = ucontrol - > value . integer . value [ 0 ] ;
spitz_ext_control ( codec ) ;
return 1 ;
}
2008-02-28 14:34:48 +03:00
static int spitz_mic_bias ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
2006-10-12 16:32:13 +04:00
{
2008-09-05 18:15:23 +04:00
if ( machine_is_borzoi ( ) | | machine_is_spitz ( ) )
gpio_set_value ( SPITZ_GPIO_MIC_BIAS ,
SND_SOC_DAPM_EVENT_ON ( event ) ) ;
2006-10-12 16:32:13 +04:00
2008-09-06 04:46:23 +04:00
if ( machine_is_akita ( ) )
gpio_set_value ( AKITA_GPIO_MIC_BIAS ,
SND_SOC_DAPM_EVENT_ON ( event ) ) ;
2006-10-12 16:32:13 +04:00
return 0 ;
}
/* spitz machine dapm widgets */
static const struct snd_soc_dapm_widget wm8750_dapm_widgets [ ] = {
SND_SOC_DAPM_HP ( " Headphone Jack " , NULL ) ,
SND_SOC_DAPM_MIC ( " Mic Jack " , spitz_mic_bias ) ,
SND_SOC_DAPM_SPK ( " Ext Spk " , NULL ) ,
SND_SOC_DAPM_LINE ( " Line Jack " , NULL ) ,
/* headset is a mic and mono headphone */
SND_SOC_DAPM_HP ( " Headset Jack " , NULL ) ,
} ;
/* Spitz machine audio_map */
2008-05-13 16:55:48 +04:00
static const struct snd_soc_dapm_route audio_map [ ] = {
2006-10-12 16:32:13 +04:00
/* headphone connected to LOUT1, ROUT1 */
{ " Headphone Jack " , NULL , " LOUT1 " } ,
{ " Headphone Jack " , NULL , " ROUT1 " } ,
/* headset connected to ROUT1 and LINPUT1 with bias (def below) */
{ " Headset Jack " , NULL , " ROUT1 " } ,
/* ext speaker connected to LOUT2, ROUT2 */
{ " Ext Spk " , NULL , " ROUT2 " } ,
{ " Ext Spk " , NULL , " LOUT2 " } ,
/* mic is connected to input 1 - with bias */
{ " LINPUT1 " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " Mic Jack " } ,
/* line is connected to input 1 - no bias */
{ " LINPUT1 " , NULL , " Line Jack " } ,
} ;
static const char * jack_function [ ] = { " Headphone " , " Mic " , " Line " , " Headset " ,
" Off " } ;
static const char * spk_function [ ] = { " On " , " Off " } ;
static const struct soc_enum spitz_enum [ ] = {
SOC_ENUM_SINGLE_EXT ( 5 , jack_function ) ,
SOC_ENUM_SINGLE_EXT ( 2 , spk_function ) ,
} ;
static const struct snd_kcontrol_new wm8750_spitz_controls [ ] = {
SOC_ENUM_EXT ( " Jack Function " , spitz_enum [ 0 ] , spitz_get_jack ,
spitz_set_jack ) ,
SOC_ENUM_EXT ( " Speaker Function " , spitz_enum [ 1 ] , spitz_get_spk ,
spitz_set_spk ) ,
} ;
/*
* Logic for a wm8750 as connected on a Sharp SL - Cxx00 Device
*/
static int spitz_wm8750_init ( struct snd_soc_codec * codec )
{
int i , err ;
/* NC codec pins */
2008-09-27 13:45:09 +04:00
snd_soc_dapm_nc_pin ( codec , " RINPUT1 " ) ;
snd_soc_dapm_nc_pin ( codec , " LINPUT2 " ) ;
snd_soc_dapm_nc_pin ( codec , " RINPUT2 " ) ;
snd_soc_dapm_nc_pin ( codec , " LINPUT3 " ) ;
snd_soc_dapm_nc_pin ( codec , " RINPUT3 " ) ;
snd_soc_dapm_nc_pin ( codec , " OUT3 " ) ;
snd_soc_dapm_nc_pin ( codec , " MONO1 " ) ;
2006-10-12 16:32:13 +04:00
/* Add spitz specific controls */
for ( i = 0 ; i < ARRAY_SIZE ( wm8750_spitz_controls ) ; i + + ) {
err = snd_ctl_add ( codec - > card ,
snd_soc_cnew ( & wm8750_spitz_controls [ i ] , codec , NULL ) ) ;
if ( err < 0 )
return err ;
}
/* Add spitz specific widgets */
2008-05-13 16:55:48 +04:00
snd_soc_dapm_new_controls ( codec , wm8750_dapm_widgets ,
ARRAY_SIZE ( wm8750_dapm_widgets ) ) ;
2006-10-12 16:32:13 +04:00
2008-05-13 16:55:48 +04:00
/* Set up spitz specific audio paths */
snd_soc_dapm_add_routes ( codec , audio_map , ARRAY_SIZE ( audio_map ) ) ;
2006-10-12 16:32:13 +04:00
2008-07-07 16:35:17 +04:00
snd_soc_dapm_sync ( codec ) ;
2006-10-12 16:32:13 +04:00
return 0 ;
}
/* spitz digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link spitz_dai = {
. name = " wm8750 " ,
. stream_name = " WM8750 " ,
. cpu_dai = & pxa_i2s_dai ,
. codec_dai = & wm8750_dai ,
. init = spitz_wm8750_init ,
2007-02-02 19:22:46 +03:00
. ops = & spitz_ops ,
2006-10-12 16:32:13 +04:00
} ;
/* spitz audio machine driver */
2008-11-18 23:50:34 +03:00
static struct snd_soc_card snd_soc_spitz = {
2006-10-12 16:32:13 +04:00
. name = " Spitz " ,
2008-12-02 19:01:14 +03:00
. platform = & pxa2xx_soc_platform ,
2006-10-12 16:32:13 +04:00
. dai_link = & spitz_dai ,
. num_links = 1 ,
} ;
/* spitz audio private data */
static struct wm8750_setup_data spitz_wm8750_setup = {
2008-09-01 21:47:00 +04:00
. i2c_bus = 0 ,
2006-10-12 16:32:13 +04:00
. i2c_address = 0x1b ,
} ;
/* spitz audio subsystem */
static struct snd_soc_device spitz_snd_devdata = {
2008-11-18 23:50:34 +03:00
. card = & snd_soc_spitz ,
2006-10-12 16:32:13 +04:00
. codec_dev = & soc_codec_dev_wm8750 ,
. codec_data = & spitz_wm8750_setup ,
} ;
static struct platform_device * spitz_snd_device ;
static int __init spitz_init ( void )
{
int ret ;
if ( ! ( machine_is_spitz ( ) | | machine_is_borzoi ( ) | | machine_is_akita ( ) ) )
return - ENODEV ;
spitz_snd_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! spitz_snd_device )
return - ENOMEM ;
platform_set_drvdata ( spitz_snd_device , & spitz_snd_devdata ) ;
spitz_snd_devdata . dev = & spitz_snd_device - > dev ;
ret = platform_device_add ( spitz_snd_device ) ;
if ( ret )
platform_device_put ( spitz_snd_device ) ;
return ret ;
}
static void __exit spitz_exit ( void )
{
platform_device_unregister ( spitz_snd_device ) ;
}
module_init ( spitz_init ) ;
module_exit ( spitz_exit ) ;
MODULE_AUTHOR ( " Richard Purdie " ) ;
MODULE_DESCRIPTION ( " ALSA SoC Spitz " ) ;
MODULE_LICENSE ( " GPL " ) ;