ideapad: define vpc commands
[deliverable/linux.git] / drivers / platform / x86 / ideapad-laptop.c
CommitLineData
58ac7aa0 1/*
a4b5a279 2 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
58ac7aa0
DW
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
9ab23989
JP
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
58ac7aa0
DW
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <acpi/acpi_bus.h>
30#include <acpi/acpi_drivers.h>
31#include <linux/rfkill.h>
98ee6919 32#include <linux/platform_device.h>
f63409ae
IP
33#include <linux/input.h>
34#include <linux/input/sparse-keymap.h>
a4ecbb8a
IP
35#include <linux/backlight.h>
36#include <linux/fb.h>
58ac7aa0 37
c1f73658 38#define IDEAPAD_RFKILL_DEV_NUM (3)
58ac7aa0 39
3371f481
IP
40#define CFG_BT_BIT (16)
41#define CFG_3G_BIT (17)
42#define CFG_WIFI_BIT (18)
a84511f7 43#define CFG_CAMERA_BIT (19)
3371f481 44
2be1dc21
IP
45enum {
46 VPCCMD_R_VPC1 = 0x10,
47 VPCCMD_R_BL_MAX,
48 VPCCMD_R_BL,
49 VPCCMD_W_BL,
50 VPCCMD_R_WIFI,
51 VPCCMD_W_WIFI,
52 VPCCMD_R_BT,
53 VPCCMD_W_BT,
54 VPCCMD_R_BL_POWER,
55 VPCCMD_R_NOVO,
56 VPCCMD_R_VPC2,
57 VPCCMD_R_TOUCHPAD,
58 VPCCMD_W_TOUCHPAD,
59 VPCCMD_R_CAMERA,
60 VPCCMD_W_CAMERA,
61 VPCCMD_R_3G,
62 VPCCMD_W_3G,
63 VPCCMD_R_ODD, /* 0x21 */
64 VPCCMD_R_RF = 0x23,
65 VPCCMD_W_RF,
66 VPCCMD_W_BL_POWER = 0x33,
67};
68
ce326329 69struct ideapad_private {
c1f73658 70 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
98ee6919 71 struct platform_device *platform_device;
f63409ae 72 struct input_dev *inputdev;
a4ecbb8a 73 struct backlight_device *blightdev;
3371f481 74 unsigned long cfg;
58ac7aa0
DW
75};
76
c1f73658 77static acpi_handle ideapad_handle;
bfa97b7d
IP
78static bool no_bt_rfkill;
79module_param(no_bt_rfkill, bool, 0444);
80MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
81
6a09f21d
IP
82/*
83 * ACPI Helpers
84 */
85#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
86
87static int read_method_int(acpi_handle handle, const char *method, int *val)
88{
89 acpi_status status;
90 unsigned long long result;
91
92 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
93 if (ACPI_FAILURE(status)) {
94 *val = -1;
95 return -1;
96 } else {
97 *val = result;
98 return 0;
99 }
100}
101
102static 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
109 params.count = 1;
110 params.pointer = &in_obj;
111 in_obj.type = ACPI_TYPE_INTEGER;
112 in_obj.integer.value = cmd;
113
114 status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
115
116 if (ACPI_FAILURE(status)) {
117 *ret = -1;
118 return -1;
119 } else {
120 *ret = result;
121 return 0;
122 }
123}
124
125static int method_vpcw(acpi_handle handle, int cmd, int data)
126{
127 struct acpi_object_list params;
128 union acpi_object in_obj[2];
129 acpi_status status;
130
131 params.count = 2;
132 params.pointer = in_obj;
133 in_obj[0].type = ACPI_TYPE_INTEGER;
134 in_obj[0].integer.value = cmd;
135 in_obj[1].type = ACPI_TYPE_INTEGER;
136 in_obj[1].integer.value = data;
137
138 status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
139 if (status != AE_OK)
140 return -1;
141 return 0;
142}
143
144static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
145{
146 int val;
147 unsigned long int end_jiffies;
148
149 if (method_vpcw(handle, 1, cmd))
150 return -1;
151
152 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
153 time_before(jiffies, end_jiffies);) {
154 schedule();
155 if (method_vpcr(handle, 1, &val))
156 return -1;
157 if (val == 0) {
158 if (method_vpcr(handle, 0, &val))
159 return -1;
160 *data = val;
161 return 0;
162 }
163 }
164 pr_err("timeout in read_ec_cmd\n");
165 return -1;
166}
167
168static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
169{
170 int val;
171 unsigned long int end_jiffies;
172
173 if (method_vpcw(handle, 0, data))
174 return -1;
175 if (method_vpcw(handle, 1, cmd))
176 return -1;
177
178 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
179 time_before(jiffies, end_jiffies);) {
180 schedule();
181 if (method_vpcr(handle, 1, &val))
182 return -1;
183 if (val == 0)
184 return 0;
185 }
186 pr_err("timeout in write_ec_cmd\n");
187 return -1;
188}
6a09f21d 189
a4b5a279 190/*
3371f481 191 * sysfs
a4b5a279 192 */
58ac7aa0
DW
193static ssize_t show_ideapad_cam(struct device *dev,
194 struct device_attribute *attr,
195 char *buf)
196{
26c81d5c 197 unsigned long result;
58ac7aa0 198
2be1dc21 199 if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result))
26c81d5c
IP
200 return sprintf(buf, "-1\n");
201 return sprintf(buf, "%lu\n", result);
58ac7aa0
DW
202}
203
204static ssize_t store_ideapad_cam(struct device *dev,
205 struct device_attribute *attr,
206 const char *buf, size_t count)
207{
208 int ret, state;
209
210 if (!count)
211 return 0;
212 if (sscanf(buf, "%i", &state) != 1)
213 return -EINVAL;
2be1dc21 214 ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
58ac7aa0
DW
215 if (ret < 0)
216 return ret;
217 return count;
218}
219
220static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
221
3371f481
IP
222static ssize_t show_ideapad_cfg(struct device *dev,
223 struct device_attribute *attr,
224 char *buf)
225{
226 struct ideapad_private *priv = dev_get_drvdata(dev);
227
228 return sprintf(buf, "0x%.8lX\n", priv->cfg);
229}
230
231static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
232
233static struct attribute *ideapad_attributes[] = {
234 &dev_attr_camera_power.attr,
235 &dev_attr_cfg.attr,
236 NULL
237};
238
a84511f7
IP
239static mode_t ideapad_is_visible(struct kobject *kobj,
240 struct attribute *attr,
241 int idx)
242{
243 struct device *dev = container_of(kobj, struct device, kobj);
244 struct ideapad_private *priv = dev_get_drvdata(dev);
245 bool supported;
246
247 if (attr == &dev_attr_camera_power.attr)
248 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
249 else
250 supported = true;
251
252 return supported ? attr->mode : 0;
253}
254
3371f481 255static struct attribute_group ideapad_attribute_group = {
a84511f7 256 .is_visible = ideapad_is_visible,
3371f481
IP
257 .attrs = ideapad_attributes
258};
259
a4b5a279
IP
260/*
261 * Rfkill
262 */
c1f73658
IP
263struct ideapad_rfk_data {
264 char *name;
265 int cfgbit;
266 int opcode;
267 int type;
268};
269
270const struct ideapad_rfk_data ideapad_rfk_data[] = {
2be1dc21
IP
271 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
272 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
273 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
c1f73658
IP
274};
275
58ac7aa0
DW
276static int ideapad_rfk_set(void *data, bool blocked)
277{
c1f73658 278 unsigned long opcode = (unsigned long)data;
fa08359e 279
c1f73658 280 return write_ec_cmd(ideapad_handle, opcode, !blocked);
58ac7aa0
DW
281}
282
283static struct rfkill_ops ideapad_rfk_ops = {
284 .set_block = ideapad_rfk_set,
285};
286
ce326329 287static void ideapad_sync_rfk_state(struct acpi_device *adevice)
58ac7aa0 288{
ce326329 289 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
2b7266bd 290 unsigned long hw_blocked;
58ac7aa0
DW
291 int i;
292
2be1dc21 293 if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked))
58ac7aa0 294 return;
2b7266bd 295 hw_blocked = !hw_blocked;
58ac7aa0 296
c1f73658 297 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 298 if (priv->rfk[i])
2b7266bd 299 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
58ac7aa0
DW
300}
301
a4b5a279
IP
302static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
303 int dev)
58ac7aa0 304{
ce326329 305 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
58ac7aa0 306 int ret;
2b7266bd 307 unsigned long sw_blocked;
58ac7aa0 308
bfa97b7d
IP
309 if (no_bt_rfkill &&
310 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
311 /* Force to enable bluetooth when no_bt_rfkill=1 */
c1f73658 312 write_ec_cmd(ideapad_handle,
bfa97b7d
IP
313 ideapad_rfk_data[dev].opcode, 1);
314 return 0;
315 }
316
2b7266bd
IP
317 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
318 ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
ce326329
DW
319 (void *)(long)dev);
320 if (!priv->rfk[dev])
58ac7aa0
DW
321 return -ENOMEM;
322
c1f73658 323 if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
2b7266bd
IP
324 &sw_blocked)) {
325 rfkill_init_sw_state(priv->rfk[dev], 0);
326 } else {
327 sw_blocked = !sw_blocked;
328 rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
329 }
330
ce326329 331 ret = rfkill_register(priv->rfk[dev]);
58ac7aa0 332 if (ret) {
ce326329 333 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
334 return ret;
335 }
336 return 0;
337}
338
a4ecbb8a 339static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
58ac7aa0 340{
ce326329
DW
341 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
342
343 if (!priv->rfk[dev])
58ac7aa0
DW
344 return;
345
ce326329
DW
346 rfkill_unregister(priv->rfk[dev]);
347 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
348}
349
98ee6919
IP
350/*
351 * Platform device
352 */
8693ae84 353static int __devinit ideapad_platform_init(struct ideapad_private *priv)
98ee6919
IP
354{
355 int result;
356
8693ae84
IP
357 priv->platform_device = platform_device_alloc("ideapad", -1);
358 if (!priv->platform_device)
98ee6919 359 return -ENOMEM;
8693ae84 360 platform_set_drvdata(priv->platform_device, priv);
98ee6919 361
8693ae84 362 result = platform_device_add(priv->platform_device);
98ee6919
IP
363 if (result)
364 goto fail_platform_device;
365
8693ae84 366 result = sysfs_create_group(&priv->platform_device->dev.kobj,
c9f718d0
IP
367 &ideapad_attribute_group);
368 if (result)
369 goto fail_sysfs;
98ee6919
IP
370 return 0;
371
c9f718d0 372fail_sysfs:
8693ae84 373 platform_device_del(priv->platform_device);
98ee6919 374fail_platform_device:
8693ae84 375 platform_device_put(priv->platform_device);
98ee6919
IP
376 return result;
377}
378
8693ae84 379static void ideapad_platform_exit(struct ideapad_private *priv)
98ee6919 380{
8693ae84 381 sysfs_remove_group(&priv->platform_device->dev.kobj,
c9f718d0 382 &ideapad_attribute_group);
8693ae84 383 platform_device_unregister(priv->platform_device);
98ee6919 384}
98ee6919 385
f63409ae
IP
386/*
387 * input device
388 */
389static const struct key_entry ideapad_keymap[] = {
390 { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
391 { KE_KEY, 0x0D, { KEY_WLAN } },
392 { KE_END, 0 },
393};
394
8693ae84 395static int __devinit ideapad_input_init(struct ideapad_private *priv)
f63409ae
IP
396{
397 struct input_dev *inputdev;
398 int error;
399
400 inputdev = input_allocate_device();
401 if (!inputdev) {
402 pr_info("Unable to allocate input device\n");
403 return -ENOMEM;
404 }
405
406 inputdev->name = "Ideapad extra buttons";
407 inputdev->phys = "ideapad/input0";
408 inputdev->id.bustype = BUS_HOST;
8693ae84 409 inputdev->dev.parent = &priv->platform_device->dev;
f63409ae
IP
410
411 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
412 if (error) {
413 pr_err("Unable to setup input device keymap\n");
414 goto err_free_dev;
415 }
416
417 error = input_register_device(inputdev);
418 if (error) {
419 pr_err("Unable to register input device\n");
420 goto err_free_keymap;
421 }
422
8693ae84 423 priv->inputdev = inputdev;
f63409ae
IP
424 return 0;
425
426err_free_keymap:
427 sparse_keymap_free(inputdev);
428err_free_dev:
429 input_free_device(inputdev);
430 return error;
431}
432
7451a55a 433static void ideapad_input_exit(struct ideapad_private *priv)
f63409ae 434{
8693ae84
IP
435 sparse_keymap_free(priv->inputdev);
436 input_unregister_device(priv->inputdev);
437 priv->inputdev = NULL;
f63409ae
IP
438}
439
8693ae84
IP
440static void ideapad_input_report(struct ideapad_private *priv,
441 unsigned long scancode)
f63409ae 442{
8693ae84 443 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
f63409ae 444}
f63409ae 445
a4ecbb8a
IP
446/*
447 * backlight
448 */
449static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
450{
451 unsigned long now;
452
2be1dc21 453 if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
a4ecbb8a
IP
454 return -EIO;
455 return now;
456}
457
458static int ideapad_backlight_update_status(struct backlight_device *blightdev)
459{
2be1dc21
IP
460 if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL,
461 blightdev->props.brightness))
a4ecbb8a 462 return -EIO;
2be1dc21 463 if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER,
a4ecbb8a
IP
464 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
465 return -EIO;
466
467 return 0;
468}
469
470static const struct backlight_ops ideapad_backlight_ops = {
471 .get_brightness = ideapad_backlight_get_brightness,
472 .update_status = ideapad_backlight_update_status,
473};
474
475static int ideapad_backlight_init(struct ideapad_private *priv)
476{
477 struct backlight_device *blightdev;
478 struct backlight_properties props;
479 unsigned long max, now, power;
480
2be1dc21 481 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max))
a4ecbb8a 482 return -EIO;
2be1dc21 483 if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
a4ecbb8a 484 return -EIO;
2be1dc21 485 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
486 return -EIO;
487
488 memset(&props, 0, sizeof(struct backlight_properties));
489 props.max_brightness = max;
490 props.type = BACKLIGHT_PLATFORM;
491 blightdev = backlight_device_register("ideapad",
492 &priv->platform_device->dev,
493 priv,
494 &ideapad_backlight_ops,
495 &props);
496 if (IS_ERR(blightdev)) {
497 pr_err("Could not register backlight device\n");
498 return PTR_ERR(blightdev);
499 }
500
501 priv->blightdev = blightdev;
502 blightdev->props.brightness = now;
503 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
504 backlight_update_status(blightdev);
505
506 return 0;
507}
508
509static void ideapad_backlight_exit(struct ideapad_private *priv)
510{
511 if (priv->blightdev)
512 backlight_device_unregister(priv->blightdev);
513 priv->blightdev = NULL;
514}
515
516static void ideapad_backlight_notify_power(struct ideapad_private *priv)
517{
518 unsigned long power;
519 struct backlight_device *blightdev = priv->blightdev;
520
d4afc775
RB
521 if (!blightdev)
522 return;
2be1dc21 523 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
524 return;
525 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
526}
527
528static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
529{
530 unsigned long now;
531
532 /* if we control brightness via acpi video driver */
533 if (priv->blightdev == NULL) {
2be1dc21 534 read_ec_data(ideapad_handle, VPCCMD_R_BL, &now);
a4ecbb8a
IP
535 return;
536 }
537
538 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
539}
540
a4b5a279
IP
541/*
542 * module init/exit
543 */
58ac7aa0
DW
544static const struct acpi_device_id ideapad_device_ids[] = {
545 { "VPC2004", 0},
546 { "", 0},
547};
548MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
549
a4b5a279 550static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
58ac7aa0 551{
3371f481
IP
552 int ret, i;
553 unsigned long cfg;
ce326329 554 struct ideapad_private *priv;
58ac7aa0 555
3371f481 556 if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
6f8371c0
IP
557 return -ENODEV;
558
ce326329
DW
559 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
560 if (!priv)
561 return -ENOMEM;
c9f718d0 562 dev_set_drvdata(&adevice->dev, priv);
c1f73658 563 ideapad_handle = adevice->handle;
3371f481 564 priv->cfg = cfg;
98ee6919 565
8693ae84 566 ret = ideapad_platform_init(priv);
98ee6919
IP
567 if (ret)
568 goto platform_failed;
ce326329 569
8693ae84 570 ret = ideapad_input_init(priv);
f63409ae
IP
571 if (ret)
572 goto input_failed;
573
c1f73658 574 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
3371f481 575 if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
c9f718d0 576 ideapad_register_rfkill(adevice, i);
c1f73658
IP
577 else
578 priv->rfk[i] = NULL;
58ac7aa0 579 }
ce326329 580 ideapad_sync_rfk_state(adevice);
c9f718d0 581
a4ecbb8a
IP
582 if (!acpi_video_backlight_support()) {
583 ret = ideapad_backlight_init(priv);
584 if (ret && ret != -ENODEV)
585 goto backlight_failed;
586 }
587
58ac7aa0 588 return 0;
98ee6919 589
a4ecbb8a
IP
590backlight_failed:
591 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
592 ideapad_unregister_rfkill(adevice, i);
7451a55a 593 ideapad_input_exit(priv);
f63409ae 594input_failed:
8693ae84 595 ideapad_platform_exit(priv);
98ee6919
IP
596platform_failed:
597 kfree(priv);
598 return ret;
58ac7aa0
DW
599}
600
a4b5a279 601static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
58ac7aa0 602{
ce326329 603 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
58ac7aa0 604 int i;
ce326329 605
a4ecbb8a 606 ideapad_backlight_exit(priv);
c1f73658 607 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 608 ideapad_unregister_rfkill(adevice, i);
8693ae84
IP
609 ideapad_input_exit(priv);
610 ideapad_platform_exit(priv);
ce326329
DW
611 dev_set_drvdata(&adevice->dev, NULL);
612 kfree(priv);
c9f718d0 613
58ac7aa0
DW
614 return 0;
615}
616
ce326329 617static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
58ac7aa0 618{
8693ae84 619 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
8e7d3543
IP
620 acpi_handle handle = adevice->handle;
621 unsigned long vpc1, vpc2, vpc_bit;
622
2be1dc21 623 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
8e7d3543 624 return;
2be1dc21 625 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
8e7d3543
IP
626 return;
627
628 vpc1 = (vpc2 << 8) | vpc1;
629 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
630 if (test_bit(vpc_bit, &vpc1)) {
a4ecbb8a
IP
631 switch (vpc_bit) {
632 case 9:
8e7d3543 633 ideapad_sync_rfk_state(adevice);
a4ecbb8a
IP
634 break;
635 case 4:
636 ideapad_backlight_notify_brightness(priv);
637 break;
638 case 2:
639 ideapad_backlight_notify_power(priv);
640 break;
641 default:
8693ae84 642 ideapad_input_report(priv, vpc_bit);
a4ecbb8a 643 }
8e7d3543
IP
644 }
645 }
58ac7aa0
DW
646}
647
648static struct acpi_driver ideapad_acpi_driver = {
649 .name = "ideapad_acpi",
650 .class = "IdeaPad",
651 .ids = ideapad_device_ids,
652 .ops.add = ideapad_acpi_add,
653 .ops.remove = ideapad_acpi_remove,
654 .ops.notify = ideapad_acpi_notify,
655 .owner = THIS_MODULE,
656};
657
58ac7aa0
DW
658static int __init ideapad_acpi_module_init(void)
659{
a4b5a279 660 return acpi_bus_register_driver(&ideapad_acpi_driver);
58ac7aa0
DW
661}
662
58ac7aa0
DW
663static void __exit ideapad_acpi_module_exit(void)
664{
665 acpi_bus_unregister_driver(&ideapad_acpi_driver);
58ac7aa0
DW
666}
667
668MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
669MODULE_DESCRIPTION("IdeaPad ACPI Extras");
670MODULE_LICENSE("GPL");
671
672module_init(ideapad_acpi_module_init);
673module_exit(ideapad_acpi_module_exit);
This page took 0.211477 seconds and 5 git commands to generate.