aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/ideapad-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/ideapad-laptop.c')
-rw-r--r--drivers/platform/x86/ideapad-laptop.c195
1 files changed, 172 insertions, 23 deletions
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index bfdda33feb26..0c595410e788 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -32,13 +32,22 @@
32#include <linux/platform_device.h> 32#include <linux/platform_device.h>
33#include <linux/input.h> 33#include <linux/input.h>
34#include <linux/input/sparse-keymap.h> 34#include <linux/input/sparse-keymap.h>
35#include <linux/backlight.h>
36#include <linux/fb.h>
35 37
36#define IDEAPAD_RFKILL_DEV_NUM (3) 38#define IDEAPAD_RFKILL_DEV_NUM (3)
37 39
40#define CFG_BT_BIT (16)
41#define CFG_3G_BIT (17)
42#define CFG_WIFI_BIT (18)
43#define CFG_CAMERA_BIT (19)
44
38struct ideapad_private { 45struct ideapad_private {
39 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 46 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
40 struct platform_device *platform_device; 47 struct platform_device *platform_device;
41 struct input_dev *inputdev; 48 struct input_dev *inputdev;
49 struct backlight_device *blightdev;
50 unsigned long cfg;
42}; 51};
43 52
44static acpi_handle ideapad_handle; 53static acpi_handle ideapad_handle;
@@ -155,7 +164,7 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
155} 164}
156 165
157/* 166/*
158 * camera power 167 * sysfs
159 */ 168 */
160static ssize_t show_ideapad_cam(struct device *dev, 169static ssize_t show_ideapad_cam(struct device *dev,
161 struct device_attribute *attr, 170 struct device_attribute *attr,
@@ -186,6 +195,44 @@ static ssize_t store_ideapad_cam(struct device *dev,
186 195
187static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 196static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
188 197
198static ssize_t show_ideapad_cfg(struct device *dev,
199 struct device_attribute *attr,
200 char *buf)
201{
202 struct ideapad_private *priv = dev_get_drvdata(dev);
203
204 return sprintf(buf, "0x%.8lX\n", priv->cfg);
205}
206
207static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
208
209static struct attribute *ideapad_attributes[] = {
210 &dev_attr_camera_power.attr,
211 &dev_attr_cfg.attr,
212 NULL
213};
214
215static mode_t ideapad_is_visible(struct kobject *kobj,
216 struct attribute *attr,
217 int idx)
218{
219 struct device *dev = container_of(kobj, struct device, kobj);
220 struct ideapad_private *priv = dev_get_drvdata(dev);
221 bool supported;
222
223 if (attr == &dev_attr_camera_power.attr)
224 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
225 else
226 supported = true;
227
228 return supported ? attr->mode : 0;
229}
230
231static struct attribute_group ideapad_attribute_group = {
232 .is_visible = ideapad_is_visible,
233 .attrs = ideapad_attributes
234};
235
189/* 236/*
190 * Rfkill 237 * Rfkill
191 */ 238 */
@@ -197,9 +244,9 @@ struct ideapad_rfk_data {
197}; 244};
198 245
199const struct ideapad_rfk_data ideapad_rfk_data[] = { 246const struct ideapad_rfk_data ideapad_rfk_data[] = {
200 { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, 247 { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
201 { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, 248 { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH },
202 { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, 249 { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN },
203}; 250};
204 251
205static int ideapad_rfk_set(void *data, bool blocked) 252static int ideapad_rfk_set(void *data, bool blocked)
@@ -265,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
265 return 0; 312 return 0;
266} 313}
267 314
268static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, 315static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
269 int dev)
270{ 316{
271 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 317 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
272 318
@@ -280,15 +326,6 @@ static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
280/* 326/*
281 * Platform device 327 * Platform device
282 */ 328 */
283static struct attribute *ideapad_attributes[] = {
284 &dev_attr_camera_power.attr,
285 NULL
286};
287
288static struct attribute_group ideapad_attribute_group = {
289 .attrs = ideapad_attributes
290};
291
292static int __devinit ideapad_platform_init(struct ideapad_private *priv) 329static int __devinit ideapad_platform_init(struct ideapad_private *priv)
293{ 330{
294 int result; 331 int result;
@@ -369,7 +406,7 @@ err_free_dev:
369 return error; 406 return error;
370} 407}
371 408
372static void __devexit ideapad_input_exit(struct ideapad_private *priv) 409static void ideapad_input_exit(struct ideapad_private *priv)
373{ 410{
374 sparse_keymap_free(priv->inputdev); 411 sparse_keymap_free(priv->inputdev);
375 input_unregister_device(priv->inputdev); 412 input_unregister_device(priv->inputdev);
@@ -383,6 +420,98 @@ static void ideapad_input_report(struct ideapad_private *priv,
383} 420}
384 421
385/* 422/*
423 * backlight
424 */
425static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
426{
427 unsigned long now;
428
429 if (read_ec_data(ideapad_handle, 0x12, &now))
430 return -EIO;
431 return now;
432}
433
434static int ideapad_backlight_update_status(struct backlight_device *blightdev)
435{
436 if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
437 return -EIO;
438 if (write_ec_cmd(ideapad_handle, 0x33,
439 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
440 return -EIO;
441
442 return 0;
443}
444
445static const struct backlight_ops ideapad_backlight_ops = {
446 .get_brightness = ideapad_backlight_get_brightness,
447 .update_status = ideapad_backlight_update_status,
448};
449
450static int ideapad_backlight_init(struct ideapad_private *priv)
451{
452 struct backlight_device *blightdev;
453 struct backlight_properties props;
454 unsigned long max, now, power;
455
456 if (read_ec_data(ideapad_handle, 0x11, &max))
457 return -EIO;
458 if (read_ec_data(ideapad_handle, 0x12, &now))
459 return -EIO;
460 if (read_ec_data(ideapad_handle, 0x18, &power))
461 return -EIO;
462
463 memset(&props, 0, sizeof(struct backlight_properties));
464 props.max_brightness = max;
465 props.type = BACKLIGHT_PLATFORM;
466 blightdev = backlight_device_register("ideapad",
467 &priv->platform_device->dev,
468 priv,
469 &ideapad_backlight_ops,
470 &props);
471 if (IS_ERR(blightdev)) {
472 pr_err("Could not register backlight device\n");
473 return PTR_ERR(blightdev);
474 }
475
476 priv->blightdev = blightdev;
477 blightdev->props.brightness = now;
478 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
479 backlight_update_status(blightdev);
480
481 return 0;
482}
483
484static void ideapad_backlight_exit(struct ideapad_private *priv)
485{
486 if (priv->blightdev)
487 backlight_device_unregister(priv->blightdev);
488 priv->blightdev = NULL;
489}
490
491static void ideapad_backlight_notify_power(struct ideapad_private *priv)
492{
493 unsigned long power;
494 struct backlight_device *blightdev = priv->blightdev;
495
496 if (read_ec_data(ideapad_handle, 0x18, &power))
497 return;
498 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
499}
500
501static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
502{
503 unsigned long now;
504
505 /* if we control brightness via acpi video driver */
506 if (priv->blightdev == NULL) {
507 read_ec_data(ideapad_handle, 0x12, &now);
508 return;
509 }
510
511 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
512}
513
514/*
386 * module init/exit 515 * module init/exit
387 */ 516 */
388static const struct acpi_device_id ideapad_device_ids[] = { 517static const struct acpi_device_id ideapad_device_ids[] = {
@@ -393,10 +522,11 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
393 522
394static int __devinit ideapad_acpi_add(struct acpi_device *adevice) 523static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
395{ 524{
396 int ret, i, cfg; 525 int ret, i;
526 unsigned long cfg;
397 struct ideapad_private *priv; 527 struct ideapad_private *priv;
398 528
399 if (read_method_int(adevice->handle, "_CFG", &cfg)) 529 if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
400 return -ENODEV; 530 return -ENODEV;
401 531
402 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 532 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -404,6 +534,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
404 return -ENOMEM; 534 return -ENOMEM;
405 dev_set_drvdata(&adevice->dev, priv); 535 dev_set_drvdata(&adevice->dev, priv);
406 ideapad_handle = adevice->handle; 536 ideapad_handle = adevice->handle;
537 priv->cfg = cfg;
407 538
408 ret = ideapad_platform_init(priv); 539 ret = ideapad_platform_init(priv);
409 if (ret) 540 if (ret)
@@ -414,15 +545,25 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
414 goto input_failed; 545 goto input_failed;
415 546
416 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { 547 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
417 if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) 548 if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
418 ideapad_register_rfkill(adevice, i); 549 ideapad_register_rfkill(adevice, i);
419 else 550 else
420 priv->rfk[i] = NULL; 551 priv->rfk[i] = NULL;
421 } 552 }
422 ideapad_sync_rfk_state(adevice); 553 ideapad_sync_rfk_state(adevice);
423 554
555 if (!acpi_video_backlight_support()) {
556 ret = ideapad_backlight_init(priv);
557 if (ret && ret != -ENODEV)
558 goto backlight_failed;
559 }
560
424 return 0; 561 return 0;
425 562
563backlight_failed:
564 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
565 ideapad_unregister_rfkill(adevice, i);
566 ideapad_input_exit(priv);
426input_failed: 567input_failed:
427 ideapad_platform_exit(priv); 568 ideapad_platform_exit(priv);
428platform_failed: 569platform_failed:
@@ -435,6 +576,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
435 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 576 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
436 int i; 577 int i;
437 578
579 ideapad_backlight_exit(priv);
438 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 580 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
439 ideapad_unregister_rfkill(adevice, i); 581 ideapad_unregister_rfkill(adevice, i);
440 ideapad_input_exit(priv); 582 ideapad_input_exit(priv);
@@ -459,12 +601,19 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
459 vpc1 = (vpc2 << 8) | vpc1; 601 vpc1 = (vpc2 << 8) | vpc1;
460 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { 602 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
461 if (test_bit(vpc_bit, &vpc1)) { 603 if (test_bit(vpc_bit, &vpc1)) {
462 if (vpc_bit == 9) 604 switch (vpc_bit) {
605 case 9:
463 ideapad_sync_rfk_state(adevice); 606 ideapad_sync_rfk_state(adevice);
464 else if (vpc_bit == 4) 607 break;
465 read_ec_data(handle, 0x12, &vpc2); 608 case 4:
466 else 609 ideapad_backlight_notify_brightness(priv);
610 break;
611 case 2:
612 ideapad_backlight_notify_power(priv);
613 break;
614 default:
467 ideapad_input_report(priv, vpc_bit); 615 ideapad_input_report(priv, vpc_bit);
616 }
468 } 617 }
469 } 618 }
470} 619}