diff options
Diffstat (limited to 'drivers/platform/x86/sony-laptop.c')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 195 |
1 files changed, 158 insertions, 37 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e642f5f29504..6fe8cd6e23b5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -138,6 +138,8 @@ MODULE_PARM_DESC(kbd_backlight_timeout, | |||
138 | "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " | 138 | "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " |
139 | "(default: 0)"); | 139 | "(default: 0)"); |
140 | 140 | ||
141 | static void sony_nc_kbd_backlight_resume(void); | ||
142 | |||
141 | enum sony_nc_rfkill { | 143 | enum sony_nc_rfkill { |
142 | SONY_WIFI, | 144 | SONY_WIFI, |
143 | SONY_BLUETOOTH, | 145 | SONY_BLUETOOTH, |
@@ -771,11 +773,6 @@ static int sony_nc_handles_setup(struct platform_device *pd) | |||
771 | if (!handles) | 773 | if (!handles) |
772 | return -ENOMEM; | 774 | return -ENOMEM; |
773 | 775 | ||
774 | sysfs_attr_init(&handles->devattr.attr); | ||
775 | handles->devattr.attr.name = "handles"; | ||
776 | handles->devattr.attr.mode = S_IRUGO; | ||
777 | handles->devattr.show = sony_nc_handles_show; | ||
778 | |||
779 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | 776 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { |
780 | if (!acpi_callsetfunc(sony_nc_acpi_handle, | 777 | if (!acpi_callsetfunc(sony_nc_acpi_handle, |
781 | "SN00", i + 0x20, &result)) { | 778 | "SN00", i + 0x20, &result)) { |
@@ -785,11 +782,18 @@ static int sony_nc_handles_setup(struct platform_device *pd) | |||
785 | } | 782 | } |
786 | } | 783 | } |
787 | 784 | ||
788 | /* allow reading capabilities via sysfs */ | 785 | if (debug) { |
789 | if (device_create_file(&pd->dev, &handles->devattr)) { | 786 | sysfs_attr_init(&handles->devattr.attr); |
790 | kfree(handles); | 787 | handles->devattr.attr.name = "handles"; |
791 | handles = NULL; | 788 | handles->devattr.attr.mode = S_IRUGO; |
792 | return -1; | 789 | handles->devattr.show = sony_nc_handles_show; |
790 | |||
791 | /* allow reading capabilities via sysfs */ | ||
792 | if (device_create_file(&pd->dev, &handles->devattr)) { | ||
793 | kfree(handles); | ||
794 | handles = NULL; | ||
795 | return -1; | ||
796 | } | ||
793 | } | 797 | } |
794 | 798 | ||
795 | return 0; | 799 | return 0; |
@@ -798,7 +802,8 @@ static int sony_nc_handles_setup(struct platform_device *pd) | |||
798 | static int sony_nc_handles_cleanup(struct platform_device *pd) | 802 | static int sony_nc_handles_cleanup(struct platform_device *pd) |
799 | { | 803 | { |
800 | if (handles) { | 804 | if (handles) { |
801 | device_remove_file(&pd->dev, &handles->devattr); | 805 | if (debug) |
806 | device_remove_file(&pd->dev, &handles->devattr); | ||
802 | kfree(handles); | 807 | kfree(handles); |
803 | handles = NULL; | 808 | handles = NULL; |
804 | } | 809 | } |
@@ -808,6 +813,11 @@ static int sony_nc_handles_cleanup(struct platform_device *pd) | |||
808 | static int sony_find_snc_handle(int handle) | 813 | static int sony_find_snc_handle(int handle) |
809 | { | 814 | { |
810 | int i; | 815 | int i; |
816 | |||
817 | /* not initialized yet, return early */ | ||
818 | if (!handles) | ||
819 | return -1; | ||
820 | |||
811 | for (i = 0; i < 0x10; i++) { | 821 | for (i = 0; i < 0x10; i++) { |
812 | if (handles->cap[i] == handle) { | 822 | if (handles->cap[i] == handle) { |
813 | dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", | 823 | dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", |
@@ -924,6 +934,14 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, | |||
924 | /* | 934 | /* |
925 | * Backlight device | 935 | * Backlight device |
926 | */ | 936 | */ |
937 | struct sony_backlight_props { | ||
938 | struct backlight_device *dev; | ||
939 | int handle; | ||
940 | u8 offset; | ||
941 | u8 maxlvl; | ||
942 | }; | ||
943 | struct sony_backlight_props sony_bl_props; | ||
944 | |||
927 | static int sony_backlight_update_status(struct backlight_device *bd) | 945 | static int sony_backlight_update_status(struct backlight_device *bd) |
928 | { | 946 | { |
929 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", | 947 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", |
@@ -944,21 +962,26 @@ static int sony_nc_get_brightness_ng(struct backlight_device *bd) | |||
944 | { | 962 | { |
945 | int result; | 963 | int result; |
946 | int *handle = (int *)bl_get_data(bd); | 964 | int *handle = (int *)bl_get_data(bd); |
965 | struct sony_backlight_props *sdev = | ||
966 | (struct sony_backlight_props *)bl_get_data(bd); | ||
947 | 967 | ||
948 | sony_call_snc_handle(*handle, 0x0200, &result); | 968 | sony_call_snc_handle(sdev->handle, 0x0200, &result); |
949 | 969 | ||
950 | return result & 0xff; | 970 | return (result & 0xff) - sdev->offset; |
951 | } | 971 | } |
952 | 972 | ||
953 | static int sony_nc_update_status_ng(struct backlight_device *bd) | 973 | static int sony_nc_update_status_ng(struct backlight_device *bd) |
954 | { | 974 | { |
955 | int value, result; | 975 | int value, result; |
956 | int *handle = (int *)bl_get_data(bd); | 976 | int *handle = (int *)bl_get_data(bd); |
977 | struct sony_backlight_props *sdev = | ||
978 | (struct sony_backlight_props *)bl_get_data(bd); | ||
957 | 979 | ||
958 | value = bd->props.brightness; | 980 | value = bd->props.brightness + sdev->offset; |
959 | sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result); | 981 | if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result)) |
982 | return -EIO; | ||
960 | 983 | ||
961 | return sony_nc_get_brightness_ng(bd); | 984 | return value; |
962 | } | 985 | } |
963 | 986 | ||
964 | static const struct backlight_ops sony_backlight_ops = { | 987 | static const struct backlight_ops sony_backlight_ops = { |
@@ -971,8 +994,6 @@ static const struct backlight_ops sony_backlight_ng_ops = { | |||
971 | .update_status = sony_nc_update_status_ng, | 994 | .update_status = sony_nc_update_status_ng, |
972 | .get_brightness = sony_nc_get_brightness_ng, | 995 | .get_brightness = sony_nc_get_brightness_ng, |
973 | }; | 996 | }; |
974 | static int backlight_ng_handle; | ||
975 | static struct backlight_device *sony_backlight_device; | ||
976 | 997 | ||
977 | /* | 998 | /* |
978 | * New SNC-only Vaios event mapping to driver known keys | 999 | * New SNC-only Vaios event mapping to driver known keys |
@@ -1168,6 +1189,9 @@ static int sony_nc_resume(struct acpi_device *device) | |||
1168 | /* re-read rfkill state */ | 1189 | /* re-read rfkill state */ |
1169 | sony_nc_rfkill_update(); | 1190 | sony_nc_rfkill_update(); |
1170 | 1191 | ||
1192 | /* restore kbd backlight states */ | ||
1193 | sony_nc_kbd_backlight_resume(); | ||
1194 | |||
1171 | return 0; | 1195 | return 0; |
1172 | } | 1196 | } |
1173 | 1197 | ||
@@ -1355,6 +1379,7 @@ out_no_enum: | |||
1355 | #define KBDBL_HANDLER 0x137 | 1379 | #define KBDBL_HANDLER 0x137 |
1356 | #define KBDBL_PRESENT 0xB00 | 1380 | #define KBDBL_PRESENT 0xB00 |
1357 | #define SET_MODE 0xC00 | 1381 | #define SET_MODE 0xC00 |
1382 | #define SET_STATE 0xD00 | ||
1358 | #define SET_TIMEOUT 0xE00 | 1383 | #define SET_TIMEOUT 0xE00 |
1359 | 1384 | ||
1360 | struct kbd_backlight { | 1385 | struct kbd_backlight { |
@@ -1377,6 +1402,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | |||
1377 | (value << 0x10) | SET_MODE, &result)) | 1402 | (value << 0x10) | SET_MODE, &result)) |
1378 | return -EIO; | 1403 | return -EIO; |
1379 | 1404 | ||
1405 | /* Try to turn the light on/off immediately */ | ||
1406 | sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, | ||
1407 | &result); | ||
1408 | |||
1380 | kbdbl_handle->mode = value; | 1409 | kbdbl_handle->mode = value; |
1381 | 1410 | ||
1382 | return 0; | 1411 | return 0; |
@@ -1458,7 +1487,7 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) | |||
1458 | { | 1487 | { |
1459 | int result; | 1488 | int result; |
1460 | 1489 | ||
1461 | if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result)) | 1490 | if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) |
1462 | return 0; | 1491 | return 0; |
1463 | if (!(result & 0x02)) | 1492 | if (!(result & 0x02)) |
1464 | return 0; | 1493 | return 0; |
@@ -1501,13 +1530,105 @@ outkzalloc: | |||
1501 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) | 1530 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) |
1502 | { | 1531 | { |
1503 | if (kbdbl_handle) { | 1532 | if (kbdbl_handle) { |
1533 | int result; | ||
1534 | |||
1504 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); | 1535 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); |
1505 | device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); | 1536 | device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); |
1537 | |||
1538 | /* restore the default hw behaviour */ | ||
1539 | sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); | ||
1540 | sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); | ||
1541 | |||
1506 | kfree(kbdbl_handle); | 1542 | kfree(kbdbl_handle); |
1507 | } | 1543 | } |
1508 | return 0; | 1544 | return 0; |
1509 | } | 1545 | } |
1510 | 1546 | ||
1547 | static void sony_nc_kbd_backlight_resume(void) | ||
1548 | { | ||
1549 | int ignore = 0; | ||
1550 | |||
1551 | if (!kbdbl_handle) | ||
1552 | return; | ||
1553 | |||
1554 | if (kbdbl_handle->mode == 0) | ||
1555 | sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); | ||
1556 | |||
1557 | if (kbdbl_handle->timeout != 0) | ||
1558 | sony_call_snc_handle(KBDBL_HANDLER, | ||
1559 | (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT, | ||
1560 | &ignore); | ||
1561 | } | ||
1562 | |||
1563 | static void sony_nc_backlight_ng_read_limits(int handle, | ||
1564 | struct sony_backlight_props *props) | ||
1565 | { | ||
1566 | int offset; | ||
1567 | acpi_status status; | ||
1568 | u8 brlvl, i; | ||
1569 | u8 min = 0xff, max = 0x00; | ||
1570 | struct acpi_object_list params; | ||
1571 | union acpi_object in_obj; | ||
1572 | union acpi_object *lvl_enum; | ||
1573 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1574 | |||
1575 | props->handle = handle; | ||
1576 | props->offset = 0; | ||
1577 | props->maxlvl = 0xff; | ||
1578 | |||
1579 | offset = sony_find_snc_handle(handle); | ||
1580 | if (offset < 0) | ||
1581 | return; | ||
1582 | |||
1583 | /* try to read the boundaries from ACPI tables, if we fail the above | ||
1584 | * defaults should be reasonable | ||
1585 | */ | ||
1586 | params.count = 1; | ||
1587 | params.pointer = &in_obj; | ||
1588 | in_obj.type = ACPI_TYPE_INTEGER; | ||
1589 | in_obj.integer.value = offset; | ||
1590 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, | ||
1591 | &buffer); | ||
1592 | if (ACPI_FAILURE(status)) | ||
1593 | return; | ||
1594 | |||
1595 | lvl_enum = (union acpi_object *) buffer.pointer; | ||
1596 | if (!lvl_enum) { | ||
1597 | pr_err("No SN06 return object."); | ||
1598 | return; | ||
1599 | } | ||
1600 | if (lvl_enum->type != ACPI_TYPE_BUFFER) { | ||
1601 | pr_err("Invalid SN06 return object 0x%.2x\n", | ||
1602 | lvl_enum->type); | ||
1603 | goto out_invalid; | ||
1604 | } | ||
1605 | |||
1606 | /* the buffer lists brightness levels available, brightness levels are | ||
1607 | * from 0 to 8 in the array, other values are used by ALS control. | ||
1608 | */ | ||
1609 | for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { | ||
1610 | |||
1611 | brlvl = *(lvl_enum->buffer.pointer + i); | ||
1612 | dprintk("Brightness level: %d\n", brlvl); | ||
1613 | |||
1614 | if (!brlvl) | ||
1615 | break; | ||
1616 | |||
1617 | if (brlvl > max) | ||
1618 | max = brlvl; | ||
1619 | if (brlvl < min) | ||
1620 | min = brlvl; | ||
1621 | } | ||
1622 | props->offset = min; | ||
1623 | props->maxlvl = max; | ||
1624 | dprintk("Brightness levels: min=%d max=%d\n", props->offset, | ||
1625 | props->maxlvl); | ||
1626 | |||
1627 | out_invalid: | ||
1628 | kfree(buffer.pointer); | ||
1629 | return; | ||
1630 | } | ||
1631 | |||
1511 | static void sony_nc_backlight_setup(void) | 1632 | static void sony_nc_backlight_setup(void) |
1512 | { | 1633 | { |
1513 | acpi_handle unused; | 1634 | acpi_handle unused; |
@@ -1516,14 +1637,14 @@ static void sony_nc_backlight_setup(void) | |||
1516 | struct backlight_properties props; | 1637 | struct backlight_properties props; |
1517 | 1638 | ||
1518 | if (sony_find_snc_handle(0x12f) != -1) { | 1639 | if (sony_find_snc_handle(0x12f) != -1) { |
1519 | backlight_ng_handle = 0x12f; | ||
1520 | ops = &sony_backlight_ng_ops; | 1640 | ops = &sony_backlight_ng_ops; |
1521 | max_brightness = 0xff; | 1641 | sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props); |
1642 | max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; | ||
1522 | 1643 | ||
1523 | } else if (sony_find_snc_handle(0x137) != -1) { | 1644 | } else if (sony_find_snc_handle(0x137) != -1) { |
1524 | backlight_ng_handle = 0x137; | ||
1525 | ops = &sony_backlight_ng_ops; | 1645 | ops = &sony_backlight_ng_ops; |
1526 | max_brightness = 0xff; | 1646 | sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props); |
1647 | max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; | ||
1527 | 1648 | ||
1528 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | 1649 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", |
1529 | &unused))) { | 1650 | &unused))) { |
@@ -1536,22 +1657,22 @@ static void sony_nc_backlight_setup(void) | |||
1536 | memset(&props, 0, sizeof(struct backlight_properties)); | 1657 | memset(&props, 0, sizeof(struct backlight_properties)); |
1537 | props.type = BACKLIGHT_PLATFORM; | 1658 | props.type = BACKLIGHT_PLATFORM; |
1538 | props.max_brightness = max_brightness; | 1659 | props.max_brightness = max_brightness; |
1539 | sony_backlight_device = backlight_device_register("sony", NULL, | 1660 | sony_bl_props.dev = backlight_device_register("sony", NULL, |
1540 | &backlight_ng_handle, | 1661 | &sony_bl_props, |
1541 | ops, &props); | 1662 | ops, &props); |
1542 | 1663 | ||
1543 | if (IS_ERR(sony_backlight_device)) { | 1664 | if (IS_ERR(sony_bl_props.dev)) { |
1544 | pr_warning(DRV_PFX "unable to register backlight device\n"); | 1665 | pr_warn(DRV_PFX "unable to register backlight device\n"); |
1545 | sony_backlight_device = NULL; | 1666 | sony_bl_props.dev = NULL; |
1546 | } else | 1667 | } else |
1547 | sony_backlight_device->props.brightness = | 1668 | sony_bl_props.dev->props.brightness = |
1548 | ops->get_brightness(sony_backlight_device); | 1669 | ops->get_brightness(sony_bl_props.dev); |
1549 | } | 1670 | } |
1550 | 1671 | ||
1551 | static void sony_nc_backlight_cleanup(void) | 1672 | static void sony_nc_backlight_cleanup(void) |
1552 | { | 1673 | { |
1553 | if (sony_backlight_device) | 1674 | if (sony_bl_props.dev) |
1554 | backlight_device_unregister(sony_backlight_device); | 1675 | backlight_device_unregister(sony_bl_props.dev); |
1555 | } | 1676 | } |
1556 | 1677 | ||
1557 | static int sony_nc_add(struct acpi_device *device) | 1678 | static int sony_nc_add(struct acpi_device *device) |
@@ -2549,7 +2670,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2549 | mutex_lock(&spic_dev.lock); | 2670 | mutex_lock(&spic_dev.lock); |
2550 | switch (cmd) { | 2671 | switch (cmd) { |
2551 | case SONYPI_IOCGBRT: | 2672 | case SONYPI_IOCGBRT: |
2552 | if (sony_backlight_device == NULL) { | 2673 | if (sony_bl_props.dev == NULL) { |
2553 | ret = -EIO; | 2674 | ret = -EIO; |
2554 | break; | 2675 | break; |
2555 | } | 2676 | } |
@@ -2562,7 +2683,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2562 | ret = -EFAULT; | 2683 | ret = -EFAULT; |
2563 | break; | 2684 | break; |
2564 | case SONYPI_IOCSBRT: | 2685 | case SONYPI_IOCSBRT: |
2565 | if (sony_backlight_device == NULL) { | 2686 | if (sony_bl_props.dev == NULL) { |
2566 | ret = -EIO; | 2687 | ret = -EIO; |
2567 | break; | 2688 | break; |
2568 | } | 2689 | } |
@@ -2576,8 +2697,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2576 | break; | 2697 | break; |
2577 | } | 2698 | } |
2578 | /* sync the backlight device status */ | 2699 | /* sync the backlight device status */ |
2579 | sony_backlight_device->props.brightness = | 2700 | sony_bl_props.dev->props.brightness = |
2580 | sony_backlight_get_brightness(sony_backlight_device); | 2701 | sony_backlight_get_brightness(sony_bl_props.dev); |
2581 | break; | 2702 | break; |
2582 | case SONYPI_IOCGBAT1CAP: | 2703 | case SONYPI_IOCGBAT1CAP: |
2583 | if (ec_read16(SONYPI_BAT1_FULL, &val16)) { | 2704 | if (ec_read16(SONYPI_BAT1_FULL, &val16)) { |