Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
[pandora-kernel.git] / drivers / platform / x86 / acer-wmi.c
index af2bb20..b848277 100644 (file)
@@ -190,6 +190,7 @@ enum interface_flags {
        ACER_AMW0,
        ACER_AMW0_V2,
        ACER_WMID,
+       ACER_WMID_v2,
 };
 
 #define ACER_DEFAULT_WIRELESS  0
@@ -205,6 +206,7 @@ static int threeg = -1;
 static int force_series;
 static bool ec_raw_mode;
 static bool has_type_aa;
+static u16 commun_func_bitmap;
 
 module_param(mailled, int, 0444);
 module_param(brightness, int, 0444);
@@ -464,6 +466,15 @@ static struct dmi_system_id acer_quirks[] = {
                },
                .driver_data = &quirk_lenovo_ideapad_s205,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Lenovo 3000 N200",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"),
+               },
+               .driver_data = &quirk_fujitsu_amilo_li_1718,
+       },
        {}
 };
 
@@ -868,6 +879,174 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
        return WMI_execute_u32(method_id, (u32)value, NULL);
 }
 
+static acpi_status wmid3_get_device_status(u32 *value, u16 device)
+{
+       struct wmid3_gds_return_value return_value;
+       acpi_status status;
+       union acpi_object *obj;
+       struct wmid3_gds_input_param params = {
+               .function_num = 0x1,
+               .hotkey_number = 0x01,
+               .devices = device,
+       };
+       struct acpi_buffer input = {
+               sizeof(struct wmid3_gds_input_param),
+               &params
+       };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = output.pointer;
+
+       if (!obj)
+               return AE_ERROR;
+       else if (obj->type != ACPI_TYPE_BUFFER) {
+               kfree(obj);
+               return AE_ERROR;
+       }
+       if (obj->buffer.length != 8) {
+               pr_warn("Unknown buffer length %d\n", obj->buffer.length);
+               kfree(obj);
+               return AE_ERROR;
+       }
+
+       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n",
+                       device,
+                       return_value.error_code,
+                       return_value.ec_return_value);
+       else
+               *value = !!(return_value.devices & device);
+
+       return status;
+}
+
+static acpi_status wmid_v2_get_u32(u32 *value, u32 cap)
+{
+       u16 device;
+
+       switch (cap) {
+       case ACER_CAP_WIRELESS:
+               device = ACER_WMID3_GDS_WIRELESS;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               device = ACER_WMID3_GDS_BLUETOOTH;
+               break;
+       case ACER_CAP_THREEG:
+               device = ACER_WMID3_GDS_THREEG;
+               break;
+       default:
+               return AE_ERROR;
+       }
+       return wmid3_get_device_status(value, device);
+}
+
+static acpi_status wmid3_set_device_status(u32 value, u16 device)
+{
+       struct wmid3_gds_return_value return_value;
+       acpi_status status;
+       union acpi_object *obj;
+       u16 devices;
+       struct wmid3_gds_input_param params = {
+               .function_num = 0x1,
+               .hotkey_number = 0x01,
+               .devices = commun_func_bitmap,
+       };
+       struct acpi_buffer input = {
+               sizeof(struct wmid3_gds_input_param),
+               &params
+       };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = output.pointer;
+
+       if (!obj)
+               return AE_ERROR;
+       else if (obj->type != ACPI_TYPE_BUFFER) {
+               kfree(obj);
+               return AE_ERROR;
+       }
+       if (obj->buffer.length != 8) {
+               pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+               kfree(obj);
+               return AE_ERROR;
+       }
+
+       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       if (return_value.error_code || return_value.ec_return_value) {
+               pr_warning("Get Current Device Status failed: "
+                       "0x%x - 0x%x\n", return_value.error_code,
+                       return_value.ec_return_value);
+               return status;
+       }
+
+       devices = return_value.devices;
+       params.function_num = 0x2;
+       params.hotkey_number = 0x01;
+       params.devices = (value) ? (devices | device) : (devices & ~device);
+
+       status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = output2.pointer;
+
+       if (!obj)
+               return AE_ERROR;
+       else if (obj->type != ACPI_TYPE_BUFFER) {
+               kfree(obj);
+               return AE_ERROR;
+       }
+       if (obj->buffer.length != 4) {
+               pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+               kfree(obj);
+               return AE_ERROR;
+       }
+
+       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               pr_warning("Set Device Status failed: "
+                       "0x%x - 0x%x\n", return_value.error_code,
+                       return_value.ec_return_value);
+
+       return status;
+}
+
+static acpi_status wmid_v2_set_u32(u32 value, u32 cap)
+{
+       u16 device;
+
+       switch (cap) {
+       case ACER_CAP_WIRELESS:
+               device = ACER_WMID3_GDS_WIRELESS;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               device = ACER_WMID3_GDS_BLUETOOTH;
+               break;
+       case ACER_CAP_THREEG:
+               device = ACER_WMID3_GDS_THREEG;
+               break;
+       default:
+               return AE_ERROR;
+       }
+       return wmid3_set_device_status(value, device);
+}
+
 static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
 {
        struct hotkey_function_type_aa *type_aa;
@@ -881,6 +1060,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
 
        pr_info("Function bitmap for Communication Button: 0x%x\n",
                type_aa->commun_func_bitmap);
+       commun_func_bitmap = type_aa->commun_func_bitmap;
 
        if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
                interface->capability |= ACER_CAP_WIRELESS;
@@ -913,17 +1093,13 @@ static acpi_status WMID_set_capabilities(void)
                return AE_ERROR;
        }
 
-       dmi_walk(type_aa_dmi_decode, NULL);
-       if (!has_type_aa) {
+       pr_info("Function bitmap for Communication Device: 0x%x\n", devices);
+       if (devices & 0x07)
                interface->capability |= ACER_CAP_WIRELESS;
-               if (devices & 0x40)
-                       interface->capability |= ACER_CAP_THREEG;
-               if (devices & 0x10)
-                       interface->capability |= ACER_CAP_BLUETOOTH;
-       }
-
-       /* WMID always provides brightness methods */
-       interface->capability |= ACER_CAP_BRIGHTNESS;
+       if (devices & 0x40)
+               interface->capability |= ACER_CAP_THREEG;
+       if (devices & 0x10)
+               interface->capability |= ACER_CAP_BLUETOOTH;
 
        if (!(devices & 0x20))
                max_brightness = 0x9;
@@ -936,6 +1112,10 @@ static struct wmi_interface wmid_interface = {
        .type = ACER_WMID,
 };
 
+static struct wmi_interface wmid_v2_interface = {
+       .type = ACER_WMID_v2,
+};
+
 /*
  * Generic Device (interface-independent)
  */
@@ -956,6 +1136,14 @@ static acpi_status get_u32(u32 *value, u32 cap)
        case ACER_WMID:
                status = WMID_get_u32(value, cap, interface);
                break;
+       case ACER_WMID_v2:
+               if (cap & (ACER_CAP_WIRELESS |
+                          ACER_CAP_BLUETOOTH |
+                          ACER_CAP_THREEG))
+                       status = wmid_v2_get_u32(value, cap);
+               else if (wmi_has_guid(WMID_GUID2))
+                       status = WMID_get_u32(value, cap, interface);
+               break;
        }
 
        return status;
@@ -989,6 +1177,13 @@ static acpi_status set_u32(u32 value, u32 cap)
                        }
                case ACER_WMID:
                        return WMID_set_u32(value, cap, interface);
+               case ACER_WMID_v2:
+                       if (cap & (ACER_CAP_WIRELESS |
+                                  ACER_CAP_BLUETOOTH |
+                                  ACER_CAP_THREEG))
+                               return wmid_v2_set_u32(value, cap);
+                       else if (wmi_has_guid(WMID_GUID2))
+                               return WMID_set_u32(value, cap, interface);
                default:
                        return AE_BAD_PARAMETER;
                }
@@ -1095,186 +1290,6 @@ static void acer_backlight_exit(void)
        backlight_device_unregister(acer_backlight_device);
 }
 
-static acpi_status wmid3_get_device_status(u32 *value, u16 device)
-{
-       struct wmid3_gds_return_value return_value;
-       acpi_status status;
-       union acpi_object *obj;
-       struct wmid3_gds_input_param params = {
-               .function_num = 0x1,
-               .hotkey_number = 0x01,
-               .devices = device,
-       };
-       struct acpi_buffer input = {
-               sizeof(struct wmid3_gds_input_param),
-               &params
-       };
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = output.pointer;
-
-       if (!obj)
-               return AE_ERROR;
-       else if (obj->type != ACPI_TYPE_BUFFER) {
-               kfree(obj);
-               return AE_ERROR;
-       }
-       if (obj->buffer.length != 8) {
-               pr_warn("Unknown buffer length %d\n", obj->buffer.length);
-               kfree(obj);
-               return AE_ERROR;
-       }
-
-       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
-       kfree(obj);
-
-       if (return_value.error_code || return_value.ec_return_value)
-               pr_warn("Get Device Status failed: 0x%x - 0x%x\n",
-                       return_value.error_code,
-                       return_value.ec_return_value);
-       else
-               *value = !!(return_value.devices & device);
-
-       return status;
-}
-
-static acpi_status get_device_status(u32 *value, u32 cap)
-{
-       if (wmi_has_guid(WMID_GUID3)) {
-               u16 device;
-
-               switch (cap) {
-               case ACER_CAP_WIRELESS:
-                       device = ACER_WMID3_GDS_WIRELESS;
-                       break;
-               case ACER_CAP_BLUETOOTH:
-                       device = ACER_WMID3_GDS_BLUETOOTH;
-                       break;
-               case ACER_CAP_THREEG:
-                       device = ACER_WMID3_GDS_THREEG;
-                       break;
-               default:
-                       return AE_ERROR;
-               }
-               return wmid3_get_device_status(value, device);
-
-       } else {
-               return get_u32(value, cap);
-       }
-}
-
-static acpi_status wmid3_set_device_status(u32 value, u16 device)
-{
-       struct wmid3_gds_return_value return_value;
-       acpi_status status;
-       union acpi_object *obj;
-       u16 devices;
-       struct wmid3_gds_input_param params = {
-               .function_num = 0x1,
-               .hotkey_number = 0x01,
-               .devices = ACER_WMID3_GDS_WIRELESS |
-                               ACER_WMID3_GDS_THREEG |
-                               ACER_WMID3_GDS_WIMAX |
-                               ACER_WMID3_GDS_BLUETOOTH,
-       };
-       struct acpi_buffer input = {
-               sizeof(struct wmid3_gds_input_param),
-               &params
-       };
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-       struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = output.pointer;
-
-       if (!obj)
-               return AE_ERROR;
-       else if (obj->type != ACPI_TYPE_BUFFER) {
-               kfree(obj);
-               return AE_ERROR;
-       }
-       if (obj->buffer.length != 8) {
-               pr_warning("Unknown buffer length %d\n", obj->buffer.length);
-               kfree(obj);
-               return AE_ERROR;
-       }
-
-       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
-       kfree(obj);
-
-       if (return_value.error_code || return_value.ec_return_value) {
-               pr_warning("Get Current Device Status failed: "
-                       "0x%x - 0x%x\n", return_value.error_code,
-                       return_value.ec_return_value);
-               return status;
-       }
-
-       devices = return_value.devices;
-       params.function_num = 0x2;
-       params.hotkey_number = 0x01;
-       params.devices = (value) ? (devices | device) : (devices & ~device);
-
-       status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = output2.pointer;
-
-       if (!obj)
-               return AE_ERROR;
-       else if (obj->type != ACPI_TYPE_BUFFER) {
-               kfree(obj);
-               return AE_ERROR;
-       }
-       if (obj->buffer.length != 4) {
-               pr_warning("Unknown buffer length %d\n", obj->buffer.length);
-               kfree(obj);
-               return AE_ERROR;
-       }
-
-       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
-       kfree(obj);
-
-       if (return_value.error_code || return_value.ec_return_value)
-               pr_warning("Set Device Status failed: "
-                       "0x%x - 0x%x\n", return_value.error_code,
-                       return_value.ec_return_value);
-
-       return status;
-}
-
-static acpi_status set_device_status(u32 value, u32 cap)
-{
-       if (wmi_has_guid(WMID_GUID3)) {
-               u16 device;
-
-               switch (cap) {
-               case ACER_CAP_WIRELESS:
-                       device = ACER_WMID3_GDS_WIRELESS;
-                       break;
-               case ACER_CAP_BLUETOOTH:
-                       device = ACER_WMID3_GDS_BLUETOOTH;
-                       break;
-               case ACER_CAP_THREEG:
-                       device = ACER_WMID3_GDS_THREEG;
-                       break;
-               default:
-                       return AE_ERROR;
-               }
-               return wmid3_set_device_status(value, device);
-
-       } else {
-               return set_u32(value, cap);
-       }
-}
-
 /*
  * Rfkill devices
  */
@@ -1285,12 +1300,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
        u32 state;
        acpi_status status;
 
-       status = get_u32(&state, ACER_CAP_WIRELESS);
-       if (ACPI_SUCCESS(status)) {
-               if (quirks->wireless == 3) {
-                       rfkill_set_hw_state(wireless_rfkill, !state);
-               } else {
-                       rfkill_set_sw_state(wireless_rfkill, !state);
+       if (has_cap(ACER_CAP_WIRELESS)) {
+               status = get_u32(&state, ACER_CAP_WIRELESS);
+               if (ACPI_SUCCESS(status)) {
+                       if (quirks->wireless == 3)
+                               rfkill_set_hw_state(wireless_rfkill, !state);
+                       else
+                               rfkill_set_sw_state(wireless_rfkill, !state);
                }
        }
 
@@ -1301,8 +1317,7 @@ static void acer_rfkill_update(struct work_struct *ignored)
        }
 
        if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
-               status = wmid3_get_device_status(&state,
-                               ACER_WMID3_GDS_THREEG);
+               status = get_u32(&state, ACER_WMID3_GDS_THREEG);
                if (ACPI_SUCCESS(status))
                        rfkill_set_sw_state(threeg_rfkill, !state);
        }
@@ -1316,7 +1331,7 @@ static int acer_rfkill_set(void *data, bool blocked)
        u32 cap = (unsigned long)data;
 
        if (rfkill_inited) {
-               status = set_device_status(!blocked, cap);
+               status = set_u32(!blocked, cap);
                if (ACPI_FAILURE(status))
                        return -ENODEV;
        }
@@ -1343,7 +1358,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
        if (!rfkill_dev)
                return ERR_PTR(-ENOMEM);
 
-       status = get_device_status(&state, cap);
+       status = get_u32(&state, cap);
 
        err = rfkill_register(rfkill_dev);
        if (err) {
@@ -1359,19 +1374,24 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
 
 static int acer_rfkill_init(struct device *dev)
 {
-       wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
-               "acer-wireless", ACER_CAP_WIRELESS);
-       if (IS_ERR(wireless_rfkill))
-               return PTR_ERR(wireless_rfkill);
+       int err;
+
+       if (has_cap(ACER_CAP_WIRELESS)) {
+               wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
+                       "acer-wireless", ACER_CAP_WIRELESS);
+               if (IS_ERR(wireless_rfkill)) {
+                       err = PTR_ERR(wireless_rfkill);
+                       goto error_wireless;
+               }
+       }
 
        if (has_cap(ACER_CAP_BLUETOOTH)) {
                bluetooth_rfkill = acer_rfkill_register(dev,
                        RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
                        ACER_CAP_BLUETOOTH);
                if (IS_ERR(bluetooth_rfkill)) {
-                       rfkill_unregister(wireless_rfkill);
-                       rfkill_destroy(wireless_rfkill);
-                       return PTR_ERR(bluetooth_rfkill);
+                       err = PTR_ERR(bluetooth_rfkill);
+                       goto error_bluetooth;
                }
        }
 
@@ -1380,30 +1400,44 @@ static int acer_rfkill_init(struct device *dev)
                        RFKILL_TYPE_WWAN, "acer-threeg",
                        ACER_CAP_THREEG);
                if (IS_ERR(threeg_rfkill)) {
-                       rfkill_unregister(wireless_rfkill);
-                       rfkill_destroy(wireless_rfkill);
-                       rfkill_unregister(bluetooth_rfkill);
-                       rfkill_destroy(bluetooth_rfkill);
-                       return PTR_ERR(threeg_rfkill);
+                       err = PTR_ERR(threeg_rfkill);
+                       goto error_threeg;
                }
        }
 
        rfkill_inited = true;
 
-       if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+       if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
+           has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
                schedule_delayed_work(&acer_rfkill_work,
                        round_jiffies_relative(HZ));
 
        return 0;
+
+error_threeg:
+       if (has_cap(ACER_CAP_BLUETOOTH)) {
+               rfkill_unregister(bluetooth_rfkill);
+               rfkill_destroy(bluetooth_rfkill);
+       }
+error_bluetooth:
+       if (has_cap(ACER_CAP_WIRELESS)) {
+               rfkill_unregister(wireless_rfkill);
+               rfkill_destroy(wireless_rfkill);
+       }
+error_wireless:
+       return err;
 }
 
 static void acer_rfkill_exit(void)
 {
-       if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+       if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
+           has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
                cancel_delayed_work_sync(&acer_rfkill_work);
 
-       rfkill_unregister(wireless_rfkill);
-       rfkill_destroy(wireless_rfkill);
+       if (has_cap(ACER_CAP_WIRELESS)) {
+               rfkill_unregister(wireless_rfkill);
+               rfkill_destroy(wireless_rfkill);
+       }
 
        if (has_cap(ACER_CAP_BLUETOOTH)) {
                rfkill_unregister(bluetooth_rfkill);
@@ -1428,11 +1462,7 @@ static ssize_t show_bool_threeg(struct device *dev,
 
        pr_info("This threeg sysfs will be removed in 2012"
                " - used by: %s\n", current->comm);
-       if (wmi_has_guid(WMID_GUID3))
-               status = wmid3_get_device_status(&result,
-                               ACER_WMID3_GDS_THREEG);
-       else
-               status = get_u32(&result, ACER_CAP_THREEG);
+       status = get_u32(&result, ACER_CAP_THREEG);
        if (ACPI_SUCCESS(status))
                return sprintf(buf, "%u\n", result);
        return sprintf(buf, "Read error\n");
@@ -1464,6 +1494,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
                return sprintf(buf, "AMW0 v2\n");
        case ACER_WMID:
                return sprintf(buf, "WMID\n");
+       case ACER_WMID_v2:
+               return sprintf(buf, "WMID v2\n");
        default:
                return sprintf(buf, "Error!\n");
        }
@@ -1883,12 +1915,20 @@ static int __init acer_wmi_init(void)
        if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
                interface = &wmid_interface;
 
+       if (wmi_has_guid(WMID_GUID3))
+               interface = &wmid_v2_interface;
+
+       if (interface)
+               dmi_walk(type_aa_dmi_decode, NULL);
+
        if (wmi_has_guid(WMID_GUID2) && interface) {
-               if (ACPI_FAILURE(WMID_set_capabilities())) {
+               if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) {
                        pr_err("Unable to detect available WMID devices\n");
                        return -ENODEV;
                }
-       } else if (!wmi_has_guid(WMID_GUID2) && interface) {
+               /* WMID always provides brightness methods */
+               interface->capability |= ACER_CAP_BRIGHTNESS;
+       } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) {
                pr_err("No WMID device detection method found\n");
                return -ENODEV;
        }
@@ -1912,7 +1952,7 @@ static int __init acer_wmi_init(void)
 
        set_quirks();
 
-       if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
+       if (acpi_video_backlight_support()) {
                interface->capability &= ~ACER_CAP_BRIGHTNESS;
                pr_info("Brightness must be controlled by "
                       "generic video driver\n");