Commit | Line | Data |
---|---|---|
b5ccc57b AS |
1 | /* |
2 | * OLPC XO-1 additional sound features | |
3 | * | |
4 | * Copyright © 2006 Jaya Kumar <jayakumar.lkml@gmail.com> | |
5 | * Copyright © 2007-2008 Andres Salomon <dilinger@debian.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | */ | |
57d4bf6d JK |
12 | #include <sound/core.h> |
13 | #include <sound/info.h> | |
14 | #include <sound/control.h> | |
15 | #include <sound/ac97_codec.h> | |
3c554946 | 16 | #include <linux/gpio.h> |
c8974be5 JC |
17 | |
18 | #include <asm/olpc.h> | |
57d4bf6d JK |
19 | #include "cs5535audio.h" |
20 | ||
3c554946 AS |
21 | #define DRV_NAME "cs5535audio-olpc" |
22 | ||
d6276b78 AS |
23 | /* |
24 | * OLPC has an additional feature on top of the regular AD1888 codec features. | |
25 | * It has an Analog Input mode that is switched into (after disabling the | |
26 | * High Pass Filter) via GPIO. It is supported on B2 and later models. | |
27 | */ | |
28 | void olpc_analog_input(struct snd_ac97 *ac97, int on) | |
29 | { | |
30 | int err; | |
31 | ||
0fb497f5 AS |
32 | if (!machine_is_olpc()) |
33 | return; | |
34 | ||
d6276b78 AS |
35 | /* update the High Pass Filter (via AC97_AD_TEST2) */ |
36 | err = snd_ac97_update_bits(ac97, AC97_AD_TEST2, | |
37 | 1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT); | |
38 | if (err < 0) { | |
00980aa9 TI |
39 | dev_err(ac97->bus->card->dev, |
40 | "setting High Pass Filter - %d\n", err); | |
d6276b78 AS |
41 | return; |
42 | } | |
43 | ||
44 | /* set Analog Input through GPIO */ | |
3c554946 | 45 | gpio_set_value(OLPC_GPIO_MIC_AC, on); |
d6276b78 | 46 | } |
57d4bf6d | 47 | |
bf1e5278 AS |
48 | /* |
49 | * OLPC XO-1's V_REFOUT is a mic bias enable. | |
50 | */ | |
51 | void olpc_mic_bias(struct snd_ac97 *ac97, int on) | |
52 | { | |
53 | int err; | |
54 | ||
0fb497f5 AS |
55 | if (!machine_is_olpc()) |
56 | return; | |
57 | ||
bf1e5278 AS |
58 | on = on ? 0 : 1; |
59 | err = snd_ac97_update_bits(ac97, AC97_AD_MISC, | |
60 | 1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT); | |
61 | if (err < 0) | |
00980aa9 | 62 | dev_err(ac97->bus->card->dev, "setting MIC Bias - %d\n", err); |
bf1e5278 AS |
63 | } |
64 | ||
466ae305 AS |
65 | static int olpc_dc_info(struct snd_kcontrol *kctl, |
66 | struct snd_ctl_elem_info *uinfo) | |
57d4bf6d JK |
67 | { |
68 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
69 | uinfo->count = 1; | |
70 | uinfo->value.integer.min = 0; | |
71 | uinfo->value.integer.max = 1; | |
72 | return 0; | |
73 | } | |
74 | ||
466ae305 | 75 | static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v) |
57d4bf6d | 76 | { |
3c554946 | 77 | v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC); |
57d4bf6d JK |
78 | return 0; |
79 | } | |
80 | ||
466ae305 | 81 | static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v) |
57d4bf6d | 82 | { |
466ae305 | 83 | struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl); |
57d4bf6d | 84 | |
466ae305 | 85 | olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]); |
57d4bf6d JK |
86 | return 1; |
87 | } | |
88 | ||
bf1e5278 AS |
89 | static int olpc_mic_info(struct snd_kcontrol *kctl, |
90 | struct snd_ctl_elem_info *uinfo) | |
91 | { | |
92 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
93 | uinfo->count = 1; | |
94 | uinfo->value.integer.min = 0; | |
95 | uinfo->value.integer.max = 1; | |
96 | return 0; | |
97 | } | |
98 | ||
99 | static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v) | |
100 | { | |
101 | struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl); | |
102 | struct snd_ac97 *ac97 = cs5535au->ac97; | |
103 | int i; | |
104 | ||
105 | i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1; | |
106 | v->value.integer.value[0] = i ? 0 : 1; | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v) | |
111 | { | |
112 | struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl); | |
113 | ||
114 | olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]); | |
115 | return 1; | |
116 | } | |
117 | ||
e23e7a14 | 118 | static struct snd_kcontrol_new olpc_cs5535audio_ctls[] = { |
57d4bf6d JK |
119 | { |
120 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
466ae305 AS |
121 | .name = "DC Mode Enable", |
122 | .info = olpc_dc_info, | |
123 | .get = olpc_dc_get, | |
124 | .put = olpc_dc_put, | |
b5ccc57b | 125 | .private_value = 0, |
bf1e5278 AS |
126 | }, |
127 | { | |
128 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
129 | .name = "MIC Bias Enable", | |
130 | .info = olpc_mic_info, | |
131 | .get = olpc_mic_get, | |
132 | .put = olpc_mic_put, | |
133 | .private_value = 0, | |
134 | }, | |
57d4bf6d JK |
135 | }; |
136 | ||
e23e7a14 BP |
137 | void olpc_prequirks(struct snd_card *card, |
138 | struct snd_ac97_template *ac97) | |
3556d184 AS |
139 | { |
140 | if (!machine_is_olpc()) | |
141 | return; | |
142 | ||
143 | /* invert EAPD if on an OLPC B3 or higher */ | |
144 | if (olpc_board_at_least(olpc_board_pre(0xb3))) | |
145 | ac97->scaps |= AC97_SCAP_INV_EAPD; | |
146 | } | |
147 | ||
e23e7a14 | 148 | int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) |
57d4bf6d | 149 | { |
466ae305 | 150 | struct snd_ctl_elem_id elem; |
bf1e5278 | 151 | int i, err; |
466ae305 | 152 | |
c8974be5 JC |
153 | if (!machine_is_olpc()) |
154 | return 0; | |
155 | ||
3c554946 | 156 | if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) { |
00980aa9 | 157 | dev_err(card->dev, "unable to allocate MIC GPIO\n"); |
3c554946 AS |
158 | return -EIO; |
159 | } | |
160 | gpio_direction_output(OLPC_GPIO_MIC_AC, 0); | |
161 | ||
466ae305 AS |
162 | /* drop the original AD1888 HPF control */ |
163 | memset(&elem, 0, sizeof(elem)); | |
164 | elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | |
57a4451d | 165 | strlcpy(elem.name, "High Pass Filter Enable", sizeof(elem.name)); |
466ae305 AS |
166 | snd_ctl_remove_id(card, &elem); |
167 | ||
bf1e5278 AS |
168 | /* drop the original V_REFOUT control */ |
169 | memset(&elem, 0, sizeof(elem)); | |
170 | elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | |
57a4451d | 171 | strlcpy(elem.name, "V_REFOUT Enable", sizeof(elem.name)); |
bf1e5278 AS |
172 | snd_ctl_remove_id(card, &elem); |
173 | ||
174 | /* add the OLPC-specific controls */ | |
175 | for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) { | |
176 | err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i], | |
177 | ac97->private_data)); | |
3c554946 AS |
178 | if (err < 0) { |
179 | gpio_free(OLPC_GPIO_MIC_AC); | |
bf1e5278 | 180 | return err; |
3c554946 | 181 | } |
bf1e5278 AS |
182 | } |
183 | ||
c8f0eeeb AS |
184 | /* turn off the mic by default */ |
185 | olpc_mic_bias(ac97, 0); | |
bf1e5278 | 186 | return 0; |
57d4bf6d | 187 | } |
3c554946 | 188 | |
e23e7a14 | 189 | void olpc_quirks_cleanup(void) |
3c554946 AS |
190 | { |
191 | gpio_free(OLPC_GPIO_MIC_AC); | |
192 | } |