|
66 | 66 | #include <linux/acpi.h> |
67 | 67 | #include <linux/pci.h> |
68 | 68 | #include <linux/power_supply.h> |
| 69 | +#include <linux/platform_profile.h> |
69 | 70 | #include <sound/core.h> |
70 | 71 | #include <sound/control.h> |
71 | 72 | #include <sound/initval.h> |
@@ -9855,16 +9856,27 @@ static bool has_lapsensor; |
9855 | 9856 | static bool palm_state; |
9856 | 9857 | static bool lap_state; |
9857 | 9858 |
|
9858 | | -static int lapsensor_get(bool *present, bool *state) |
| 9859 | +static int dytc_command(int command, int *output) |
9859 | 9860 | { |
9860 | 9861 | acpi_handle dytc_handle; |
9861 | | - int output; |
9862 | 9862 |
|
9863 | | - *present = false; |
9864 | | - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) |
| 9863 | + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) { |
| 9864 | + /* Platform doesn't support DYTC */ |
9865 | 9865 | return -ENODEV; |
9866 | | - if (!acpi_evalf(dytc_handle, &output, NULL, "dd", DYTC_CMD_GET)) |
| 9866 | + } |
| 9867 | + if (!acpi_evalf(dytc_handle, output, NULL, "dd", command)) |
9867 | 9868 | return -EIO; |
| 9869 | + return 0; |
| 9870 | +} |
| 9871 | + |
| 9872 | +static int lapsensor_get(bool *present, bool *state) |
| 9873 | +{ |
| 9874 | + int output, err; |
| 9875 | + |
| 9876 | + *present = false; |
| 9877 | + err = dytc_command(DYTC_CMD_GET, &output); |
| 9878 | + if (err) |
| 9879 | + return err; |
9868 | 9880 |
|
9869 | 9881 | *present = true; /*If we get his far, we have lapmode support*/ |
9870 | 9882 | *state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false; |
@@ -9983,6 +9995,264 @@ static struct ibm_struct proxsensor_driver_data = { |
9983 | 9995 | .exit = proxsensor_exit, |
9984 | 9996 | }; |
9985 | 9997 |
|
| 9998 | +#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE) |
| 9999 | + |
| 10000 | +/************************************************************************* |
| 10001 | + * DYTC Platform Profile interface |
| 10002 | + */ |
| 10003 | + |
| 10004 | +#define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */ |
| 10005 | +#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */ |
| 10006 | +#define DYTC_CMD_RESET 0x1ff /* To reset back to default */ |
| 10007 | + |
| 10008 | +#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */ |
| 10009 | +#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */ |
| 10010 | +#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */ |
| 10011 | + |
| 10012 | +#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */ |
| 10013 | +#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */ |
| 10014 | + |
| 10015 | +#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */ |
| 10016 | +#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */ |
| 10017 | +#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */ |
| 10018 | + |
| 10019 | +#define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */ |
| 10020 | +#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */ |
| 10021 | +#define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */ |
| 10022 | + |
| 10023 | +#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */ |
| 10024 | +#define DYTC_MODE_LOWPOWER 3 /* Low power mode */ |
| 10025 | +#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */ |
| 10026 | + |
| 10027 | +#define DYTC_SET_COMMAND(function, mode, on) \ |
| 10028 | + (DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \ |
| 10029 | + (mode) << DYTC_SET_MODE_BIT | \ |
| 10030 | + (on) << DYTC_SET_VALID_BIT) |
| 10031 | + |
| 10032 | +#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0) |
| 10033 | + |
| 10034 | +#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1) |
| 10035 | + |
| 10036 | +static bool dytc_profile_available; |
| 10037 | +static enum platform_profile_option dytc_current_profile; |
| 10038 | +static atomic_t dytc_ignore_event = ATOMIC_INIT(0); |
| 10039 | +static DEFINE_MUTEX(dytc_mutex); |
| 10040 | + |
| 10041 | +static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile) |
| 10042 | +{ |
| 10043 | + switch (dytcmode) { |
| 10044 | + case DYTC_MODE_LOWPOWER: |
| 10045 | + *profile = PLATFORM_PROFILE_LOW_POWER; |
| 10046 | + break; |
| 10047 | + case DYTC_MODE_BALANCE: |
| 10048 | + *profile = PLATFORM_PROFILE_BALANCED; |
| 10049 | + break; |
| 10050 | + case DYTC_MODE_PERFORM: |
| 10051 | + *profile = PLATFORM_PROFILE_PERFORMANCE; |
| 10052 | + break; |
| 10053 | + default: /* Unknown mode */ |
| 10054 | + return -EINVAL; |
| 10055 | + } |
| 10056 | + return 0; |
| 10057 | +} |
| 10058 | + |
| 10059 | +static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode) |
| 10060 | +{ |
| 10061 | + switch (profile) { |
| 10062 | + case PLATFORM_PROFILE_LOW_POWER: |
| 10063 | + *perfmode = DYTC_MODE_LOWPOWER; |
| 10064 | + break; |
| 10065 | + case PLATFORM_PROFILE_BALANCED: |
| 10066 | + *perfmode = DYTC_MODE_BALANCE; |
| 10067 | + break; |
| 10068 | + case PLATFORM_PROFILE_PERFORMANCE: |
| 10069 | + *perfmode = DYTC_MODE_PERFORM; |
| 10070 | + break; |
| 10071 | + default: /* Unknown profile */ |
| 10072 | + return -EOPNOTSUPP; |
| 10073 | + } |
| 10074 | + return 0; |
| 10075 | +} |
| 10076 | + |
| 10077 | +/* |
| 10078 | + * dytc_profile_get: Function to register with platform_profile |
| 10079 | + * handler. Returns current platform profile. |
| 10080 | + */ |
| 10081 | +int dytc_profile_get(struct platform_profile_handler *pprof, |
| 10082 | + enum platform_profile_option *profile) |
| 10083 | +{ |
| 10084 | + *profile = dytc_current_profile; |
| 10085 | + return 0; |
| 10086 | +} |
| 10087 | + |
| 10088 | +/* |
| 10089 | + * Helper function - check if we are in CQL mode and if we are |
| 10090 | + * - disable CQL, |
| 10091 | + * - run the command |
| 10092 | + * - enable CQL |
| 10093 | + * If not in CQL mode, just run the command |
| 10094 | + */ |
| 10095 | +int dytc_cql_command(int command, int *output) |
| 10096 | +{ |
| 10097 | + int err, cmd_err, dummy; |
| 10098 | + int cur_funcmode; |
| 10099 | + |
| 10100 | + /* Determine if we are in CQL mode. This alters the commands we do */ |
| 10101 | + err = dytc_command(DYTC_CMD_GET, output); |
| 10102 | + if (err) |
| 10103 | + return err; |
| 10104 | + |
| 10105 | + cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF; |
| 10106 | + /* Check if we're OK to return immediately */ |
| 10107 | + if ((command == DYTC_CMD_GET) && (cur_funcmode != DYTC_FUNCTION_CQL)) |
| 10108 | + return 0; |
| 10109 | + |
| 10110 | + if (cur_funcmode == DYTC_FUNCTION_CQL) { |
| 10111 | + atomic_inc(&dytc_ignore_event); |
| 10112 | + err = dytc_command(DYTC_DISABLE_CQL, &dummy); |
| 10113 | + if (err) |
| 10114 | + return err; |
| 10115 | + } |
| 10116 | + |
| 10117 | + cmd_err = dytc_command(command, output); |
| 10118 | + /* Check return condition after we've restored CQL state */ |
| 10119 | + |
| 10120 | + if (cur_funcmode == DYTC_FUNCTION_CQL) { |
| 10121 | + err = dytc_command(DYTC_ENABLE_CQL, &dummy); |
| 10122 | + if (err) |
| 10123 | + return err; |
| 10124 | + } |
| 10125 | + |
| 10126 | + return cmd_err; |
| 10127 | +} |
| 10128 | + |
| 10129 | +/* |
| 10130 | + * dytc_profile_set: Function to register with platform_profile |
| 10131 | + * handler. Sets current platform profile. |
| 10132 | + */ |
| 10133 | +int dytc_profile_set(struct platform_profile_handler *pprof, |
| 10134 | + enum platform_profile_option profile) |
| 10135 | +{ |
| 10136 | + int output; |
| 10137 | + int err; |
| 10138 | + |
| 10139 | + if (!dytc_profile_available) |
| 10140 | + return -ENODEV; |
| 10141 | + |
| 10142 | + err = mutex_lock_interruptible(&dytc_mutex); |
| 10143 | + if (err) |
| 10144 | + return err; |
| 10145 | + |
| 10146 | + if (profile == PLATFORM_PROFILE_BALANCED) { |
| 10147 | + /* To get back to balanced mode we just issue a reset command */ |
| 10148 | + err = dytc_command(DYTC_CMD_RESET, &output); |
| 10149 | + if (err) |
| 10150 | + goto unlock; |
| 10151 | + } else { |
| 10152 | + int perfmode; |
| 10153 | + |
| 10154 | + err = convert_profile_to_dytc(profile, &perfmode); |
| 10155 | + if (err) |
| 10156 | + goto unlock; |
| 10157 | + |
| 10158 | + /* Determine if we are in CQL mode. This alters the commands we do */ |
| 10159 | + err = dytc_cql_command(DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1), &output); |
| 10160 | + if (err) |
| 10161 | + goto unlock; |
| 10162 | + } |
| 10163 | + /* Success - update current profile */ |
| 10164 | + dytc_current_profile = profile; |
| 10165 | +unlock: |
| 10166 | + mutex_unlock(&dytc_mutex); |
| 10167 | + return err; |
| 10168 | +} |
| 10169 | + |
| 10170 | +static void dytc_profile_refresh(void) |
| 10171 | +{ |
| 10172 | + enum platform_profile_option profile; |
| 10173 | + int output, err; |
| 10174 | + int perfmode; |
| 10175 | + |
| 10176 | + mutex_lock(&dytc_mutex); |
| 10177 | + err = dytc_cql_command(DYTC_CMD_GET, &output); |
| 10178 | + mutex_unlock(&dytc_mutex); |
| 10179 | + if (err) |
| 10180 | + return; |
| 10181 | + |
| 10182 | + perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF; |
| 10183 | + convert_dytc_to_profile(perfmode, &profile); |
| 10184 | + if (profile != dytc_current_profile) { |
| 10185 | + dytc_current_profile = profile; |
| 10186 | + platform_profile_notify(); |
| 10187 | + } |
| 10188 | +} |
| 10189 | + |
| 10190 | +static struct platform_profile_handler dytc_profile = { |
| 10191 | + .profile_get = dytc_profile_get, |
| 10192 | + .profile_set = dytc_profile_set, |
| 10193 | +}; |
| 10194 | + |
| 10195 | +static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) |
| 10196 | +{ |
| 10197 | + int err, output; |
| 10198 | + |
| 10199 | + /* Setup supported modes */ |
| 10200 | + set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices); |
| 10201 | + set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); |
| 10202 | + set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); |
| 10203 | + |
| 10204 | + dytc_profile_available = false; |
| 10205 | + err = dytc_command(DYTC_CMD_QUERY, &output); |
| 10206 | + /* |
| 10207 | + * If support isn't available (ENODEV) then don't return an error |
| 10208 | + * and don't create the sysfs group |
| 10209 | + */ |
| 10210 | + if (err == -ENODEV) |
| 10211 | + return 0; |
| 10212 | + /* For all other errors we can flag the failure */ |
| 10213 | + if (err) |
| 10214 | + return err; |
| 10215 | + |
| 10216 | + /* Check DYTC is enabled and supports mode setting */ |
| 10217 | + if (output & BIT(DYTC_QUERY_ENABLE_BIT)) { |
| 10218 | + /* Only DYTC v5.0 and later has this feature. */ |
| 10219 | + int dytc_version; |
| 10220 | + |
| 10221 | + dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; |
| 10222 | + if (dytc_version >= 5) { |
| 10223 | + dbg_printk(TPACPI_DBG_INIT, |
| 10224 | + "DYTC version %d: thermal mode available\n", dytc_version); |
| 10225 | + /* Create platform_profile structure and register */ |
| 10226 | + err = platform_profile_register(&dytc_profile); |
| 10227 | + /* |
| 10228 | + * If for some reason platform_profiles aren't enabled |
| 10229 | + * don't quit terminally. |
| 10230 | + */ |
| 10231 | + if (err) |
| 10232 | + return 0; |
| 10233 | + |
| 10234 | + dytc_profile_available = true; |
| 10235 | + /* Ensure initial values are correct */ |
| 10236 | + dytc_profile_refresh(); |
| 10237 | + } |
| 10238 | + } |
| 10239 | + return 0; |
| 10240 | +} |
| 10241 | + |
| 10242 | +static void dytc_profile_exit(void) |
| 10243 | +{ |
| 10244 | + if (dytc_profile_available) { |
| 10245 | + dytc_profile_available = false; |
| 10246 | + platform_profile_remove(); |
| 10247 | + } |
| 10248 | +} |
| 10249 | + |
| 10250 | +static struct ibm_struct dytc_profile_driver_data = { |
| 10251 | + .name = "dytc-profile", |
| 10252 | + .exit = dytc_profile_exit, |
| 10253 | +}; |
| 10254 | +#endif /* CONFIG_ACPI_PLATFORM_PROFILE */ |
| 10255 | + |
9986 | 10256 | /************************************************************************* |
9987 | 10257 | * Keyboard language interface |
9988 | 10258 | */ |
@@ -10204,8 +10474,14 @@ static void tpacpi_driver_event(const unsigned int hkey_event) |
10204 | 10474 | mutex_unlock(&kbdlight_mutex); |
10205 | 10475 | } |
10206 | 10476 |
|
10207 | | - if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) |
| 10477 | + if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) { |
10208 | 10478 | lapsensor_refresh(); |
| 10479 | +#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE) |
| 10480 | + /* If we are already accessing DYTC then skip dytc update */ |
| 10481 | + if (!atomic_add_unless(&dytc_ignore_event, -1, 0)) |
| 10482 | + dytc_profile_refresh(); |
| 10483 | +#endif |
| 10484 | + } |
10209 | 10485 | } |
10210 | 10486 |
|
10211 | 10487 | static void hotkey_driver_event(const unsigned int scancode) |
@@ -10648,6 +10924,12 @@ static struct ibm_init_struct ibms_init[] __initdata = { |
10648 | 10924 | .init = tpacpi_proxsensor_init, |
10649 | 10925 | .data = &proxsensor_driver_data, |
10650 | 10926 | }, |
| 10927 | +#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE) |
| 10928 | + { |
| 10929 | + .init = tpacpi_dytc_profile_init, |
| 10930 | + .data = &dytc_profile_driver_data, |
| 10931 | + }, |
| 10932 | +#endif |
10651 | 10933 | { |
10652 | 10934 | .init = tpacpi_kbdlang_init, |
10653 | 10935 | .data = &kbdlang_driver_data, |
|
0 commit comments