2008-11-07 00:53:26 +03:00
/*
* OLPC XO - 1 additional sound features
*
* Copyright © 2006 Jaya Kumar < jayakumar . lkml @ gmail . com >
* Copyright © 2007 - 2008 Andres Salomon < dilinger @ debian . org >
*
* 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 .
*/
2008-11-07 00:43:34 +03:00
# include <sound/core.h>
# include <sound/info.h>
# include <sound/control.h>
# include <sound/ac97_codec.h>
2009-12-15 05:00:36 +03:00
# include <linux/gpio.h>
2008-11-07 00:43:53 +03:00
# include <asm/olpc.h>
2008-11-07 00:43:34 +03:00
# include "cs5535audio.h"
2009-12-15 05:00:36 +03:00
# define DRV_NAME "cs5535audio-olpc"
2008-11-07 00:49:38 +03:00
/*
* OLPC has an additional feature on top of the regular AD1888 codec features .
* It has an Analog Input mode that is switched into ( after disabling the
* High Pass Filter ) via GPIO . It is supported on B2 and later models .
*/
void olpc_analog_input ( struct snd_ac97 * ac97 , int on )
{
int err ;
2008-11-07 00:53:34 +03:00
if ( ! machine_is_olpc ( ) )
return ;
2008-11-07 00:49:38 +03:00
/* update the High Pass Filter (via AC97_AD_TEST2) */
err = snd_ac97_update_bits ( ac97 , AC97_AD_TEST2 ,
1 < < AC97_AD_HPFD_SHIFT , on < < AC97_AD_HPFD_SHIFT ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " setting High Pass Filter - %d \n " , err ) ;
return ;
}
/* set Analog Input through GPIO */
2009-12-15 05:00:36 +03:00
gpio_set_value ( OLPC_GPIO_MIC_AC , on ) ;
2008-11-07 00:49:38 +03:00
}
2008-11-07 00:43:34 +03:00
2008-11-07 00:53:03 +03:00
/*
* OLPC XO - 1 ' s V_REFOUT is a mic bias enable .
*/
void olpc_mic_bias ( struct snd_ac97 * ac97 , int on )
{
int err ;
2008-11-07 00:53:34 +03:00
if ( ! machine_is_olpc ( ) )
return ;
2008-11-07 00:53:03 +03:00
on = on ? 0 : 1 ;
err = snd_ac97_update_bits ( ac97 , AC97_AD_MISC ,
1 < < AC97_AD_VREFD_SHIFT , on < < AC97_AD_VREFD_SHIFT ) ;
if ( err < 0 )
snd_printk ( KERN_ERR " setting MIC Bias - %d \n " , err ) ;
}
2008-11-07 00:49:46 +03:00
static int olpc_dc_info ( struct snd_kcontrol * kctl ,
struct snd_ctl_elem_info * uinfo )
2008-11-07 00:43:34 +03:00
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
return 0 ;
}
2008-11-07 00:49:46 +03:00
static int olpc_dc_get ( struct snd_kcontrol * kctl , struct snd_ctl_elem_value * v )
2008-11-07 00:43:34 +03:00
{
2009-12-15 05:00:36 +03:00
v - > value . integer . value [ 0 ] = gpio_get_value ( OLPC_GPIO_MIC_AC ) ;
2008-11-07 00:43:34 +03:00
return 0 ;
}
2008-11-07 00:49:46 +03:00
static int olpc_dc_put ( struct snd_kcontrol * kctl , struct snd_ctl_elem_value * v )
2008-11-07 00:43:34 +03:00
{
2008-11-07 00:49:46 +03:00
struct cs5535audio * cs5535au = snd_kcontrol_chip ( kctl ) ;
2008-11-07 00:43:34 +03:00
2008-11-07 00:49:46 +03:00
olpc_analog_input ( cs5535au - > ac97 , v - > value . integer . value [ 0 ] ) ;
2008-11-07 00:43:34 +03:00
return 1 ;
}
2008-11-07 00:53:03 +03:00
static int olpc_mic_info ( struct snd_kcontrol * kctl ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
return 0 ;
}
static int olpc_mic_get ( struct snd_kcontrol * kctl , struct snd_ctl_elem_value * v )
{
struct cs5535audio * cs5535au = snd_kcontrol_chip ( kctl ) ;
struct snd_ac97 * ac97 = cs5535au - > ac97 ;
int i ;
i = ( snd_ac97_read ( ac97 , AC97_AD_MISC ) > > AC97_AD_VREFD_SHIFT ) & 0x1 ;
v - > value . integer . value [ 0 ] = i ? 0 : 1 ;
return 0 ;
}
static int olpc_mic_put ( struct snd_kcontrol * kctl , struct snd_ctl_elem_value * v )
{
struct cs5535audio * cs5535au = snd_kcontrol_chip ( kctl ) ;
olpc_mic_bias ( cs5535au - > ac97 , v - > value . integer . value [ 0 ] ) ;
return 1 ;
}
2012-12-06 21:35:10 +04:00
static struct snd_kcontrol_new olpc_cs5535audio_ctls [ ] = {
2008-11-07 00:43:34 +03:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2008-11-07 00:49:46 +03:00
. name = " DC Mode Enable " ,
. info = olpc_dc_info ,
. get = olpc_dc_get ,
. put = olpc_dc_put ,
2008-11-07 00:53:26 +03:00
. private_value = 0 ,
2008-11-07 00:53:03 +03:00
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " MIC Bias Enable " ,
. info = olpc_mic_info ,
. get = olpc_mic_get ,
. put = olpc_mic_put ,
. private_value = 0 ,
} ,
2008-11-07 00:43:34 +03:00
} ;
2012-12-06 21:35:10 +04:00
void olpc_prequirks ( struct snd_card * card ,
struct snd_ac97_template * ac97 )
2008-11-07 00:44:08 +03:00
{
if ( ! machine_is_olpc ( ) )
return ;
/* invert EAPD if on an OLPC B3 or higher */
if ( olpc_board_at_least ( olpc_board_pre ( 0xb3 ) ) )
ac97 - > scaps | = AC97_SCAP_INV_EAPD ;
}
2012-12-06 21:35:10 +04:00
int olpc_quirks ( struct snd_card * card , struct snd_ac97 * ac97 )
2008-11-07 00:43:34 +03:00
{
2008-11-07 00:49:46 +03:00
struct snd_ctl_elem_id elem ;
2008-11-07 00:53:03 +03:00
int i , err ;
2008-11-07 00:49:46 +03:00
2008-11-07 00:43:53 +03:00
if ( ! machine_is_olpc ( ) )
return 0 ;
2009-12-15 05:00:36 +03:00
if ( gpio_request ( OLPC_GPIO_MIC_AC , DRV_NAME ) ) {
printk ( KERN_ERR DRV_NAME " : unable to allocate MIC GPIO \n " ) ;
return - EIO ;
}
gpio_direction_output ( OLPC_GPIO_MIC_AC , 0 ) ;
2008-11-07 00:49:46 +03:00
/* drop the original AD1888 HPF control */
memset ( & elem , 0 , sizeof ( elem ) ) ;
elem . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
2013-10-29 18:26:12 +04:00
strlcpy ( elem . name , " High Pass Filter Enable " , sizeof ( elem . name ) ) ;
2008-11-07 00:49:46 +03:00
snd_ctl_remove_id ( card , & elem ) ;
2008-11-07 00:53:03 +03:00
/* drop the original V_REFOUT control */
memset ( & elem , 0 , sizeof ( elem ) ) ;
elem . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
2013-10-29 18:26:12 +04:00
strlcpy ( elem . name , " V_REFOUT Enable " , sizeof ( elem . name ) ) ;
2008-11-07 00:53:03 +03:00
snd_ctl_remove_id ( card , & elem ) ;
/* add the OLPC-specific controls */
for ( i = 0 ; i < ARRAY_SIZE ( olpc_cs5535audio_ctls ) ; i + + ) {
err = snd_ctl_add ( card , snd_ctl_new1 ( & olpc_cs5535audio_ctls [ i ] ,
ac97 - > private_data ) ) ;
2009-12-15 05:00:36 +03:00
if ( err < 0 ) {
gpio_free ( OLPC_GPIO_MIC_AC ) ;
2008-11-07 00:53:03 +03:00
return err ;
2009-12-15 05:00:36 +03:00
}
2008-11-07 00:53:03 +03:00
}
2008-11-07 00:53:19 +03:00
/* turn off the mic by default */
olpc_mic_bias ( ac97 , 0 ) ;
2008-11-07 00:53:03 +03:00
return 0 ;
2008-11-07 00:43:34 +03:00
}
2009-12-15 05:00:36 +03:00
2012-12-06 21:35:10 +04:00
void olpc_quirks_cleanup ( void )
2009-12-15 05:00:36 +03:00
{
gpio_free ( OLPC_GPIO_MIC_AC ) ;
}