((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
         (idx))
 
+/**
+ * snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs
+ * @nid: widget NID
+ * @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
+ * @idx: input index value
+ *
+ * Returns an encoded pseudo register.
+ */
+#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx)               \
+       (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) |  \
+        AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */      \
+        ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
+        (idx))
+
 /**
  * snd_hdac_regmap_write - Write a verb with caching
  * @nid: codec NID
        return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
 }
 
+/**
+ * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ * @val: the pointer to store the value
+ *
+ * Read stereo AMP values.  The lower byte is left, the upper byte is right.
+ * Returns the value or a negative error.
+ */
+static inline int
+snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
+                              int dir, int idx)
+{
+       unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
+       int err, val;
+
+       err = snd_hdac_regmap_read_raw(codec, cmd, &val);
+       return err < 0 ? err : val;
+}
+
+/**
+ * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the stereo AMP value with a bit mask.
+ * The lower byte is left, the upper byte is right.
+ * Returns 0 if the value is unchanged, 1 if changed, or a negative error.
+ */
+static inline int
+snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
+                                 int dir, int idx, int mask, int val)
+{
+       unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
+
+       return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
+}
+
 #endif /* __SOUND_HDA_REGMAP_H */
 
        return hda_writeable_reg(dev, reg);
 }
 
+/*
+ * Stereo amp pseudo register:
+ * for making easier to handle the stereo volume control, we provide a
+ * fake register to deal both left and right channels by a single
+ * (pseudo) register access.  A verb consisting of SET_AMP_GAIN with
+ * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
+ * for the left and the upper 8bit for the right channel.
+ */
+static bool is_stereo_amp_verb(unsigned int reg)
+{
+       if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
+               return false;
+       return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
+               (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+}
+
+/* read a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_read_stereo_amp(struct hdac_device *codec,
+                                  unsigned int reg, unsigned int *val)
+{
+       unsigned int left, right;
+       int err;
+
+       reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
+       if (err < 0)
+               return err;
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
+       if (err < 0)
+               return err;
+       *val = left | (right << 8);
+       return 0;
+}
+
+/* write a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_write_stereo_amp(struct hdac_device *codec,
+                                   unsigned int reg, unsigned int val)
+{
+       int err;
+       unsigned int verb, left, right;
+
+       verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
+       if (reg & AC_AMP_GET_OUTPUT)
+               verb |= AC_AMP_SET_OUTPUT;
+       else
+               verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
+       reg = (reg & ~0xfffff) | verb;
+
+       left = val & 0xff;
+       right = (val >> 8) & 0xff;
+       if (left == right) {
+               reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+               return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
+       }
+
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
+       if (err < 0)
+               return err;
+       err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
 static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
 {
        struct hdac_device *codec = context;
        if (!codec_is_running(codec))
                return -EAGAIN;
        reg |= (codec->addr << 28);
+       if (is_stereo_amp_verb(reg))
+               return hda_reg_read_stereo_amp(codec, reg, val);
        return snd_hdac_exec_verb(codec, reg, 0, val);
 }
 
 
        reg &= ~0x00080000U; /* drop GET bit */
        reg |= (codec->addr << 28);
-       verb = get_verb(reg);
 
+       if (is_stereo_amp_verb(reg))
+               return hda_reg_write_stereo_amp(codec, reg, val);
+
+       verb = get_verb(reg);
        switch (verb & 0xf00) {
        case AC_VERB_SET_AMP_GAIN_MUTE:
                verb = AC_VERB_SET_AMP_GAIN_MUTE;