ideapad-laptop.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  23. #include <linux/kernel.h>
  24. #include <linux/module.h>
  25. #include <linux/init.h>
  26. #include <linux/types.h>
  27. #include <linux/acpi.h>
  28. #include <linux/rfkill.h>
  29. #include <linux/platform_device.h>
  30. #include <linux/input.h>
  31. #include <linux/input/sparse-keymap.h>
  32. #include <linux/backlight.h>
  33. #include <linux/fb.h>
  34. #include <linux/debugfs.h>
  35. #include <linux/seq_file.h>
  36. #include <linux/i8042.h>
  37. #define IDEAPAD_RFKILL_DEV_NUM (3)
  38. #define CFG_BT_BIT (16)
  39. #define CFG_3G_BIT (17)
  40. #define CFG_WIFI_BIT (18)
  41. #define CFG_CAMERA_BIT (19)
  42. enum {
  43. VPCCMD_R_VPC1 = 0x10,
  44. VPCCMD_R_BL_MAX,
  45. VPCCMD_R_BL,
  46. VPCCMD_W_BL,
  47. VPCCMD_R_WIFI,
  48. VPCCMD_W_WIFI,
  49. VPCCMD_R_BT,
  50. VPCCMD_W_BT,
  51. VPCCMD_R_BL_POWER,
  52. VPCCMD_R_NOVO,
  53. VPCCMD_R_VPC2,
  54. VPCCMD_R_TOUCHPAD,
  55. VPCCMD_W_TOUCHPAD,
  56. VPCCMD_R_CAMERA,
  57. VPCCMD_W_CAMERA,
  58. VPCCMD_R_3G,
  59. VPCCMD_W_3G,
  60. VPCCMD_R_ODD, /* 0x21 */
  61. VPCCMD_W_FAN,
  62. VPCCMD_R_RF,
  63. VPCCMD_W_RF,
  64. VPCCMD_R_FAN = 0x2B,
  65. VPCCMD_R_SPECIAL_BUTTONS = 0x31,
  66. VPCCMD_W_BL_POWER = 0x33,
  67. };
  68. struct ideapad_rfk_priv {
  69. int dev;
  70. struct ideapad_private *priv;
  71. };
  72. struct ideapad_private {
  73. struct acpi_device *adev;
  74. struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
  75. struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
  76. struct platform_device *platform_device;
  77. struct input_dev *inputdev;
  78. struct backlight_device *blightdev;
  79. struct dentry *debug;
  80. unsigned long cfg;
  81. };
  82. static bool no_bt_rfkill;
  83. module_param(no_bt_rfkill, bool, 0444);
  84. MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
  85. /*
  86. * ACPI Helpers
  87. */
  88. #define IDEAPAD_EC_TIMEOUT (100) /* in ms */
  89. static int read_method_int(acpi_handle handle, const char *method, int *val)
  90. {
  91. acpi_status status;
  92. unsigned long long result;
  93. status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
  94. if (ACPI_FAILURE(status)) {
  95. *val = -1;
  96. return -1;
  97. } else {
  98. *val = result;
  99. return 0;
  100. }
  101. }
  102. static int method_vpcr(acpi_handle handle, int cmd, int *ret)
  103. {
  104. acpi_status status;
  105. unsigned long long result;
  106. struct acpi_object_list params;
  107. union acpi_object in_obj;
  108. params.count = 1;
  109. params.pointer = &in_obj;
  110. in_obj.type = ACPI_TYPE_INTEGER;
  111. in_obj.integer.value = cmd;
  112. status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
  113. if (ACPI_FAILURE(status)) {
  114. *ret = -1;
  115. return -1;
  116. } else {
  117. *ret = result;
  118. return 0;
  119. }
  120. }
  121. static int method_vpcw(acpi_handle handle, int cmd, int data)
  122. {
  123. struct acpi_object_list params;
  124. union acpi_object in_obj[2];
  125. acpi_status status;
  126. params.count = 2;
  127. params.pointer = in_obj;
  128. in_obj[0].type = ACPI_TYPE_INTEGER;
  129. in_obj[0].integer.value = cmd;
  130. in_obj[1].type = ACPI_TYPE_INTEGER;
  131. in_obj[1].integer.value = data;
  132. status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
  133. if (status != AE_OK)
  134. return -1;
  135. return 0;
  136. }
  137. static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
  138. {
  139. int val;
  140. unsigned long int end_jiffies;
  141. if (method_vpcw(handle, 1, cmd))
  142. return -1;
  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. if (method_vpcr(handle, 0, &val))
  150. return -1;
  151. *data = val;
  152. return 0;
  153. }
  154. }
  155. pr_err("timeout in read_ec_cmd\n");
  156. return -1;
  157. }
  158. static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
  159. {
  160. int val;
  161. unsigned long int end_jiffies;
  162. if (method_vpcw(handle, 0, data))
  163. return -1;
  164. if (method_vpcw(handle, 1, cmd))
  165. return -1;
  166. for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
  167. time_before(jiffies, end_jiffies);) {
  168. schedule();
  169. if (method_vpcr(handle, 1, &val))
  170. return -1;
  171. if (val == 0)
  172. return 0;
  173. }
  174. pr_err("timeout in write_ec_cmd\n");
  175. return -1;
  176. }
  177. /*
  178. * debugfs
  179. */
  180. static int debugfs_status_show(struct seq_file *s, void *data)
  181. {
  182. struct ideapad_private *priv = s->private;
  183. unsigned long value;
  184. if (!priv)
  185. return -EINVAL;
  186. if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
  187. seq_printf(s, "Backlight max:\t%lu\n", value);
  188. if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
  189. seq_printf(s, "Backlight now:\t%lu\n", value);
  190. if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
  191. seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
  192. seq_printf(s, "=====================\n");
  193. if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
  194. seq_printf(s, "Radio status:\t%s(%lu)\n",
  195. value ? "On" : "Off", value);
  196. if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
  197. seq_printf(s, "Wifi status:\t%s(%lu)\n",
  198. value ? "On" : "Off", value);
  199. if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
  200. seq_printf(s, "BT status:\t%s(%lu)\n",
  201. value ? "On" : "Off", value);
  202. if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
  203. seq_printf(s, "3G status:\t%s(%lu)\n",
  204. value ? "On" : "Off", value);
  205. seq_printf(s, "=====================\n");
  206. if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
  207. seq_printf(s, "Touchpad status:%s(%lu)\n",
  208. value ? "On" : "Off", value);
  209. if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
  210. seq_printf(s, "Camera status:\t%s(%lu)\n",
  211. value ? "On" : "Off", value);
  212. return 0;
  213. }
  214. static int debugfs_status_open(struct inode *inode, struct file *file)
  215. {
  216. return single_open(file, debugfs_status_show, inode->i_private);
  217. }
  218. static const struct file_operations debugfs_status_fops = {
  219. .owner = THIS_MODULE,
  220. .open = debugfs_status_open,
  221. .read = seq_read,
  222. .llseek = seq_lseek,
  223. .release = single_release,
  224. };
  225. static int debugfs_cfg_show(struct seq_file *s, void *data)
  226. {
  227. struct ideapad_private *priv = s->private;
  228. if (!priv) {
  229. seq_printf(s, "cfg: N/A\n");
  230. } else {
  231. seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
  232. priv->cfg);
  233. if (test_bit(CFG_BT_BIT, &priv->cfg))
  234. seq_printf(s, "Bluetooth ");
  235. if (test_bit(CFG_3G_BIT, &priv->cfg))
  236. seq_printf(s, "3G ");
  237. if (test_bit(CFG_WIFI_BIT, &priv->cfg))
  238. seq_printf(s, "Wireless ");
  239. if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
  240. seq_printf(s, "Camera ");
  241. seq_printf(s, "\nGraphic: ");
  242. switch ((priv->cfg)&0x700) {
  243. case 0x100:
  244. seq_printf(s, "Intel");
  245. break;
  246. case 0x200:
  247. seq_printf(s, "ATI");
  248. break;
  249. case 0x300:
  250. seq_printf(s, "Nvidia");
  251. break;
  252. case 0x400:
  253. seq_printf(s, "Intel and ATI");
  254. break;
  255. case 0x500:
  256. seq_printf(s, "Intel and Nvidia");
  257. break;
  258. }
  259. seq_printf(s, "\n");
  260. }
  261. return 0;
  262. }
  263. static int debugfs_cfg_open(struct inode *inode, struct file *file)
  264. {
  265. return single_open(file, debugfs_cfg_show, inode->i_private);
  266. }
  267. static const struct file_operations debugfs_cfg_fops = {
  268. .owner = THIS_MODULE,
  269. .open = debugfs_cfg_open,
  270. .read = seq_read,
  271. .llseek = seq_lseek,
  272. .release = single_release,
  273. };
  274. static int ideapad_debugfs_init(struct ideapad_private *priv)
  275. {
  276. struct dentry *node;
  277. priv->debug = debugfs_create_dir("ideapad", NULL);
  278. if (priv->debug == NULL) {
  279. pr_err("failed to create debugfs directory");
  280. goto errout;
  281. }
  282. node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
  283. &debugfs_cfg_fops);
  284. if (!node) {
  285. pr_err("failed to create cfg in debugfs");
  286. goto errout;
  287. }
  288. node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
  289. &debugfs_status_fops);
  290. if (!node) {
  291. pr_err("failed to create status in debugfs");
  292. goto errout;
  293. }
  294. return 0;
  295. errout:
  296. return -ENOMEM;
  297. }
  298. static void ideapad_debugfs_exit(struct ideapad_private *priv)
  299. {
  300. debugfs_remove_recursive(priv->debug);
  301. priv->debug = NULL;
  302. }
  303. /*
  304. * sysfs
  305. */
  306. static ssize_t show_ideapad_cam(struct device *dev,
  307. struct device_attribute *attr,
  308. char *buf)
  309. {
  310. unsigned long result;
  311. struct ideapad_private *priv = dev_get_drvdata(dev);
  312. if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
  313. return sprintf(buf, "-1\n");
  314. return sprintf(buf, "%lu\n", result);
  315. }
  316. static ssize_t store_ideapad_cam(struct device *dev,
  317. struct device_attribute *attr,
  318. const char *buf, size_t count)
  319. {
  320. int ret, state;
  321. struct ideapad_private *priv = dev_get_drvdata(dev);
  322. if (!count)
  323. return 0;
  324. if (sscanf(buf, "%i", &state) != 1)
  325. return -EINVAL;
  326. ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
  327. if (ret < 0)
  328. return -EIO;
  329. return count;
  330. }
  331. static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
  332. static ssize_t show_ideapad_fan(struct device *dev,
  333. struct device_attribute *attr,
  334. char *buf)
  335. {
  336. unsigned long result;
  337. struct ideapad_private *priv = dev_get_drvdata(dev);
  338. if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
  339. return sprintf(buf, "-1\n");
  340. return sprintf(buf, "%lu\n", result);
  341. }
  342. static ssize_t store_ideapad_fan(struct device *dev,
  343. struct device_attribute *attr,
  344. const char *buf, size_t count)
  345. {
  346. int ret, state;
  347. struct ideapad_private *priv = dev_get_drvdata(dev);
  348. if (!count)
  349. return 0;
  350. if (sscanf(buf, "%i", &state) != 1)
  351. return -EINVAL;
  352. if (state < 0 || state > 4 || state == 3)
  353. return -EINVAL;
  354. ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
  355. if (ret < 0)
  356. return -EIO;
  357. return count;
  358. }
  359. static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
  360. static struct attribute *ideapad_attributes[] = {
  361. &dev_attr_camera_power.attr,
  362. &dev_attr_fan_mode.attr,
  363. NULL
  364. };
  365. static umode_t ideapad_is_visible(struct kobject *kobj,
  366. struct attribute *attr,
  367. int idx)
  368. {
  369. struct device *dev = container_of(kobj, struct device, kobj);
  370. struct ideapad_private *priv = dev_get_drvdata(dev);
  371. bool supported;
  372. if (attr == &dev_attr_camera_power.attr)
  373. supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
  374. else if (attr == &dev_attr_fan_mode.attr) {
  375. unsigned long value;
  376. supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
  377. &value);
  378. } else
  379. supported = true;
  380. return supported ? attr->mode : 0;
  381. }
  382. static struct attribute_group ideapad_attribute_group = {
  383. .is_visible = ideapad_is_visible,
  384. .attrs = ideapad_attributes
  385. };
  386. /*
  387. * Rfkill
  388. */
  389. struct ideapad_rfk_data {
  390. char *name;
  391. int cfgbit;
  392. int opcode;
  393. int type;
  394. };
  395. const struct ideapad_rfk_data ideapad_rfk_data[] = {
  396. { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
  397. { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
  398. { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
  399. };
  400. static int ideapad_rfk_set(void *data, bool blocked)
  401. {
  402. struct ideapad_rfk_priv *priv = data;
  403. return write_ec_cmd(priv->priv->adev->handle, priv->dev, !blocked);
  404. }
  405. static struct rfkill_ops ideapad_rfk_ops = {
  406. .set_block = ideapad_rfk_set,
  407. };
  408. static void ideapad_sync_rfk_state(struct ideapad_private *priv)
  409. {
  410. unsigned long hw_blocked;
  411. int i;
  412. if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
  413. return;
  414. hw_blocked = !hw_blocked;
  415. for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
  416. if (priv->rfk[i])
  417. rfkill_set_hw_state(priv->rfk[i], hw_blocked);
  418. }
  419. static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
  420. {
  421. int ret;
  422. unsigned long sw_blocked;
  423. if (no_bt_rfkill &&
  424. (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
  425. /* Force to enable bluetooth when no_bt_rfkill=1 */
  426. write_ec_cmd(priv->adev->handle,
  427. ideapad_rfk_data[dev].opcode, 1);
  428. return 0;
  429. }
  430. priv->rfk_priv[dev].dev = dev;
  431. priv->rfk_priv[dev].priv = priv;
  432. priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
  433. &priv->platform_device->dev,
  434. ideapad_rfk_data[dev].type,
  435. &ideapad_rfk_ops,
  436. &priv->rfk_priv[dev]);
  437. if (!priv->rfk[dev])
  438. return -ENOMEM;
  439. if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
  440. &sw_blocked)) {
  441. rfkill_init_sw_state(priv->rfk[dev], 0);
  442. } else {
  443. sw_blocked = !sw_blocked;
  444. rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
  445. }
  446. ret = rfkill_register(priv->rfk[dev]);
  447. if (ret) {
  448. rfkill_destroy(priv->rfk[dev]);
  449. return ret;
  450. }
  451. return 0;
  452. }
  453. static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
  454. {
  455. if (!priv->rfk[dev])
  456. return;
  457. rfkill_unregister(priv->rfk[dev]);
  458. rfkill_destroy(priv->rfk[dev]);
  459. }
  460. /*
  461. * Platform device
  462. */
  463. static int ideapad_sysfs_init(struct ideapad_private *priv)
  464. {
  465. return sysfs_create_group(&priv->platform_device->dev.kobj,
  466. &ideapad_attribute_group);
  467. }
  468. static void ideapad_sysfs_exit(struct ideapad_private *priv)
  469. {
  470. sysfs_remove_group(&priv->platform_device->dev.kobj,
  471. &ideapad_attribute_group);
  472. }
  473. /*
  474. * input device
  475. */
  476. static const struct key_entry ideapad_keymap[] = {
  477. { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
  478. { KE_KEY, 7, { KEY_CAMERA } },
  479. { KE_KEY, 11, { KEY_F16 } },
  480. { KE_KEY, 13, { KEY_WLAN } },
  481. { KE_KEY, 16, { KEY_PROG1 } },
  482. { KE_KEY, 17, { KEY_PROG2 } },
  483. { KE_KEY, 64, { KEY_PROG3 } },
  484. { KE_KEY, 65, { KEY_PROG4 } },
  485. { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
  486. { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
  487. { KE_END, 0 },
  488. };
  489. static int ideapad_input_init(struct ideapad_private *priv)
  490. {
  491. struct input_dev *inputdev;
  492. int error;
  493. inputdev = input_allocate_device();
  494. if (!inputdev)
  495. return -ENOMEM;
  496. inputdev->name = "Ideapad extra buttons";
  497. inputdev->phys = "ideapad/input0";
  498. inputdev->id.bustype = BUS_HOST;
  499. inputdev->dev.parent = &priv->platform_device->dev;
  500. error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
  501. if (error) {
  502. pr_err("Unable to setup input device keymap\n");
  503. goto err_free_dev;
  504. }
  505. error = input_register_device(inputdev);
  506. if (error) {
  507. pr_err("Unable to register input device\n");
  508. goto err_free_keymap;
  509. }
  510. priv->inputdev = inputdev;
  511. return 0;
  512. err_free_keymap:
  513. sparse_keymap_free(inputdev);
  514. err_free_dev:
  515. input_free_device(inputdev);
  516. return error;
  517. }
  518. static void ideapad_input_exit(struct ideapad_private *priv)
  519. {
  520. sparse_keymap_free(priv->inputdev);
  521. input_unregister_device(priv->inputdev);
  522. priv->inputdev = NULL;
  523. }
  524. static void ideapad_input_report(struct ideapad_private *priv,
  525. unsigned long scancode)
  526. {
  527. sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
  528. }
  529. static void ideapad_input_novokey(struct ideapad_private *priv)
  530. {
  531. unsigned long long_pressed;
  532. if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
  533. return;
  534. if (long_pressed)
  535. ideapad_input_report(priv, 17);
  536. else
  537. ideapad_input_report(priv, 16);
  538. }
  539. static void ideapad_check_special_buttons(struct ideapad_private *priv)
  540. {
  541. unsigned long bit, value;
  542. read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
  543. for (bit = 0; bit < 16; bit++) {
  544. if (test_bit(bit, &value)) {
  545. switch (bit) {
  546. case 0: /* Z580 */
  547. case 6: /* Z570 */
  548. /* Thermal Management button */
  549. ideapad_input_report(priv, 65);
  550. break;
  551. case 1:
  552. /* OneKey Theater button */
  553. ideapad_input_report(priv, 64);
  554. break;
  555. default:
  556. pr_info("Unknown special button: %lu\n", bit);
  557. break;
  558. }
  559. }
  560. }
  561. }
  562. /*
  563. * backlight
  564. */
  565. static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
  566. {
  567. struct ideapad_private *priv = bl_get_data(blightdev);
  568. unsigned long now;
  569. if (!priv)
  570. return -EINVAL;
  571. if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
  572. return -EIO;
  573. return now;
  574. }
  575. static int ideapad_backlight_update_status(struct backlight_device *blightdev)
  576. {
  577. struct ideapad_private *priv = bl_get_data(blightdev);
  578. if (!priv)
  579. return -EINVAL;
  580. if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
  581. blightdev->props.brightness))
  582. return -EIO;
  583. if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
  584. blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
  585. return -EIO;
  586. return 0;
  587. }
  588. static const struct backlight_ops ideapad_backlight_ops = {
  589. .get_brightness = ideapad_backlight_get_brightness,
  590. .update_status = ideapad_backlight_update_status,
  591. };
  592. static int ideapad_backlight_init(struct ideapad_private *priv)
  593. {
  594. struct backlight_device *blightdev;
  595. struct backlight_properties props;
  596. unsigned long max, now, power;
  597. if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
  598. return -EIO;
  599. if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
  600. return -EIO;
  601. if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
  602. return -EIO;
  603. memset(&props, 0, sizeof(struct backlight_properties));
  604. props.max_brightness = max;
  605. props.type = BACKLIGHT_PLATFORM;
  606. blightdev = backlight_device_register("ideapad",
  607. &priv->platform_device->dev,
  608. priv,
  609. &ideapad_backlight_ops,
  610. &props);
  611. if (IS_ERR(blightdev)) {
  612. pr_err("Could not register backlight device\n");
  613. return PTR_ERR(blightdev);
  614. }
  615. priv->blightdev = blightdev;
  616. blightdev->props.brightness = now;
  617. blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
  618. backlight_update_status(blightdev);
  619. return 0;
  620. }
  621. static void ideapad_backlight_exit(struct ideapad_private *priv)
  622. {
  623. if (priv->blightdev)
  624. backlight_device_unregister(priv->blightdev);
  625. priv->blightdev = NULL;
  626. }
  627. static void ideapad_backlight_notify_power(struct ideapad_private *priv)
  628. {
  629. unsigned long power;
  630. struct backlight_device *blightdev = priv->blightdev;
  631. if (!blightdev)
  632. return;
  633. if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
  634. return;
  635. blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
  636. }
  637. static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
  638. {
  639. unsigned long now;
  640. /* if we control brightness via acpi video driver */
  641. if (priv->blightdev == NULL) {
  642. read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
  643. return;
  644. }
  645. backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
  646. }
  647. /*
  648. * module init/exit
  649. */
  650. static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
  651. {
  652. unsigned long value;
  653. /* Without reading from EC touchpad LED doesn't switch state */
  654. if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
  655. /* Some IdeaPads don't really turn off touchpad - they only
  656. * switch the LED state. We (de)activate KBC AUX port to turn
  657. * touchpad off and on. We send KEY_TOUCHPAD_OFF and
  658. * KEY_TOUCHPAD_ON to not to get out of sync with LED */
  659. unsigned char param;
  660. i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
  661. I8042_CMD_AUX_DISABLE);
  662. ideapad_input_report(priv, value ? 67 : 66);
  663. }
  664. }
  665. static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
  666. {
  667. struct ideapad_private *priv = data;
  668. unsigned long vpc1, vpc2, vpc_bit;
  669. if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
  670. return;
  671. if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
  672. return;
  673. vpc1 = (vpc2 << 8) | vpc1;
  674. for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
  675. if (test_bit(vpc_bit, &vpc1)) {
  676. switch (vpc_bit) {
  677. case 9:
  678. ideapad_sync_rfk_state(priv);
  679. break;
  680. case 13:
  681. case 11:
  682. case 7:
  683. case 6:
  684. ideapad_input_report(priv, vpc_bit);
  685. break;
  686. case 5:
  687. ideapad_sync_touchpad_state(priv);
  688. break;
  689. case 4:
  690. ideapad_backlight_notify_brightness(priv);
  691. break;
  692. case 3:
  693. ideapad_input_novokey(priv);
  694. break;
  695. case 2:
  696. ideapad_backlight_notify_power(priv);
  697. break;
  698. case 0:
  699. ideapad_check_special_buttons(priv);
  700. break;
  701. default:
  702. pr_info("Unknown event: %lu\n", vpc_bit);
  703. }
  704. }
  705. }
  706. }
  707. static int ideapad_acpi_add(struct platform_device *pdev)
  708. {
  709. int ret, i;
  710. int cfg;
  711. struct ideapad_private *priv;
  712. struct acpi_device *adev;
  713. ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
  714. if (ret)
  715. return -ENODEV;
  716. if (read_method_int(adev->handle, "_CFG", &cfg))
  717. return -ENODEV;
  718. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  719. if (!priv)
  720. return -ENOMEM;
  721. dev_set_drvdata(&pdev->dev, priv);
  722. priv->cfg = cfg;
  723. priv->adev = adev;
  724. priv->platform_device = pdev;
  725. ret = ideapad_sysfs_init(priv);
  726. if (ret)
  727. goto sysfs_failed;
  728. ret = ideapad_debugfs_init(priv);
  729. if (ret)
  730. goto debugfs_failed;
  731. ret = ideapad_input_init(priv);
  732. if (ret)
  733. goto input_failed;
  734. for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
  735. if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
  736. ideapad_register_rfkill(priv, i);
  737. else
  738. priv->rfk[i] = NULL;
  739. }
  740. ideapad_sync_rfk_state(priv);
  741. ideapad_sync_touchpad_state(priv);
  742. if (!acpi_video_backlight_support()) {
  743. ret = ideapad_backlight_init(priv);
  744. if (ret && ret != -ENODEV)
  745. goto backlight_failed;
  746. }
  747. ret = acpi_install_notify_handler(adev->handle,
  748. ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
  749. if (ret)
  750. goto notification_failed;
  751. return 0;
  752. notification_failed:
  753. ideapad_backlight_exit(priv);
  754. backlight_failed:
  755. for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
  756. ideapad_unregister_rfkill(priv, i);
  757. ideapad_input_exit(priv);
  758. input_failed:
  759. ideapad_debugfs_exit(priv);
  760. debugfs_failed:
  761. ideapad_sysfs_exit(priv);
  762. sysfs_failed:
  763. kfree(priv);
  764. return ret;
  765. }
  766. static int ideapad_acpi_remove(struct platform_device *pdev)
  767. {
  768. struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
  769. int i;
  770. acpi_remove_notify_handler(priv->adev->handle,
  771. ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
  772. ideapad_backlight_exit(priv);
  773. for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
  774. ideapad_unregister_rfkill(priv, i);
  775. ideapad_input_exit(priv);
  776. ideapad_debugfs_exit(priv);
  777. ideapad_sysfs_exit(priv);
  778. dev_set_drvdata(&pdev->dev, NULL);
  779. kfree(priv);
  780. return 0;
  781. }
  782. #ifdef CONFIG_PM_SLEEP
  783. static int ideapad_acpi_resume(struct device *device)
  784. {
  785. struct ideapad_private *priv;
  786. if (!device)
  787. return -EINVAL;
  788. priv = dev_get_drvdata(device);
  789. ideapad_sync_rfk_state(priv);
  790. ideapad_sync_touchpad_state(priv);
  791. return 0;
  792. }
  793. #endif
  794. static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
  795. static const struct acpi_device_id ideapad_device_ids[] = {
  796. { "VPC2004", 0},
  797. { "", 0},
  798. };
  799. MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
  800. static struct platform_driver ideapad_acpi_driver = {
  801. .probe = ideapad_acpi_add,
  802. .remove = ideapad_acpi_remove,
  803. .driver = {
  804. .name = "ideapad_acpi",
  805. .owner = THIS_MODULE,
  806. .pm = &ideapad_pm,
  807. .acpi_match_table = ACPI_PTR(ideapad_device_ids),
  808. },
  809. };
  810. module_platform_driver(ideapad_acpi_driver);
  811. MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
  812. MODULE_DESCRIPTION("IdeaPad ACPI Extras");
  813. MODULE_LICENSE("GPL");