From 9322ca549771f2e84a93ac3f509ade1e4c3cdb35 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Feb 2012 14:28:01 +0100 Subject: ALSA: hda - Add suffix argument to snd_hda_add_vmaster() In most cases, the slave strings for vmaster are identical between volumes and switches except for "xxx Volume" and "xxx Switch" suffix. Now snd_hda_add_vmaster() takes the optional suffix argument so that each string can be composed with the given suffix, and we can share the slave name strings in both volume and switch calls nicely. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 66 +++++++++----------------------------------- 1 file changed, 13 insertions(+), 53 deletions(-) (limited to 'sound/pci/hda/patch_analog.c') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9cb14b42dfff..9771b0702455 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -137,51 +137,17 @@ static int ad198x_init(struct hda_codec *codec) return 0; } -static const char * const ad_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Mono Playback Volume", - "Speaker Playback Volume", - "IEC958 Playback Volume", +static const char * const ad_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Mono", "Speaker", "IEC958", NULL }; -static const char * const ad_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Mono Playback Switch", - "Speaker Playback Switch", - "IEC958 Playback Switch", +static const char * const ad1988_6stack_fp_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", "IEC958", NULL }; -static const char * const ad1988_6stack_fp_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "IEC958 Playback Volume", - NULL -}; - -static const char * const ad1988_6stack_fp_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "IEC958 Playback Switch", - NULL -}; static void ad198x_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -260,7 +226,8 @@ static int ad198x_build_controls(struct hda_codec *codec) err = snd_hda_add_vmaster(codec, "Master Playback Volume", vmaster_tlv, (spec->slave_vols ? - spec->slave_vols : ad_slave_vols)); + spec->slave_vols : ad_slave_pfxs), + "Playback Volume"); if (err < 0) return err; } @@ -268,7 +235,8 @@ static int ad198x_build_controls(struct hda_codec *codec) err = snd_hda_add_vmaster(codec, "Master Playback Switch", NULL, (spec->slave_sws ? - spec->slave_sws : ad_slave_sws)); + spec->slave_sws : ad_slave_pfxs), + "Playback Switch"); if (err < 0) return err; } @@ -3385,8 +3353,8 @@ static int patch_ad1988(struct hda_codec *codec) if (spec->autocfg.hp_pins[0]) { spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; - spec->slave_vols = ad1988_6stack_fp_slave_vols; - spec->slave_sws = ad1988_6stack_fp_slave_sws; + spec->slave_vols = ad1988_6stack_fp_slave_pfxs; + spec->slave_sws = ad1988_6stack_fp_slave_pfxs; spec->alt_dac_nid = ad1988_alt_dac_nid; spec->stream_analog_alt_playback = &ad198x_pcm_analog_alt_playback; @@ -3594,16 +3562,8 @@ static const struct hda_amp_list ad1884_loopbacks[] = { #endif static const char * const ad1884_slave_vols[] = { - "PCM Playback Volume", - "Mic Playback Volume", - "Mono Playback Volume", - "Front Mic Playback Volume", - "Mic Playback Volume", - "CD Playback Volume", - "Internal Mic Playback Volume", - "Docking Mic Playback Volume", - /* "Beep Playback Volume", */ - "IEC958 Playback Volume", + "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", + "Internal Mic", "Docking Mic", /* "Beep", */ "IEC958", NULL }; -- cgit v1.2.3 From 18478e8b626edc2d181dcb1b93e1f99ad72095e9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 9 Mar 2012 17:51:10 +0100 Subject: ALSA: hda - Initialize vmaster slave volumes When the driver is changed to use vmaster or a new slave element is added by the improvement of the parser code, user may face often the silent output because of the muted slave mixer although Master volume is properly set. And they complain. And I get upset. Although such a mixer element should be initialized via "alsactl init", it'd be more user-friendly if the known output slaves are unmuted and set to 0dB so that user can control the volume only with Master as default. Since Master is still set muted as default even with this change, no risk of the speaker blow up, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 65 ++++++++++++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_local.h | 6 ++-- sound/pci/hda/patch_analog.c | 8 ++++-- 3 files changed, 72 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda/patch_analog.c') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0527ae1ab96e..0c0ac0e1d504 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -2340,6 +2341,56 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) return 1; } +/* guess the value corresponding to 0dB */ +static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) +{ + int _tlv[4]; + const int *tlv = NULL; + int val = -1; + + if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + /* FIXME: set_fs() hack for obtaining user-space TLV data */ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv)) + tlv = _tlv; + set_fs(fs); + } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) + tlv = kctl->tlv.p; + if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) + val = -tlv[2] / tlv[3]; + return val; +} + +/* call kctl->put with the given value(s) */ +static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) +{ + struct snd_ctl_elem_value *ucontrol; + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return -ENOMEM; + ucontrol->value.integer.value[0] = val; + ucontrol->value.integer.value[1] = val; + kctl->put(kctl, ucontrol); + kfree(ucontrol); + return 0; +} + +/* initialize the slave volume with 0dB */ +static int init_slave_0dB(void *data, struct snd_kcontrol *slave) +{ + int offset = get_kctl_0dB_offset(slave); + if (offset > 0) + put_kctl_with_value(slave, offset); + return 0; +} + +/* unmute the slave */ +static int init_slave_unmute(void *data, struct snd_kcontrol *slave) +{ + return put_kctl_with_value(slave, 1); +} + /** * snd_hda_add_vmaster - create a virtual master control and add slaves * @codec: HD-audio codec @@ -2347,6 +2398,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) * @tlv: TLV data (optional) * @slaves: slave control names (optional) * @suffix: suffix string to each slave name (optional) + * @init_slave_vol: initialize slaves to unmute/0dB * * Create a virtual master control with the given name. The TLV data * must be either NULL or a valid data. @@ -2357,9 +2409,9 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) * * This function returns zero if successful or a negative error code. */ -int snd_hda_add_vmaster(struct hda_codec *codec, char *name, +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char * const *slaves, - const char *suffix) + const char *suffix, bool init_slave_vol) { struct snd_kcontrol *kctl; int err; @@ -2380,9 +2432,16 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, (map_slave_func_t)snd_ctl_add_slave, kctl); if (err < 0) return err; + + /* init with master mute & zero volume */ + put_kctl_with_value(kctl, 0); + if (init_slave_vol) + map_slaves(codec, slaves, suffix, + tlv ? init_slave_0dB : init_slave_unmute, kctl); + return 0; } -EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); +EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster); /** * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 6094dea82bc3..caa64686267b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -139,9 +139,11 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name); -int snd_hda_add_vmaster(struct hda_codec *codec, char *name, +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char * const *slaves, - const char *suffix); + const char *suffix, bool init_slave_vol); +#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ + __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true) int snd_hda_codec_reset(struct hda_codec *codec); /* amp value bits */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9771b0702455..fa97a0c5ced0 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -82,6 +82,7 @@ struct ad198x_spec { unsigned int inv_jack_detect: 1;/* inverted jack-detection */ unsigned int inv_eapd: 1; /* inverted EAPD implementation */ unsigned int analog_beep: 1; /* analog beep input present */ + unsigned int avoid_init_slave_vol:1; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; @@ -223,11 +224,12 @@ static int ad198x_build_controls(struct hda_codec *codec) unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", + err = __snd_hda_add_vmaster(codec, "Master Playback Volume", vmaster_tlv, (spec->slave_vols ? spec->slave_vols : ad_slave_pfxs), - "Playback Volume"); + "Playback Volume", + !spec->avoid_init_slave_vol); if (err < 0) return err; } @@ -3604,6 +3606,8 @@ static int patch_ad1884(struct hda_codec *codec) spec->vmaster_nid = 0x04; /* we need to cover all playback volumes */ spec->slave_vols = ad1884_slave_vols; + /* slaves may contain input volumes, so we can't raise to 0dB blindly */ + spec->avoid_init_slave_vol = 1; codec->patch_ops = ad198x_patch_ops; -- cgit v1.2.3 From 29e5853d618282d8277ce8a8304f7424eb60deb5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Mar 2012 12:25:03 +0100 Subject: ALSA: hda - Return the created kcontrol in __snd_hda_add_vmaster() It'll be used for adding hooks in later patches. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 ++++++++- sound/pci/hda/hda_local.h | 7 ++++--- sound/pci/hda/patch_analog.c | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda/patch_analog.c') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0c0ac0e1d504..b79ee3444654 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2399,6 +2399,7 @@ static int init_slave_unmute(void *data, struct snd_kcontrol *slave) * @slaves: slave control names (optional) * @suffix: suffix string to each slave name (optional) * @init_slave_vol: initialize slaves to unmute/0dB + * @ctl_ret: store the vmaster kcontrol in return * * Create a virtual master control with the given name. The TLV data * must be either NULL or a valid data. @@ -2411,11 +2412,15 @@ static int init_slave_unmute(void *data, struct snd_kcontrol *slave) */ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char * const *slaves, - const char *suffix, bool init_slave_vol) + const char *suffix, bool init_slave_vol, + struct snd_kcontrol **ctl_ret) { struct snd_kcontrol *kctl; int err; + if (ctl_ret) + *ctl_ret = NULL; + err = map_slaves(codec, slaves, suffix, check_slave_present, NULL); if (err != 1) { snd_printdd("No slave found for %s\n", name); @@ -2439,6 +2444,8 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, map_slaves(codec, slaves, suffix, tlv ? init_slave_0dB : init_slave_unmute, kctl); + if (ctl_ret) + *ctl_ret = kctl; return 0; } EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index caa64686267b..c3ee4ede4482 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -140,10 +140,11 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name); int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *slaves, - const char *suffix, bool init_slave_vol); + unsigned int *tlv, const char * const *slaves, + const char *suffix, bool init_slave_vol, + struct snd_kcontrol **ctl_ret); #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ - __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true) + __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) int snd_hda_codec_reset(struct hda_codec *codec); /* amp value bits */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index fa97a0c5ced0..7143393927da 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -229,7 +229,7 @@ static int ad198x_build_controls(struct hda_codec *codec) (spec->slave_vols ? spec->slave_vols : ad_slave_pfxs), "Playback Volume", - !spec->avoid_init_slave_vol); + !spec->avoid_init_slave_vol, NULL); if (err < 0) return err; } -- cgit v1.2.3