[alsa-devel] [PATCH 0/2] Mic fixes for Acer AO756 and C7
The mic sense pin on these systems always indicates mic present if the combo jack is empty. The Mic presence must be ignored unless the Headphone jack is plugged. To handle this, introduce the concept of a jack that is gated by the state of another jack. Does this method seem reasonable? Am I missing a better way to handle it?
The second patch also fixes up the pin configs for the A0756 netbook, the C7 ChromeOS variant doesn't need these but they don't hurt either.
Dylan Reid (2): ALSA: hda - Allow jack state to depend on another jack. ALSA: hda - Fix Acer Aspire models with analog mics.
sound/pci/hda/hda_jack.c | 46 +++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_jack.h | 4 ++++ sound/pci/hda/patch_realtek.c | 29 +++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-)
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.
Signed-off-by: Dylan Reid dgreid@chromium.org --- sound/pci/hda/hda_jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_jack.h | 4 ++++ 2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..e851d88 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) + /* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,6 +136,27 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
+ /* A gating jack indicates the jack is invalid if gating is unplugged */ + if (jack->gating_jack && + !get_jack_plug_state(jack->gating_jack->pin_sense)) + jack->pin_sense &= ~AC_PINSENSE_PRESENCE; + + /* If a jack is gated by this one update it. */ + if (jack->gated_jack) { + struct hda_jack_tbl *gated = jack->gated_jack; + + if (gated->callback) + gated->callback(codec, gated); + + /* If this jack is plugged, then check the gated jack, otherwise + * not plugged. + */ + if (get_jack_plug_state(jack->pin_sense)) + gated->pin_sense = read_pin_sense(codec, gated->nid); + else + gated->pin_sense &= ~AC_PINSENSE_PRESENCE; + } + jack->jack_dirty = 0; }
@@ -173,8 +196,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) - /** * snd_hda_jack_detect - query pin Presence Detect status * @codec: the CODEC to sense @@ -222,6 +243,27 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
/** + * snd_hda_jack_set_gating_jack - Set gating jack. + * + * Indicates the gated jack is only valid when the gating jack is plugged. + */ +int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_nid) +{ + struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); + struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); + + if (!gated || !gating) + return -EINVAL; + + gated->gating_jack = gating; + gating->gated_jack = gated; + + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack); + +/** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed */ void snd_hda_jack_report_sync(struct hda_codec *codec) diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 4487785..71553b2 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -29,6 +29,8 @@ struct hda_jack_tbl { unsigned int jack_dirty:1; /* needs to update? */ unsigned int phantom_jack:1; /* a fixed, always present port? */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ + struct hda_jack_tbl *gating_jack;/* valid when gating jack plugged */ + struct hda_jack_tbl *gated_jack;/* gated is dependent on this jack */ #ifdef CONFIG_SND_HDA_INPUT_JACK int type; struct snd_jack *jack; @@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, unsigned char action, hda_jack_callback cb);
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
At Sun, 18 Nov 2012 22:56:39 -0800, Dylan Reid wrote:
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.
The snd_array stuff may reallocate the whole chunks, so you can't keep the pointers pointing to the array elements. Instead of that, NIDs should be kept and resolve on demand.
In addition to that...
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_jack.h | 4 ++++ 2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..e851d88 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,6 +136,27 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
- /* A gating jack indicates the jack is invalid if gating is unplugged */
- if (jack->gating_jack &&
!get_jack_plug_state(jack->gating_jack->pin_sense))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
- /* If a jack is gated by this one update it. */
- if (jack->gated_jack) {
struct hda_jack_tbl *gated = jack->gated_jack;
if (gated->callback)
gated->callback(codec, gated);
I don't think this is the right place to call the callback of the gated pin. It should be called where the callback of gating pin is called.
/* If this jack is plugged, then check the gated jack, otherwise
* not plugged.
*/
if (get_jack_plug_state(jack->pin_sense))
gated->pin_sense = read_pin_sense(codec, gated->nid);
else
gated->pin_sense &= ~AC_PINSENSE_PRESENCE;
- }
... and the handling of gated pin can be done more easily by calling jack_detect_update() again.
And, another place to fix is the jack sync code. Since now you have a dependency between jacks, the update and check have to be split to two: update all jacks, and call jack sync for each pin. Otherwise, it won't be handled properly if a gating jack appears in the table after a gated jack.
The below is my revised patch. Could you check it?
thanks,
Takashi
--- diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..4e19480 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) + /* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
+ /* A gating jack indicates the jack is invalid if gating is unplugged */ + if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack)) + jack->pin_sense &= ~AC_PINSENSE_PRESENCE; + jack->jack_dirty = 0; + + /* If a jack is gated by this one update it. */ + if (jack->gated_jack) { + struct hda_jack_tbl *gated = + snd_hda_jack_tbl_get(codec, jack->gated_jack); + if (gated) { + gated->jack_dirty = 1; + jack_detect_update(codec, gated); + } + } }
/** @@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) - /** * snd_hda_jack_detect - query pin Presence Detect status * @codec: the CODEC to sense @@ -222,16 +236,46 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
/** + * snd_hda_jack_set_gating_jack - Set gating jack. + * + * Indicates the gated jack is only valid when the gating jack is plugged. + */ +int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_nid) +{ + struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); + struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); + + if (!gated || !gating) + return -EINVAL; + + gated->gating_jack = gating_nid; + gating->gated_jack = gated_nid; + + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack); + +/** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed */ void snd_hda_jack_report_sync(struct hda_codec *codec) { - struct hda_jack_tbl *jack = codec->jacktbl.list; + struct hda_jack_tbl *jack; int i, state;
+ /* update all jacks at first */ + jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) { + if (jack->nid) jack_detect_update(codec, jack); + + /* report the updated jacks; it's done after updating all jacks + * to make sure that all gating jacks properly have been set + */ + jack = codec->jacktbl.list; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) { if (!jack->kctl) continue; state = get_jack_plug_state(jack->pin_sense); @@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
+static void call_jack_callback(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + if (jack->callback) + jack->callback(codec, jack); + if (jack->gated_jack) { + struct hda_jack_tbl *gated = + snd_hda_jack_tbl_get(codec, jack->gated_jack); + if (gated && gated->callback) + gated->callback(codec, gated); + } +} + void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; @@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) return; event->jack_dirty = 1;
- if (event->callback) - event->callback(codec, event); - + call_jack_callback(codec, event); snd_hda_jack_report_sync(codec); } EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); @@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (old_sense == get_jack_plug_state(jack->pin_sense)) continue; changes = 1; - if (jack->callback) - jack->callback(codec, jack); + call_jack_callback(codec, jack); } if (changes) snd_hda_jack_report_sync(codec); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 4487785..ec12abd 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -28,6 +28,8 @@ struct hda_jack_tbl { unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ unsigned int phantom_jack:1; /* a fixed, always present port? */ + hda_nid_t gating_jack; /* valid when gating jack plugged */ + hda_nid_t gated_jack; /* gated is dependent on this jack */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ #ifdef CONFIG_SND_HDA_INPUT_JACK int type; @@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, unsigned char action, hda_jack_callback cb);
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, + hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
On Mon, Nov 19, 2012 at 2:55 AM, Takashi Iwai tiwai@suse.de wrote:
At Sun, 18 Nov 2012 22:56:39 -0800, Dylan Reid wrote:
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.
The snd_array stuff may reallocate the whole chunks, so you can't keep the pointers pointing to the array elements. Instead of that, NIDs should be kept and resolve on demand.
In addition to that...
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_jack.h | 4 ++++ 2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..e851d88 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,6 +136,27 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack &&
!get_jack_plug_state(jack->gating_jack->pin_sense))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated = jack->gated_jack;
if (gated->callback)
gated->callback(codec, gated);
I don't think this is the right place to call the callback of the gated pin. It should be called where the callback of gating pin is called.
/* If this jack is plugged, then check the gated jack, otherwise
* not plugged.
*/
if (get_jack_plug_state(jack->pin_sense))
gated->pin_sense = read_pin_sense(codec, gated->nid);
else
gated->pin_sense &= ~AC_PINSENSE_PRESENCE;
}
... and the handling of gated pin can be done more easily by calling jack_detect_update() again.
And, another place to fix is the jack sync code. Since now you have a dependency between jacks, the update and check have to be split to two: update all jacks, and call jack sync for each pin. Otherwise, it won't be handled properly if a gating jack appears in the table after a gated jack.
The below is my revised patch. Could you check it?
Tested, works great. Thanks for looking and fixing Takashi.
-Dylan
thanks,
Takashi
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..4e19480 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
jack->jack_dirty = 0;
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated) {
gated->jack_dirty = 1;
jack_detect_update(codec, gated);
}
}
}
/** @@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/**
- snd_hda_jack_detect - query pin Presence Detect status
- @codec: the CODEC to sense
@@ -222,16 +236,46 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
/**
- snd_hda_jack_set_gating_jack - Set gating jack.
- Indicates the gated jack is only valid when the gating jack is plugged.
- */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid)
+{
struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
if (!gated || !gating)
return -EINVAL;
gated->gating_jack = gating_nid;
gating->gated_jack = gated_nid;
return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack);
+/**
- snd_hda_jack_report_sync - sync the states of all jacks and report if changed
*/ void snd_hda_jack_report_sync(struct hda_codec *codec) {
struct hda_jack_tbl *jack = codec->jacktbl.list;
struct hda_jack_tbl *jack; int i, state;
/* update all jacks at first */
jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) {
if (jack->nid) jack_detect_update(codec, jack);
/* report the updated jacks; it's done after updating all jacks
* to make sure that all gating jacks properly have been set
*/
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) { if (!jack->kctl) continue; state = get_jack_plug_state(jack->pin_sense);
@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
+static void call_jack_callback(struct hda_codec *codec,
struct hda_jack_tbl *jack)
+{
if (jack->callback)
jack->callback(codec, jack);
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated && gated->callback)
gated->callback(codec, gated);
}
+}
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; @@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) return; event->jack_dirty = 1;
if (event->callback)
event->callback(codec, event);
call_jack_callback(codec, event); snd_hda_jack_report_sync(codec);
} EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); @@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (old_sense == get_jack_plug_state(jack->pin_sense)) continue; changes = 1;
if (jack->callback)
jack->callback(codec, jack);
call_jack_callback(codec, jack); } if (changes) snd_hda_jack_report_sync(codec);
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 4487785..ec12abd 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -28,6 +28,8 @@ struct hda_jack_tbl { unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ unsigned int phantom_jack:1; /* a fixed, always present port? */
hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
#ifdef CONFIG_SND_HDA_INPUT_JACK int type; @@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, unsigned char action, hda_jack_callback cb);
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
At Mon, 19 Nov 2012 09:36:21 -0800, Dylan Reid wrote:
On Mon, Nov 19, 2012 at 2:55 AM, Takashi Iwai tiwai@suse.de wrote:
At Sun, 18 Nov 2012 22:56:39 -0800, Dylan Reid wrote:
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.
The snd_array stuff may reallocate the whole chunks, so you can't keep the pointers pointing to the array elements. Instead of that, NIDs should be kept and resolve on demand.
In addition to that...
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_jack.h | 4 ++++ 2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..e851d88 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,6 +136,27 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack &&
!get_jack_plug_state(jack->gating_jack->pin_sense))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated = jack->gated_jack;
if (gated->callback)
gated->callback(codec, gated);
I don't think this is the right place to call the callback of the gated pin. It should be called where the callback of gating pin is called.
/* If this jack is plugged, then check the gated jack, otherwise
* not plugged.
*/
if (get_jack_plug_state(jack->pin_sense))
gated->pin_sense = read_pin_sense(codec, gated->nid);
else
gated->pin_sense &= ~AC_PINSENSE_PRESENCE;
}
... and the handling of gated pin can be done more easily by calling jack_detect_update() again.
And, another place to fix is the jack sync code. Since now you have a dependency between jacks, the update and check have to be split to two: update all jacks, and call jack sync for each pin. Otherwise, it won't be handled properly if a gating jack appears in the table after a gated jack.
The below is my revised patch. Could you check it?
Tested, works great. Thanks for looking and fixing Takashi.
OK, merged both patches now to for-next branch.
thanks,
Takashi
-Dylan
thanks,
Takashi
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 5bdbada..4e19480 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) snd_array_free(&codec->jacktbl); }
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) @@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec, else jack->pin_sense = read_pin_sense(codec, jack->nid);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
jack->jack_dirty = 0;
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated) {
gated->jack_dirty = 1;
jack_detect_update(codec, gated);
}
}
}
/** @@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/**
- snd_hda_jack_detect - query pin Presence Detect status
- @codec: the CODEC to sense
@@ -222,16 +236,46 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
/**
- snd_hda_jack_set_gating_jack - Set gating jack.
- Indicates the gated jack is only valid when the gating jack is plugged.
- */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid)
+{
struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
if (!gated || !gating)
return -EINVAL;
gated->gating_jack = gating_nid;
gating->gated_jack = gated_nid;
return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack);
+/**
- snd_hda_jack_report_sync - sync the states of all jacks and report if changed
*/ void snd_hda_jack_report_sync(struct hda_codec *codec) {
struct hda_jack_tbl *jack = codec->jacktbl.list;
struct hda_jack_tbl *jack; int i, state;
/* update all jacks at first */
jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) {
if (jack->nid) jack_detect_update(codec, jack);
/* report the updated jacks; it's done after updating all jacks
* to make sure that all gating jacks properly have been set
*/
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) { if (!jack->kctl) continue; state = get_jack_plug_state(jack->pin_sense);
@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
+static void call_jack_callback(struct hda_codec *codec,
struct hda_jack_tbl *jack)
+{
if (jack->callback)
jack->callback(codec, jack);
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated && gated->callback)
gated->callback(codec, gated);
}
+}
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; @@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) return; event->jack_dirty = 1;
if (event->callback)
event->callback(codec, event);
call_jack_callback(codec, event); snd_hda_jack_report_sync(codec);
} EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); @@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (old_sense == get_jack_plug_state(jack->pin_sense)) continue; changes = 1;
if (jack->callback)
jack->callback(codec, jack);
call_jack_callback(codec, jack); } if (changes) snd_hda_jack_report_sync(codec);
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 4487785..ec12abd 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -28,6 +28,8 @@ struct hda_jack_tbl { unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ unsigned int phantom_jack:1; /* a fixed, always present port? */
hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
#ifdef CONFIG_SND_HDA_INPUT_JACK int type; @@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, unsigned char action, hda_jack_callback cb);
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
The Acer Aspire AO756 has an analog built-in mic on nid 0x1b and an external mic on nid 0x19. The BIOS doesn't set this up.
The mic detect on this Acer Aspire netbook and Acer C7 ChromeBook is only valid when the headphone is plugged. The detect circuit relies on the tip detect switch being closed on the jack. Tell hda_jack to ignore the mic sense unless the headphones are plugged.
Signed-off-by: Dylan Reid dgreid@chromium.org --- sound/pci/hda/patch_realtek.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 89737ae..fe99176 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6013,6 +6013,16 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, } }
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec, + const struct alc_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == ALC_FIXUP_ACT_PROBE) + snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin, + spec->autocfg.hp_pins[0]); +}
enum { ALC269_FIXUP_SONY_VAIO, @@ -6035,6 +6045,8 @@ enum { ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, + ALC271_FIXUP_AMIC_MIC2, + ALC271_FIXUP_HP_GATE_MIC_JACK, };
static const struct alc_fixup alc269_fixups[] = { @@ -6179,6 +6191,22 @@ static const struct alc_fixup alc269_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, }, + [ALC271_FIXUP_AMIC_MIC2] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x19, 0x01a19c20 }, /* mic */ + { 0x1b, 0x99a7012f }, /* int-mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + }, + [ALC271_FIXUP_HP_GATE_MIC_JACK] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc271_hp_gate_mic_jack, + .chained = true, + .chain_id = ALC271_FIXUP_AMIC_MIC2, + }, };
static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6199,6 +6227,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
participants (2)
-
Dylan Reid
-
Takashi Iwai