msi-laptop: merge quirk tables to one
authorLee, Chun-Yi <jlee@suse.com>
Sat, 15 Dec 2012 17:31:27 +0000 (19:31 +0200)
committerMatthew Garrett <matthew.garrett@nebula.com>
Sun, 24 Feb 2013 22:49:53 +0000 (14:49 -0800)
This patch introduced a quirk_entry struct, then we merged all quirk
tables to msi_dmi_table. Then we can more easily to set different quirk
attributes for different machine.

Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
Changed this patch so that it could be applied before MSI Wind U100
support patch. Changed rfkill logic for ec_read_only quirk support.
Removed delays if ec_delay = false.

Signed-off-by: Maxim Mikityanskiy <maxtram95@gmail.com>
Acked-by: Lee, Chun-Yi <jlee@suse.com>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
drivers/platform/x86/msi-laptop.c

index 7ba107ae1d097dd84a891d246585477c24a70151..0bf94b5c27448da263cfee64c0757eb2c31a2aa3 100644 (file)
@@ -108,23 +108,38 @@ static const struct key_entry msi_laptop_keymap[] = {
 
 static struct input_dev *msi_laptop_input_dev;
 
-static bool old_ec_model;
 static int wlan_s, bluetooth_s, threeg_s;
 static int threeg_exists;
-
-/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
- * those netbook will load the SCM (windows app) to disable the original
- * Wlan/Bluetooth control by BIOS when user press fn key, then control
- * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
- * cann't on/off 3G module on those 3G netbook.
- * On Linux, msi-laptop driver will do the same thing to disable the
- * original BIOS control, then might need use HAL or other userland
- * application to do the software control that simulate with SCM.
- * e.g. MSI N034 netbook
- */
-static bool load_scm_model;
 static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
 
+/* MSI laptop quirks */
+struct quirk_entry {
+       bool old_ec_model;
+
+       /* Some MSI 3G netbook only have one fn key to control
+        * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
+        * disable the original Wlan/Bluetooth control by BIOS when user press
+        * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
+        * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
+        * On Linux, msi-laptop driver will do the same thing to disable the
+        * original BIOS control, then might need use HAL or other userland
+        * application to do the software control that simulate with SCM.
+        * e.g. MSI N034 netbook
+        */
+       bool load_scm_model;
+
+       /* Some MSI laptops need delay before reading from EC */
+       bool ec_delay;
+
+       /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
+        * some features working (e.g. ECO mode), but we cannot change
+        * Wlan/Bluetooth state in software and we can only read its state.
+        */
+       bool ec_read_only;
+};
+
+static struct quirk_entry *quirks;
+
 /* Hardware access */
 
 static int set_lcd_level(int level)
@@ -195,6 +210,9 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
        if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
                return -EINVAL;
 
+       if (quirks->ec_read_only)
+               return -EOPNOTSUPP;
+
        /* read current device state */
        result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
        if (result < 0)
@@ -293,7 +311,7 @@ static ssize_t show_wlan(struct device *dev,
 
        int ret, enabled = 0;
 
-       if (old_ec_model) {
+       if (quirks->old_ec_model) {
                ret = get_wireless_state(&enabled, NULL);
        } else {
                ret = get_wireless_state_ec_standard();
@@ -317,7 +335,7 @@ static ssize_t show_bluetooth(struct device *dev,
 
        int ret, enabled = 0;
 
-       if (old_ec_model) {
+       if (quirks->old_ec_model) {
                ret = get_wireless_state(NULL, &enabled);
        } else {
                ret = get_wireless_state_ec_standard();
@@ -342,7 +360,7 @@ static ssize_t show_threeg(struct device *dev,
        int ret;
 
        /* old msi ec not support 3G */
-       if (old_ec_model)
+       if (quirks->old_ec_model)
                return -ENODEV;
 
        ret = get_wireless_state_ec_standard();
@@ -448,9 +466,26 @@ static struct platform_device *msipf_device;
 
 /* Initialization */
 
-static int dmi_check_cb(const struct dmi_system_id *id)
+static struct quirk_entry quirk_old_ec_model = {
+       .old_ec_model = true,
+};
+
+static struct quirk_entry quirk_load_scm_model = {
+       .load_scm_model = true,
+       .ec_delay = true,
+};
+
+static struct quirk_entry quirk_load_scm_ro_model = {
+       .load_scm_model = true,
+       .ec_read_only = true,
+};
+
+static int dmi_check_cb(const struct dmi_system_id *dmi)
 {
-       pr_info("Identified laptop model '%s'\n", id->ident);
+       pr_info("Identified laptop model '%s'\n", dmi->ident);
+
+       quirks = dmi->driver_data;
+
        return 1;
 }
 
@@ -464,6 +499,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
                        DMI_MATCH(DMI_CHASSIS_VENDOR,
                                  "MICRO-STAR INT'L CO.,LTD")
                },
+               .driver_data = &quirk_old_ec_model,
                .callback = dmi_check_cb
        },
        {
@@ -474,6 +510,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
                        DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
                },
+               .driver_data = &quirk_old_ec_model,
                .callback = dmi_check_cb
        },
        {
@@ -484,6 +521,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
                        DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
                },
+               .driver_data = &quirk_old_ec_model,
                .callback = dmi_check_cb
        },
        {
@@ -495,12 +533,9 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
                        DMI_MATCH(DMI_CHASSIS_VENDOR,
                                  "MICRO-STAR INT'L CO.,LTD")
                },
+               .driver_data = &quirk_old_ec_model,
                .callback = dmi_check_cb
        },
-       { }
-};
-
-static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
        {
                .ident = "MSI N034",
                .matches = {
@@ -510,6 +545,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                        DMI_MATCH(DMI_CHASSIS_VENDOR,
                        "MICRO-STAR INTERNATIONAL CO., LTD")
                },
+               .driver_data = &quirk_load_scm_model,
                .callback = dmi_check_cb
        },
        {
@@ -521,6 +557,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                        DMI_MATCH(DMI_CHASSIS_VENDOR,
                        "MICRO-STAR INTERNATIONAL CO., LTD")
                },
+               .driver_data = &quirk_load_scm_model,
                .callback = dmi_check_cb
        },
        {
@@ -530,6 +567,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                                "MICRO-STAR INTERNATIONAL CO., LTD"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
                },
+               .driver_data = &quirk_load_scm_model,
                .callback = dmi_check_cb
        },
        {
@@ -539,6 +577,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                                "Micro-Star International"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
                },
+               .driver_data = &quirk_load_scm_model,
                .callback = dmi_check_cb
        },
        {
@@ -548,6 +587,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                                "Micro-Star International Co., Ltd."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
                },
+               .driver_data = &quirk_load_scm_model,
                .callback = dmi_check_cb
        },
        { }
@@ -560,32 +600,26 @@ static int rfkill_bluetooth_set(void *data, bool blocked)
         * blocked == false is on
         * blocked == true is off
         */
-       if (blocked)
-               set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
-       else
-               set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
+       int result = set_device_state(blocked ? "0" : "1", 0,
+                       MSI_STANDARD_EC_BLUETOOTH_MASK);
 
-       return 0;
+       return min(result, 0);
 }
 
 static int rfkill_wlan_set(void *data, bool blocked)
 {
-       if (blocked)
-               set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
-       else
-               set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
+       int result = set_device_state(blocked ? "0" : "1", 0,
+                       MSI_STANDARD_EC_WLAN_MASK);
 
-       return 0;
+       return min(result, 0);
 }
 
 static int rfkill_threeg_set(void *data, bool blocked)
 {
-       if (blocked)
-               set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
-       else
-               set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
+       int result = set_device_state(blocked ? "0" : "1", 0,
+                       MSI_STANDARD_EC_3G_MASK);
 
-       return 0;
+       return min(result, 0);
 }
 
 static const struct rfkill_ops rfkill_bluetooth_ops = {
@@ -618,18 +652,27 @@ static void rfkill_cleanup(void)
        }
 }
 
+static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
+{
+       if (quirks->ec_read_only)
+               return rfkill_set_hw_state(rfkill, blocked);
+       else
+               return rfkill_set_sw_state(rfkill, blocked);
+}
+
 static void msi_update_rfkill(struct work_struct *ignored)
 {
        get_wireless_state_ec_standard();
 
        if (rfk_wlan)
-               rfkill_set_sw_state(rfk_wlan, !wlan_s);
+               msi_rfkill_set_state(rfk_wlan, !wlan_s);
        if (rfk_bluetooth)
-               rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
+               msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
        if (rfk_threeg)
-               rfkill_set_sw_state(rfk_threeg, !threeg_s);
+               msi_rfkill_set_state(rfk_threeg, !threeg_s);
 }
-static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
+static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
+static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
 
 static void msi_send_touchpad_key(struct work_struct *ignored)
 {
@@ -644,7 +687,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
                (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
                KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
 }
-static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
+static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
+static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
 
 static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
                                struct serio *port)
@@ -662,14 +706,20 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
                extended = false;
                switch (data) {
                case 0xE4:
-                       schedule_delayed_work(&msi_touchpad_work,
-                               round_jiffies_relative(0.5 * HZ));
+                       if (quirks->ec_delay) {
+                               schedule_delayed_work(&msi_touchpad_dwork,
+                                       round_jiffies_relative(0.5 * HZ));
+                       } else
+                               schedule_work(&msi_touchpad_work);
                        break;
                case 0x54:
                case 0x62:
                case 0x76:
-                       schedule_delayed_work(&msi_rfkill_work,
-                               round_jiffies_relative(0.5 * HZ));
+                       if (quirks->ec_delay) {
+                               schedule_delayed_work(&msi_rfkill_dwork,
+                                       round_jiffies_relative(0.5 * HZ));
+                       } else
+                               schedule_work(&msi_rfkill_work);
                        break;
                }
        }
@@ -736,8 +786,11 @@ static int rfkill_init(struct platform_device *sdev)
        }
 
        /* schedule to run rfkill state initial */
-       schedule_delayed_work(&msi_rfkill_init,
-                               round_jiffies_relative(1 * HZ));
+       if (quirks->ec_delay) {
+               schedule_delayed_work(&msi_rfkill_init,
+                       round_jiffies_relative(1 * HZ));
+       } else
+               schedule_work(&msi_rfkill_work);
 
        return 0;
 
@@ -761,7 +814,7 @@ static int msi_laptop_resume(struct device *device)
        u8 data;
        int result;
 
-       if (!load_scm_model)
+       if (!quirks->load_scm_model)
                return 0;
 
        /* set load SCM to disable hardware control by fn key */
@@ -819,13 +872,15 @@ static int __init load_scm_model_init(struct platform_device *sdev)
        u8 data;
        int result;
 
-       /* allow userland write sysfs file  */
-       dev_attr_bluetooth.store = store_bluetooth;
-       dev_attr_wlan.store = store_wlan;
-       dev_attr_threeg.store = store_threeg;
-       dev_attr_bluetooth.attr.mode |= S_IWUSR;
-       dev_attr_wlan.attr.mode |= S_IWUSR;
-       dev_attr_threeg.attr.mode |= S_IWUSR;
+       if (!quirks->ec_read_only) {
+               /* allow userland write sysfs file  */
+               dev_attr_bluetooth.store = store_bluetooth;
+               dev_attr_wlan.store = store_wlan;
+               dev_attr_threeg.store = store_threeg;
+               dev_attr_bluetooth.attr.mode |= S_IWUSR;
+               dev_attr_wlan.attr.mode |= S_IWUSR;
+               dev_attr_threeg.attr.mode |= S_IWUSR;
+       }
 
        /* disable hardware control by fn key */
        result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
@@ -874,15 +929,16 @@ static int __init msi_init(void)
        if (acpi_disabled)
                return -ENODEV;
 
-       if (force || dmi_check_system(msi_dmi_table))
-               old_ec_model = 1;
+       dmi_check_system(msi_dmi_table);
+       if (!quirks)
+               /* quirks may be NULL if no match in DMI table */
+               quirks = &quirk_load_scm_model;
+       if (force)
+               quirks = &quirk_old_ec_model;
 
-       if (!old_ec_model)
+       if (!quirks->old_ec_model)
                get_threeg_exists();
 
-       if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
-               load_scm_model = 1;
-
        if (auto_brightness < 0 || auto_brightness > 2)
                return -EINVAL;
 
@@ -918,7 +974,7 @@ static int __init msi_init(void)
        if (ret)
                goto fail_platform_device1;
 
-       if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
+       if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
                ret = -EINVAL;
                goto fail_platform_device1;
        }
@@ -928,7 +984,7 @@ static int __init msi_init(void)
        if (ret)
                goto fail_platform_device2;
 
-       if (!old_ec_model) {
+       if (!quirks->old_ec_model) {
                if (threeg_exists)
                        ret = device_create_file(&msipf_device->dev,
                                                &dev_attr_threeg);
@@ -949,9 +1005,10 @@ static int __init msi_init(void)
 
 fail_platform_device2:
 
-       if (load_scm_model) {
+       if (quirks->load_scm_model) {
                i8042_remove_filter(msi_laptop_i8042_filter);
-               cancel_delayed_work_sync(&msi_rfkill_work);
+               cancel_delayed_work_sync(&msi_rfkill_dwork);
+               cancel_work_sync(&msi_rfkill_work);
                rfkill_cleanup();
        }
        platform_device_del(msipf_device);
@@ -973,15 +1030,16 @@ fail_backlight:
 
 static void __exit msi_cleanup(void)
 {
-       if (load_scm_model) {
+       if (quirks->load_scm_model) {
                i8042_remove_filter(msi_laptop_i8042_filter);
                msi_laptop_input_destroy();
-               cancel_delayed_work_sync(&msi_rfkill_work);
+               cancel_delayed_work_sync(&msi_rfkill_dwork);
+               cancel_work_sync(&msi_rfkill_work);
                rfkill_cleanup();
        }
 
        sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
-       if (!old_ec_model && threeg_exists)
+       if (!quirks->old_ec_model && threeg_exists)
                device_remove_file(&msipf_device->dev, &dev_attr_threeg);
        platform_device_unregister(msipf_device);
        platform_driver_unregister(&msipf_driver);