ALSA: cs5535audio: OLPC analog input support
This is a 2nd cut at adding support for OLPC analog input. Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com> Signed-off-by: Andres Salomon <dilinger@debian.org>
This commit is contained in:
		
							parent
							
								
									b6c52a2cdb
								
							
						
					
					
						commit
						57d4bf6d8e
					
				| @ -5,5 +5,9 @@ | ||||
| snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o | ||||
| snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o | ||||
| 
 | ||||
| ifdef CONFIG_OLPC | ||||
| snd-cs5535audio-objs += cs5535audio_olpc.o | ||||
| endif | ||||
| 
 | ||||
| # Toplevel Module Dependency
 | ||||
| obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o | ||||
|  | ||||
| @ -171,6 +171,13 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au) | ||||
| 
 | ||||
| 	snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); | ||||
| 
 | ||||
| 	/* olpc_quirks is dummied out if not olpc */ | ||||
| 	err = olpc_quirks(card, cs5535au->ac97); | ||||
| 	if (err < 0) { | ||||
| 		snd_printk(KERN_ERR "olpc quirks failed\n"); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -92,6 +92,9 @@ struct cs5535audio { | ||||
| 	struct snd_pcm_substream *playback_substream; | ||||
| 	struct snd_pcm_substream *capture_substream; | ||||
| 	struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; | ||||
| #ifdef CONFIG_OLPC | ||||
| 	int ec_analog_input_mode; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| @ -99,6 +102,12 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); | ||||
| int snd_cs5535audio_resume(struct pci_dev *pci); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_OLPC | ||||
| int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97); | ||||
| #else | ||||
| #define olpc_quirks(arg, arg2) (0) | ||||
| #endif | ||||
| 
 | ||||
| int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); | ||||
| 
 | ||||
| #endif /* __SOUND_CS5535AUDIO_H */ | ||||
|  | ||||
							
								
								
									
										113
									
								
								sound/pci/cs5535audio/cs5535audio_olpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								sound/pci/cs5535audio/cs5535audio_olpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | ||||
| #include <linux/olpc.h> | ||||
| #include <sound/driver.h> | ||||
| #include <sound/core.h> | ||||
| #include <sound/info.h> | ||||
| #include <sound/control.h> | ||||
| #include <sound/ac97_codec.h> | ||||
| #include "cs5535audio.h" | ||||
| 
 | ||||
| /* OLPC has an additional feature on top of regular AD1888 codec
 | ||||
| features. This is support for an analog input mode. This is a | ||||
| 2 step process. First, to turn off the AD1888 codec bias voltage | ||||
| and high pass filter. Second, to tell the embedded controller to | ||||
| reroute from a capacitive trace to a direct trace using an analog | ||||
| switch. The *_ec()s are what talk to that controller */ | ||||
| 
 | ||||
| static int snd_cs5535audio_ctl_info(struct snd_kcontrol *kcontrol, | ||||
| 					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; | ||||
| } | ||||
| 
 | ||||
| #define AD1888_VREFOUT_EN_BIT (1 << 2) | ||||
| #define AD1888_HPF_EN_BIT (1 << 12) | ||||
| static int snd_cs5535audio_ctl_get(struct snd_kcontrol *kcontrol, | ||||
| 					struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct cs5535audio *cs5535au = snd_kcontrol_chip(kcontrol); | ||||
| 	u16 reg1, reg2; | ||||
| 
 | ||||
| 	/* if either AD1888 VRef Bias and High Pass Filter are enabled
 | ||||
| 	or the EC is not in analog mode then flag as not in analog mode. | ||||
| 	No EC command to read current analog state so we cache that. */ | ||||
| 	reg1 = snd_ac97_read(cs5535au->ac97, AC97_AD_MISC); | ||||
| 	reg2 = snd_ac97_read(cs5535au->ac97, AC97_AD_TEST2); | ||||
| 
 | ||||
| 	if ((reg1 & AD1888_VREFOUT_EN_BIT) && (reg2 & AD1888_HPF_EN_BIT) && | ||||
| 		cs5535au->ec_analog_input_mode) | ||||
| 		ucontrol->value.integer.value[0] = 1; | ||||
| 	else | ||||
| 		ucontrol->value.integer.value[0] = 0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_cs5535audio_ctl_put(struct snd_kcontrol *kcontrol, | ||||
| 					struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	int err; | ||||
| 	struct cs5535audio *cs5535au = snd_kcontrol_chip(kcontrol); | ||||
| 	u8 value; | ||||
| 	struct snd_ac97 *ac97 = cs5535au->ac97; | ||||
| 
 | ||||
| 	/* value is 1 if analog input is desired */ | ||||
| 	value = ucontrol->value.integer.value[0]; | ||||
| 
 | ||||
| 	/* use ec mode as flag to determine if any change needed */ | ||||
| 	if (cs5535au->ec_analog_input_mode == value) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* sets High Z on VREF Bias if 1 */ | ||||
| 	if (value) | ||||
| 		err = snd_ac97_update_bits(ac97, AC97_AD_MISC, | ||||
| 				AD1888_VREFOUT_EN_BIT, AD1888_VREFOUT_EN_BIT); | ||||
| 	else | ||||
| 		err = snd_ac97_update_bits(ac97, AC97_AD_MISC, | ||||
| 				AD1888_VREFOUT_EN_BIT, 0); | ||||
| 	if (err < 0) | ||||
| 		snd_printk(KERN_ERR "Error updating AD_MISC %d\n", err); | ||||
| 
 | ||||
| 	/* turns off High Pass Filter if 1 */ | ||||
| 	if (value) | ||||
| 		err = snd_ac97_update_bits(ac97, AC97_AD_TEST2, | ||||
| 				AD1888_HPF_EN_BIT, AD1888_HPF_EN_BIT); | ||||
| 	else | ||||
| 		err = snd_ac97_update_bits(ac97, AC97_AD_TEST2, | ||||
| 				AD1888_HPF_EN_BIT, 0); | ||||
| 	if (err < 0) | ||||
| 		snd_printk(KERN_ERR "Error updating AD_TEST2 %d\n", err); | ||||
| 
 | ||||
| 	if (value) | ||||
| 		err = write_ec_command(0x01); /* activate MIC_AC_OFF */ | ||||
| 	else | ||||
| 		err = write_ec_command(0x02); /* deactivates MIC_AC_OFF */ | ||||
| 
 | ||||
| 	if (err < 0) | ||||
| 		snd_printk(KERN_ERR "Error talking to EC %d\n", err); | ||||
| 
 | ||||
| 	cs5535au->ec_analog_input_mode = value; | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static struct snd_kcontrol_new snd_cs5535audio_controls __devinitdata = | ||||
| { | ||||
| 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 	.name = "Analog Input Switch", | ||||
| 	.info = snd_cs5535audio_ctl_info, | ||||
| 	.get = snd_cs5535audio_ctl_get, | ||||
| 	.put = snd_cs5535audio_ctl_put, | ||||
| 	.private_value = 0 | ||||
| }; | ||||
| 
 | ||||
| int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) | ||||
| { | ||||
| 	/* setup callback for mixer control that does analog input mode */ | ||||
| 	return snd_ctl_add(card, snd_ctl_new1(&snd_cs5535audio_controls, | ||||
| 						ac97->private_data)); | ||||
| } | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user