diff options
author | Dylan Reid <dgreid@chromium.org> | 2012-11-19 13:48:07 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-11-19 13:49:58 -0500 |
commit | 0619ba8c17b121ef0273be181198659b17d84247 (patch) | |
tree | 81cb3273e7376734b517d4695efa3d02455458ad /sound/pci/hda/hda_jack.c | |
parent | 379170a42c84cee5f95fac536a4b5b76843baf90 (diff) |
ALSA: hda - Allow jack state to depend on another jack
Introduce the concept of a "gated" jack. The gated jack's pin sense
is
only valid when the "gating" jack is plugged. This requires checking
the gating jack when the gated jack changes and re-checking the gated
jack when the gating jack is plugged/unplugged.
This allows handling of devices where the mic jack detect floats when
the headphone jack is unplugged.
[Rewritten for fixing the possible snd_array reallocation, covering
the missing callback calls and jack sync operations, as well as some
code cleanups -- tiwai]
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_jack.c')
-rw-r--r-- | sound/pci/hda/hda_jack.c | 72 |
1 files changed, 63 insertions, 9 deletions
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbadaa20fe..4e1948001338 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c | |||
@@ -122,6 +122,8 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) | |||
122 | snd_array_free(&codec->jacktbl); | 122 | snd_array_free(&codec->jacktbl); |
123 | } | 123 | } |
124 | 124 | ||
125 | #define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) | ||
126 | |||
125 | /* update the cached value and notification flag if needed */ | 127 | /* update the cached value and notification flag if needed */ |
126 | static void jack_detect_update(struct hda_codec *codec, | 128 | static void jack_detect_update(struct hda_codec *codec, |
127 | struct hda_jack_tbl *jack) | 129 | struct hda_jack_tbl *jack) |
@@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec, | |||
134 | else | 136 | else |
135 | jack->pin_sense = read_pin_sense(codec, jack->nid); | 137 | jack->pin_sense = read_pin_sense(codec, jack->nid); |
136 | 138 | ||
139 | /* A gating jack indicates the jack is invalid if gating is unplugged */ | ||
140 | if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack)) | ||
141 | jack->pin_sense &= ~AC_PINSENSE_PRESENCE; | ||
142 | |||
137 | jack->jack_dirty = 0; | 143 | jack->jack_dirty = 0; |
144 | |||
145 | /* If a jack is gated by this one update it. */ | ||
146 | if (jack->gated_jack) { | ||
147 | struct hda_jack_tbl *gated = | ||
148 | snd_hda_jack_tbl_get(codec, jack->gated_jack); | ||
149 | if (gated) { | ||
150 | gated->jack_dirty = 1; | ||
151 | jack_detect_update(codec, gated); | ||
152 | } | ||
153 | } | ||
138 | } | 154 | } |
139 | 155 | ||
140 | /** | 156 | /** |
@@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) | |||
173 | } | 189 | } |
174 | EXPORT_SYMBOL_HDA(snd_hda_pin_sense); | 190 | EXPORT_SYMBOL_HDA(snd_hda_pin_sense); |
175 | 191 | ||
176 | #define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) | ||
177 | |||
178 | /** | 192 | /** |
179 | * snd_hda_jack_detect - query pin Presence Detect status | 193 | * snd_hda_jack_detect - query pin Presence Detect status |
180 | * @codec: the CODEC to sense | 194 | * @codec: the CODEC to sense |
@@ -222,16 +236,46 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, | |||
222 | EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); | 236 | EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); |
223 | 237 | ||
224 | /** | 238 | /** |
239 | * snd_hda_jack_set_gating_jack - Set gating jack. | ||
240 | * | ||
241 | * Indicates the gated jack is only valid when the gating jack is plugged. | ||
242 | */ | ||
243 | int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, | ||
244 | hda_nid_t gating_nid) | ||
245 | { | ||
246 | struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); | ||
247 | struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); | ||
248 | |||
249 | if (!gated || !gating) | ||
250 | return -EINVAL; | ||
251 | |||
252 | gated->gating_jack = gating_nid; | ||
253 | gating->gated_jack = gated_nid; | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack); | ||
258 | |||
259 | /** | ||
225 | * snd_hda_jack_report_sync - sync the states of all jacks and report if changed | 260 | * snd_hda_jack_report_sync - sync the states of all jacks and report if changed |
226 | */ | 261 | */ |
227 | void snd_hda_jack_report_sync(struct hda_codec *codec) | 262 | void snd_hda_jack_report_sync(struct hda_codec *codec) |
228 | { | 263 | { |
229 | struct hda_jack_tbl *jack = codec->jacktbl.list; | 264 | struct hda_jack_tbl *jack; |
230 | int i, state; | 265 | int i, state; |
231 | 266 | ||
267 | /* update all jacks at first */ | ||
268 | jack = codec->jacktbl.list; | ||
232 | for (i = 0; i < codec->jacktbl.used; i++, jack++) | 269 | for (i = 0; i < codec->jacktbl.used; i++, jack++) |
233 | if (jack->nid) { | 270 | if (jack->nid) |
234 | jack_detect_update(codec, jack); | 271 | jack_detect_update(codec, jack); |
272 | |||
273 | /* report the updated jacks; it's done after updating all jacks | ||
274 | * to make sure that all gating jacks properly have been set | ||
275 | */ | ||
276 | jack = codec->jacktbl.list; | ||
277 | for (i = 0; i < codec->jacktbl.used; i++, jack++) | ||
278 | if (jack->nid) { | ||
235 | if (!jack->kctl) | 279 | if (!jack->kctl) |
236 | continue; | 280 | continue; |
237 | state = get_jack_plug_state(jack->pin_sense); | 281 | state = get_jack_plug_state(jack->pin_sense); |
@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, | |||
424 | } | 468 | } |
425 | EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); | 469 | EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); |
426 | 470 | ||
471 | static void call_jack_callback(struct hda_codec *codec, | ||
472 | struct hda_jack_tbl *jack) | ||
473 | { | ||
474 | if (jack->callback) | ||
475 | jack->callback(codec, jack); | ||
476 | if (jack->gated_jack) { | ||
477 | struct hda_jack_tbl *gated = | ||
478 | snd_hda_jack_tbl_get(codec, jack->gated_jack); | ||
479 | if (gated && gated->callback) | ||
480 | gated->callback(codec, gated); | ||
481 | } | ||
482 | } | ||
483 | |||
427 | void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) | 484 | void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) |
428 | { | 485 | { |
429 | struct hda_jack_tbl *event; | 486 | struct hda_jack_tbl *event; |
@@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) | |||
434 | return; | 491 | return; |
435 | event->jack_dirty = 1; | 492 | event->jack_dirty = 1; |
436 | 493 | ||
437 | if (event->callback) | 494 | call_jack_callback(codec, event); |
438 | event->callback(codec, event); | ||
439 | |||
440 | snd_hda_jack_report_sync(codec); | 495 | snd_hda_jack_report_sync(codec); |
441 | } | 496 | } |
442 | EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); | 497 | EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); |
@@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) | |||
455 | if (old_sense == get_jack_plug_state(jack->pin_sense)) | 510 | if (old_sense == get_jack_plug_state(jack->pin_sense)) |
456 | continue; | 511 | continue; |
457 | changes = 1; | 512 | changes = 1; |
458 | if (jack->callback) | 513 | call_jack_callback(codec, jack); |
459 | jack->callback(codec, jack); | ||
460 | } | 514 | } |
461 | if (changes) | 515 | if (changes) |
462 | snd_hda_jack_report_sync(codec); | 516 | snd_hda_jack_report_sync(codec); |