1 /* 2 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras 3 * 4 * Copyright © 2010 Intel Corporation 5 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 * 02110-1301, USA. 21 */ 22 23 #include <linux/kernel.h> 24 #include <linux/module.h> 25 #include <linux/init.h> 26 #include <linux/types.h> 27 #include <acpi/acpi_bus.h> 28 #include <acpi/acpi_drivers.h> 29 #include <linux/rfkill.h> 30 #include <linux/platform_device.h> 31 #include <linux/input.h> 32 #include <linux/input/sparse-keymap.h> 33 34 #define IDEAPAD_RFKILL_DEV_NUM (3) 35 36 struct ideapad_private { 37 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 38 struct platform_device *platform_device; 39 struct input_dev *inputdev; 40 }; 41 42 static acpi_handle ideapad_handle; 43 static bool no_bt_rfkill; 44 module_param(no_bt_rfkill, bool, 0444); 45 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 46 47 /* 48 * ACPI Helpers 49 */ 50 #define IDEAPAD_EC_TIMEOUT (100) /* in ms */ 51 52 static int read_method_int(acpi_handle handle, const char *method, int *val) 53 { 54 acpi_status status; 55 unsigned long long result; 56 57 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 58 if (ACPI_FAILURE(status)) { 59 *val = -1; 60 return -1; 61 } else { 62 *val = result; 63 return 0; 64 } 65 } 66 67 static int method_vpcr(acpi_handle handle, int cmd, int *ret) 68 { 69 acpi_status status; 70 unsigned long long result; 71 struct acpi_object_list params; 72 union acpi_object in_obj; 73 74 params.count = 1; 75 params.pointer = &in_obj; 76 in_obj.type = ACPI_TYPE_INTEGER; 77 in_obj.integer.value = cmd; 78 79 status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); 80 81 if (ACPI_FAILURE(status)) { 82 *ret = -1; 83 return -1; 84 } else { 85 *ret = result; 86 return 0; 87 } 88 } 89 90 static int method_vpcw(acpi_handle handle, int cmd, int data) 91 { 92 struct acpi_object_list params; 93 union acpi_object in_obj[2]; 94 acpi_status status; 95 96 params.count = 2; 97 params.pointer = in_obj; 98 in_obj[0].type = ACPI_TYPE_INTEGER; 99 in_obj[0].integer.value = cmd; 100 in_obj[1].type = ACPI_TYPE_INTEGER; 101 in_obj[1].integer.value = data; 102 103 status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); 104 if (status != AE_OK) 105 return -1; 106 return 0; 107 } 108 109 static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) 110 { 111 int val; 112 unsigned long int end_jiffies; 113 114 if (method_vpcw(handle, 1, cmd)) 115 return -1; 116 117 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 118 time_before(jiffies, end_jiffies);) { 119 schedule(); 120 if (method_vpcr(handle, 1, &val)) 121 return -1; 122 if (val == 0) { 123 if (method_vpcr(handle, 0, &val)) 124 return -1; 125 *data = val; 126 return 0; 127 } 128 } 129 pr_err("timeout in read_ec_cmd\n"); 130 return -1; 131 } 132 133 static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) 134 { 135 int val; 136 unsigned long int end_jiffies; 137 138 if (method_vpcw(handle, 0, data)) 139 return -1; 140 if (method_vpcw(handle, 1, cmd)) 141 return -1; 142 143 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 144 time_before(jiffies, end_jiffies);) { 145 schedule(); 146 if (method_vpcr(handle, 1, &val)) 147 return -1; 148 if (val == 0) 149 return 0; 150 } 151 pr_err("timeout in write_ec_cmd\n"); 152 return -1; 153 } 154 155 /* 156 * camera power 157 */ 158 static ssize_t show_ideapad_cam(struct device *dev, 159 struct device_attribute *attr, 160 char *buf) 161 { 162 unsigned long result; 163 164 if (read_ec_data(ideapad_handle, 0x1D, &result)) 165 return sprintf(buf, "-1\n"); 166 return sprintf(buf, "%lu\n", result); 167 } 168 169 static ssize_t store_ideapad_cam(struct device *dev, 170 struct device_attribute *attr, 171 const char *buf, size_t count) 172 { 173 int ret, state; 174 175 if (!count) 176 return 0; 177 if (sscanf(buf, "%i", &state) != 1) 178 return -EINVAL; 179 ret = write_ec_cmd(ideapad_handle, 0x1E, state); 180 if (ret < 0) 181 return ret; 182 return count; 183 } 184 185 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 186 187 /* 188 * Rfkill 189 */ 190 struct ideapad_rfk_data { 191 char *name; 192 int cfgbit; 193 int opcode; 194 int type; 195 }; 196 197 const struct ideapad_rfk_data ideapad_rfk_data[] = { 198 { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, 199 { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, 200 { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, 201 }; 202 203 static int ideapad_rfk_set(void *data, bool blocked) 204 { 205 unsigned long opcode = (unsigned long)data; 206 207 return write_ec_cmd(ideapad_handle, opcode, !blocked); 208 } 209 210 static struct rfkill_ops ideapad_rfk_ops = { 211 .set_block = ideapad_rfk_set, 212 }; 213 214 static void ideapad_sync_rfk_state(struct acpi_device *adevice) 215 { 216 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 217 unsigned long hw_blocked; 218 int i; 219 220 if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) 221 return; 222 hw_blocked = !hw_blocked; 223 224 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 225 if (priv->rfk[i]) 226 rfkill_set_hw_state(priv->rfk[i], hw_blocked); 227 } 228 229 static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, 230 int dev) 231 { 232 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 233 int ret; 234 unsigned long sw_blocked; 235 236 if (no_bt_rfkill && 237 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { 238 /* Force to enable bluetooth when no_bt_rfkill=1 */ 239 write_ec_cmd(ideapad_handle, 240 ideapad_rfk_data[dev].opcode, 1); 241 return 0; 242 } 243 244 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, 245 ideapad_rfk_data[dev].type, &ideapad_rfk_ops, 246 (void *)(long)dev); 247 if (!priv->rfk[dev]) 248 return -ENOMEM; 249 250 if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, 251 &sw_blocked)) { 252 rfkill_init_sw_state(priv->rfk[dev], 0); 253 } else { 254 sw_blocked = !sw_blocked; 255 rfkill_init_sw_state(priv->rfk[dev], sw_blocked); 256 } 257 258 ret = rfkill_register(priv->rfk[dev]); 259 if (ret) { 260 rfkill_destroy(priv->rfk[dev]); 261 return ret; 262 } 263 return 0; 264 } 265 266 static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, 267 int dev) 268 { 269 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 270 271 if (!priv->rfk[dev]) 272 return; 273 274 rfkill_unregister(priv->rfk[dev]); 275 rfkill_destroy(priv->rfk[dev]); 276 } 277 278 /* 279 * Platform device 280 */ 281 static struct attribute *ideapad_attributes[] = { 282 &dev_attr_camera_power.attr, 283 NULL 284 }; 285 286 static struct attribute_group ideapad_attribute_group = { 287 .attrs = ideapad_attributes 288 }; 289 290 static int __devinit ideapad_platform_init(struct ideapad_private *priv) 291 { 292 int result; 293 294 priv->platform_device = platform_device_alloc("ideapad", -1); 295 if (!priv->platform_device) 296 return -ENOMEM; 297 platform_set_drvdata(priv->platform_device, priv); 298 299 result = platform_device_add(priv->platform_device); 300 if (result) 301 goto fail_platform_device; 302 303 result = sysfs_create_group(&priv->platform_device->dev.kobj, 304 &ideapad_attribute_group); 305 if (result) 306 goto fail_sysfs; 307 return 0; 308 309 fail_sysfs: 310 platform_device_del(priv->platform_device); 311 fail_platform_device: 312 platform_device_put(priv->platform_device); 313 return result; 314 } 315 316 static void ideapad_platform_exit(struct ideapad_private *priv) 317 { 318 sysfs_remove_group(&priv->platform_device->dev.kobj, 319 &ideapad_attribute_group); 320 platform_device_unregister(priv->platform_device); 321 } 322 323 /* 324 * input device 325 */ 326 static const struct key_entry ideapad_keymap[] = { 327 { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, 328 { KE_KEY, 0x0D, { KEY_WLAN } }, 329 { KE_END, 0 }, 330 }; 331 332 static int __devinit ideapad_input_init(struct ideapad_private *priv) 333 { 334 struct input_dev *inputdev; 335 int error; 336 337 inputdev = input_allocate_device(); 338 if (!inputdev) { 339 pr_info("Unable to allocate input device\n"); 340 return -ENOMEM; 341 } 342 343 inputdev->name = "Ideapad extra buttons"; 344 inputdev->phys = "ideapad/input0"; 345 inputdev->id.bustype = BUS_HOST; 346 inputdev->dev.parent = &priv->platform_device->dev; 347 348 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); 349 if (error) { 350 pr_err("Unable to setup input device keymap\n"); 351 goto err_free_dev; 352 } 353 354 error = input_register_device(inputdev); 355 if (error) { 356 pr_err("Unable to register input device\n"); 357 goto err_free_keymap; 358 } 359 360 priv->inputdev = inputdev; 361 return 0; 362 363 err_free_keymap: 364 sparse_keymap_free(inputdev); 365 err_free_dev: 366 input_free_device(inputdev); 367 return error; 368 } 369 370 static void __devexit ideapad_input_exit(struct ideapad_private *priv) 371 { 372 sparse_keymap_free(priv->inputdev); 373 input_unregister_device(priv->inputdev); 374 priv->inputdev = NULL; 375 } 376 377 static void ideapad_input_report(struct ideapad_private *priv, 378 unsigned long scancode) 379 { 380 sparse_keymap_report_event(priv->inputdev, scancode, 1, true); 381 } 382 383 /* 384 * module init/exit 385 */ 386 static const struct acpi_device_id ideapad_device_ids[] = { 387 { "VPC2004", 0}, 388 { "", 0}, 389 }; 390 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 391 392 static int __devinit ideapad_acpi_add(struct acpi_device *adevice) 393 { 394 int ret, i, cfg; 395 struct ideapad_private *priv; 396 397 if (read_method_int(adevice->handle, "_CFG", &cfg)) 398 return -ENODEV; 399 400 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 401 if (!priv) 402 return -ENOMEM; 403 dev_set_drvdata(&adevice->dev, priv); 404 ideapad_handle = adevice->handle; 405 406 ret = ideapad_platform_init(priv); 407 if (ret) 408 goto platform_failed; 409 410 ret = ideapad_input_init(priv); 411 if (ret) 412 goto input_failed; 413 414 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { 415 if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) 416 ideapad_register_rfkill(adevice, i); 417 else 418 priv->rfk[i] = NULL; 419 } 420 ideapad_sync_rfk_state(adevice); 421 422 return 0; 423 424 input_failed: 425 ideapad_platform_exit(priv); 426 platform_failed: 427 kfree(priv); 428 return ret; 429 } 430 431 static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) 432 { 433 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 434 int i; 435 436 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 437 ideapad_unregister_rfkill(adevice, i); 438 ideapad_input_exit(priv); 439 ideapad_platform_exit(priv); 440 dev_set_drvdata(&adevice->dev, NULL); 441 kfree(priv); 442 443 return 0; 444 } 445 446 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) 447 { 448 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 449 acpi_handle handle = adevice->handle; 450 unsigned long vpc1, vpc2, vpc_bit; 451 452 if (read_ec_data(handle, 0x10, &vpc1)) 453 return; 454 if (read_ec_data(handle, 0x1A, &vpc2)) 455 return; 456 457 vpc1 = (vpc2 << 8) | vpc1; 458 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { 459 if (test_bit(vpc_bit, &vpc1)) { 460 if (vpc_bit == 9) 461 ideapad_sync_rfk_state(adevice); 462 else 463 ideapad_input_report(priv, vpc_bit); 464 } 465 } 466 } 467 468 static struct acpi_driver ideapad_acpi_driver = { 469 .name = "ideapad_acpi", 470 .class = "IdeaPad", 471 .ids = ideapad_device_ids, 472 .ops.add = ideapad_acpi_add, 473 .ops.remove = ideapad_acpi_remove, 474 .ops.notify = ideapad_acpi_notify, 475 .owner = THIS_MODULE, 476 }; 477 478 static int __init ideapad_acpi_module_init(void) 479 { 480 return acpi_bus_register_driver(&ideapad_acpi_driver); 481 } 482 483 static void __exit ideapad_acpi_module_exit(void) 484 { 485 acpi_bus_unregister_driver(&ideapad_acpi_driver); 486 } 487 488 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 489 MODULE_DESCRIPTION("IdeaPad ACPI Extras"); 490 MODULE_LICENSE("GPL"); 491 492 module_init(ideapad_acpi_module_init); 493 module_exit(ideapad_acpi_module_exit); 494