From 04f5ade6afc4326dc6cd10d235500972fba548eb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 27 Oct 2011 20:47:07 +0200 Subject: [PATCH 01/15] ALSA: hda - Introduce snd_hda_get_pin_label() Create a new helper function snd_hda_get_pin_label() for getting a label string for both input and output pins. hda_get_input_pin_label() is obsoleted by this function, and the callers are replaced appropriately now by this patch. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 63 ++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_local.h | 4 +-- sound/pci/hda/patch_ca0110.c | 2 +- sound/pci/hda/patch_cirrus.c | 2 +- sound/pci/hda/patch_sigmatel.c | 4 +-- 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e44b107fdc7..c6ff9b9de55 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5004,8 +5004,8 @@ EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); * "Rear", "Internal". */ -const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin, - int check_location) +static const char *hda_get_input_pin_label(struct hda_codec *codec, + hda_nid_t pin, bool check_location) { unsigned int def_conf; static const char * const mic_names[] = { @@ -5044,7 +5044,6 @@ const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin, return "Misc"; } } -EXPORT_SYMBOL_HDA(hda_get_input_pin_label); /* Check whether the location prefix needs to be added to the label. * If all mic-jacks are in the same location (e.g. rear panel), we don't @@ -5101,6 +5100,64 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); +/** + * snd_hda_get_pin_label - Get a label for the given I/O pin + * + * Get a label for the given pin. This function works for both input and + * output pins. When @cfg is given as non-NULL, the function tries to get + * an optimized label using hda_get_autocfg_input_label(). + */ +const char *snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int attr; + int i; + + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + return NULL; + + attr = snd_hda_get_input_pin_attr(def_conf); + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + switch (attr) { + case INPUT_PIN_ATTR_INT: + return "Speaker"; + case INPUT_PIN_ATTR_DOCK: + return "Dock Line-Out"; + case INPUT_PIN_ATTR_FRONT: + return "Front Line-Out"; + default: + return "Line-Out"; + } + case AC_JACK_SPEAKER: + return "Speaker"; + case AC_JACK_HP_OUT: + switch (attr) { + case INPUT_PIN_ATTR_DOCK: + return "Dock Headphone"; + case INPUT_PIN_ATTR_FRONT: + return "Front Headphone"; + default: + return "Headphone"; + } + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) + return "HDMI"; + else + return "SPDIF"; + } + + if (cfg) { + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == nid) + return hda_get_autocfg_input_label(codec, cfg, i); + } + return hda_get_input_pin_label(codec, nid, true); +} +EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); + /** * snd_hda_add_imux_item - Add an item to input_mux * diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 618ddad1723..7a10fe95211 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -394,11 +394,11 @@ struct auto_pin_cfg_item { }; struct auto_pin_cfg; -const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin, - int check_location); const char *hda_get_autocfg_input_label(struct hda_codec *codec, const struct auto_pin_cfg *cfg, int input); +const char *snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg); int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, int index, int *type_index_ret); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 993757b6573..6bd602bfe0f 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -476,7 +476,7 @@ static void parse_input(struct hda_codec *codec) if (j >= cfg->num_inputs) continue; spec->input_pins[n] = pin; - spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1); + spec->input_labels[n] = snd_hda_get_pin_label(codec, pin, NULL); spec->adcs[n] = nid; n++; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 2a2d8645ba0..34a460bd27b 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -711,7 +711,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = spec->num_inputs - 1; idx = spec->input_idx[uinfo->value.enumerated.item]; strcpy(uinfo->value.enumerated.name, - hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1)); + snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, NULL)); return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 470f6f286e8..3b4ef0cba0e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2872,7 +2872,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, } if (control) { - strcpy(name, hda_get_input_pin_label(codec, nid, 1)); + strcpy(name, snd_hda_get_pin_label(codec, nid, NULL)); return stac92xx_add_control(codec->spec, control, strcat(name, " Jack Mode"), nid); } @@ -3563,7 +3563,7 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (index < 0) continue; - label = hda_get_input_pin_label(codec, nid, 1); + label = snd_hda_get_pin_label(codec, nid, NULL); snd_hda_add_imux_item(dimux, label, index, &type_idx); if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) snd_hda_add_imux_item(imux, label, index, &type_idx); From 1835a0f9a2121ce3198dab67507d4d3e960cc09e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 27 Oct 2011 22:12:46 +0200 Subject: [PATCH 02/15] ALSA: hda - Cache the jack-detection value Introduce a table containing the pins and their jack-detection states for avoiding the unnecessary verbs to check the pin status at each time. When the unsol event is enabled via snd_hda_jack_detect_enable(), it automatically adds the given NID to the table. Then the driver supposes that the codec driver will set the dirty flag appropariately when an unsolicited event is invoked for that pin. The behavior for reading other pins that aren't registered in the table doesn't change. Only the pins assigned to the table are cached, so far. In near futre, this table can be extended to use the central place for the unsolicited events of all pins, etc, and eventually include the jack-detect kcontrols that replace the current input-jack stuff. Signed-off-by: Takashi Iwai --- sound/pci/hda/Makefile | 2 +- sound/pci/hda/hda_codec.c | 40 +-------- sound/pci/hda/hda_codec.h | 3 + sound/pci/hda/hda_jack.c | 148 +++++++++++++++++++++++++++++++++ sound/pci/hda/hda_jack.h | 63 ++++++++++++++ sound/pci/hda/hda_local.h | 15 ---- sound/pci/hda/patch_analog.c | 1 + sound/pci/hda/patch_cirrus.c | 18 ++-- sound/pci/hda/patch_conexant.c | 20 ++--- sound/pci/hda/patch_hdmi.c | 6 +- sound/pci/hda/patch_realtek.c | 20 ++--- sound/pci/hda/patch_sigmatel.c | 46 +++++----- sound/pci/hda/patch_via.c | 19 +++-- 13 files changed, 284 insertions(+), 117 deletions(-) create mode 100644 sound/pci/hda/hda_jack.c create mode 100644 sound/pci/hda/hda_jack.h diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index f928d663472..ace157cc3d1 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,6 +1,6 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-y := hda_codec.o +snd-hda-codec-y := hda_codec.o hda_jack.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c6ff9b9de55..8217ff7ff7b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -33,6 +33,7 @@ #include #include "hda_local.h" #include "hda_beep.h" +#include "hda_jack.h" #include #define CREATE_TRACE_POINTS @@ -1723,43 +1724,6 @@ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); -/** - * snd_hda_pin_sense - execute pin sense measurement - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * - * Execute necessary pin sense measurement and return its Presence Detect, - * Impedance, ELD Valid etc. status bits. - */ -u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) -{ - u32 pincap; - - if (!codec->no_trigger_sense) { - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - } - return snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); -} -EXPORT_SYMBOL_HDA(snd_hda_pin_sense); - -/** - * snd_hda_jack_detect - query pin Presence Detect status - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * - * Query and return the pin's Presence Detect status. - */ -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) -{ - u32 sense = snd_hda_pin_sense(codec, nid); - return !!(sense & AC_PINSENSE_PRESENCE); -} -EXPORT_SYMBOL_HDA(snd_hda_jack_detect); - /* * read the current volume to info * if the cache exists, read the cache value. @@ -2308,6 +2272,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) } if (codec->patch_ops.free) codec->patch_ops.free(codec); + snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; free_hda_cache(&codec->amp_cache); @@ -3364,6 +3329,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) restore_pincfgs(codec); /* restore all current pin configs */ restore_shutup_pins(codec); hda_exec_init_verbs(codec); + snd_hda_jack_set_dirty_all(codec); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); else { diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 564471169ca..97aa65d1f1d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -869,6 +869,9 @@ struct hda_codec { void (*proc_widget_hook)(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid); + /* jack detection */ + struct snd_array jacktbl; + #ifdef CONFIG_SND_HDA_INPUT_JACK /* jack detection */ struct snd_array jacks; diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c new file mode 100644 index 00000000000..64b78a2e20e --- /dev/null +++ b/sound/pci/hda/hda_jack.c @@ -0,0 +1,148 @@ +/* + * Jack-detection handling for HD-audio + * + * Copyright (c) 2011 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" +#include "hda_jack.h" + +/* execute pin sense measurement */ +static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + u32 pincap; + + if (!codec->no_trigger_sense) { + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_SENSE, 0); + } + return snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); +} + +/** + * snd_hda_jack_tbl_get - query the jack-table entry for the given NID + */ +struct hda_jack_tbl * +snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!nid || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid == nid) + return jack; + return NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get); + +/** + * snd_hda_jack_tbl_new - create a jack-table entry for the given NID + */ +struct hda_jack_tbl * +snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + if (jack) + return jack; + snd_array_init(&codec->jacktbl, sizeof(*jack), 16); + jack = snd_array_new(&codec->jacktbl); + if (!jack) + return NULL; + jack->nid = nid; + jack->jack_dirty = 1; + return jack; +} + +void snd_hda_jack_tbl_clear(struct hda_codec *codec) +{ + snd_array_free(&codec->jacktbl); +} + +/* update the cached value and notification flag if needed */ +static void jack_detect_update(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + if (jack->jack_dirty) { + jack->pin_sense = read_pin_sense(codec, jack->nid); + jack->jack_dirty = 0; + } +} + +/** + * snd_hda_set_dirty_all - Mark all the cached as dirty + * + * This function sets the dirty flag to all entries of jack table. + * It's called from the resume path in hda_codec.c. + */ +void snd_hda_jack_set_dirty_all(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) + jack->jack_dirty = 1; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_set_dirty_all); + +/** + * snd_hda_pin_sense - execute pin sense measurement + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Execute necessary pin sense measurement and return its Presence Detect, + * Impedance, ELD Valid etc. status bits. + */ +u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + if (jack) { + jack_detect_update(codec, jack); + return jack->pin_sense; + } + return read_pin_sense(codec, nid); +} +EXPORT_SYMBOL_HDA(snd_hda_pin_sense); + +/** + * snd_hda_jack_detect - query pin Presence Detect status + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Query and return the pin's Presence Detect status. + */ +int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + u32 sense = snd_hda_pin_sense(codec, nid); + return !!(sense & AC_PINSENSE_PRESENCE); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_detect); + +/** + * snd_hda_jack_detect_enable - enable the jack-detection + */ +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, + unsigned int tag) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); + if (!jack) + return -ENOMEM; + return snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | tag); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h new file mode 100644 index 00000000000..5c1bcb8cf56 --- /dev/null +++ b/sound/pci/hda/hda_jack.h @@ -0,0 +1,63 @@ +/* + * Jack-detection handling for HD-audio + * + * Copyright (c) 2011 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SOUND_HDA_JACK_H +#define __SOUND_HDA_JACK_H + +struct hda_jack_tbl { + hda_nid_t nid; + unsigned int pin_sense; /* cached pin-sense value */ + unsigned int jack_dirty:1; /* needs to update? */ +}; + +struct hda_jack_tbl * +snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); + +struct hda_jack_tbl * +snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid); +void snd_hda_jack_tbl_clear(struct hda_codec *codec); + +/** + * snd_hda_jack_set_dirty - set the dirty flag for the given jack-entry + * + * Call this function when a pin-state may change, e.g. when the hardware + * notifies via an unsolicited event. + */ +static inline void snd_hda_jack_set_dirty(struct hda_codec *codec, + hda_nid_t nid) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + if (jack) + jack->jack_dirty = 1; +} + +void snd_hda_jack_set_dirty_all(struct hda_codec *codec); + +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, + unsigned int tag); + +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); + +static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) +{ + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) + return false; + if (!codec->ignore_misc_bit && + (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE)) + return false; + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) + return false; + return true; +} + +#endif /* __SOUND_HDA_JACK_H */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7a10fe95211..08e88b826d2 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -505,21 +505,6 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps); -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); - -static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) -{ - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) - return false; - if (!codec->ignore_misc_bit && - (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) - return false; - return true; -} /* flags for hda_nid_item */ #define HDA_NID_ITEM_AMP (1<<0) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index bcb3310c394..9cb14b42dff 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -29,6 +29,7 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" +#include "hda_jack.h" struct ad198x_spec { const struct snd_kcontrol_new *mixers[6]; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 34a460bd27b..6f158777f71 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -26,6 +26,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_jack.h" #include /* @@ -1020,9 +1021,7 @@ static void init_output(struct hda_codec *codec) if (!cfg->speaker_outs) continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | HP_EVENT); + snd_hda_jack_detect_enable(codec, nid, HP_EVENT); spec->hp_detect = 1; } } @@ -1063,9 +1062,7 @@ static void init_input(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(spec->adc_idx[i])); if (spec->mic_detect && spec->automic_idx == i) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | MIC_EVENT); + snd_hda_jack_detect_enable(codec, pin, MIC_EVENT); } /* specific to CS421x */ if (spec->vendor_nid == CS421X_VENDOR_NID) { @@ -1227,6 +1224,8 @@ static void cs_free(struct hda_codec *codec) static void cs_unsol_event(struct hda_codec *codec, unsigned int res) { + snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ + switch ((res >> 26) & 0x7f) { case HP_EVENT: cs_automute(codec); @@ -1585,10 +1584,7 @@ static void init_cs421x_digital(struct hda_codec *codec) if (!cfg->speaker_outs) continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | SPDIF_EVENT); + snd_hda_jack_detect_enable(codec, nid, SPDIF_EVENT); spec->spdif_detect = 1; } } @@ -1806,6 +1802,8 @@ static int cs421x_build_controls(struct hda_codec *codec) static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) { + snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ + switch ((res >> 26) & 0x3f) { case HP_EVENT: case SPDIF_EVENT: diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 0de21193a2b..220e567ccff 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -31,6 +31,7 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" +#include "hda_jack.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -3756,6 +3757,7 @@ static void cx_auto_automic(struct hda_codec *codec) static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) { int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; + snd_hda_jack_set_dirty(codec, nid); switch (res >> 26) { case CONEXANT_HP_EVENT: cx_auto_hp_automute(codec); @@ -3983,9 +3985,7 @@ static void enable_unsol_pins(struct hda_codec *codec, int num_pins, { int i; for (i = 0; i < num_pins; i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | tag); + snd_hda_jack_detect_enable(codec, pins[i], tag); } static void cx_auto_init_output(struct hda_codec *codec) @@ -4060,16 +4060,14 @@ static void cx_auto_init_input(struct hda_codec *codec) if (spec->auto_mic) { if (spec->auto_mic_ext >= 0) { - snd_hda_codec_write(codec, - cfg->inputs[spec->auto_mic_ext].pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | CONEXANT_MIC_EVENT); + snd_hda_jack_detect_enable(codec, + cfg->inputs[spec->auto_mic_ext].pin, + CONEXANT_MIC_EVENT); } if (spec->auto_mic_dock >= 0) { - snd_hda_codec_write(codec, - cfg->inputs[spec->auto_mic_dock].pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | CONEXANT_MIC_EVENT); + snd_hda_jack_detect_enable(codec, + cfg->inputs[spec->auto_mic_dock].pin, + CONEXANT_MIC_EVENT); } cx_auto_automic(codec); } else { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 9850c5b481e..ea6d85d4844 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -36,6 +36,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_jack.h" static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); @@ -766,6 +767,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) if (pin_idx < 0) return; + snd_hda_jack_set_dirty(codec, pin_nid); hdmi_present_sense(&spec->pins[pin_idx], true); } @@ -1282,9 +1284,7 @@ static int generic_hdmi_init(struct hda_codec *codec) struct hdmi_eld *eld = &per_pin->sink_eld; hdmi_init_pin(codec, pin_nid); - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | pin_nid); + snd_hda_jack_detect_enable(codec, pin_nid, pin_nid); per_pin->codec = codec; INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 14feecf2d80..da9d2276e68 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -33,6 +33,7 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" +#include "hda_jack.h" /* unsol event tags */ #define ALC_FRONT_EVENT 0x01 @@ -664,6 +665,7 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) res >>= 28; else res >>= 26; + snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ switch (res) { case ALC_HP_EVENT: alc_hp_automute(codec); @@ -964,9 +966,7 @@ static void alc_init_automute(struct hda_codec *codec) continue; snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC_HP_EVENT); + snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT); spec->detect_hp = 1; } @@ -978,9 +978,8 @@ static void alc_init_automute(struct hda_codec *codec) continue; snd_printdd("realtek: Enable Line-Out " "auto-muting on NID 0x%x\n", nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC_FRONT_EVENT); + snd_hda_jack_detect_enable(codec, nid, + ALC_FRONT_EVENT); spec->detect_lo = 1; } spec->automute_lo_possible = spec->detect_hp; @@ -1108,13 +1107,10 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) return false; /* no corresponding imux */ } - snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC_MIC_EVENT); + snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT); if (spec->dock_mic_pin) - snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC_MIC_EVENT); + snd_hda_jack_detect_enable(codec, spec->dock_mic_pin, + ALC_MIC_EVENT); spec->auto_mic_valid_imux = 1; spec->auto_mic = 1; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3b4ef0cba0e..97c6df9db5e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -37,6 +37,7 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" +#include "hda_jack.h" enum { STAC_VREF_EVENT = 1, @@ -4244,9 +4245,7 @@ static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, if (tag < 0) return 0; } - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | tag); + snd_hda_jack_detect_enable(codec, nid, tag); return 1; } @@ -4795,24 +4794,11 @@ static void stac92xx_mic_detect(struct hda_codec *codec) mic->mux_idx); } -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_event *event = stac_get_event(codec, nid); - if (!event) - return; - codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26); -} - -static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) +static void handle_unsol_event(struct hda_codec *codec, + struct sigmatel_event *event) { struct sigmatel_spec *spec = codec->spec; - struct sigmatel_event *event; - int tag, data; - - tag = (res >> 26) & 0x7f; - event = stac_get_event_from_tag(codec, tag); - if (!event) - return; + int data; switch (event->type) { case STAC_HP_EVENT: @@ -4862,6 +4848,28 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) } } +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_event *event = stac_get_event(codec, nid); + if (!event) + return; + handle_unsol_event(codec, event); +} + +static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_event *event; + int tag; + + tag = (res >> 26) & 0x7f; + event = stac_get_event_from_tag(codec, tag); + if (!event) + return; + snd_hda_jack_set_dirty(codec, event->nid); + handle_unsol_event(codec, event); +} + static int hp_blike_system(u32 subsystem_id); static void set_hp_led_gpio(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 431c0d417ee..3467d0c23fe 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,6 +54,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_jack.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 @@ -1708,6 +1709,8 @@ static void via_gpio_control(struct hda_codec *codec) static void via_unsol_event(struct hda_codec *codec, unsigned int res) { + snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ + res >>= 26; if (res & VIA_JACK_EVENT) @@ -2729,9 +2732,8 @@ static void via_auto_init_unsol_event(struct hda_codec *codec) int i; if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) - snd_hda_codec_write(codec, cfg->hp_pins[0], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); + snd_hda_jack_detect_enable(codec, cfg->hp_pins[0], + VIA_HP_EVENT | VIA_JACK_EVENT); if (cfg->speaker_pins[0]) ev = VIA_LINE_EVENT; @@ -2740,16 +2742,14 @@ static void via_auto_init_unsol_event(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) { if (cfg->line_out_pins[i] && is_jack_detectable(codec, cfg->line_out_pins[i])) - snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ev | VIA_JACK_EVENT); + snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i], + ev | VIA_JACK_EVENT); } for (i = 0; i < cfg->num_inputs; i++) { if (is_jack_detectable(codec, cfg->inputs[i].pin)) - snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT); + snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin, + VIA_JACK_EVENT); } } @@ -2781,6 +2781,7 @@ static void vt1708_update_hp_jack_state(struct work_struct *work) vt1708_hp_work.work); if (spec->codec_type != VT1708) return; + snd_hda_jack_set_dirty_all(spec->codec); /* if jack state toggled */ if (spec->vt1708_hp_present != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { From 01a61e12b4602c82bde9797d0e153f3e53c95b04 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 28 Oct 2011 00:03:22 +0200 Subject: [PATCH 03/15] ALSA: hda - Create jack-detection kcontrols Create kcontrols for pin jack-detections, which work similarly like jack-input layer. Each control will notify when the jack is plugged or unplugged, and also user can read the value at any time via the normal control API. The control elements are created with iface=CARD, so that they won't appear in the mixer apps. So far, only the pins that enabled the jack-detection are registered. For covering all pins, the transition of the common unsol-tag handling would be needed. Stay tuned. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 168 ++++++++++++++++++++++++++++++++- sound/pci/hda/hda_jack.h | 12 +++ sound/pci/hda/patch_cirrus.c | 27 +++++- sound/pci/hda/patch_conexant.c | 2 + sound/pci/hda/patch_hdmi.c | 6 ++ sound/pci/hda/patch_realtek.c | 7 ++ sound/pci/hda/patch_sigmatel.c | 7 ++ sound/pci/hda/patch_via.c | 7 ++ 8 files changed, 232 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 64b78a2e20e..cee6a00bd85 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -76,9 +77,13 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) { - if (jack->jack_dirty) { - jack->pin_sense = read_pin_sense(codec, jack->nid); + if (jack->jack_dirty || !jack->jack_cachable) { + unsigned int val = read_pin_sense(codec, jack->nid); jack->jack_dirty = 0; + if (val != jack->pin_sense) { + jack->need_notify = 1; + jack->pin_sense = val; + } } } @@ -141,8 +146,167 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) return -ENOMEM; + if (jack->jack_cachable) + return 0; /* already registered */ + jack->jack_cachable = 1; return snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | tag); } EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); + +/* queue the notification when needed */ +static void jack_detect_report(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + jack_detect_update(codec, jack); + if (jack->need_notify) { + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &jack->kctl->id); + jack->need_notify = 0; + } +} + +/** + * snd_hda_jack_report - notify kctl when the jack state was changed + */ +void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + + if (jack) + jack_detect_report(codec, jack); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_report); + +/** + * 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; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid) { + jack_detect_update(codec, jack); + jack_detect_report(codec, jack); + } +} +EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync); + +/* + * jack-detection kcontrols + */ + +#define jack_detect_kctl_info snd_ctl_boolean_mono_info + +static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + + ucontrol->value.integer.value[0] = snd_hda_jack_detect(codec, nid); + return 0; +} + +static struct snd_kcontrol_new jack_detect_kctl = { + /* name is filled later */ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = jack_detect_kctl_info, + .get = jack_detect_kctl_get, +}; + +/** + * snd_hda_jack_add_kctl - Add a kctl for the given pin + * + * This assigns a jack-detection kctl to the given pin. The kcontrol + * will have the given name and index. + */ +int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, + const char *name, int idx) +{ + struct hda_jack_tbl *jack; + struct snd_kcontrol *kctl; + + jack = snd_hda_jack_tbl_get(codec, nid); + if (!jack) + return 0; + if (jack->kctl) + return 0; /* already created */ + kctl = snd_ctl_new1(&jack_detect_kctl, codec); + if (!kctl) + return -ENOMEM; + snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name); + kctl->id.index = idx; + kctl->private_value = nid; + if (snd_hda_ctl_add(codec, nid, kctl) < 0) + return -ENOMEM; + jack->kctl = kctl; + return 0; +} + +static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx, + const struct auto_pin_cfg *cfg) +{ + if (!nid) + return 0; + if (!is_jack_detectable(codec, nid)) + return 0; + return snd_hda_jack_add_kctl(codec, nid, + snd_hda_get_pin_label(codec, nid, cfg), + idx); +} + +/** + * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg + * + * As of now, it assigns only to the pins that enabled the detection. + * Usually this is called at the end of build_controls callback. + */ +int snd_hda_jack_add_kctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + const hda_nid_t *p; + int i, err; + + for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { + err = add_jack_kctl(codec, *p, i, cfg); + if (err < 0) + return err; + } + for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { + if (*p == *cfg->line_out_pins) /* might be duplicated */ + break; + err = add_jack_kctl(codec, *p, i, cfg); + if (err < 0) + return err; + } + for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { + if (*p == *cfg->line_out_pins) /* might be duplicated */ + break; + err = add_jack_kctl(codec, *p, i, cfg); + if (err < 0) + return err; + } + for (i = 0; i < cfg->num_inputs; i++) { + err = add_jack_kctl(codec, cfg->inputs[i].pin, 0, cfg); + if (err < 0) + return err; + } + for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { + err = add_jack_kctl(codec, *p, i, cfg); + if (err < 0) + return err; + } + err = add_jack_kctl(codec, cfg->dig_in_pin, 0, cfg); + if (err < 0) + return err; + err = add_jack_kctl(codec, cfg->mono_out_pin, 0, cfg); + if (err < 0) + return err; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 5c1bcb8cf56..b5983eaea51 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -15,7 +15,10 @@ struct hda_jack_tbl { hda_nid_t nid; unsigned int pin_sense; /* cached pin-sense value */ + unsigned int jack_cachable:1; /* can be updated via unsol events */ unsigned int jack_dirty:1; /* needs to update? */ + unsigned int need_notify:1; /* to be notified? */ + struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ }; struct hda_jack_tbl * @@ -60,4 +63,13 @@ static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) return true; } +int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, + const char *name, int idx); +int snd_hda_jack_add_kctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg); + +void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid); +void snd_hda_jack_report_sync(struct hda_codec *codec); + + #endif /* __SOUND_HDA_JACK_H */ diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 6f158777f71..135fd49cd49 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1192,11 +1192,14 @@ static int cs_init(struct hda_codec *codec) init_output(codec); init_input(codec); init_digital(codec); + snd_hda_jack_report_sync(codec); + return 0; } static int cs_build_controls(struct hda_codec *codec) { + struct cs_spec *spec = codec->spec; int err; err = build_output(codec); @@ -1211,7 +1214,15 @@ static int cs_build_controls(struct hda_codec *codec) err = build_digital_input(codec); if (err < 0) return err; - return cs_init(codec); + err = cs_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + + return 0; } static void cs_free(struct hda_codec *codec) @@ -1234,6 +1245,7 @@ static void cs_unsol_event(struct hda_codec *codec, unsigned int res) cs_automic(codec); break; } + snd_hda_jack_report_sync(codec); } static const struct hda_codec_ops cs_patch_ops = { @@ -1611,6 +1623,7 @@ static int cs421x_init(struct hda_codec *codec) init_output(codec); init_input(codec); init_cs421x_digital(codec); + snd_hda_jack_report_sync(codec); return 0; } @@ -1786,6 +1799,7 @@ static int build_cs421x_output(struct hda_codec *codec) static int cs421x_build_controls(struct hda_codec *codec) { + struct cs_spec *spec = codec->spec; int err; err = build_cs421x_output(codec); @@ -1797,7 +1811,15 @@ static int cs421x_build_controls(struct hda_codec *codec) err = build_digital_output(codec); if (err < 0) return err; - return cs421x_init(codec); + err = cs421x_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + + return 0; } static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1814,6 +1836,7 @@ static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) cs_automic(codec); break; } + snd_hda_jack_report_sync(codec); } static int parse_cs421x_input(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 220e567ccff..25fdd1e9561 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3770,6 +3770,7 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_input_jack_report(codec, nid); break; } + snd_hda_jack_report_sync(codec); } /* check whether the pin config is suitable for auto-mic switching; @@ -4095,6 +4096,7 @@ static int cx_auto_init(struct hda_codec *codec) cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); + snd_hda_jack_report_sync(codec); return 0; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ea6d85d4844..f01c5efde8e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -769,6 +769,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_set_dirty(codec, pin_nid); hdmi_present_sense(&spec->pins[pin_idx], true); + snd_hda_jack_report_sync(codec); } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -1268,6 +1269,10 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; + err = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, + "HDMI", pin_idx); + if (err < 0) + return err; } return 0; @@ -1290,6 +1295,7 @@ static int generic_hdmi_init(struct hda_codec *codec) INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); snd_hda_eld_proc_new(codec, eld, pin_idx); } + snd_hda_jack_report_sync(codec); return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index da9d2276e68..04beae034fe 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -677,6 +677,7 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) alc_mic_automute(codec); break; } + snd_hda_jack_report_sync(codec); } /* call init functions of standard auto-mute helpers */ @@ -2054,6 +2055,10 @@ static int alc_build_controls(struct hda_codec *codec) alc_free_kctls(codec); /* no longer needed */ + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } @@ -2081,6 +2086,8 @@ static int alc_init(struct hda_codec *codec) alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); + snd_hda_jack_report_sync(codec); + hda_call_check_power_status(codec, 0x01); return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 97c6df9db5e..90954b8269c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1212,6 +1212,10 @@ static int stac92xx_build_controls(struct hda_codec *codec) return err; } + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } @@ -4473,6 +4477,8 @@ static int stac92xx_init(struct hda_codec *codec) stac_toggle_power_map(codec, nid, 0); } + snd_hda_jack_report_sync(codec); + /* sync mute LED */ if (spec->gpio_led) hda_call_check_power_status(codec, 0x01); @@ -4868,6 +4874,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) return; snd_hda_jack_set_dirty(codec, event->nid); handle_unsol_event(codec, event); + snd_hda_jack_report_sync(codec); } static int hp_blike_system(u32 subsystem_id); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 3467d0c23fe..852939658dd 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1500,6 +1500,11 @@ static int via_build_controls(struct hda_codec *codec) analog_low_current_mode(codec); via_free_kctls(codec); /* no longer needed */ + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } @@ -1722,6 +1727,7 @@ static void via_unsol_event(struct hda_codec *codec, via_hp_automute(codec); else if (res == VIA_GPIO_EVENT) via_gpio_control(codec); + snd_hda_jack_report_sync(codec); } #ifdef CONFIG_PM @@ -2771,6 +2777,7 @@ static int via_init(struct hda_codec *codec) via_auto_init_unsol_event(codec); via_hp_automute(codec); + snd_hda_jack_report_sync(codec); return 0; } From 3a93897ea37cbb8277f8a4232c12c0c18168a7db Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 28 Oct 2011 01:16:55 +0200 Subject: [PATCH 04/15] ALSA: hda - Manage unsol tags in hda_jack.c Manage the tags assigned for unsolicited events dynamically together with the jack-detection routines. Basically this is almost same as what we've done in patch_sigmatel.c. Assign the new tag number for each new unsol event, associate with the given NID and the action type, etc. With this change, now all pins looked over in snd_hda_jack_add_kctls() are actually enabled for detection now even if the pins aren't used for jack-retasking by the driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 61 ++++++++++++------- sound/pci/hda/hda_jack.h | 28 +++++---- sound/pci/hda/patch_cirrus.c | 8 +-- sound/pci/hda/patch_conexant.c | 8 +-- sound/pci/hda/patch_hdmi.c | 13 ++++- sound/pci/hda/patch_realtek.c | 6 +- sound/pci/hda/patch_sigmatel.c | 104 +++++++++------------------------ sound/pci/hda/patch_via.c | 3 +- 8 files changed, 105 insertions(+), 126 deletions(-) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index cee6a00bd85..8829d5c83fe 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -50,6 +50,24 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get); +/** + * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag + */ +struct hda_jack_tbl * +snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!tag || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->tag == tag) + return jack; + return NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get_from_tag); + /** * snd_hda_jack_tbl_new - create a jack-table entry for the given NID */ @@ -65,6 +83,7 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) return NULL; jack->nid = nid; jack->jack_dirty = 1; + jack->tag = codec->jacktbl.used; return jack; } @@ -77,7 +96,7 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) { - if (jack->jack_dirty || !jack->jack_cachable) { + if (jack->jack_dirty || !jack->jack_detect) { unsigned int val = read_pin_sense(codec, jack->nid); jack->jack_dirty = 0; if (val != jack->pin_sense) { @@ -141,17 +160,19 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect); * snd_hda_jack_detect_enable - enable the jack-detection */ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned int tag) + unsigned char action) { struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) return -ENOMEM; - if (jack->jack_cachable) + if (jack->jack_detect) return 0; /* already registered */ - jack->jack_cachable = 1; + jack->jack_detect = 1; + if (action) + jack->action = action; return snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | tag); + AC_USRSP_EN | jack->tag); } EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); @@ -167,18 +188,6 @@ static void jack_detect_report(struct hda_codec *codec, } } -/** - * snd_hda_jack_report - notify kctl when the jack state was changed - */ -void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); - - if (jack) - jack_detect_report(codec, jack); -} -EXPORT_SYMBOL_HDA(snd_hda_jack_report); - /** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed */ @@ -231,7 +240,7 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, struct hda_jack_tbl *jack; struct snd_kcontrol *kctl; - jack = snd_hda_jack_tbl_get(codec, nid); + jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) return 0; if (jack->kctl) @@ -251,20 +260,28 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx, const struct auto_pin_cfg *cfg) { + unsigned int def_conf, conn; + int err; + if (!nid) return 0; if (!is_jack_detectable(codec, nid)) return 0; - return snd_hda_jack_add_kctl(codec, nid, + def_conf = snd_hda_codec_get_pincfg(codec, nid); + conn = get_defcfg_connect(def_conf); + if (conn != AC_JACK_PORT_COMPLEX) + return 0; + + err = snd_hda_jack_add_kctl(codec, nid, snd_hda_get_pin_label(codec, nid, cfg), idx); + if (err < 0) + return err; + return snd_hda_jack_detect_enable(codec, nid, 0); } /** * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg - * - * As of now, it assigns only to the pins that enabled the detection. - * Usually this is called at the end of build_controls callback. */ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index b5983eaea51..69a67f8e4f4 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -14,8 +14,12 @@ struct hda_jack_tbl { hda_nid_t nid; + unsigned char action; /* event action (0 = none) */ + unsigned char tag; /* unsol event tag */ + unsigned int private_data; /* arbitrary data */ + /* jack-detection stuff */ unsigned int pin_sense; /* cached pin-sense value */ - unsigned int jack_cachable:1; /* can be updated via unsol events */ + unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ unsigned int need_notify:1; /* to be notified? */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ @@ -23,29 +27,34 @@ struct hda_jack_tbl { struct hda_jack_tbl * snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); +struct hda_jack_tbl * +snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag); struct hda_jack_tbl * snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid); void snd_hda_jack_tbl_clear(struct hda_codec *codec); /** - * snd_hda_jack_set_dirty - set the dirty flag for the given jack-entry + * snd_hda_jack_get_action - get jack-tbl entry for the tag * - * Call this function when a pin-state may change, e.g. when the hardware - * notifies via an unsolicited event. + * Call this from the unsol event handler to get the assigned action for the + * event. This will mark the dirty flag for the later reporting, too. */ -static inline void snd_hda_jack_set_dirty(struct hda_codec *codec, - hda_nid_t nid) +static inline unsigned char +snd_hda_jack_get_action(struct hda_codec *codec, unsigned int tag) { - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); - if (jack) + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_from_tag(codec, tag); + if (jack) { jack->jack_dirty = 1; + return jack->action; + } + return 0; } void snd_hda_jack_set_dirty_all(struct hda_codec *codec); int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned int tag); + unsigned char action); 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); @@ -68,7 +77,6 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg); -void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid); void snd_hda_jack_report_sync(struct hda_codec *codec); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 135fd49cd49..0e34554bc45 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1235,9 +1235,7 @@ static void cs_free(struct hda_codec *codec) static void cs_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ - - switch ((res >> 26) & 0x7f) { + switch (snd_hda_jack_get_action(codec, res >> 26)) { case HP_EVENT: cs_automute(codec); break; @@ -1824,9 +1822,7 @@ static int cs421x_build_controls(struct hda_codec *codec) static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ - - switch ((res >> 26) & 0x3f) { + switch (snd_hda_jack_get_action(codec, res >> 26)) { case HP_EVENT: case SPDIF_EVENT: cs_automute(codec); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 25fdd1e9561..40bd75b293b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3757,8 +3757,8 @@ static void cx_auto_automic(struct hda_codec *codec) static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) { int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; - snd_hda_jack_set_dirty(codec, nid); - switch (res >> 26) { + + switch (snd_hda_jack_get_action(codec, res >> 26)) { case CONEXANT_HP_EVENT: cx_auto_hp_automute(codec); break; @@ -3982,11 +3982,11 @@ static void mute_outputs(struct hda_codec *codec, int num_nids, } static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int tag) + hda_nid_t *pins, unsigned int action) { int i; for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable(codec, pins[i], tag); + snd_hda_jack_detect_enable(codec, pins[i], action); } static void cx_auto_init_output(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index f01c5efde8e..ea30bf4a4f5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -754,10 +754,18 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, bool retry); static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { struct hdmi_spec *spec = codec->spec; - int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int pin_nid; int pd = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); int pin_idx; + struct hda_jack_tbl *jack; + + jack = snd_hda_jack_tbl_get_from_tag(codec, tag); + if (!jack) + return; + pin_nid = jack->nid; + jack->jack_dirty = 1; printk(KERN_INFO "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", @@ -767,7 +775,6 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) if (pin_idx < 0) return; - snd_hda_jack_set_dirty(codec, pin_nid); hdmi_present_sense(&spec->pins[pin_idx], true); snd_hda_jack_report_sync(codec); } @@ -801,7 +808,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (pin_nid_to_pin_index(spec, tag) < 0) { + if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04beae034fe..9a90cdac78f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -185,6 +185,7 @@ struct alc_spec { unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ + unsigned int use_jack_tbl:1; /* 1 for model=auto */ /* auto-mute control */ int automute_mode; @@ -661,11 +662,13 @@ static void alc_mic_automute(struct hda_codec *codec) /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { + struct alc_spec *spec = codec->spec; if (codec->vendor_id == 0x10ec0880) res >>= 28; else res >>= 26; - snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ + if (spec->use_jack_tbl) + res = snd_hda_jack_get_action(codec, res); switch (res) { case ALC_HP_EVENT: alc_hp_automute(codec); @@ -3896,6 +3899,7 @@ static void set_capture_mixer(struct hda_codec *codec) static void alc_auto_init_std(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + spec->use_jack_tbl = 1; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 90954b8269c..dd6569ffcff 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -177,13 +177,6 @@ enum { STAC_9872_MODELS }; -struct sigmatel_event { - hda_nid_t nid; - unsigned char type; - unsigned char tag; - int data; -}; - struct sigmatel_mic_route { hda_nid_t pin; signed char mux_idx; @@ -231,9 +224,6 @@ struct sigmatel_spec { const hda_nid_t *pwr_nids; const hda_nid_t *dac_list; - /* events */ - struct snd_array events; - /* playback */ struct hda_input_mux *mono_mux; unsigned int cur_mmux; @@ -4182,49 +4172,18 @@ static int stac92xx_add_jack(struct hda_codec *codec, #endif /* CONFIG_SND_HDA_INPUT_JACK */ } -static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid, +static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, unsigned char type, int data) { - struct sigmatel_event *event; + struct hda_jack_tbl *event; - snd_array_init(&spec->events, sizeof(*event), 32); - event = snd_array_new(&spec->events); + event = snd_hda_jack_tbl_new(codec, nid); if (!event) return -ENOMEM; - event->nid = nid; - event->type = type; - event->tag = spec->events.used; - event->data = data; + event->action = type; + event->private_data = data; - return event->tag; -} - -static struct sigmatel_event *stac_get_event(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_event *event = spec->events.list; - int i; - - for (i = 0; i < spec->events.used; i++, event++) { - if (event->nid == nid) - return event; - } - return NULL; -} - -static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec, - unsigned char tag) -{ - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_event *event = spec->events.list; - int i; - - for (i = 0; i < spec->events.used; i++, event++) { - if (event->tag == tag) - return event; - } - return NULL; + return 0; } /* check if given nid is a valid pin and no other events are assigned @@ -4234,22 +4193,17 @@ static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec, static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, unsigned int type) { - struct sigmatel_event *event; - int tag; + struct hda_jack_tbl *event; if (!is_jack_detectable(codec, nid)) return 0; - event = stac_get_event(codec, nid); - if (event) { - if (event->type != type) - return 0; - tag = event->tag; - } else { - tag = stac_add_event(codec->spec, nid, type, 0); - if (tag < 0) - return 0; - } - snd_hda_jack_detect_enable(codec, nid, tag); + event = snd_hda_jack_tbl_new(codec, nid); + if (!event) + return -ENOMEM; + if (event->action && event->action != type) + return 0; + event->action = type; + snd_hda_jack_detect_enable(codec, nid, 0); return 1; } @@ -4536,7 +4490,6 @@ static void stac92xx_free(struct hda_codec *codec) stac92xx_shutup(codec); snd_hda_input_jack_free(codec); - snd_array_free(&spec->events); kfree(spec); snd_hda_detach_beep_device(codec); @@ -4801,12 +4754,12 @@ static void stac92xx_mic_detect(struct hda_codec *codec) } static void handle_unsol_event(struct hda_codec *codec, - struct sigmatel_event *event) + struct hda_jack_tbl *event) { struct sigmatel_spec *spec = codec->spec; int data; - switch (event->type) { + switch (event->action) { case STAC_HP_EVENT: case STAC_LO_EVENT: stac92xx_hp_detect(codec); @@ -4816,7 +4769,7 @@ static void handle_unsol_event(struct hda_codec *codec, break; } - switch (event->type) { + switch (event->action) { case STAC_HP_EVENT: case STAC_LO_EVENT: case STAC_MIC_EVENT: @@ -4849,14 +4802,14 @@ static void handle_unsol_event(struct hda_codec *codec, AC_VERB_GET_GPIO_DATA, 0); /* toggle VREF state based on GPIOx status */ snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, - !!(data & (1 << event->data))); + !!(data & (1 << event->private_data))); break; } } static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) { - struct sigmatel_event *event = stac_get_event(codec, nid); + struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid); if (!event) return; handle_unsol_event(codec, event); @@ -4864,15 +4817,14 @@ static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) { - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_event *event; + struct hda_jack_tbl *event; int tag; tag = (res >> 26) & 0x7f; - event = stac_get_event_from_tag(codec, tag); + event = snd_hda_jack_tbl_get_from_tag(codec, tag); if (!event) return; - snd_hda_jack_set_dirty(codec, event->nid); + event->jack_dirty = 1; handle_unsol_event(codec, event); snd_hda_jack_report_sync(codec); } @@ -5857,15 +5809,13 @@ again: switch (spec->board_config) { case STAC_HP_M4: /* Enable VREF power saving on GPIO1 detect */ - err = stac_add_event(spec, codec->afg, + err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x02); if (err < 0) return err; snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | err); + snd_hda_jack_detect_enable(codec, codec->afg, 0); spec->gpio_mask |= 0x02; break; } @@ -6338,14 +6288,12 @@ static int patch_stac9205(struct hda_codec *codec) snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); /* Enable unsol response for GPIO4/Dock HP connection */ - err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01); + err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); if (err < 0) return err; snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | err); + snd_hda_jack_detect_enable(codec, codec->afg, 0); spec->gpio_dir = 0x0b; spec->eapd_mask = 0x01; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 852939658dd..f73c98638ab 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1714,9 +1714,8 @@ static void via_gpio_control(struct hda_codec *codec) static void via_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */ - res >>= 26; + res = snd_hda_jack_get_action(codec, res); if (res & VIA_JACK_EVENT) set_widgets_power_state(codec); From 35be544af367170a9c6bf63adcf9d0cb2d569dbb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 2 Nov 2011 08:36:06 +0100 Subject: [PATCH 05/15] ALSA: Introduce common helper functions for jack-detection control Now move the helper function for creating and reporting the jack-detection to the common place. The driver that needs this functionality should select CONFIG_SND_KCTL_JACK kconfig. Signed-off-by: Takashi Iwai --- include/sound/control.h | 8 ++++++ sound/core/Kconfig | 3 +++ sound/core/Makefile | 1 + sound/core/ctljack.c | 55 +++++++++++++++++++++++++++++++++++++++ sound/pci/hda/Kconfig | 1 + sound/pci/hda/hda_jack.c | 56 ++++++---------------------------------- sound/pci/hda/hda_jack.h | 1 - 7 files changed, 76 insertions(+), 49 deletions(-) create mode 100644 sound/core/ctljack.c diff --git a/include/sound/control.h b/include/sound/control.h index 1a94a216ed9..b2796e83c7a 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -227,4 +227,12 @@ snd_ctl_add_slave_uncached(struct snd_kcontrol *master, return _snd_ctl_add_slave(master, slave, SND_CTL_SLAVE_NEED_UPDATE); } +/* + * Helper functions for jack-detection controls + */ +struct snd_kcontrol * +snd_kctl_jack_new(const char *name, int idx, void *private_data); +void snd_kctl_jack_report(struct snd_card *card, + struct snd_kcontrol *kctl, bool status); + #endif /* __SOUND_CONTROL_H */ diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 475455c7661..66f287f2f75 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -207,6 +207,9 @@ config SND_PCM_XRUN_DEBUG config SND_VMASTER bool +config SND_KCTL_JACK + bool + config SND_DMA_SGBUF def_bool y depends on X86 diff --git a/sound/core/Makefile b/sound/core/Makefile index 350a08d277f..b4637c3e776 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -7,6 +7,7 @@ snd-y := sound.o init.o memory.o info.o control.o misc.o device.o snd-$(CONFIG_ISA_DMA_API) += isadma.o snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o snd-$(CONFIG_SND_VMASTER) += vmaster.o +snd-$(CONFIG_SND_KCTL_JACK) += ctljack.o snd-$(CONFIG_SND_JACK) += jack.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c new file mode 100644 index 00000000000..af0e78a3809 --- /dev/null +++ b/sound/core/ctljack.c @@ -0,0 +1,55 @@ +/* + * Helper functions for jack-detection kcontrols + * + * Copyright (c) 2011 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include + +#define jack_detect_kctl_info snd_ctl_boolean_mono_info + +static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = kcontrol->private_value; + return 0; +} + +static struct snd_kcontrol_new jack_detect_kctl = { + /* name is filled later */ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = jack_detect_kctl_info, + .get = jack_detect_kctl_get, +}; + +struct snd_kcontrol * +snd_kctl_jack_new(const char *name, int idx, void *private_data) +{ + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(&jack_detect_kctl, private_data); + if (!kctl) + return NULL; + snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name); + kctl->id.index = idx; + kctl->private_value = 0; + return kctl; +} +EXPORT_SYMBOL_GPL(snd_kctl_jack_new); + +void snd_kctl_jack_report(struct snd_card *card, + struct snd_kcontrol *kctl, bool status) +{ + if (kctl->private_value == status) + return; + kctl->private_value = status; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} +EXPORT_SYMBOL_GPL(snd_kctl_jack_report); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index bb7e102d672..163b6b5de3e 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -2,6 +2,7 @@ menuconfig SND_HDA_INTEL tristate "Intel HD Audio" select SND_PCM select SND_VMASTER + select SND_KCTL_JACK help Say Y here to include support for Intel "High Definition Audio" (Azalia) and its compatible devices. diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 8829d5c83fe..a2ab52b2726 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -97,12 +97,8 @@ static void jack_detect_update(struct hda_codec *codec, struct hda_jack_tbl *jack) { if (jack->jack_dirty || !jack->jack_detect) { - unsigned int val = read_pin_sense(codec, jack->nid); + jack->pin_sense = read_pin_sense(codec, jack->nid); jack->jack_dirty = 0; - if (val != jack->pin_sense) { - jack->need_notify = 1; - jack->pin_sense = val; - } } } @@ -142,6 +138,8 @@ 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 @@ -152,7 +150,7 @@ EXPORT_SYMBOL_HDA(snd_hda_pin_sense); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) { u32 sense = snd_hda_pin_sense(codec, nid); - return !!(sense & AC_PINSENSE_PRESENCE); + return get_jack_plug_state(sense); } EXPORT_SYMBOL_HDA(snd_hda_jack_detect); @@ -176,58 +174,23 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); -/* queue the notification when needed */ -static void jack_detect_report(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - jack_detect_update(codec, jack); - if (jack->need_notify) { - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &jack->kctl->id); - jack->need_notify = 0; - } -} - /** * 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; - int i; + int i, state; for (i = 0; i < codec->jacktbl.used; i++, jack++) if (jack->nid) { jack_detect_update(codec, jack); - jack_detect_report(codec, jack); + state = get_jack_plug_state(jack->pin_sense); + snd_kctl_jack_notify(codec->bus->card, jack->kctl, state); } } EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync); -/* - * jack-detection kcontrols - */ - -#define jack_detect_kctl_info snd_ctl_boolean_mono_info - -static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - - ucontrol->value.integer.value[0] = snd_hda_jack_detect(codec, nid); - return 0; -} - -static struct snd_kcontrol_new jack_detect_kctl = { - /* name is filled later */ - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .info = jack_detect_kctl_info, - .get = jack_detect_kctl_get, -}; - /** * snd_hda_jack_add_kctl - Add a kctl for the given pin * @@ -245,12 +208,9 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; if (jack->kctl) return 0; /* already created */ - kctl = snd_ctl_new1(&jack_detect_kctl, codec); + kctl = snd_kctl_jack_new(name, idx, codec); if (!kctl) return -ENOMEM; - snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name); - kctl->id.index = idx; - kctl->private_value = nid; if (snd_hda_ctl_add(codec, nid, kctl) < 0) return -ENOMEM; jack->kctl = kctl; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 69a67f8e4f4..4bb75ee16b5 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -21,7 +21,6 @@ struct hda_jack_tbl { unsigned int pin_sense; /* cached pin-sense value */ unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ - unsigned int need_notify:1; /* to be notified? */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ }; From aad37dbd563010252e1bedb6dad6cddb867b9235 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 2 Nov 2011 08:54:51 +0100 Subject: [PATCH 06/15] ALSA: hda - Merge input-jack helpers to hda_jack.c We can use the very same table in hda_jack.c for managing the list for input-jack elements, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 108 --------------------------------- sound/pci/hda/hda_jack.c | 97 ++++++++++++++++++++++++++++- sound/pci/hda/hda_jack.h | 4 ++ sound/pci/hda/hda_local.h | 4 -- sound/pci/hda/patch_conexant.c | 1 - sound/pci/hda/patch_hdmi.c | 1 - sound/pci/hda/patch_realtek.c | 1 - sound/pci/hda/patch_sigmatel.c | 1 - 8 files changed, 100 insertions(+), 117 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8217ff7ff7b..e57698f611a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5275,113 +5275,5 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen) } EXPORT_SYMBOL_HDA(snd_print_pcm_bits); -#ifdef CONFIG_SND_HDA_INPUT_JACK -/* - * Input-jack notification support - */ -struct hda_jack_item { - hda_nid_t nid; - int type; - struct snd_jack *jack; -}; - -static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid, - int type) -{ - switch (type) { - case SND_JACK_HEADPHONE: - return "Headphone"; - case SND_JACK_MICROPHONE: - return "Mic"; - case SND_JACK_LINEOUT: - return "Line-out"; - case SND_JACK_LINEIN: - return "Line-in"; - case SND_JACK_HEADSET: - return "Headset"; - case SND_JACK_VIDEOOUT: - return "HDMI/DP"; - default: - return "Misc"; - } -} - -static void hda_free_jack_priv(struct snd_jack *jack) -{ - struct hda_jack_item *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} - -int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, - const char *name) -{ - struct hda_jack_item *jack; - int err; - - snd_array_init(&codec->jacks, sizeof(*jack), 32); - jack = snd_array_new(&codec->jacks); - if (!jack) - return -ENOMEM; - - jack->nid = nid; - jack->type = type; - if (!name) - name = get_jack_default_name(codec, nid, type); - err = snd_jack_new(codec->bus->card, name, type, &jack->jack); - if (err < 0) { - jack->nid = 0; - return err; - } - jack->jack->private_data = jack; - jack->jack->private_free = hda_free_jack_priv; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_input_jack_add); - -void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_item *jacks = codec->jacks.list; - int i; - - if (!jacks) - return; - - for (i = 0; i < codec->jacks.used; i++, jacks++) { - unsigned int pin_ctl; - unsigned int present; - int type; - - if (jacks->nid != nid) - continue; - present = snd_hda_jack_detect(codec, nid); - type = jacks->type; - if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) { - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - type = (pin_ctl & AC_PINCTL_HP_EN) ? - SND_JACK_HEADPHONE : SND_JACK_LINEOUT; - } - snd_jack_report(jacks->jack, present ? type : 0); - } -} -EXPORT_SYMBOL_HDA(snd_hda_input_jack_report); - -/* free jack instances manually when clearing/reconfiguring */ -void snd_hda_input_jack_free(struct hda_codec *codec) -{ - if (!codec->bus->shutdown && codec->jacks.list) { - struct hda_jack_item *jacks = codec->jacks.list; - int i; - for (i = 0; i < codec->jacks.used; i++, jacks++) { - if (jacks->jack) - snd_device_free(codec->bus->card, jacks->jack); - } - } - snd_array_free(&codec->jacks); -} -EXPORT_SYMBOL_HDA(snd_hda_input_jack_free); -#endif /* CONFIG_SND_HDA_INPUT_JACK */ - MODULE_DESCRIPTION("HDA codec core"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index a2ab52b2726..1389958b201 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -87,8 +88,15 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) return jack; } +#ifdef CONFIG_SND_HDA_INPUT_JACK +static void snd_hda_input_jack_free(struct hda_codec *codec); +#else +#define snd_hda_input_jack_free(codec) +#endif + void snd_hda_jack_tbl_clear(struct hda_codec *codec) { + snd_hda_input_jack_free(codec); snd_array_free(&codec->jacktbl); } @@ -186,7 +194,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (jack->nid) { jack_detect_update(codec, jack); state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_notify(codec->bus->card, jack->kctl, state); + snd_kctl_jack_report(codec->bus->card, jack->kctl, state); } } EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync); @@ -287,3 +295,90 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, return 0; } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); + +#ifdef CONFIG_SND_HDA_INPUT_JACK +/* + * Input-jack notification support + */ +static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid, + int type) +{ + switch (type) { + case SND_JACK_HEADPHONE: + return "Headphone"; + case SND_JACK_MICROPHONE: + return "Mic"; + case SND_JACK_LINEOUT: + return "Line-out"; + case SND_JACK_LINEIN: + return "Line-in"; + case SND_JACK_HEADSET: + return "Headset"; + case SND_JACK_VIDEOOUT: + return "HDMI/DP"; + default: + return "Misc"; + } +} + +static void hda_free_jack_priv(struct snd_jack *jack) +{ + struct hda_jack_tbl *jacks = jack->private_data; + jacks->nid = 0; + jacks->jack = NULL; +} + +int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, + const char *name) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); + int err; + + if (!jack) + return -ENOMEM; + if (!name) + name = get_jack_default_name(codec, nid, type); + err = snd_jack_new(codec->bus->card, name, type, &jack->jack); + if (err < 0) + return err; + jack->jack->private_data = jack; + jack->jack->private_free = hda_free_jack_priv; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_input_jack_add); + +void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + unsigned int pin_ctl; + unsigned int present; + int type; + + if (!jack) + return; + + present = snd_hda_jack_detect(codec, nid); + type = jack->type; + if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) { + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + type = (pin_ctl & AC_PINCTL_HP_EN) ? + SND_JACK_HEADPHONE : SND_JACK_LINEOUT; + } + snd_jack_report(jack->jack, present ? type : 0); +} +EXPORT_SYMBOL_HDA(snd_hda_input_jack_report); + +/* free jack instances manually when clearing/reconfiguring */ +static void snd_hda_input_jack_free(struct hda_codec *codec) +{ + if (!codec->bus->shutdown && codec->jacktbl.list) { + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (jack->jack) + snd_device_free(codec->bus->card, jack->jack); + } + } +} +#endif /* CONFIG_SND_HDA_INPUT_JACK */ diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 4bb75ee16b5..f8f97c71c9c 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -22,6 +22,10 @@ struct hda_jack_tbl { unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ +#ifdef CONFIG_SND_HDA_INPUT_JACK + int type; + struct snd_jack *jack; +#endif }; struct hda_jack_tbl * diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 08e88b826d2..13f681480a3 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -680,7 +680,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, const char *name); void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid); -void snd_hda_input_jack_free(struct hda_codec *codec); #else /* CONFIG_SND_HDA_INPUT_JACK */ static inline int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, @@ -692,9 +691,6 @@ static inline void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) { } -static inline void snd_hda_input_jack_free(struct hda_codec *codec) -{ -} #endif /* CONFIG_SND_HDA_INPUT_JACK */ #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 40bd75b293b..ae9c028d825 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -475,7 +475,6 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { - snd_hda_input_jack_free(codec); snd_hda_detach_beep_device(codec); kfree(codec->spec); } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ea30bf4a4f5..bb8cfc68cd7 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1318,7 +1318,6 @@ static void generic_hdmi_free(struct hda_codec *codec) cancel_delayed_work(&per_pin->work); snd_hda_eld_proc_free(codec, eld); } - snd_hda_input_jack_free(codec); flush_workqueue(codec->bus->workq); kfree(spec); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a90cdac78f..933c8cf23b2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2474,7 +2474,6 @@ static void alc_free(struct hda_codec *codec) return; alc_shutup(codec); - snd_hda_input_jack_free(codec); alc_free_kctls(codec); alc_free_bind_ctls(codec); kfree(spec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index dd6569ffcff..73bf7cd0a6e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4489,7 +4489,6 @@ static void stac92xx_free(struct hda_codec *codec) return; stac92xx_shutup(codec); - snd_hda_input_jack_free(codec); kfree(spec); snd_hda_detach_beep_device(codec); From d1cb620081f51c78cf95224efb593a886875078f Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 11 Nov 2011 17:13:15 +0100 Subject: [PATCH 07/15] ALSA: HDA: Jack: Export required functions from hda_jack.c These two functions are being used by the codec-idt and codec-hdmi modules, so they need to be exported properly. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 1389958b201..eac002d01fd 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -87,6 +87,7 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) jack->tag = codec->jacktbl.used; return jack; } +EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_new); #ifdef CONFIG_SND_HDA_INPUT_JACK static void snd_hda_input_jack_free(struct hda_codec *codec); @@ -224,6 +225,7 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, jack->kctl = kctl; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx, const struct auto_pin_cfg *cfg) From cfc7c9d307b6a3557e333f960218d344d3a70ce7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 Nov 2011 17:53:03 +0100 Subject: [PATCH 08/15] ALSA: hda/jack - Fix NULL-dereference at probing At probing time, the elements that aren't assigned to kctl or jack may be called. Need proper NULL-checks. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index eac002d01fd..ef36cbb9e96 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -194,6 +194,8 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) for (i = 0; i < codec->jacktbl.used; i++, jack++) if (jack->nid) { jack_detect_update(codec, jack); + if (!jack->kctl) + continue; state = get_jack_plug_state(jack->pin_sense); snd_kctl_jack_report(codec->bus->card, jack->kctl, state); } @@ -356,7 +358,7 @@ void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) unsigned int present; int type; - if (!jack) + if (!jack || !jack->jack) return; present = snd_hda_jack_detect(codec, nid); From 344b01aecdc1e1173b5aa86208ef583489de7710 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 Nov 2011 17:54:19 +0100 Subject: [PATCH 09/15] ALSA: hda/jack - Fix the assignment of input jack-type The type field was lost during the transition. Restored. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index ef36cbb9e96..3bcf623862c 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -345,6 +345,7 @@ int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, err = snd_jack_new(codec->bus->card, name, type, &jack->jack); if (err < 0) return err; + jack->type = type; jack->jack->private_data = jack; jack->jack->private_free = hda_free_jack_priv; return 0; From fc5b15f13886afad43fc6c0040af7cb5172a0bd8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Nov 2011 10:32:21 +0100 Subject: [PATCH 10/15] ALSA: hda - Add missing initialization of kctl jack status Otherwise the jack kctls will report invalid values until the jack is re-plugged. Reported-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 3bcf623862c..e014562edc0 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -225,6 +225,8 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, if (snd_hda_ctl_add(codec, nid, kctl) < 0) return -ENOMEM; jack->kctl = kctl; + snd_kctl_jack_report(codec->bus->card, kctl, + snd_hda_jack_detect(codec, nid)); return 0; } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); From bf815bf0a3c3b8ad6cd97cda6bc29cc3708fe749 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Nov 2011 14:28:33 +0100 Subject: [PATCH 11/15] ALSA: hda - Add missing inclusion of linux/export.h This is needed newly since 3.2... Signed-off-by: Takashi Iwai --- sound/core/ctljack.c | 1 + sound/pci/hda/hda_jack.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c index af0e78a3809..e4b38fbe51d 100644 --- a/sound/core/ctljack.c +++ b/sound/core/ctljack.c @@ -10,6 +10,7 @@ */ #include +#include #include #include diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index e014562edc0..25f756533be 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include From 201e06ffa9ef9b5265e636617f4fa20cd1490343 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Nov 2011 15:33:26 +0100 Subject: [PATCH 12/15] ALSA: hda - Give more unique names by snd_hda_get_pin_label() The function now gives more unique names for the output pins by adding some prefix and suffix for the location and the channels. Otherwise, it can pass the index number. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 141 +++++++++++++++++++++++++-------- sound/pci/hda/hda_jack.c | 24 +++--- sound/pci/hda/hda_local.h | 5 +- sound/pci/hda/patch_ca0110.c | 6 +- sound/pci/hda/patch_cirrus.c | 5 +- sound/pci/hda/patch_sigmatel.c | 8 +- 6 files changed, 135 insertions(+), 54 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e57698f611a..e050f893bf7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5066,61 +5066,136 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); +/* get a unique suffix or an index number */ +static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, + int num_pins, int *indexp) +{ + static const char * const channel_sfx[] = { + " Front", " Surrount", " CLFE", " Side" + }; + int i; + + for (i = 0; i < num_pins; i++) { + if (pins[i] == nid) { + if (num_pins == 1) + return ""; + if (num_pins > ARRAY_SIZE(channel_sfx)) { + if (indexp) + *indexp = i; + return ""; + } + return channel_sfx[i]; + } + } + return NULL; +} + +static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + const char *name, char *label, int maxlen, + int *indexp) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int attr = snd_hda_get_input_pin_attr(def_conf); + const char *pfx = "", *sfx = ""; + + /* handle as a speaker if it's a fixed line-out */ + if (!strcmp(name, "Line-Out") && attr == INPUT_PIN_ATTR_INT) + name = "Speaker"; + /* check the location */ + switch (attr) { + case INPUT_PIN_ATTR_DOCK: + pfx = "Dock "; + break; + case INPUT_PIN_ATTR_FRONT: + pfx = "Front "; + break; + } + if (cfg) { + /* try to give a unique suffix if needed */ + sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, + indexp); + if (!sfx) + sfx = check_output_sfx(nid, cfg->hp_pins, cfg->hp_outs, + indexp); + if (!sfx) + sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, + indexp); + if (!sfx) + sfx = ""; + } + snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); + return 1; +} + /** * snd_hda_get_pin_label - Get a label for the given I/O pin * * Get a label for the given pin. This function works for both input and * output pins. When @cfg is given as non-NULL, the function tries to get * an optimized label using hda_get_autocfg_input_label(). + * + * This function tries to give a unique label string for the pin as much as + * possible. For example, when the multiple line-outs are present, it adds + * the channel suffix like "Front", "Surround", etc (only when @cfg is given). + * If no unique name with a suffix is available and @indexp is non-NULL, the + * index number is stored in the pointer. */ -const char *snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg) +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp) { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr; + const char *name = NULL; int i; + if (indexp) + *indexp = 0; if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - return NULL; + return 0; - attr = snd_hda_get_input_pin_attr(def_conf); switch (get_defcfg_device(def_conf)) { case AC_JACK_LINE_OUT: - switch (attr) { - case INPUT_PIN_ATTR_INT: - return "Speaker"; - case INPUT_PIN_ATTR_DOCK: - return "Dock Line-Out"; - case INPUT_PIN_ATTR_FRONT: - return "Front Line-Out"; - default: - return "Line-Out"; - } + return fill_audio_out_name(codec, nid, cfg, "Line-Out", + label, maxlen, indexp); case AC_JACK_SPEAKER: - return "Speaker"; + return fill_audio_out_name(codec, nid, cfg, "Speaker", + label, maxlen, indexp); case AC_JACK_HP_OUT: - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - return "Dock Headphone"; - case INPUT_PIN_ATTR_FRONT: - return "Front Headphone"; - default: - return "Headphone"; - } + return fill_audio_out_name(codec, nid, cfg, "Headphone", + label, maxlen, indexp); case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) - return "HDMI"; + name = "HDMI"; else - return "SPDIF"; + name = "SPDIF"; + if (cfg && indexp) { + for (i = 0; i < cfg->dig_outs; i++) + if (cfg->dig_out_pins[i] == nid) { + *indexp = i; + break; + } + } + break; + default: + if (cfg) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + name = hda_get_autocfg_input_label(codec, cfg, i); + if (name) + break; + } + } + if (!name) + name = hda_get_input_pin_label(codec, nid, true); + break; } - - if (cfg) { - for (i = 0; i < cfg->num_inputs; i++) - if (cfg->inputs[i].pin == nid) - return hda_get_autocfg_input_label(codec, cfg, i); - } - return hda_get_input_pin_label(codec, nid, true); + if (!name) + return 0; + strlcpy(label, name, maxlen); + return 1; } EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 25f756533be..394901515d9 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -232,11 +232,12 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); -static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx, +static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg) { unsigned int def_conf, conn; - int err; + char name[44]; + int idx, err; if (!nid) return 0; @@ -247,9 +248,8 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx, if (conn != AC_JACK_PORT_COMPLEX) return 0; - err = snd_hda_jack_add_kctl(codec, nid, - snd_hda_get_pin_label(codec, nid, cfg), - idx); + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); + err = snd_hda_jack_add_kctl(codec, nid, name, idx); if (err < 0) return err; return snd_hda_jack_detect_enable(codec, nid, 0); @@ -265,38 +265,38 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, int i, err; for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, i, cfg); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, i, cfg); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, i, cfg); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, 0, cfg); + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg); if (err < 0) return err; } for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, i, cfg); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } - err = add_jack_kctl(codec, cfg->dig_in_pin, 0, cfg); + err = add_jack_kctl(codec, cfg->dig_in_pin, cfg); if (err < 0) return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, 0, cfg); + err = add_jack_kctl(codec, cfg->mono_out_pin, cfg); if (err < 0) return err; return 0; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 13f681480a3..ef09716aeb6 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -397,8 +397,9 @@ struct auto_pin_cfg; const char *hda_get_autocfg_input_label(struct hda_codec *codec, const struct auto_pin_cfg *cfg, int input); -const char *snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg); +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp); int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, int index, int *type_index_ret); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 6bd602bfe0f..09ccfabb4a1 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -41,7 +41,7 @@ struct ca0110_spec { hda_nid_t dig_out; hda_nid_t dig_in; unsigned int num_inputs; - const char *input_labels[AUTO_PIN_LAST]; + char input_labels[AUTO_PIN_LAST][32]; struct hda_pcm pcm_rec[2]; /* PCM information */ }; @@ -476,7 +476,9 @@ static void parse_input(struct hda_codec *codec) if (j >= cfg->num_inputs) continue; spec->input_pins[n] = pin; - spec->input_labels[n] = snd_hda_get_pin_label(codec, pin, NULL); + snd_hda_get_pin_label(codec, pin, cfg, + spec->input_labels[n], + sizeof(spec->input_labels[n]), NULL); spec->adcs[n] = nid; n++; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 0e34554bc45..0ba03878c5e 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -711,8 +711,9 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= spec->num_inputs) uinfo->value.enumerated.item = spec->num_inputs - 1; idx = spec->input_idx[uinfo->value.enumerated.item]; - strcpy(uinfo->value.enumerated.name, - snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, NULL)); + snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg, + uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), NULL); return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 73bf7cd0a6e..0988dc4890a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2867,7 +2867,8 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, } if (control) { - strcpy(name, snd_hda_get_pin_label(codec, nid, NULL)); + snd_hda_get_pin_label(codec, nid, &spec->autocfg, + name, sizeof(name), NULL); return stac92xx_add_control(codec->spec, control, strcat(name, " Jack Mode"), nid); } @@ -3545,7 +3546,7 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, for (i = 0; i < spec->num_dmics; i++) { hda_nid_t nid; int index, type_idx; - const char *label; + char label[32]; nid = spec->dmic_nids[i]; if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) @@ -3558,7 +3559,8 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (index < 0) continue; - label = snd_hda_get_pin_label(codec, nid, NULL); + snd_hda_get_pin_label(codec, nid, &spec->autocfg, + label, sizeof(label), NULL); snd_hda_add_imux_item(dimux, label, index, &type_idx); if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) snd_hda_add_imux_item(imux, label, index, &type_idx); From d6018bb566f6eef277184278b105e04705e8aeb6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Nov 2011 14:17:16 +0100 Subject: [PATCH 13/15] ALSA: hda - Fix a typo Reported-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e050f893bf7..b703e25b624 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5071,7 +5071,7 @@ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, int num_pins, int *indexp) { static const char * const channel_sfx[] = { - " Front", " Surrount", " CLFE", " Side" + " Front", " Surround", " CLFE", " Side" }; int i; From 358b6e62b86f6313d114e0f6b7d8f8adaf85ed9c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Nov 2011 14:34:20 +0100 Subject: [PATCH 14/15] ALSA: hda - Don't add channel suffix for headphone pin labels The multiple headphone pins are usually handled as copied from the same source, not as individual channels like front and surround. Thus it'd be more correct to avoid the channel suffix for "Headphone" pin labels in snd_hda_get_pin_label() but give an index number instead. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 52 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b703e25b624..de3325e7908 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5066,6 +5066,16 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + /* get a unique suffix or an index number */ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, int num_pins, int *indexp) @@ -5075,19 +5085,17 @@ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, }; int i; - for (i = 0; i < num_pins; i++) { - if (pins[i] == nid) { - if (num_pins == 1) - return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; - return ""; - } - return channel_sfx[i]; - } + i = find_idx_in_nid_list(nid, pins, num_pins); + if (i < 0) + return NULL; + if (num_pins == 1) + return ""; + if (num_pins > ARRAY_SIZE(channel_sfx)) { + if (indexp) + *indexp = i; + return ""; } - return NULL; + return channel_sfx[i]; } static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, @@ -5115,14 +5123,17 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, /* try to give a unique suffix if needed */ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, indexp); - if (!sfx) - sfx = check_output_sfx(nid, cfg->hp_pins, cfg->hp_outs, - indexp); if (!sfx) sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, indexp); - if (!sfx) + if (!sfx) { + /* don't add channel suffix for Headphone controls */ + int idx = find_idx_in_nid_list(nid, cfg->hp_pins, + cfg->hp_outs); + if (idx >= 0) + *indexp = idx; sfx = ""; + } } snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); return 1; @@ -5171,11 +5182,10 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, else name = "SPDIF"; if (cfg && indexp) { - for (i = 0; i < cfg->dig_outs; i++) - if (cfg->dig_out_pins[i] == nid) { - *indexp = i; - break; - } + i = find_idx_in_nid_list(nid, cfg->dig_out_pins, + cfg->dig_outs); + if (i >= 0) + *indexp = i; } break; default: From 31ef22579302ac42054bebecb528710f46580925 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Dec 2011 17:41:36 +0100 Subject: [PATCH 15/15] ALSA: hda - Integrate input-jack stuff into kctl-jack Instead of managing input-jack stuff separately, call all stuff inside the kctl-jack creation, deletion and report. The caller no longer needs to care about input-jack. The better integration between input-jack and kctl-jack should be done in the upper layer in near future, but for now, it's implemented locally for more tests. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 159 +++++++++++++-------------------- sound/pci/hda/hda_local.h | 20 ----- sound/pci/hda/patch_conexant.c | 52 ++--------- sound/pci/hda/patch_hdmi.c | 22 ++--- sound/pci/hda/patch_realtek.c | 52 ----------- sound/pci/hda/patch_sigmatel.c | 46 ---------- 6 files changed, 74 insertions(+), 277 deletions(-) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 394901515d9..d8a35da0803 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -90,15 +90,19 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_new); -#ifdef CONFIG_SND_HDA_INPUT_JACK -static void snd_hda_input_jack_free(struct hda_codec *codec); -#else -#define snd_hda_input_jack_free(codec) -#endif - void snd_hda_jack_tbl_clear(struct hda_codec *codec) { - snd_hda_input_jack_free(codec); +#ifdef CONFIG_SND_HDA_INPUT_JACK + /* free jack instances manually when clearing/reconfiguring */ + if (!codec->bus->shutdown && codec->jacktbl.list) { + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (jack->jack) + snd_device_free(codec->bus->card, jack->jack); + } + } +#endif snd_array_free(&codec->jacktbl); } @@ -199,10 +203,44 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) continue; state = get_jack_plug_state(jack->pin_sense); snd_kctl_jack_report(codec->bus->card, jack->kctl, state); +#ifdef CONFIG_SND_HDA_INPUT_JACK + if (jack->jack) + snd_jack_report(jack->jack, + state ? jack->type : 0); +#endif } } EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync); +#ifdef CONFIG_SND_HDA_INPUT_JACK +/* guess the jack type from the pin-config */ +static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + return SND_JACK_LINEOUT; + case AC_JACK_HP_OUT: + return SND_JACK_HEADPHONE; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return SND_JACK_AVOUT; + case AC_JACK_MIC_IN: + return SND_JACK_MICROPHONE; + default: + return SND_JACK_LINEIN; + } +} + +static void hda_free_jack_priv(struct snd_jack *jack) +{ + struct hda_jack_tbl *jacks = jack->private_data; + jacks->nid = 0; + jacks->jack = NULL; +} +#endif + /** * snd_hda_jack_add_kctl - Add a kctl for the given pin * @@ -214,6 +252,7 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, { struct hda_jack_tbl *jack; struct snd_kcontrol *kctl; + int err, state; jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) @@ -223,11 +262,21 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, kctl = snd_kctl_jack_new(name, idx, codec); if (!kctl) return -ENOMEM; - if (snd_hda_ctl_add(codec, nid, kctl) < 0) - return -ENOMEM; + err = snd_hda_ctl_add(codec, nid, kctl); + if (err < 0) + return err; jack->kctl = kctl; - snd_kctl_jack_report(codec->bus->card, kctl, - snd_hda_jack_detect(codec, nid)); + state = snd_hda_jack_detect(codec, nid); + snd_kctl_jack_report(codec->bus->card, kctl, state); +#ifdef CONFIG_SND_HDA_INPUT_JACK + jack->type = get_input_jack_type(codec, nid); + err = snd_jack_new(codec->bus->card, name, jack->type, &jack->jack); + if (err < 0) + return err; + jack->jack->private_data = jack; + jack->jack->private_free = hda_free_jack_priv; + snd_jack_report(jack->jack, state ? jack->type : 0); +#endif return 0; } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); @@ -302,91 +351,3 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, return 0; } EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); - -#ifdef CONFIG_SND_HDA_INPUT_JACK -/* - * Input-jack notification support - */ -static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid, - int type) -{ - switch (type) { - case SND_JACK_HEADPHONE: - return "Headphone"; - case SND_JACK_MICROPHONE: - return "Mic"; - case SND_JACK_LINEOUT: - return "Line-out"; - case SND_JACK_LINEIN: - return "Line-in"; - case SND_JACK_HEADSET: - return "Headset"; - case SND_JACK_VIDEOOUT: - return "HDMI/DP"; - default: - return "Misc"; - } -} - -static void hda_free_jack_priv(struct snd_jack *jack) -{ - struct hda_jack_tbl *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} - -int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, - const char *name) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); - int err; - - if (!jack) - return -ENOMEM; - if (!name) - name = get_jack_default_name(codec, nid, type); - err = snd_jack_new(codec->bus->card, name, type, &jack->jack); - if (err < 0) - return err; - jack->type = type; - jack->jack->private_data = jack; - jack->jack->private_free = hda_free_jack_priv; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_input_jack_add); - -void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); - unsigned int pin_ctl; - unsigned int present; - int type; - - if (!jack || !jack->jack) - return; - - present = snd_hda_jack_detect(codec, nid); - type = jack->type; - if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) { - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - type = (pin_ctl & AC_PINCTL_HP_EN) ? - SND_JACK_HEADPHONE : SND_JACK_LINEOUT; - } - snd_jack_report(jack->jack, present ? type : 0); -} -EXPORT_SYMBOL_HDA(snd_hda_input_jack_report); - -/* free jack instances manually when clearing/reconfiguring */ -static void snd_hda_input_jack_free(struct hda_codec *codec) -{ - if (!codec->bus->shutdown && codec->jacktbl.list) { - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - if (jack->jack) - snd_device_free(codec->bus->card, jack->jack); - } - } -} -#endif /* CONFIG_SND_HDA_INPUT_JACK */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ef09716aeb6..e1abc07f743 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -674,24 +674,4 @@ static inline void snd_hda_eld_proc_free(struct hda_codec *codec, #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); -/* - * Input-jack notification support - */ -#ifdef CONFIG_SND_HDA_INPUT_JACK -int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, - const char *name); -void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid); -#else /* CONFIG_SND_HDA_INPUT_JACK */ -static inline int snd_hda_input_jack_add(struct hda_codec *codec, - hda_nid_t nid, int type, - const char *name) -{ - return 0; -} -static inline void snd_hda_input_jack_report(struct hda_codec *codec, - hda_nid_t nid) -{ -} -#endif /* CONFIG_SND_HDA_INPUT_JACK */ - #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ae9c028d825..bf14a0a0ce1 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -416,40 +416,6 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, &spec->cur_mux[adc_idx]); } -static int conexant_init_jacks(struct hda_codec *codec) -{ -#ifdef CONFIG_SND_HDA_INPUT_JACK - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) { - const struct hda_verb *hv; - - hv = spec->init_verbs[i]; - while (hv->nid) { - int err = 0; - switch (hv->param ^ AC_USRSP_EN) { - case CONEXANT_HP_EVENT: - err = snd_hda_input_jack_add(codec, hv->nid, - SND_JACK_HEADPHONE, NULL); - snd_hda_input_jack_report(codec, hv->nid); - break; - case CXT5051_PORTC_EVENT: - case CONEXANT_MIC_EVENT: - err = snd_hda_input_jack_add(codec, hv->nid, - SND_JACK_MICROPHONE, NULL); - snd_hda_input_jack_report(codec, hv->nid); - break; - } - if (err < 0) - return err; - ++hv; - } - } -#endif /* CONFIG_SND_HDA_INPUT_JACK */ - return 0; -} - static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { @@ -1750,7 +1716,6 @@ static void cxt5051_hp_automute(struct hda_codec *codec) static void cxt5051_hp_unsol_event(struct hda_codec *codec, unsigned int res) { - int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5051_hp_automute(codec); @@ -1762,7 +1727,6 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, cxt5051_portc_automic(codec); break; } - snd_hda_input_jack_report(codec, nid); } static const struct snd_kcontrol_new cxt5051_playback_mixers[] = { @@ -1901,8 +1865,6 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | event); - snd_hda_input_jack_add(codec, nid, SND_JACK_MICROPHONE, NULL); - snd_hda_input_jack_report(codec, nid); } static const struct hda_verb cxt5051_ideapad_init_verbs[] = { @@ -1918,7 +1880,6 @@ static int cxt5051_init(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; conexant_init(codec); - conexant_init_jacks(codec); if (spec->auto_mic & AUTO_MIC_PORTB) cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT); @@ -3450,7 +3411,6 @@ static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) hda_nid_t nid = pins[i]; if (!nid || !is_jack_detectable(codec, nid)) break; - snd_hda_input_jack_report(codec, nid); present |= snd_hda_jack_detect(codec, nid); } return present; @@ -3755,8 +3715,6 @@ static void cx_auto_automic(struct hda_codec *codec) static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) { - int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; - switch (snd_hda_jack_get_action(codec, res >> 26)) { case CONEXANT_HP_EVENT: cx_auto_hp_automute(codec); @@ -3766,7 +3724,6 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) break; case CONEXANT_MIC_EVENT: cx_auto_automic(codec); - snd_hda_input_jack_report(codec, nid); break; } snd_hda_jack_report_sync(codec); @@ -4325,6 +4282,7 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) static int cx_auto_build_controls(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; int err; err = cx_auto_build_output_controls(codec); @@ -4333,7 +4291,13 @@ static int cx_auto_build_controls(struct hda_codec *codec) err = cx_auto_build_input_controls(codec); if (err < 0) return err; - return conexant_build_controls(codec); + err = conexant_build_controls(codec); + if (err < 0) + return err; + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } static int cx_auto_search_adcs(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bb8cfc68cd7..66dc7e65175 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1005,8 +1005,6 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, bool retry) msecs_to_jiffies(300)); } } - - snd_hda_input_jack_report(codec, pin_nid); } static void hdmi_repoll_eld(struct work_struct *work) @@ -1232,21 +1230,15 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) { - int err; - char hdmi_str[32]; + char hdmi_str[32] = "HDMI/DP"; struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; int pcmdev = spec->pcm_rec[pin_idx].device; - snprintf(hdmi_str, sizeof(hdmi_str), "HDMI/DP,pcm=%d", pcmdev); + if (pcmdev > 0) + sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - err = snd_hda_input_jack_add(codec, per_pin->pin_nid, - SND_JACK_VIDEOOUT, pcmdev > 0 ? hdmi_str : NULL); - if (err < 0) - return err; - - hdmi_present_sense(per_pin, false); - return 0; + return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0); } static int generic_hdmi_build_controls(struct hda_codec *codec) @@ -1276,10 +1268,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; - err = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, - "HDMI", pin_idx); - if (err < 0) - return err; + + hdmi_present_sense(per_pin, false); } return 0; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 933c8cf23b2..641c8295b64 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -460,46 +460,6 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } -/* - * Jack-reporting via input-jack layer - */ - -/* initialization of jacks; currently checks only a few known pins */ -static int alc_init_jacks(struct hda_codec *codec) -{ -#ifdef CONFIG_SND_HDA_INPUT_JACK - struct alc_spec *spec = codec->spec; - int err; - unsigned int hp_nid = spec->autocfg.hp_pins[0]; - unsigned int mic_nid = spec->ext_mic_pin; - unsigned int dock_nid = spec->dock_mic_pin; - - if (hp_nid) { - err = snd_hda_input_jack_add(codec, hp_nid, - SND_JACK_HEADPHONE, NULL); - if (err < 0) - return err; - snd_hda_input_jack_report(codec, hp_nid); - } - - if (mic_nid) { - err = snd_hda_input_jack_add(codec, mic_nid, - SND_JACK_MICROPHONE, NULL); - if (err < 0) - return err; - snd_hda_input_jack_report(codec, mic_nid); - } - if (dock_nid) { - err = snd_hda_input_jack_add(codec, dock_nid, - SND_JACK_MICROPHONE, NULL); - if (err < 0) - return err; - snd_hda_input_jack_report(codec, dock_nid); - } -#endif /* CONFIG_SND_HDA_INPUT_JACK */ - return 0; -} - /* * Jack detections for HP auto-mute and mic-switch */ @@ -513,7 +473,6 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) hda_nid_t nid = pins[i]; if (!nid) break; - snd_hda_input_jack_report(codec, nid); present |= snd_hda_jack_detect(codec, nid); } return present; @@ -653,10 +612,6 @@ static void alc_mic_automute(struct hda_codec *codec) alc_mux_select(codec, 0, spec->dock_mic_idx, false); else alc_mux_select(codec, 0, spec->int_mic_idx, false); - - snd_hda_input_jack_report(codec, pins[spec->ext_mic_idx]); - if (spec->dock_mic_idx >= 0) - snd_hda_input_jack_report(codec, pins[spec->dock_mic_idx]); } /* unsolicited event for HP jack sensing */ @@ -4686,7 +4641,6 @@ static int patch_alc882(struct hda_codec *codec) if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; - alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc882_loopbacks; @@ -4866,7 +4820,6 @@ static int patch_alc262(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; - alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc262_loopbacks; @@ -4979,8 +4932,6 @@ static int patch_alc268(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; - alc_init_jacks(codec); - return 0; error: @@ -5538,7 +5489,6 @@ static int patch_alc269(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc269_shutup; - alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc269_loopbacks; @@ -6153,8 +6103,6 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; - alc_init_jacks(codec); - #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc662_loopbacks; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0988dc4890a..2d4156c583c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1084,13 +1084,10 @@ static const char * const slave_sws[] = { }; static void stac92xx_free_kctls(struct hda_codec *codec); -static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type); static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; int err; int i; @@ -1176,32 +1173,6 @@ static int stac92xx_build_controls(struct hda_codec *codec) stac92xx_free_kctls(codec); /* no longer needed */ - /* create jack input elements */ - if (spec->hp_detect) { - for (i = 0; i < cfg->hp_outs; i++) { - int type = SND_JACK_HEADPHONE; - nid = cfg->hp_pins[i]; - /* jack detection */ - if (cfg->hp_outs == i) - type |= SND_JACK_LINEOUT; - err = stac92xx_add_jack(codec, nid, type); - if (err < 0) - return err; - } - } - for (i = 0; i < cfg->line_outs; i++) { - err = stac92xx_add_jack(codec, cfg->line_out_pins[i], - SND_JACK_LINEOUT); - if (err < 0) - return err; - } - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE); - if (err < 0) - return err; - } - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4158,22 +4129,6 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } -static int stac92xx_add_jack(struct hda_codec *codec, - hda_nid_t nid, int type) -{ -#ifdef CONFIG_SND_HDA_INPUT_JACK - int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int connectivity = get_defcfg_connect(def_conf); - - if (connectivity && connectivity != AC_JACK_PORT_FIXED) - return 0; - - return snd_hda_input_jack_add(codec, nid, type, NULL); -#else - return 0; -#endif /* CONFIG_SND_HDA_INPUT_JACK */ -} - static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, unsigned char type, int data) { @@ -4778,7 +4733,6 @@ static void handle_unsol_event(struct hda_codec *codec, case STAC_PWR_EVENT: if (spec->num_pwrs > 0) stac92xx_pin_sense(codec, event->nid); - snd_hda_input_jack_report(codec, event->nid); switch (codec->subsystem_id) { case 0x103c308f: