ALSA: hda_intel: Digital PC Beep - change behaviour for input layer
authorJaroslav Kysela <perex@perex.cz>
Wed, 21 Oct 2009 12:48:23 +0000 (14:48 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 16 Nov 2009 10:34:41 +0000 (11:34 +0100)
Original implementation was keeping registered input device for SND_BEEP
and SND_TONE events all time. This patch changes this behaviour:
If digital PC Beep is turned off using universal control switch,
the input device is unregistered.

Explanation: The kd_mksound() send SND_BEEP and SND_TONE only to last
registered device acceping those events. It means that the HDA Intel
audio driver blocks also the internal PC Speaker device (pcspkr.c
driver) even if the HDA Beep is muted. The user can easy disable
all beeps using 'setterm -blength 0' or 'xset b off' command.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c

index 3f51a981e604cce559b063bd2843a8b9c4985399..0e986537d570340408bba4ec79453f347618927a 100644 (file)
@@ -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);
index 0c3de787c7171a63e31c85fa8d1aa88117a35c48..68465f679d8c688c889dc743213c3a8482c76730 100644 (file)
@@ -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
index 444d9039c1ace6df37ae3f7b1cb1d77f6b6a0e6f..7fd2abe1129d4a08388a426f980c07a18220715c 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/tlv.h>
 #include <sound/initval.h>
 #include "hda_local.h"
+#include "hda_beep.h"
 #include <sound/hda_hwdep.h>
 
 /*
@@ -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
  *
index c1ca3182e6a4b3c4e6a0b6b44a7bf8c4f86cb62d..3001794ad2913e2f2623387ee3f936e3d9789756 100644 (file)
 /* 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);
index 2d603f6aba6319bbf149a8dc5b966aec328aba8e..a0293614a0b900786d92b7b601e5defc5997f04e 100644 (file)
@@ -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 */
 };
 
index 49de107db16bc860025ac53f24ceff7730eaf3b7..8c04e0e0f655043539c91bd9419c56c455bfe225 100644 (file)
@@ -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 */
 };
 
index 8d65d2b25234972cec830fb1a248cc86356260fa..87ba239ff1c95e6165c7c4483faeda788aa6d1a9 100644 (file)
@@ -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 = {