Path: blob/master/sound/pci/cs5535audio/cs5535audio_olpc.c
10817 views
/*1* OLPC XO-1 additional sound features2*3* Copyright © 2006 Jaya Kumar <[email protected]>4* Copyright © 2007-2008 Andres Salomon <[email protected]>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; either version 2 of the License, or9* (at your option) any later version.10*/11#include <sound/core.h>12#include <sound/info.h>13#include <sound/control.h>14#include <sound/ac97_codec.h>15#include <linux/gpio.h>1617#include <asm/olpc.h>18#include "cs5535audio.h"1920#define DRV_NAME "cs5535audio-olpc"2122/*23* OLPC has an additional feature on top of the regular AD1888 codec features.24* It has an Analog Input mode that is switched into (after disabling the25* High Pass Filter) via GPIO. It is supported on B2 and later models.26*/27void olpc_analog_input(struct snd_ac97 *ac97, int on)28{29int err;3031if (!machine_is_olpc())32return;3334/* update the High Pass Filter (via AC97_AD_TEST2) */35err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,361 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);37if (err < 0) {38snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);39return;40}4142/* set Analog Input through GPIO */43gpio_set_value(OLPC_GPIO_MIC_AC, on);44}4546/*47* OLPC XO-1's V_REFOUT is a mic bias enable.48*/49void olpc_mic_bias(struct snd_ac97 *ac97, int on)50{51int err;5253if (!machine_is_olpc())54return;5556on = on ? 0 : 1;57err = snd_ac97_update_bits(ac97, AC97_AD_MISC,581 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);59if (err < 0)60snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);61}6263static int olpc_dc_info(struct snd_kcontrol *kctl,64struct snd_ctl_elem_info *uinfo)65{66uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;67uinfo->count = 1;68uinfo->value.integer.min = 0;69uinfo->value.integer.max = 1;70return 0;71}7273static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)74{75v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);76return 0;77}7879static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)80{81struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);8283olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);84return 1;85}8687static int olpc_mic_info(struct snd_kcontrol *kctl,88struct snd_ctl_elem_info *uinfo)89{90uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;91uinfo->count = 1;92uinfo->value.integer.min = 0;93uinfo->value.integer.max = 1;94return 0;95}9697static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)98{99struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);100struct snd_ac97 *ac97 = cs5535au->ac97;101int i;102103i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;104v->value.integer.value[0] = i ? 0 : 1;105return 0;106}107108static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)109{110struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);111112olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);113return 1;114}115116static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {117{118.iface = SNDRV_CTL_ELEM_IFACE_MIXER,119.name = "DC Mode Enable",120.info = olpc_dc_info,121.get = olpc_dc_get,122.put = olpc_dc_put,123.private_value = 0,124},125{126.iface = SNDRV_CTL_ELEM_IFACE_MIXER,127.name = "MIC Bias Enable",128.info = olpc_mic_info,129.get = olpc_mic_get,130.put = olpc_mic_put,131.private_value = 0,132},133};134135void __devinit olpc_prequirks(struct snd_card *card,136struct snd_ac97_template *ac97)137{138if (!machine_is_olpc())139return;140141/* invert EAPD if on an OLPC B3 or higher */142if (olpc_board_at_least(olpc_board_pre(0xb3)))143ac97->scaps |= AC97_SCAP_INV_EAPD;144}145146int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)147{148struct snd_ctl_elem_id elem;149int i, err;150151if (!machine_is_olpc())152return 0;153154if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {155printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");156return -EIO;157}158gpio_direction_output(OLPC_GPIO_MIC_AC, 0);159160/* drop the original AD1888 HPF control */161memset(&elem, 0, sizeof(elem));162elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;163strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));164snd_ctl_remove_id(card, &elem);165166/* drop the original V_REFOUT control */167memset(&elem, 0, sizeof(elem));168elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;169strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));170snd_ctl_remove_id(card, &elem);171172/* add the OLPC-specific controls */173for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {174err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],175ac97->private_data));176if (err < 0) {177gpio_free(OLPC_GPIO_MIC_AC);178return err;179}180}181182/* turn off the mic by default */183olpc_mic_bias(ac97, 0);184return 0;185}186187void __devexit olpc_quirks_cleanup(void)188{189gpio_free(OLPC_GPIO_MIC_AC);190}191192193