diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_jack.c | 168 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.h | 12 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 27 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 6 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 7 | ||||
-rw-r--r-- | 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 <linux/init.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/control.h> #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; } |