ALSA: cs5535audio: invert EAPD for OLPC (newer than B3)
[linux-2.6-block.git] / sound / pci / cs5535audio / cs5535audio_olpc.c
1 #include <sound/driver.h>
2 #include <sound/core.h>
3 #include <sound/info.h>
4 #include <sound/control.h>
5 #include <sound/ac97_codec.h>
6
7 #include <asm/olpc.h>
8 #include "cs5535audio.h"
9
10 /* OLPC has an additional feature on top of regular AD1888 codec
11 features. This is support for an analog input mode. This is a
12 2 step process. First, to turn off the AD1888 codec bias voltage
13 and high pass filter. Second, to tell the embedded controller to
14 reroute from a capacitive trace to a direct trace using an analog
15 switch. The *_ec()s are what talk to that controller */
16
17 static int snd_cs5535audio_ctl_info(struct snd_kcontrol *kcontrol,
18                                         struct snd_ctl_elem_info *uinfo)
19 {
20         uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
21         uinfo->count = 1;
22         uinfo->value.integer.min = 0;
23         uinfo->value.integer.max = 1;
24         return 0;
25 }
26
27 #define AD1888_VREFOUT_EN_BIT (1 << 2)
28 #define AD1888_HPF_EN_BIT (1 << 12)
29 static int snd_cs5535audio_ctl_get(struct snd_kcontrol *kcontrol,
30                                         struct snd_ctl_elem_value *ucontrol)
31 {
32         struct cs5535audio *cs5535au = snd_kcontrol_chip(kcontrol);
33         u16 reg1, reg2;
34
35         /* if either AD1888 VRef Bias and High Pass Filter are enabled
36         or the EC is not in analog mode then flag as not in analog mode.
37         No EC command to read current analog state so we cache that. */
38         reg1 = snd_ac97_read(cs5535au->ac97, AC97_AD_MISC);
39         reg2 = snd_ac97_read(cs5535au->ac97, AC97_AD_TEST2);
40
41         if ((reg1 & AD1888_VREFOUT_EN_BIT) && (reg2 & AD1888_HPF_EN_BIT) &&
42                 cs5535au->ec_analog_input_mode)
43                 ucontrol->value.integer.value[0] = 1;
44         else
45                 ucontrol->value.integer.value[0] = 0;
46
47         return 0;
48 }
49
50 static int snd_cs5535audio_ctl_put(struct snd_kcontrol *kcontrol,
51                                         struct snd_ctl_elem_value *ucontrol)
52 {
53         int err;
54         struct cs5535audio *cs5535au = snd_kcontrol_chip(kcontrol);
55         u8 value;
56         struct snd_ac97 *ac97 = cs5535au->ac97;
57
58         /* value is 1 if analog input is desired */
59         value = ucontrol->value.integer.value[0];
60
61         /* use ec mode as flag to determine if any change needed */
62         if (cs5535au->ec_analog_input_mode == value)
63                 return 0;
64
65         /* sets High Z on VREF Bias if 1 */
66         if (value)
67                 err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
68                                 AD1888_VREFOUT_EN_BIT, AD1888_VREFOUT_EN_BIT);
69         else
70                 err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
71                                 AD1888_VREFOUT_EN_BIT, 0);
72         if (err < 0)
73                 snd_printk(KERN_ERR "Error updating AD_MISC %d\n", err);
74
75         /* turns off High Pass Filter if 1 */
76         if (value)
77                 err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
78                                 AD1888_HPF_EN_BIT, AD1888_HPF_EN_BIT);
79         else
80                 err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
81                                 AD1888_HPF_EN_BIT, 0);
82         if (err < 0)
83                 snd_printk(KERN_ERR "Error updating AD_TEST2 %d\n", err);
84
85         /* B2 and newer writes directly to a GPIO pin */
86         if (value)
87                 geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
88         else
89                 geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
90
91         cs5535au->ec_analog_input_mode = value;
92
93         return 1;
94 }
95
96 static struct snd_kcontrol_new snd_cs5535audio_controls __devinitdata =
97 {
98         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
99         .name = "Analog Input Switch",
100         .info = snd_cs5535audio_ctl_info,
101         .get = snd_cs5535audio_ctl_get,
102         .put = snd_cs5535audio_ctl_put,
103         .private_value = 0
104 };
105
106 void __devinit olpc_prequirks(struct snd_card *card,
107                 struct snd_ac97_template *ac97)
108 {
109         if (!machine_is_olpc())
110                 return;
111
112         /* invert EAPD if on an OLPC B3 or higher */
113         if (olpc_board_at_least(olpc_board_pre(0xb3)))
114                 ac97->scaps |= AC97_SCAP_INV_EAPD;
115 }
116
117 int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
118 {
119         if (!machine_is_olpc())
120                 return 0;
121
122         /* setup callback for mixer control that does analog input mode */
123         return snd_ctl_add(card, snd_ctl_new1(&snd_cs5535audio_controls,
124                                                 ac97->private_data));
125 }
126