aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-12-25 05:40:28 -0500
committerTakashi Iwai <tiwai@suse.de>2008-12-25 05:40:28 -0500
commit313769d9edb1bbd59d2e94f5069950eecfe6fa44 (patch)
treec83c381ed3a6d77ff10129a43023026c7618c427
parent8afabfa74bbe81ac496e66f7f0ed8943dff5fdb5 (diff)
parentf144b7f6679d9833bd3b94b91e452592b6d0e502 (diff)
Merge branch 'topic/cs5535audio' into to-push
-rw-r--r--include/sound/ac97_codec.h2
-rw-r--r--sound/pci/ac97/ac97_patch.c5
-rw-r--r--sound/pci/cs5535audio/Makefile3
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c12
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h39
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c179
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c15
7 files changed, 252 insertions, 3 deletions
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 9c309daf492..251fc1cd500 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -281,10 +281,12 @@
281/* specific - Analog Devices */ 281/* specific - Analog Devices */
282#define AC97_AD_TEST 0x5a /* test register */ 282#define AC97_AD_TEST 0x5a /* test register */
283#define AC97_AD_TEST2 0x5c /* undocumented test register 2 */ 283#define AC97_AD_TEST2 0x5c /* undocumented test register 2 */
284#define AC97_AD_HPFD_SHIFT 12 /* High Pass Filter Disable */
284#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ 285#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */
285#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ 286#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */
286#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ 287#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */
287#define AC97_AD_MISC 0x76 /* Misc Control Bits */ 288#define AC97_AD_MISC 0x76 /* Misc Control Bits */
289#define AC97_AD_VREFD_SHIFT 2 /* V_REFOUT Disable (AD1888) */
288 290
289/* specific - Cirrus Logic */ 291/* specific - Cirrus Logic */
290#define AC97_CSR_ACMODE 0x5e /* AC Mode Register */ 292#define AC97_CSR_ACMODE 0x5e /* AC Mode Register */
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 2c7cd97d223..81bc93e5f1e 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -2054,8 +2054,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
2054 .get = snd_ac97_ad1888_lohpsel_get, 2054 .get = snd_ac97_ad1888_lohpsel_get,
2055 .put = snd_ac97_ad1888_lohpsel_put 2055 .put = snd_ac97_ad1888_lohpsel_put
2056 }, 2056 },
2057 AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1), 2057 AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
2058 AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1), 2058 AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
2059 AC97_AD_HPFD_SHIFT, 1, 1),
2059 AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), 2060 AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
2060 { 2061 {
2061 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2062 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index bb3d57e6a3c..fda7a94c992 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -4,6 +4,9 @@
4 4
5snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o 5snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
6snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o 6snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
7ifdef CONFIG_MGEODE_LX
8snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
9endif
7 10
8# Toplevel Module Dependency 11# Toplevel Module Dependency
9obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o 12obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 1d8b1605253..826e6dec2e9 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -159,10 +159,14 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
159 return err; 159 return err;
160 160
161 memset(&ac97, 0, sizeof(ac97)); 161 memset(&ac97, 0, sizeof(ac97));
162 ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM; 162 ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
163 | AC97_SCAP_POWER_SAVE;
163 ac97.private_data = cs5535au; 164 ac97.private_data = cs5535au;
164 ac97.pci = cs5535au->pci; 165 ac97.pci = cs5535au->pci;
165 166
167 /* set any OLPC-specific scaps */
168 olpc_prequirks(card, &ac97);
169
166 if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) { 170 if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
167 snd_printk(KERN_ERR "mixer failed\n"); 171 snd_printk(KERN_ERR "mixer failed\n");
168 return err; 172 return err;
@@ -170,6 +174,12 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
170 174
171 snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); 175 snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
172 176
177 err = olpc_quirks(card, cs5535au->ac97);
178 if (err < 0) {
179 snd_printk(KERN_ERR "olpc quirks failed\n");
180 return err;
181 }
182
173 return 0; 183 return 0;
174} 184}
175 185
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 66bae766419..7a298ac662e 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -78,6 +78,7 @@ struct cs5535audio_dma {
78 unsigned int buf_addr, buf_bytes; 78 unsigned int buf_addr, buf_bytes;
79 unsigned int period_bytes, periods; 79 unsigned int period_bytes, periods;
80 u32 saved_prd; 80 u32 saved_prd;
81 int pcm_open_flag;
81}; 82};
82 83
83struct cs5535audio { 84struct cs5535audio {
@@ -93,8 +94,46 @@ struct cs5535audio {
93 struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; 94 struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
94}; 95};
95 96
97#ifdef CONFIG_PM
96int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); 98int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
97int snd_cs5535audio_resume(struct pci_dev *pci); 99int snd_cs5535audio_resume(struct pci_dev *pci);
100#endif
101
102#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
103void __devinit olpc_prequirks(struct snd_card *card,
104 struct snd_ac97_template *ac97);
105int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
106void olpc_analog_input(struct snd_ac97 *ac97, int on);
107void olpc_mic_bias(struct snd_ac97 *ac97, int on);
108
109static inline void olpc_capture_open(struct snd_ac97 *ac97)
110{
111 /* default to Analog Input off */
112 olpc_analog_input(ac97, 0);
113 /* enable MIC Bias for recording */
114 olpc_mic_bias(ac97, 1);
115}
116
117static inline void olpc_capture_close(struct snd_ac97 *ac97)
118{
119 /* disable Analog Input */
120 olpc_analog_input(ac97, 0);
121 /* disable the MIC Bias (so the recording LED turns off) */
122 olpc_mic_bias(ac97, 0);
123}
124#else
125static inline void olpc_prequirks(struct snd_card *card,
126 struct snd_ac97_template *ac97) { }
127static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
128{
129 return 0;
130}
131static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
132static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
133static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
134static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
135#endif
136
98int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); 137int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
99 138
100#endif /* __SOUND_CS5535AUDIO_H */ 139#endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
new file mode 100644
index 00000000000..5c6814335cd
--- /dev/null
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -0,0 +1,179 @@
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 */
12#include <sound/core.h>
13#include <sound/info.h>
14#include <sound/control.h>
15#include <sound/ac97_codec.h>
16
17#include <asm/olpc.h>
18#include "cs5535audio.h"
19
20/*
21 * OLPC has an additional feature on top of the regular AD1888 codec features.
22 * It has an Analog Input mode that is switched into (after disabling the
23 * High Pass Filter) via GPIO. It is supported on B2 and later models.
24 */
25void olpc_analog_input(struct snd_ac97 *ac97, int on)
26{
27 int err;
28
29 if (!machine_is_olpc())
30 return;
31
32 /* update the High Pass Filter (via AC97_AD_TEST2) */
33 err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
34 1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
35 if (err < 0) {
36 snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
37 return;
38 }
39
40 /* set Analog Input through GPIO */
41 if (on)
42 geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
43 else
44 geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
45}
46
47/*
48 * OLPC XO-1's V_REFOUT is a mic bias enable.
49 */
50void olpc_mic_bias(struct snd_ac97 *ac97, int on)
51{
52 int err;
53
54 if (!machine_is_olpc())
55 return;
56
57 on = on ? 0 : 1;
58 err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
59 1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
60 if (err < 0)
61 snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
62}
63
64static int olpc_dc_info(struct snd_kcontrol *kctl,
65 struct snd_ctl_elem_info *uinfo)
66{
67 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
68 uinfo->count = 1;
69 uinfo->value.integer.min = 0;
70 uinfo->value.integer.max = 1;
71 return 0;
72}
73
74static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
75{
76 v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
77 GPIO_OUTPUT_VAL);
78 return 0;
79}
80
81static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
82{
83 struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
84
85 olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
86 return 1;
87}
88
89static 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
99static 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
110static 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
118static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
119{
120 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
121 .name = "DC Mode Enable",
122 .info = olpc_dc_info,
123 .get = olpc_dc_get,
124 .put = olpc_dc_put,
125 .private_value = 0,
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},
135};
136
137void __devinit olpc_prequirks(struct snd_card *card,
138 struct snd_ac97_template *ac97)
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
148int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
149{
150 struct snd_ctl_elem_id elem;
151 int i, err;
152
153 if (!machine_is_olpc())
154 return 0;
155
156 /* drop the original AD1888 HPF control */
157 memset(&elem, 0, sizeof(elem));
158 elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
159 strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
160 snd_ctl_remove_id(card, &elem);
161
162 /* drop the original V_REFOUT control */
163 memset(&elem, 0, sizeof(elem));
164 elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
165 strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
166 snd_ctl_remove_id(card, &elem);
167
168 /* add the OLPC-specific controls */
169 for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
170 err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
171 ac97->private_data));
172 if (err < 0)
173 return err;
174 }
175
176 /* turn off the mic by default */
177 olpc_mic_bias(ac97, 0);
178 return 0;
179}
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index cdcda87116c..0f48a871f17 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -260,6 +260,9 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
260 err = cs5535audio_build_dma_packets(cs5535au, dma, substream, 260 err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
261 params_periods(hw_params), 261 params_periods(hw_params),
262 params_period_bytes(hw_params)); 262 params_period_bytes(hw_params));
263 if (!err)
264 dma->pcm_open_flag = 1;
265
263 return err; 266 return err;
264} 267}
265 268
@@ -268,6 +271,15 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
268 struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); 271 struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
269 struct cs5535audio_dma *dma = substream->runtime->private_data; 272 struct cs5535audio_dma *dma = substream->runtime->private_data;
270 273
274 if (dma->pcm_open_flag) {
275 if (substream == cs5535au->playback_substream)
276 snd_ac97_update_power(cs5535au->ac97,
277 AC97_PCM_FRONT_DAC_RATE, 0);
278 else
279 snd_ac97_update_power(cs5535au->ac97,
280 AC97_PCM_LR_ADC_RATE, 0);
281 dma->pcm_open_flag = 0;
282 }
271 cs5535audio_clear_dma_packets(cs5535au, dma, substream); 283 cs5535audio_clear_dma_packets(cs5535au, dma, substream);
272 return snd_pcm_lib_free_pages(substream); 284 return snd_pcm_lib_free_pages(substream);
273} 285}
@@ -351,11 +363,14 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
351 if ((err = snd_pcm_hw_constraint_integer(runtime, 363 if ((err = snd_pcm_hw_constraint_integer(runtime,
352 SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 364 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
353 return err; 365 return err;
366 olpc_capture_open(cs5535au->ac97);
354 return 0; 367 return 0;
355} 368}
356 369
357static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream) 370static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
358{ 371{
372 struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
373 olpc_capture_close(cs5535au->ac97);
359 return 0; 374 return 0;
360} 375}
361 376