diff options
Diffstat (limited to '')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 325 |
1 files changed, 133 insertions, 192 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4e9d9445e18..fc114e522480 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -91,6 +91,12 @@ static void snd_hda_gen_spec_free(struct hda_gen_spec *spec) free_kctls(spec); snd_array_free(&spec->paths); snd_array_free(&spec->loopback_list); +#ifdef CONFIG_SND_HDA_GENERIC_LEDS + if (spec->led_cdevs[LED_AUDIO_MUTE]) + led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]); + if (spec->led_cdevs[LED_AUDIO_MICMUTE]) + led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]); +#endif } /* @@ -813,7 +819,7 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, } } -/* sync power of each widget in the the given path */ +/* sync power of each widget in the given path */ static hda_nid_t path_power_update(struct hda_codec *codec, struct nid_path *path, bool allow_powerdown) @@ -981,6 +987,8 @@ add_control(struct hda_gen_spec *spec, int type, const char *name, knew->index = cidx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; + if (knew->access == 0) + knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; knew->private_value = val; return knew; } @@ -1202,11 +1210,17 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, *index = ch; return "Headphone"; case AUTO_PIN_LINE_OUT: - /* This deals with the case where we have two DACs and - * one LO, one HP and one Speaker */ - if (!ch && cfg->speaker_outs && cfg->hp_outs) { - bool hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); - bool spk_lo_shared = !path_has_mixer(codec, spec->speaker_paths[0], ctl_type); + /* This deals with the case where one HP or one Speaker or + * one HP + one Speaker need to share the DAC with LO + */ + if (!ch) { + bool hp_lo_shared = false, spk_lo_shared = false; + + if (cfg->speaker_outs) + spk_lo_shared = !path_has_mixer(codec, + spec->speaker_paths[0], ctl_type); + if (cfg->hp_outs) + hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); if (hp_lo_shared && spk_lo_shared) return spec->vmaster_mute.hook ? "PCM" : "Master"; if (hp_lo_shared) @@ -1364,16 +1378,20 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, struct nid_path *path; hda_nid_t pin = pins[i]; - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (path) { - badness += assign_out_path_ctls(codec, path); - continue; + if (!spec->obey_preferred_dacs) { + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (path) { + badness += assign_out_path_ctls(codec, path); + continue; + } } dacs[i] = get_preferred_dac(codec, pin); if (dacs[i]) { if (is_dac_already_used(codec, dacs[i])) badness += bad->shared_primary; + } else if (spec->obey_preferred_dacs) { + badness += BAD_NO_PRIMARY_DAC; } if (!dacs[i]) @@ -1421,7 +1439,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, path = snd_hda_add_new_path(codec, dac, pin, 0); } if (!path) { - dac = dacs[i] = 0; + dacs[i] = 0; badness += bad->no_dac; } else { /* print_nid_path(codec, "output", path); */ @@ -3448,7 +3466,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, struct hda_gen_spec *spec = codec->spec; const struct hda_input_mux *imux; struct nid_path *path; - int i, adc_idx, err = 0; + int i, adc_idx, ret, err = 0; imux = &spec->input_mux; adc_idx = kcontrol->id.index; @@ -3458,9 +3476,13 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, if (!path || !path->ctls[type]) continue; kcontrol->private_value = path->ctls[type]; - err = func(kcontrol, ucontrol); - if (err < 0) + ret = func(kcontrol, ucontrol); + if (ret < 0) { + err = ret; break; + } + if (ret > 0) + err = 1; } mutex_unlock(&codec->control_mutex); if (err >= 0 && spec->cap_sync_hook) @@ -3508,6 +3530,7 @@ static int cap_sw_put(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new cap_sw_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = cap_sw_info, .get = cap_sw_get, .put = cap_sw_put, @@ -3614,8 +3637,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, chs)); if (!knew) return -ENOMEM; - if (is_switch) + if (is_switch) { knew->put = cap_single_sw_put; + if (spec->mic_mute_led) + knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; + } if (!inv_dmic) return 0; @@ -3630,8 +3656,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, 2)); if (!knew) return -ENOMEM; - if (is_switch) + if (is_switch) { knew->put = cap_single_sw_put; + if (spec->mic_mute_led) + knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; + } return 0; } @@ -3672,6 +3701,8 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, knew->index = idx; knew->private_value = sw_ctl; knew->subdevice = HDA_SUBDEV_AMP_FLAG; + if (spec->mic_mute_led) + knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; } return 0; } @@ -3887,176 +3918,101 @@ static int parse_mic_boost(struct hda_codec *codec) return 0; } +#ifdef CONFIG_SND_HDA_GENERIC_LEDS /* - * mic mute LED hook helpers + * vmaster mute LED hook helpers */ -enum { - MICMUTE_LED_ON, - MICMUTE_LED_OFF, - MICMUTE_LED_FOLLOW_CAPTURE, - MICMUTE_LED_FOLLOW_MUTE, -}; -static void call_micmute_led_update(struct hda_codec *codec) +static int create_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness), + bool micmute) { struct hda_gen_spec *spec = codec->spec; - unsigned int val; + struct led_classdev *cdev; + int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE; + int err; - switch (spec->micmute_led.led_mode) { - case MICMUTE_LED_ON: - val = 1; - break; - case MICMUTE_LED_OFF: - val = 0; - break; - case MICMUTE_LED_FOLLOW_CAPTURE: - val = !!spec->micmute_led.capture; - break; - case MICMUTE_LED_FOLLOW_MUTE: - default: - val = !spec->micmute_led.capture; - break; - } + cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; - if (val == spec->micmute_led.led_value) - return; - spec->micmute_led.led_value = val; - if (spec->micmute_led.update) - spec->micmute_led.update(codec); + cdev->name = micmute ? "hda::micmute" : "hda::mute"; + cdev->max_brightness = 1; + cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; + cdev->brightness_set_blocking = callback; + cdev->brightness = ledtrig_audio_get(idx); + cdev->flags = LED_CORE_SUSPENDRESUME; + + err = led_classdev_register(&codec->core.dev, cdev); + if (err < 0) + return err; + spec->led_cdevs[idx] = cdev; + return 0; } -static void update_micmute_led(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/** + * snd_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED + * @codec: the HDA codec + * @callback: the callback for LED classdev brightness_set_blocking + */ +int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)) { struct hda_gen_spec *spec = codec->spec; - unsigned int mask; - - if (spec->micmute_led.old_hook) - spec->micmute_led.old_hook(codec, kcontrol, ucontrol); + int err; - if (!ucontrol) - return; - mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - if (!strcmp("Capture Switch", ucontrol->id.name)) { - /* TODO: How do I verify if it's a mono or stereo here? */ - if (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]) - spec->micmute_led.capture |= mask; - else - spec->micmute_led.capture &= ~mask; - call_micmute_led_update(codec); + if (callback) { + err = create_mute_led_cdev(codec, callback, false); + if (err) { + codec_warn(codec, "failed to create a mute LED cdev\n"); + return err; + } } -} - -static int micmute_led_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "On", "Off", "Follow Capture", "Follow Mute", - }; - return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); -} + if (spec->vmaster_mute.hook) + codec_err(codec, "vmaster hook already present before cdev!\n"); -static int micmute_led_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode; + spec->vmaster_mute_led = 1; return 0; } - -static int micmute_led_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int mode; - - mode = ucontrol->value.enumerated.item[0]; - if (mode > MICMUTE_LED_FOLLOW_MUTE) - mode = MICMUTE_LED_FOLLOW_MUTE; - if (mode == spec->micmute_led.led_mode) - return 0; - spec->micmute_led.led_mode = mode; - call_micmute_led_update(codec); - return 1; -} - -static const struct snd_kcontrol_new micmute_led_mode_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic Mute-LED Mode", - .info = micmute_led_mode_info, - .get = micmute_led_mode_get, - .put = micmute_led_mode_put, -}; +EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev); /** - * snd_hda_gen_add_micmute_led - helper for setting up mic mute LED hook + * snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED * @codec: the HDA codec - * @hook: the callback for updating LED + * @callback: the callback for LED classdev brightness_set_blocking * * Called from the codec drivers for offering the mic mute LED controls. - * When established, it sets up cap_sync_hook and triggers the callback at - * each time when the capture mixer switch changes. The callback is supposed - * to update the LED accordingly. + * This creates a LED classdev and sets up the cap_sync_hook that is called at + * each time when the capture mixer switch changes. + * + * When NULL is passed to @callback, no classdev is created but only the + * LED-trigger is set up. * - * Returns 0 if the hook is established or a negative error code. + * Returns 0 or a negative error. */ -int snd_hda_gen_add_micmute_led(struct hda_codec *codec, - void (*hook)(struct hda_codec *)) -{ - struct hda_gen_spec *spec = codec->spec; - - spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE; - spec->micmute_led.capture = 0; - spec->micmute_led.led_value = 0; - spec->micmute_led.old_hook = spec->cap_sync_hook; - spec->micmute_led.update = hook; - spec->cap_sync_hook = update_micmute_led; - if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl)) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); - -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) -static void call_ledtrig_micmute(struct hda_codec *codec) +int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)) { struct hda_gen_spec *spec = codec->spec; + int err; - ledtrig_audio_set(LED_AUDIO_MICMUTE, - spec->micmute_led.led_value ? LED_ON : LED_OFF); -} -#endif + if (callback) { + err = create_mute_led_cdev(codec, callback, true); + if (err) { + codec_warn(codec, "failed to create a mic-mute LED cdev\n"); + return err; + } + } -/** - * snd_hda_gen_fixup_micmute_led - A fixup for mic-mute LED trigger - * - * Pass this function to the quirk entry if another driver supports the - * audio mic-mute LED trigger. Then this will bind the mixer capture switch - * change with the LED. - * - * Note that this fixup has to be called after other fixup that sets - * cap_sync_hook. Otherwise the chaining wouldn't work. - * - * @codec: the HDA codec - * @fix: fixup pointer - * @action: only supports HDA_FIXUP_ACT_PROBE value - * - */ -void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_gen_add_micmute_led(codec, call_ledtrig_micmute); -#endif + spec->mic_mute_led = 1; + return 0; } -EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); +EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); +#endif /* CONFIG_SND_HDA_GENERIC_LEDS */ /* * parse digital I/Os and set up NIDs in BIOS auto-parse mode @@ -4068,7 +4024,7 @@ static void parse_digital(struct hda_codec *codec) int i, nums; hda_nid_t dig_nid, pin; - /* support multiple SPDIFs; the secondary is set up as a slave */ + /* support multiple SPDIFs; the secondary is set up as a follower */ nums = 0; for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; @@ -4087,10 +4043,10 @@ static void parse_digital(struct hda_codec *codec) spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; } else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) + spec->multiout.follower_dig_outs = spec->follower_dig_outs; + if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1) break; - spec->slave_dig_outs[nums - 1] = dig_nid; + spec->follower_dig_outs[nums - 1] = dig_nid; } nums++; } @@ -4545,7 +4501,7 @@ static void call_update_outputs(struct hda_codec *codec) else snd_hda_gen_update_outputs(codec); - /* sync the whole vmaster slaves to reflect the new auto-mute status */ + /* sync the whole vmaster followers to reflect the new auto-mute status */ if (spec->auto_mute_via_amp && !codec->bus->shutdown) snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); } @@ -5012,6 +4968,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, parse_user_hints(codec); + if (spec->vmaster_mute_led || spec->mic_mute_led) + snd_ctl_led_request(); + if (spec->mixer_nid && !spec->mixer_merge_nid) spec->mixer_merge_nid = spec->mixer_nid; @@ -5189,8 +5148,8 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config); * Build control elements */ -/* slave controls for virtual master */ -static const char * const slave_pfxs[] = { +/* follower controls for virtual master */ +static const char * const follower_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "Headphone", "Speaker", "Mono", "Line Out", "CLFE", "Bass Speaker", "PCM", @@ -5242,22 +5201,23 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, slave_pfxs, - "Playback Volume"); + spec->vmaster_tlv, follower_pfxs, + "Playback Volume", 0); if (err < 0) return err; } if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); + NULL, follower_pfxs, + "Playback Switch", true, + spec->vmaster_mute_led ? + SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0, + &spec->vmaster_mute.sw_kctl); if (err < 0) return err; if (spec->vmaster_mute.hook) { - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, - spec->vmaster_mute_enum); + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute); snd_hda_sync_vmaster_hook(&spec->vmaster_mute); } } @@ -5673,7 +5633,7 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, if (*str) return; - strlcpy(str, chip_name, len); + strscpy(str, chip_name, len); /* drop non-alnum chars after a space */ for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { @@ -5765,7 +5725,7 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) spec->stream_name_digital); if (!info) return -ENOMEM; - codec->slave_dig_outs = spec->multiout.slave_dig_outs; + codec->follower_dig_outs = spec->multiout.follower_dig_outs; spec->pcm_rec[1] = info; if (spec->dig_out_type) info->pcm_type = spec->dig_out_type; @@ -6057,24 +6017,6 @@ void snd_hda_gen_free(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_gen_free); -/** - * snd_hda_gen_reboot_notify - Make codec enter D3 before rebooting - * @codec: the HDA codec - * - * This can be put as patch_ops reboot_notify function. - */ -void snd_hda_gen_reboot_notify(struct hda_codec *codec) -{ - /* Make the codec enter D3 to avoid spurious noises from the internal - * speaker during (and after) reboot - */ - snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - msleep(10); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_reboot_notify); - #ifdef CONFIG_PM /** * snd_hda_gen_check_power_status - check the loopback power save state @@ -6102,7 +6044,6 @@ static const struct hda_codec_ops generic_patch_ops = { .init = snd_hda_gen_init, .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, - .reboot_notify = snd_hda_gen_reboot_notify, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, #endif |