| .. | .. |
|---|
| 30 | 30 | {"INT33D6", 0}, |
|---|
| 31 | 31 | {"", 0}, |
|---|
| 32 | 32 | }; |
|---|
| 33 | +MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids); |
|---|
| 33 | 34 | |
|---|
| 34 | 35 | /* In theory, these are HID usages. */ |
|---|
| 35 | 36 | static const struct key_entry intel_vbtn_keymap[] = { |
|---|
| .. | .. |
|---|
| 66 | 67 | struct intel_vbtn_priv { |
|---|
| 67 | 68 | struct key_entry keymap[KEYMAP_LEN]; |
|---|
| 68 | 69 | struct input_dev *input_dev; |
|---|
| 70 | + bool has_buttons; |
|---|
| 69 | 71 | bool has_switches; |
|---|
| 70 | 72 | bool wakeup_mode; |
|---|
| 71 | 73 | }; |
|---|
| 74 | + |
|---|
| 75 | +static void detect_tablet_mode(struct platform_device *device) |
|---|
| 76 | +{ |
|---|
| 77 | + struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); |
|---|
| 78 | + acpi_handle handle = ACPI_HANDLE(&device->dev); |
|---|
| 79 | + unsigned long long vgbs; |
|---|
| 80 | + acpi_status status; |
|---|
| 81 | + int m; |
|---|
| 82 | + |
|---|
| 83 | + status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs); |
|---|
| 84 | + if (ACPI_FAILURE(status)) |
|---|
| 85 | + return; |
|---|
| 86 | + |
|---|
| 87 | + m = !(vgbs & VGBS_TABLET_MODE_FLAGS); |
|---|
| 88 | + input_report_switch(priv->input_dev, SW_TABLET_MODE, m); |
|---|
| 89 | + m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0; |
|---|
| 90 | + input_report_switch(priv->input_dev, SW_DOCK, m); |
|---|
| 91 | +} |
|---|
| 72 | 92 | |
|---|
| 73 | 93 | static int intel_vbtn_input_setup(struct platform_device *device) |
|---|
| 74 | 94 | { |
|---|
| 75 | 95 | struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); |
|---|
| 76 | 96 | int ret, keymap_len = 0; |
|---|
| 77 | 97 | |
|---|
| 78 | | - if (true) { |
|---|
| 98 | + if (priv->has_buttons) { |
|---|
| 79 | 99 | memcpy(&priv->keymap[keymap_len], intel_vbtn_keymap, |
|---|
| 80 | 100 | ARRAY_SIZE(intel_vbtn_keymap) * |
|---|
| 81 | 101 | sizeof(struct key_entry)); |
|---|
| .. | .. |
|---|
| 102 | 122 | priv->input_dev->dev.parent = &device->dev; |
|---|
| 103 | 123 | priv->input_dev->name = "Intel Virtual Button driver"; |
|---|
| 104 | 124 | priv->input_dev->id.bustype = BUS_HOST; |
|---|
| 125 | + |
|---|
| 126 | + if (priv->has_switches) |
|---|
| 127 | + detect_tablet_mode(device); |
|---|
| 105 | 128 | |
|---|
| 106 | 129 | return input_register_device(priv->input_dev); |
|---|
| 107 | 130 | } |
|---|
| .. | .. |
|---|
| 148 | 171 | dev_dbg(&device->dev, "unknown event index 0x%x\n", event); |
|---|
| 149 | 172 | } |
|---|
| 150 | 173 | |
|---|
| 151 | | -static void detect_tablet_mode(struct platform_device *device) |
|---|
| 174 | +static bool intel_vbtn_has_buttons(acpi_handle handle) |
|---|
| 152 | 175 | { |
|---|
| 153 | | - struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); |
|---|
| 154 | | - acpi_handle handle = ACPI_HANDLE(&device->dev); |
|---|
| 155 | | - unsigned long long vgbs; |
|---|
| 156 | 176 | acpi_status status; |
|---|
| 157 | | - int m; |
|---|
| 158 | 177 | |
|---|
| 159 | | - status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs); |
|---|
| 160 | | - if (ACPI_FAILURE(status)) |
|---|
| 161 | | - return; |
|---|
| 162 | | - |
|---|
| 163 | | - m = !(vgbs & VGBS_TABLET_MODE_FLAGS); |
|---|
| 164 | | - input_report_switch(priv->input_dev, SW_TABLET_MODE, m); |
|---|
| 165 | | - m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0; |
|---|
| 166 | | - input_report_switch(priv->input_dev, SW_DOCK, m); |
|---|
| 178 | + status = acpi_evaluate_object(handle, "VBDL", NULL, NULL); |
|---|
| 179 | + return ACPI_SUCCESS(status); |
|---|
| 167 | 180 | } |
|---|
| 168 | 181 | |
|---|
| 169 | 182 | /* |
|---|
| .. | .. |
|---|
| 235 | 248 | static int intel_vbtn_probe(struct platform_device *device) |
|---|
| 236 | 249 | { |
|---|
| 237 | 250 | acpi_handle handle = ACPI_HANDLE(&device->dev); |
|---|
| 251 | + bool has_buttons, has_switches; |
|---|
| 238 | 252 | struct intel_vbtn_priv *priv; |
|---|
| 239 | 253 | acpi_status status; |
|---|
| 240 | 254 | int err; |
|---|
| 241 | 255 | |
|---|
| 242 | | - status = acpi_evaluate_object(handle, "VBDL", NULL, NULL); |
|---|
| 243 | | - if (ACPI_FAILURE(status)) { |
|---|
| 256 | + has_buttons = intel_vbtn_has_buttons(handle); |
|---|
| 257 | + has_switches = intel_vbtn_has_switches(handle); |
|---|
| 258 | + |
|---|
| 259 | + if (!has_buttons && !has_switches) { |
|---|
| 244 | 260 | dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n"); |
|---|
| 245 | 261 | return -ENODEV; |
|---|
| 246 | 262 | } |
|---|
| .. | .. |
|---|
| 250 | 266 | return -ENOMEM; |
|---|
| 251 | 267 | dev_set_drvdata(&device->dev, priv); |
|---|
| 252 | 268 | |
|---|
| 253 | | - priv->has_switches = intel_vbtn_has_switches(handle); |
|---|
| 269 | + priv->has_buttons = has_buttons; |
|---|
| 270 | + priv->has_switches = has_switches; |
|---|
| 254 | 271 | |
|---|
| 255 | 272 | err = intel_vbtn_input_setup(device); |
|---|
| 256 | 273 | if (err) { |
|---|
| 257 | 274 | pr_err("Failed to setup Intel Virtual Button\n"); |
|---|
| 258 | 275 | return err; |
|---|
| 259 | 276 | } |
|---|
| 260 | | - |
|---|
| 261 | | - if (priv->has_switches) |
|---|
| 262 | | - detect_tablet_mode(device); |
|---|
| 263 | 277 | |
|---|
| 264 | 278 | status = acpi_install_notify_handler(handle, |
|---|
| 265 | 279 | ACPI_DEVICE_NOTIFY, |
|---|
| .. | .. |
|---|
| 269 | 283 | return -EBUSY; |
|---|
| 270 | 284 | |
|---|
| 271 | 285 | device_init_wakeup(&device->dev, true); |
|---|
| 286 | + /* |
|---|
| 287 | + * In order for system wakeup to work, the EC GPE has to be marked as |
|---|
| 288 | + * a wakeup one, so do that here (this setting will persist, but it has |
|---|
| 289 | + * no effect until the wakeup mask is set for the EC GPE). |
|---|
| 290 | + */ |
|---|
| 291 | + acpi_ec_mark_gpe_for_wake(); |
|---|
| 272 | 292 | return 0; |
|---|
| 273 | 293 | } |
|---|
| 274 | 294 | |
|---|
| .. | .. |
|---|
| 288 | 308 | |
|---|
| 289 | 309 | static int intel_vbtn_pm_prepare(struct device *dev) |
|---|
| 290 | 310 | { |
|---|
| 291 | | - struct intel_vbtn_priv *priv = dev_get_drvdata(dev); |
|---|
| 311 | + if (device_may_wakeup(dev)) { |
|---|
| 312 | + struct intel_vbtn_priv *priv = dev_get_drvdata(dev); |
|---|
| 292 | 313 | |
|---|
| 293 | | - priv->wakeup_mode = true; |
|---|
| 314 | + priv->wakeup_mode = true; |
|---|
| 315 | + } |
|---|
| 294 | 316 | return 0; |
|---|
| 295 | 317 | } |
|---|
| 296 | 318 | |
|---|
| 297 | | -static int intel_vbtn_pm_resume(struct device *dev) |
|---|
| 319 | +static void intel_vbtn_pm_complete(struct device *dev) |
|---|
| 298 | 320 | { |
|---|
| 299 | 321 | struct intel_vbtn_priv *priv = dev_get_drvdata(dev); |
|---|
| 300 | 322 | |
|---|
| 301 | 323 | priv->wakeup_mode = false; |
|---|
| 324 | +} |
|---|
| 325 | + |
|---|
| 326 | +static int intel_vbtn_pm_resume(struct device *dev) |
|---|
| 327 | +{ |
|---|
| 328 | + intel_vbtn_pm_complete(dev); |
|---|
| 302 | 329 | return 0; |
|---|
| 303 | 330 | } |
|---|
| 304 | 331 | |
|---|
| 305 | 332 | static const struct dev_pm_ops intel_vbtn_pm_ops = { |
|---|
| 306 | 333 | .prepare = intel_vbtn_pm_prepare, |
|---|
| 334 | + .complete = intel_vbtn_pm_complete, |
|---|
| 307 | 335 | .resume = intel_vbtn_pm_resume, |
|---|
| 308 | 336 | .restore = intel_vbtn_pm_resume, |
|---|
| 309 | 337 | .thaw = intel_vbtn_pm_resume, |
|---|
| .. | .. |
|---|
| 318 | 346 | .probe = intel_vbtn_probe, |
|---|
| 319 | 347 | .remove = intel_vbtn_remove, |
|---|
| 320 | 348 | }; |
|---|
| 321 | | -MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids); |
|---|
| 322 | 349 | |
|---|
| 323 | 350 | static acpi_status __init |
|---|
| 324 | 351 | check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) |
|---|