ALSA: hda - Limit the amp value to write
authorTakashi Iwai <tiwai@suse.de>
Thu, 8 Jul 2010 16:40:37 +0000 (18:40 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 9 Jul 2010 08:08:57 +0000 (10:08 +0200)
Check the amp max value at put callbacks and set the upper limit
so that the driver won't write any invalid value over the defined
range.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c

index a3d638c8c1fd96a407c64b36ee2098d60e087bea..88a1c6acbcbd92570ff47c2459959630f7185b6a 100644 (file)
@@ -1539,6 +1539,17 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int ofs)
+{
+       u32 caps = query_amp_caps(codec, nid, dir);
+       /* get num steps */
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       if (ofs < caps)
+               caps -= ofs;
+       return caps;
+}
+
 /**
  * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
  *
@@ -1553,23 +1564,17 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        u8 chs = get_amp_channels(kcontrol);
        int dir = get_amp_direction(kcontrol);
        unsigned int ofs = get_amp_offset(kcontrol);
-       u32 caps;
 
-       caps = query_amp_caps(codec, nid, dir);
-       /* num steps */
-       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-       if (!caps) {
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+       if (!uinfo->value.integer.max) {
                printk(KERN_WARNING "hda_codec: "
                       "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
                       kcontrol->id.name);
                return -EINVAL;
        }
-       if (ofs < caps)
-               caps -= ofs;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = chs == 3 ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = caps;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
@@ -1594,8 +1599,13 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
                 int ch, int dir, int idx, unsigned int ofs,
                 unsigned int val)
 {
+       unsigned int maxval;
+
        if (val > 0)
                val += ofs;
+       maxval = get_amp_max_value(codec, nid, dir, ofs);
+       if (val > maxval)
+               val = maxval;
        return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
                                        HDA_AMP_VOLMASK, val);
 }