*/
 
 #define TPACPI_VERSION "0.23"
-#define TPACPI_SYSFS_VERSION 0x020600
+#define TPACPI_SYSFS_VERSION 0x020700
 
 /*
  *  Changelog:
        u32 fan_ctrl_status_undef:1;
        u32 second_fan:1;
        u32 beep_needs_two_args:1;
+       u32 mixer_no_level_control:1;
        u32 input_device_registered:1;
        u32 platform_drv_registered:1;
        u32 platform_drv_attrs_registered:1;
          .ec = TPACPI_MATCH_ANY,               \
          .quirks = (__quirk) }
 
+#define TPACPI_QEC_LNV(__id1, __id2, __quirk)  \
+       { .vendor = PCI_VENDOR_ID_LENOVO,       \
+         .bios = TPACPI_MATCH_ANY,             \
+         .ec = TPID(__id1, __id2),             \
+         .quirks = (__quirk) }
+
 struct tpacpi_quirk {
        unsigned int vendor;
        u16 bios;
        TPACPI_VOL_MODE_MAX
 };
 
+enum tpacpi_volume_capabilities {
+       TPACPI_VOL_CAP_AUTO = 0,        /* Use white/blacklist */
+       TPACPI_VOL_CAP_VOLMUTE,         /* Output vol and mute */
+       TPACPI_VOL_CAP_MUTEONLY,        /* Output mute only */
+       TPACPI_VOL_CAP_MAX
+};
+
 static enum tpacpi_volume_access_mode volume_mode =
        TPACPI_VOL_MODE_MAX;
 
+static enum tpacpi_volume_capabilities volume_capabilities;
 
 /*
  * Used to syncronize writers to TP_EC_AUDIO and
 {
        u8 lec = 0;
        u8 b_nvram;
-       const u8 ec_mask = TP_EC_AUDIO_LVL_MSK | TP_EC_AUDIO_MUTESW_MSK;
+       u8 ec_mask;
 
        if (volume_mode != TPACPI_VOL_MODE_ECNVRAM)
                return;
        vdbg_printk(TPACPI_DBG_MIXER,
                "trying to checkpoint mixer state to NVRAM...\n");
 
+       if (tp_features.mixer_no_level_control)
+               ec_mask = TP_EC_AUDIO_MUTESW_MSK;
+       else
+               ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK;
+
        if (mutex_lock_killable(&volume_mutex) < 0)
                return;
 
        tpacpi_volume_checkpoint_nvram();
 }
 
+#define TPACPI_VOL_Q_MUTEONLY  0x0001  /* Mute-only control available */
+#define TPACPI_VOL_Q_LEVEL     0x0002  /* Volume control available */
+
+static const struct tpacpi_quirk volume_quirk_table[] __initconst = {
+       /* Whitelist volume level on all IBM by default */
+       { .vendor = PCI_VENDOR_ID_IBM,
+         .bios   = TPACPI_MATCH_ANY,
+         .ec     = TPACPI_MATCH_ANY,
+         .quirks = TPACPI_VOL_Q_LEVEL },
+
+       /* Lenovo models with volume control (needs confirmation) */
+       TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */
+       TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */
+       TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */
+       TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */
+       TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */
+       TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */
+       TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */
+
+       /* Whitelist mute-only on all Lenovo by default */
+       { .vendor = PCI_VENDOR_ID_LENOVO,
+         .bios   = TPACPI_MATCH_ANY,
+         .ec     = TPACPI_MATCH_ANY,
+         .quirks = TPACPI_VOL_Q_MUTEONLY }
+};
+
 static int __init volume_init(struct ibm_init_struct *iibm)
 {
+       unsigned long quirks;
+
        vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n");
 
        mutex_init(&volume_mutex);
                return 1;
        }
 
+       if (volume_capabilities >= TPACPI_VOL_CAP_MAX)
+               return -EINVAL;
+
+       quirks = tpacpi_check_quirks(volume_quirk_table,
+                                    ARRAY_SIZE(volume_quirk_table));
+
+       switch (volume_capabilities) {
+       case TPACPI_VOL_CAP_AUTO:
+               if (quirks & TPACPI_VOL_Q_MUTEONLY)
+                       tp_features.mixer_no_level_control = 1;
+               else if (quirks & TPACPI_VOL_Q_LEVEL)
+                       tp_features.mixer_no_level_control = 0;
+               else
+                       return 1; /* no mixer */
+               break;
+       case TPACPI_VOL_CAP_VOLMUTE:
+               tp_features.mixer_no_level_control = 0;
+               break;
+       case TPACPI_VOL_CAP_MUTEONLY:
+               tp_features.mixer_no_level_control = 1;
+               break;
+       default:
+               return 1;
+       }
+
+       if (volume_capabilities != TPACPI_VOL_CAP_AUTO)
+               dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+                               "using user-supplied volume_capabilities=%d\n",
+                               volume_capabilities);
+
        if (volume_mode == TPACPI_VOL_MODE_AUTO ||
            volume_mode == TPACPI_VOL_MODE_MAX) {
                volume_mode = TPACPI_VOL_MODE_ECNVRAM;
        }
 
        vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
-                       "volume is supported\n");
+                       "mute is supported, volume control is %s\n",
+                       str_supported(!tp_features.mixer_no_level_control));
 
        return 0;
 }
        if (volume_get_status(&status) < 0) {
                len += sprintf(p + len, "level:\t\tunreadable\n");
        } else {
-               len += sprintf(p + len, "level:\t\t%d\n",
-                               status & TP_EC_AUDIO_LVL_MSK);
+               if (tp_features.mixer_no_level_control)
+                       len += sprintf(p + len, "level:\t\tunsupported\n");
+               else
+                       len += sprintf(p + len, "level:\t\t%d\n",
+                                       status & TP_EC_AUDIO_LVL_MSK);
+
                len += sprintf(p + len, "mute:\t\t%s\n",
                                onoff(status, TP_EC_AUDIO_MUTESW));
-               len += sprintf(p + len, "commands:\tup, down, mute\n");
-               len += sprintf(p + len, "commands:\tlevel <level>"
+
+               len += sprintf(p + len, "commands:\tunmute, mute\n");
+               if (!tp_features.mixer_no_level_control) {
+                       len += sprintf(p + len, "commands:\tup, down\n");
+                       len += sprintf(p + len, "commands:\tlevel <level>"
                               " (<level> is 0-%d)\n", TP_EC_VOLUME_MAX);
+               }
        }
 
        return len;
        new_mute  = s & TP_EC_AUDIO_MUTESW_MSK;
 
        while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "up") == 0) {
-                       if (new_mute)
-                               new_mute = 0;
-                       else if (new_level < TP_EC_VOLUME_MAX)
-                               new_level++;
-               } else if (strlencmp(cmd, "down") == 0) {
-                       if (new_mute)
-                               new_mute = 0;
-                       else if (new_level > 0)
-                               new_level--;
-               } else if (sscanf(cmd, "level %u", &l) == 1 &&
-                          l >= 0 && l <= TP_EC_VOLUME_MAX) {
-                               new_level = l;
-               } else if (strlencmp(cmd, "mute") == 0) {
+               if (!tp_features.mixer_no_level_control) {
+                       if (strlencmp(cmd, "up") == 0) {
+                               if (new_mute)
+                                       new_mute = 0;
+                               else if (new_level < TP_EC_VOLUME_MAX)
+                                       new_level++;
+                               continue;
+                       } else if (strlencmp(cmd, "down") == 0) {
+                               if (new_mute)
+                                       new_mute = 0;
+                               else if (new_level > 0)
+                                       new_level--;
+                               continue;
+                       } else if (sscanf(cmd, "level %u", &l) == 1 &&
+                                  l >= 0 && l <= TP_EC_VOLUME_MAX) {
+                                       new_level = l;
+                               continue;
+                       }
+               }
+               if (strlencmp(cmd, "mute") == 0)
                        new_mute = TP_EC_AUDIO_MUTESW_MSK;
-               } else
+               else if (strlencmp(cmd, "unmute") == 0)
+                       new_mute = 0;
+               else
                        return -EINVAL;
        }
 
-       tpacpi_disclose_usertask("procfs volume",
-                               "%smute and set level to %d\n",
-                               new_mute ? "" : "un", new_level);
-
-       rc = volume_set_status(new_mute | new_level);
+       if (tp_features.mixer_no_level_control) {
+               tpacpi_disclose_usertask("procfs volume", "%smute\n",
+                                       new_mute ? "" : "un");
+               rc = volume_set_mute(!!new_mute);
+       } else {
+               tpacpi_disclose_usertask("procfs volume",
+                                       "%smute and set level to %d\n",
+                                       new_mute ? "" : "un", new_level);
+               rc = volume_set_status(new_mute | new_level);
+       }
 
        return (rc == -EINTR) ? -ERESTARTSYS : rc;
 }
                 "Selects volume control strategy: "
                 "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
 
+module_param_named(volume_capabilities, volume_capabilities, uint, 0444);
+MODULE_PARM_DESC(volume_capabilities,
+                "Selects the mixer capabilites: "
+                "0=auto, 1=volume and mute, 2=mute only");
+
 #define TPACPI_PARAM(feature) \
        module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
        MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \