ideapad-laptop.c (0b9112a58836ad6a7e84eebec06a2de9778b7573) ideapad-laptop.c (eabe533904cbcb6c7df530fd807cf2a3c3567d35)
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
4 *
5 * Copyright © 2010 Intel Corporation
6 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/types.h>
15#include <linux/acpi.h>
16#include <linux/rfkill.h>
17#include <linux/platform_device.h>
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
4 *
5 * Copyright © 2010 Intel Corporation
6 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/types.h>
15#include <linux/acpi.h>
16#include <linux/rfkill.h>
17#include <linux/platform_device.h>
18#include <linux/platform_profile.h>
18#include <linux/input.h>
19#include <linux/input/sparse-keymap.h>
20#include <linux/backlight.h>
21#include <linux/fb.h>
22#include <linux/debugfs.h>
23#include <linux/seq_file.h>
24#include <linux/i8042.h>
25#include <linux/dmi.h>

--- 46 unchanged lines hidden (view full) ---

72 VPCCMD_W_FAN,
73 VPCCMD_R_RF,
74 VPCCMD_W_RF,
75 VPCCMD_R_FAN = 0x2B,
76 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
77 VPCCMD_W_BL_POWER = 0x33,
78};
79
19#include <linux/input.h>
20#include <linux/input/sparse-keymap.h>
21#include <linux/backlight.h>
22#include <linux/fb.h>
23#include <linux/debugfs.h>
24#include <linux/seq_file.h>
25#include <linux/i8042.h>
26#include <linux/dmi.h>

--- 46 unchanged lines hidden (view full) ---

73 VPCCMD_W_FAN,
74 VPCCMD_R_RF,
75 VPCCMD_W_RF,
76 VPCCMD_R_FAN = 0x2B,
77 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
78 VPCCMD_W_BL_POWER = 0x33,
79};
80
81struct ideapad_dytc_priv {
82 enum platform_profile_option current_profile;
83 struct platform_profile_handler pprof;
84 struct mutex mutex;
85 struct ideapad_private *priv;
86};
87
80struct ideapad_rfk_priv {
81 int dev;
82 struct ideapad_private *priv;
83};
84
85struct ideapad_private {
86 struct acpi_device *adev;
87 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
88 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
89 struct platform_device *platform_device;
90 struct input_dev *inputdev;
91 struct backlight_device *blightdev;
88struct ideapad_rfk_priv {
89 int dev;
90 struct ideapad_private *priv;
91};
92
93struct ideapad_private {
94 struct acpi_device *adev;
95 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
96 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
97 struct platform_device *platform_device;
98 struct input_dev *inputdev;
99 struct backlight_device *blightdev;
100 struct ideapad_dytc_priv *dytc;
92 struct dentry *debug;
93 unsigned long cfg;
94 bool has_hw_rfkill_switch;
95 bool has_touchpad_switch;
96 const char *fnesc_guid;
97};
98
99static bool no_bt_rfkill;

--- 32 unchanged lines hidden (view full) ---

132static int method_int1(acpi_handle handle, char *method, int cmd)
133{
134 acpi_status status;
135
136 status = acpi_execute_simple_method(handle, method, cmd);
137 return ACPI_FAILURE(status) ? -1 : 0;
138}
139
101 struct dentry *debug;
102 unsigned long cfg;
103 bool has_hw_rfkill_switch;
104 bool has_touchpad_switch;
105 const char *fnesc_guid;
106};
107
108static bool no_bt_rfkill;

--- 32 unchanged lines hidden (view full) ---

141static int method_int1(acpi_handle handle, char *method, int cmd)
142{
143 acpi_status status;
144
145 status = acpi_execute_simple_method(handle, method, cmd);
146 return ACPI_FAILURE(status) ? -1 : 0;
147}
148
149static int method_dytc(acpi_handle handle, int cmd, int *ret)
150{
151 acpi_status status;
152 unsigned long long result;
153 struct acpi_object_list params;
154 union acpi_object in_obj;
155
156 params.count = 1;
157 params.pointer = &in_obj;
158 in_obj.type = ACPI_TYPE_INTEGER;
159 in_obj.integer.value = cmd;
160
161 status = acpi_evaluate_integer(handle, "DYTC", &params, &result);
162
163 if (ACPI_FAILURE(status)) {
164 *ret = -1;
165 return -1;
166 }
167 *ret = result;
168 return 0;
169}
170
140static int method_vpcr(acpi_handle handle, int cmd, int *ret)
141{
142 acpi_status status;
143 unsigned long long result;
144 struct acpi_object_list params;
145 union acpi_object in_obj;
146
147 params.count = 1;

--- 397 unchanged lines hidden (view full) ---

545}
546
547static const struct attribute_group ideapad_attribute_group = {
548 .is_visible = ideapad_is_visible,
549 .attrs = ideapad_attributes
550};
551
552/*
171static int method_vpcr(acpi_handle handle, int cmd, int *ret)
172{
173 acpi_status status;
174 unsigned long long result;
175 struct acpi_object_list params;
176 union acpi_object in_obj;
177
178 params.count = 1;

--- 397 unchanged lines hidden (view full) ---

576}
577
578static const struct attribute_group ideapad_attribute_group = {
579 .is_visible = ideapad_is_visible,
580 .attrs = ideapad_attributes
581};
582
583/*
584 * DYTC Platform profile
585 */
586#define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */
587#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */
588#define DYTC_CMD_GET 2 /* To get current IC function and mode */
589#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
590
591#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */
592#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */
593#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */
594
595#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
596#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
597
598#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */
599#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */
600#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */
601
602#define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */
603#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */
604#define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */
605
606#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */
607#define DYTC_MODE_LOW_POWER 3 /* Low power mode aka quiet */
608#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */
609
610#define DYTC_SET_COMMAND(function, mode, on) \
611 (DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \
612 (mode) << DYTC_SET_MODE_BIT | \
613 (on) << DYTC_SET_VALID_BIT)
614
615#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0)
616
617#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)
618
619static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
620{
621 switch (dytcmode) {
622 case DYTC_MODE_LOW_POWER:
623 *profile = PLATFORM_PROFILE_LOW_POWER;
624 break;
625 case DYTC_MODE_BALANCE:
626 *profile = PLATFORM_PROFILE_BALANCED;
627 break;
628 case DYTC_MODE_PERFORM:
629 *profile = PLATFORM_PROFILE_PERFORMANCE;
630 break;
631 default: /* Unknown mode */
632 return -EINVAL;
633 }
634 return 0;
635}
636
637static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode)
638{
639 switch (profile) {
640 case PLATFORM_PROFILE_LOW_POWER:
641 *perfmode = DYTC_MODE_LOW_POWER;
642 break;
643 case PLATFORM_PROFILE_BALANCED:
644 *perfmode = DYTC_MODE_BALANCE;
645 break;
646 case PLATFORM_PROFILE_PERFORMANCE:
647 *perfmode = DYTC_MODE_PERFORM;
648 break;
649 default: /* Unknown profile */
650 return -EOPNOTSUPP;
651 }
652 return 0;
653}
654
655/*
656 * dytc_profile_get: Function to register with platform_profile
657 * handler. Returns current platform profile.
658 */
659int dytc_profile_get(struct platform_profile_handler *pprof,
660 enum platform_profile_option *profile)
661{
662 struct ideapad_dytc_priv *dytc;
663
664 dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
665 *profile = dytc->current_profile;
666 return 0;
667}
668
669/*
670 * Helper function - check if we are in CQL mode and if we are
671 * - disable CQL,
672 * - run the command
673 * - enable CQL
674 * If not in CQL mode, just run the command
675 */
676int dytc_cql_command(struct ideapad_private *priv, int command, int *output)
677{
678 int err, cmd_err, dummy;
679 int cur_funcmode;
680
681 /* Determine if we are in CQL mode. This alters the commands we do */
682 err = method_dytc(priv->adev->handle, DYTC_CMD_GET, output);
683 if (err)
684 return err;
685
686 cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF;
687 /* Check if we're OK to return immediately */
688 if ((command == DYTC_CMD_GET) && (cur_funcmode != DYTC_FUNCTION_CQL))
689 return 0;
690
691 if (cur_funcmode == DYTC_FUNCTION_CQL) {
692 err = method_dytc(priv->adev->handle, DYTC_DISABLE_CQL, &dummy);
693 if (err)
694 return err;
695 }
696
697 cmd_err = method_dytc(priv->adev->handle, command, output);
698 /* Check return condition after we've restored CQL state */
699
700 if (cur_funcmode == DYTC_FUNCTION_CQL) {
701 err = method_dytc(priv->adev->handle, DYTC_ENABLE_CQL, &dummy);
702 if (err)
703 return err;
704 }
705
706 return cmd_err;
707}
708
709/*
710 * dytc_profile_set: Function to register with platform_profile
711 * handler. Sets current platform profile.
712 */
713int dytc_profile_set(struct platform_profile_handler *pprof,
714 enum platform_profile_option profile)
715{
716 struct ideapad_dytc_priv *dytc;
717 struct ideapad_private *priv;
718 int output;
719 int err;
720
721 dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
722 priv = dytc->priv;
723
724 err = mutex_lock_interruptible(&dytc->mutex);
725 if (err)
726 return err;
727
728 if (profile == PLATFORM_PROFILE_BALANCED) {
729 /* To get back to balanced mode we just issue a reset command */
730 err = method_dytc(priv->adev->handle, DYTC_CMD_RESET, &output);
731 if (err)
732 goto unlock;
733 } else {
734 int perfmode;
735
736 err = convert_profile_to_dytc(profile, &perfmode);
737 if (err)
738 goto unlock;
739
740 /* Determine if we are in CQL mode. This alters the commands we do */
741 err = dytc_cql_command(priv,
742 DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),
743 &output);
744 if (err)
745 goto unlock;
746 }
747 /* Success - update current profile */
748 dytc->current_profile = profile;
749unlock:
750 mutex_unlock(&dytc->mutex);
751 return err;
752}
753
754static void dytc_profile_refresh(struct ideapad_private *priv)
755{
756 enum platform_profile_option profile;
757 int output, err;
758 int perfmode;
759
760 mutex_lock(&priv->dytc->mutex);
761 err = dytc_cql_command(priv, DYTC_CMD_GET, &output);
762 mutex_unlock(&priv->dytc->mutex);
763 if (err)
764 return;
765
766 perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;
767 convert_dytc_to_profile(perfmode, &profile);
768 if (profile != priv->dytc->current_profile) {
769 priv->dytc->current_profile = profile;
770 platform_profile_notify();
771 }
772}
773
774static int ideapad_dytc_profile_init(struct ideapad_private *priv)
775{
776 int err, output, dytc_version;
777
778 err = method_dytc(priv->adev->handle, DYTC_CMD_QUERY, &output);
779 /* For all other errors we can flag the failure */
780 if (err)
781 return err;
782
783 /* Check DYTC is enabled and supports mode setting */
784 if (!(output & BIT(DYTC_QUERY_ENABLE_BIT)))
785 return -ENODEV;
786
787 dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
788 if (dytc_version < 5)
789 return -ENODEV;
790
791 priv->dytc = kzalloc(sizeof(struct ideapad_dytc_priv), GFP_KERNEL);
792 if (!priv->dytc)
793 return -ENOMEM;
794
795 mutex_init(&priv->dytc->mutex);
796
797 priv->dytc->priv = priv;
798 priv->dytc->pprof.profile_get = dytc_profile_get;
799 priv->dytc->pprof.profile_set = dytc_profile_set;
800
801 /* Setup supported modes */
802 set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);
803 set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);
804 set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);
805
806 /* Create platform_profile structure and register */
807 err = platform_profile_register(&priv->dytc->pprof);
808 if (err)
809 goto mutex_destroy;
810
811 /* Ensure initial values are correct */
812 dytc_profile_refresh(priv);
813
814 return 0;
815
816mutex_destroy:
817 mutex_destroy(&priv->dytc->mutex);
818 kfree(priv->dytc);
819 priv->dytc = NULL;
820 return err;
821}
822
823static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
824{
825 if (!priv->dytc)
826 return;
827
828 platform_profile_remove();
829 mutex_destroy(&priv->dytc->mutex);
830 kfree(priv->dytc);
831 priv->dytc = NULL;
832}
833
834/*
553 * Rfkill
554 */
555struct ideapad_rfk_data {
556 char *name;
557 int cfgbit;
558 int opcode;
559 int type;
560};

--- 460 unchanged lines hidden (view full) ---

1021
1022 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1023 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1024 ideapad_register_rfkill(priv, i);
1025
1026 ideapad_sync_rfk_state(priv);
1027 ideapad_sync_touchpad_state(priv);
1028
835 * Rfkill
836 */
837struct ideapad_rfk_data {
838 char *name;
839 int cfgbit;
840 int opcode;
841 int type;
842};

--- 460 unchanged lines hidden (view full) ---

1303
1304 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1305 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1306 ideapad_register_rfkill(priv, i);
1307
1308 ideapad_sync_rfk_state(priv);
1309 ideapad_sync_touchpad_state(priv);
1310
1311 ideapad_dytc_profile_init(priv);
1312
1029 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1030 ret = ideapad_backlight_init(priv);
1031 if (ret && ret != -ENODEV)
1032 goto backlight_failed;
1033 }
1034 ret = acpi_install_notify_handler(adev->handle,
1035 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
1036 if (ret)

--- 37 unchanged lines hidden (view full) ---

1074
1075#if IS_ENABLED(CONFIG_ACPI_WMI)
1076 if (priv->fnesc_guid)
1077 wmi_remove_notify_handler(priv->fnesc_guid);
1078#endif
1079 acpi_remove_notify_handler(priv->adev->handle,
1080 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1081 ideapad_backlight_exit(priv);
1313 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1314 ret = ideapad_backlight_init(priv);
1315 if (ret && ret != -ENODEV)
1316 goto backlight_failed;
1317 }
1318 ret = acpi_install_notify_handler(adev->handle,
1319 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
1320 if (ret)

--- 37 unchanged lines hidden (view full) ---

1358
1359#if IS_ENABLED(CONFIG_ACPI_WMI)
1360 if (priv->fnesc_guid)
1361 wmi_remove_notify_handler(priv->fnesc_guid);
1362#endif
1363 acpi_remove_notify_handler(priv->adev->handle,
1364 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1365 ideapad_backlight_exit(priv);
1366 ideapad_dytc_profile_exit(priv);
1082 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1083 ideapad_unregister_rfkill(priv, i);
1084 ideapad_input_exit(priv);
1085 ideapad_debugfs_exit(priv);
1086 ideapad_sysfs_exit(priv);
1087 dev_set_drvdata(&pdev->dev, NULL);
1088
1089 return 0;

--- 5 unchanged lines hidden (view full) ---

1095 struct ideapad_private *priv;
1096
1097 if (!device)
1098 return -EINVAL;
1099 priv = dev_get_drvdata(device);
1100
1101 ideapad_sync_rfk_state(priv);
1102 ideapad_sync_touchpad_state(priv);
1367 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1368 ideapad_unregister_rfkill(priv, i);
1369 ideapad_input_exit(priv);
1370 ideapad_debugfs_exit(priv);
1371 ideapad_sysfs_exit(priv);
1372 dev_set_drvdata(&pdev->dev, NULL);
1373
1374 return 0;

--- 5 unchanged lines hidden (view full) ---

1380 struct ideapad_private *priv;
1381
1382 if (!device)
1383 return -EINVAL;
1384 priv = dev_get_drvdata(device);
1385
1386 ideapad_sync_rfk_state(priv);
1387 ideapad_sync_touchpad_state(priv);
1388
1389 if (priv->dytc)
1390 dytc_profile_refresh(priv);
1391
1103 return 0;
1104}
1105#endif
1106static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
1107
1108static const struct acpi_device_id ideapad_device_ids[] = {
1109 { "VPC2004", 0},
1110 { "", 0},

--- 18 unchanged lines hidden ---
1392 return 0;
1393}
1394#endif
1395static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
1396
1397static const struct acpi_device_id ideapad_device_ids[] = {
1398 { "VPC2004", 0},
1399 { "", 0},

--- 18 unchanged lines hidden ---