Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57d4bf6d authored by Jaya Kumar's avatar Jaya Kumar Committed by Takashi Iwai
Browse files

ALSA: cs5535audio: OLPC analog input support



This is a 2nd cut at adding support for OLPC analog input.

Signed-off-by: default avatarJaya Kumar <jayakumar.lkml@gmail.com>
Signed-off-by: default avatarAndres Salomon <dilinger@debian.org>
parent b6c52a2c
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -5,5 +5,9 @@
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o


ifdef CONFIG_OLPC
snd-cs5535audio-objs += cs5535audio_olpc.o
endif

# Toplevel Module Dependency
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
+7 −0
Original line number Original line Diff line number Diff line
@@ -171,6 +171,13 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)


	snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
	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;
	return 0;
}
}


+9 −0
Original line number Original line Diff line number Diff line
@@ -92,6 +92,9 @@ struct cs5535audio {
	struct snd_pcm_substream *playback_substream;
	struct snd_pcm_substream *playback_substream;
	struct snd_pcm_substream *capture_substream;
	struct snd_pcm_substream *capture_substream;
	struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
	struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
#ifdef CONFIG_OLPC
	int ec_analog_input_mode;
#endif
};
};


#ifdef CONFIG_PM
#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);
int snd_cs5535audio_resume(struct pci_dev *pci);
#endif
#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);
int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);


#endif /* __SOUND_CS5535AUDIO_H */
#endif /* __SOUND_CS5535AUDIO_H */
+113 −0
Original line number Original line Diff line number Diff line
#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));
}