diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 3f51a981e604cce559b063bd2843a8b9c4985399..0e986537d570340408bba4ec79453f347618927a 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, return 0; } -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +static void snd_hda_do_detach(struct hda_beep *beep) +{ + input_unregister_device(beep->dev); + beep->dev = NULL; + cancel_work_sync(&beep->beep_work); + /* turn off beep for sure */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); +} + +static int snd_hda_do_attach(struct hda_beep *beep) { struct input_dev *input_dev; - struct hda_beep *beep; + struct hda_codec *codec = beep->codec; int err; - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly */ - - beep = kzalloc(sizeof(*beep), GFP_KERNEL); - if (beep == NULL) - return -ENOMEM; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); input_dev = input_allocate_device(); if (!input_dev) { - kfree(beep); + printk(KERN_INFO "hda_beep: unable to allocate input device\n"); return -ENOMEM; } @@ -151,21 +153,71 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) err = input_register_device(input_dev); if (err < 0) { input_free_device(input_dev); - kfree(beep); + printk(KERN_INFO "hda_beep: unable to register input device\n"); return err; } + beep->dev = input_dev; + return 0; +} + +static void snd_hda_do_register(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, register_work); + int request; + + mutex_lock(&beep->mutex); + request = beep->request_enable; + if (beep->enabled != request) { + if (!request) { + snd_hda_do_detach(beep); + } else { + if (snd_hda_do_attach(beep) < 0) + goto __out; + } + beep->enabled = request; + } + __out: + mutex_unlock(&beep->mutex); +} + +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) +{ + struct hda_beep *beep = codec->beep; + enable = !!enable; + if (beep && beep->enabled != enable) { + beep->request_enable = enable; + schedule_work(&beep->register_work); + return 1; + } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); + +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +{ + struct hda_beep *beep; + + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly */ + beep = kzalloc(sizeof(*beep), GFP_KERNEL); + if (beep == NULL) + return -ENOMEM; + snprintf(beep->phys, sizeof(beep->phys), + "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); beep->nid = nid; - beep->dev = input_dev; beep->codec = codec; - beep->enabled = 1; codec->beep = beep; + INIT_WORK(&beep->register_work, &snd_hda_do_register); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); + mutex_init(&beep->mutex); + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); @@ -174,11 +226,11 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; if (beep) { - cancel_work_sync(&beep->beep_work); - - input_unregister_device(beep->dev); - kfree(beep); + cancel_work_sync(&beep->register_work); + if (beep->enabled) + snd_hda_do_detach(beep); codec->beep = NULL; + kfree(beep); } } EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 0c3de787c7171a63e31c85fa8d1aa88117a35c48..68465f679d8c688c889dc743213c3a8482c76730 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -32,11 +32,15 @@ struct hda_beep { int tone; hda_nid_t nid; unsigned int enabled:1; + unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ + struct work_struct register_work; /* scheduled task for beep event */ struct work_struct beep_work; /* scheduled task for beep event */ + struct mutex mutex; }; #ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); void snd_hda_detach_beep_device(struct hda_codec *codec); #else diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 444d9039c1ace6df37ae3f7b1cb1d77f6b6a0e6f..7fd2abe1129d4a08388a426f980c07a18220715c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -30,6 +30,7 @@ #include #include #include "hda_local.h" +#include "hda_beep.h" #include /* @@ -1734,6 +1735,17 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + + snd_hda_enable_beep_device(codec, *valp); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); + /* * bound volume controls * diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c1ca3182e6a4b3c4e6a0b6b44a7bf8c4f86cb62d..3001794ad2913e2f2623387ee3f936e3d9789756 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -66,6 +66,19 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put_beep, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +/* special beep mono mute switch */ +#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* special beep stereo mute switch */ +#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); @@ -81,6 +94,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d603f6aba6319bbf149a8dc5b966aec328aba8e..a0293614a0b900786d92b7b601e5defc5997f04e 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -159,7 +159,7 @@ static void ad198x_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 49de107db16bc860025ac53f24ceff7730eaf3b7..8c04e0e0f655043539c91bd9419c56c455bfe225 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2413,7 +2413,7 @@ static void alc_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8d65d2b25234972cec830fb1a248cc86356260fa..87ba239ff1c95e6165c7c4483faeda788aa6d1a9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2648,6 +2648,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MUTE_BEEP, STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, @@ -2658,6 +2659,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), STAC_MONO_MUX, STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), @@ -3221,11 +3223,14 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err; + int err, type = STAC_CTL_WIDGET_MUTE_BEEP; + + if (spec->anabeep_nid == nid) + type = STAC_CTL_WIDGET_MUTE; /* check for mute support for the the amp */ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + err = stac92xx_add_control(spec, type, "Beep Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) @@ -3258,12 +3263,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int enabled = !!ucontrol->value.integer.value[0]; - if (codec->beep->enabled != enabled) { - codec->beep->enabled = enabled; - return 1; - } - return 0; + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); } static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {