1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Alienware WMAX WMI device driver 4 * 5 * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com> 6 * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com> 7 */ 8 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11 #include <linux/bitfield.h> 12 #include <linux/bits.h> 13 #include <linux/dmi.h> 14 #include <linux/moduleparam.h> 15 #include <linux/platform_profile.h> 16 #include <linux/wmi.h> 17 #include "alienware-wmi.h" 18 19 #define WMAX_METHOD_HDMI_SOURCE 0x1 20 #define WMAX_METHOD_HDMI_STATUS 0x2 21 #define WMAX_METHOD_HDMI_CABLE 0x5 22 #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 23 #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B 24 #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C 25 #define WMAX_METHOD_BRIGHTNESS 0x3 26 #define WMAX_METHOD_ZONE_CONTROL 0x4 27 #define WMAX_METHOD_THERMAL_INFORMATION 0x14 28 #define WMAX_METHOD_THERMAL_CONTROL 0x15 29 #define WMAX_METHOD_GAME_SHIFT_STATUS 0x25 30 31 #define WMAX_THERMAL_MODE_GMODE 0xAB 32 33 #define WMAX_FAILURE_CODE 0xFFFFFFFF 34 #define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4) 35 #define WMAX_THERMAL_MODE_MASK GENMASK(3, 0) 36 #define WMAX_SENSOR_ID_MASK BIT(8) 37 38 static bool force_platform_profile; 39 module_param_unsafe(force_platform_profile, bool, 0); 40 MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available"); 41 42 static bool force_gmode; 43 module_param_unsafe(force_gmode, bool, 0); 44 MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected"); 45 46 struct awcc_quirks { 47 bool pprof; 48 bool gmode; 49 }; 50 51 static struct awcc_quirks g_series_quirks = { 52 .pprof = true, 53 .gmode = true, 54 }; 55 56 static struct awcc_quirks generic_quirks = { 57 .pprof = true, 58 .gmode = false, 59 }; 60 61 static struct awcc_quirks empty_quirks; 62 63 static const struct dmi_system_id awcc_dmi_table[] __initconst = { 64 { 65 .ident = "Alienware Area-51m R2", 66 .matches = { 67 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 68 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware Area-51m R2"), 69 }, 70 .driver_data = &generic_quirks, 71 }, 72 { 73 .ident = "Alienware m15 R7", 74 .matches = { 75 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 76 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m15 R7"), 77 }, 78 .driver_data = &generic_quirks, 79 }, 80 { 81 .ident = "Alienware m16 R1", 82 .matches = { 83 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 84 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1"), 85 }, 86 .driver_data = &g_series_quirks, 87 }, 88 { 89 .ident = "Alienware m16 R1 AMD", 90 .matches = { 91 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 92 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"), 93 }, 94 .driver_data = &g_series_quirks, 95 }, 96 { 97 .ident = "Alienware m16 R2", 98 .matches = { 99 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 100 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R2"), 101 }, 102 .driver_data = &generic_quirks, 103 }, 104 { 105 .ident = "Alienware m17 R5", 106 .matches = { 107 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 108 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), 109 }, 110 .driver_data = &generic_quirks, 111 }, 112 { 113 .ident = "Alienware m18 R2", 114 .matches = { 115 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 116 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"), 117 }, 118 .driver_data = &generic_quirks, 119 }, 120 { 121 .ident = "Alienware x15 R1", 122 .matches = { 123 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 124 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"), 125 }, 126 .driver_data = &generic_quirks, 127 }, 128 { 129 .ident = "Alienware x15 R2", 130 .matches = { 131 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 132 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R2"), 133 }, 134 .driver_data = &generic_quirks, 135 }, 136 { 137 .ident = "Alienware x17 R2", 138 .matches = { 139 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 140 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"), 141 }, 142 .driver_data = &generic_quirks, 143 }, 144 { 145 .ident = "Dell Inc. G15 5510", 146 .matches = { 147 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 148 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"), 149 }, 150 .driver_data = &g_series_quirks, 151 }, 152 { 153 .ident = "Dell Inc. G15 5511", 154 .matches = { 155 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 156 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"), 157 }, 158 .driver_data = &g_series_quirks, 159 }, 160 { 161 .ident = "Dell Inc. G15 5515", 162 .matches = { 163 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 164 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"), 165 }, 166 .driver_data = &g_series_quirks, 167 }, 168 { 169 .ident = "Dell Inc. G16 7630", 170 .matches = { 171 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 172 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G16 7630"), 173 }, 174 .driver_data = &g_series_quirks, 175 }, 176 { 177 .ident = "Dell Inc. G3 3500", 178 .matches = { 179 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 180 DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"), 181 }, 182 .driver_data = &g_series_quirks, 183 }, 184 { 185 .ident = "Dell Inc. G3 3590", 186 .matches = { 187 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 188 DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"), 189 }, 190 .driver_data = &g_series_quirks, 191 }, 192 { 193 .ident = "Dell Inc. G5 5500", 194 .matches = { 195 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 196 DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"), 197 }, 198 .driver_data = &g_series_quirks, 199 }, 200 { 201 .ident = "Dell Inc. G5 5505", 202 .matches = { 203 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 204 DMI_MATCH(DMI_PRODUCT_NAME, "G5 5505"), 205 }, 206 .driver_data = &g_series_quirks, 207 }, 208 }; 209 210 enum WMAX_THERMAL_INFORMATION_OPERATIONS { 211 WMAX_OPERATION_SYS_DESCRIPTION = 0x02, 212 WMAX_OPERATION_LIST_IDS = 0x03, 213 WMAX_OPERATION_CURRENT_PROFILE = 0x0B, 214 }; 215 216 enum WMAX_THERMAL_CONTROL_OPERATIONS { 217 WMAX_OPERATION_ACTIVATE_PROFILE = 0x01, 218 }; 219 220 enum WMAX_GAME_SHIFT_STATUS_OPERATIONS { 221 WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01, 222 WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02, 223 }; 224 225 enum WMAX_THERMAL_TABLES { 226 WMAX_THERMAL_TABLE_BASIC = 0x90, 227 WMAX_THERMAL_TABLE_USTT = 0xA0, 228 }; 229 230 enum wmax_thermal_mode { 231 THERMAL_MODE_USTT_BALANCED, 232 THERMAL_MODE_USTT_BALANCED_PERFORMANCE, 233 THERMAL_MODE_USTT_COOL, 234 THERMAL_MODE_USTT_QUIET, 235 THERMAL_MODE_USTT_PERFORMANCE, 236 THERMAL_MODE_USTT_LOW_POWER, 237 THERMAL_MODE_BASIC_QUIET, 238 THERMAL_MODE_BASIC_BALANCED, 239 THERMAL_MODE_BASIC_BALANCED_PERFORMANCE, 240 THERMAL_MODE_BASIC_PERFORMANCE, 241 THERMAL_MODE_LAST, 242 }; 243 244 struct wmax_led_args { 245 u32 led_mask; 246 struct color_platform colors; 247 u8 state; 248 } __packed; 249 250 struct wmax_brightness_args { 251 u32 led_mask; 252 u32 percentage; 253 }; 254 255 struct wmax_basic_args { 256 u8 arg; 257 }; 258 259 struct wmax_u32_args { 260 u8 operation; 261 u8 arg1; 262 u8 arg2; 263 u8 arg3; 264 }; 265 266 struct awcc_priv { 267 struct wmi_device *wdev; 268 struct device *ppdev; 269 enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST]; 270 }; 271 272 static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = { 273 [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED, 274 [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, 275 [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL, 276 [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET, 277 [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, 278 [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER, 279 [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET, 280 [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED, 281 [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, 282 [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, 283 }; 284 285 static struct awcc_quirks *awcc; 286 287 /* 288 * The HDMI mux sysfs node indicates the status of the HDMI input mux. 289 * It can toggle between standard system GPU output and HDMI input. 290 */ 291 static ssize_t cable_show(struct device *dev, struct device_attribute *attr, 292 char *buf) 293 { 294 struct alienfx_platdata *pdata = dev_get_platdata(dev); 295 struct wmax_basic_args in_args = { 296 .arg = 0, 297 }; 298 u32 out_data; 299 int ret; 300 301 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE, 302 &in_args, sizeof(in_args), &out_data); 303 if (!ret) { 304 if (out_data == 0) 305 return sysfs_emit(buf, "[unconnected] connected unknown\n"); 306 else if (out_data == 1) 307 return sysfs_emit(buf, "unconnected [connected] unknown\n"); 308 } 309 310 pr_err("alienware-wmi: unknown HDMI cable status: %d\n", ret); 311 return sysfs_emit(buf, "unconnected connected [unknown]\n"); 312 } 313 314 static ssize_t source_show(struct device *dev, struct device_attribute *attr, 315 char *buf) 316 { 317 struct alienfx_platdata *pdata = dev_get_platdata(dev); 318 struct wmax_basic_args in_args = { 319 .arg = 0, 320 }; 321 u32 out_data; 322 int ret; 323 324 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS, 325 &in_args, sizeof(in_args), &out_data); 326 if (!ret) { 327 if (out_data == 1) 328 return sysfs_emit(buf, "[input] gpu unknown\n"); 329 else if (out_data == 2) 330 return sysfs_emit(buf, "input [gpu] unknown\n"); 331 } 332 333 pr_err("alienware-wmi: unknown HDMI source status: %u\n", ret); 334 return sysfs_emit(buf, "input gpu [unknown]\n"); 335 } 336 337 static ssize_t source_store(struct device *dev, struct device_attribute *attr, 338 const char *buf, size_t count) 339 { 340 struct alienfx_platdata *pdata = dev_get_platdata(dev); 341 struct wmax_basic_args args; 342 int ret; 343 344 if (strcmp(buf, "gpu\n") == 0) 345 args.arg = 1; 346 else if (strcmp(buf, "input\n") == 0) 347 args.arg = 2; 348 else 349 args.arg = 3; 350 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); 351 352 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args, 353 sizeof(args), NULL); 354 if (ret < 0) 355 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret); 356 357 return count; 358 } 359 360 static DEVICE_ATTR_RO(cable); 361 static DEVICE_ATTR_RW(source); 362 363 static bool hdmi_group_visible(struct kobject *kobj) 364 { 365 return alienware_interface == WMAX && alienfx->hdmi_mux; 366 } 367 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi); 368 369 static struct attribute *hdmi_attrs[] = { 370 &dev_attr_cable.attr, 371 &dev_attr_source.attr, 372 NULL, 373 }; 374 375 const struct attribute_group wmax_hdmi_attribute_group = { 376 .name = "hdmi", 377 .is_visible = SYSFS_GROUP_VISIBLE(hdmi), 378 .attrs = hdmi_attrs, 379 }; 380 381 /* 382 * Alienware GFX amplifier support 383 * - Currently supports reading cable status 384 * - Leaving expansion room to possibly support dock/undock events later 385 */ 386 static ssize_t status_show(struct device *dev, struct device_attribute *attr, 387 char *buf) 388 { 389 struct alienfx_platdata *pdata = dev_get_platdata(dev); 390 struct wmax_basic_args in_args = { 391 .arg = 0, 392 }; 393 u32 out_data; 394 int ret; 395 396 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE, 397 &in_args, sizeof(in_args), &out_data); 398 if (!ret) { 399 if (out_data == 0) 400 return sysfs_emit(buf, "[unconnected] connected unknown\n"); 401 else if (out_data == 1) 402 return sysfs_emit(buf, "unconnected [connected] unknown\n"); 403 } 404 405 pr_err("alienware-wmi: unknown amplifier cable status: %d\n", ret); 406 return sysfs_emit(buf, "unconnected connected [unknown]\n"); 407 } 408 409 static DEVICE_ATTR_RO(status); 410 411 static bool amplifier_group_visible(struct kobject *kobj) 412 { 413 return alienware_interface == WMAX && alienfx->amplifier; 414 } 415 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier); 416 417 static struct attribute *amplifier_attrs[] = { 418 &dev_attr_status.attr, 419 NULL, 420 }; 421 422 const struct attribute_group wmax_amplifier_attribute_group = { 423 .name = "amplifier", 424 .is_visible = SYSFS_GROUP_VISIBLE(amplifier), 425 .attrs = amplifier_attrs, 426 }; 427 428 /* 429 * Deep Sleep Control support 430 * - Modifies BIOS setting for deep sleep control allowing extra wakeup events 431 */ 432 static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr, 433 char *buf) 434 { 435 struct alienfx_platdata *pdata = dev_get_platdata(dev); 436 struct wmax_basic_args in_args = { 437 .arg = 0, 438 }; 439 u32 out_data; 440 int ret; 441 442 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS, 443 &in_args, sizeof(in_args), &out_data); 444 if (!ret) { 445 if (out_data == 0) 446 return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); 447 else if (out_data == 1) 448 return sysfs_emit(buf, "disabled [s5] s5_s4\n"); 449 else if (out_data == 2) 450 return sysfs_emit(buf, "disabled s5 [s5_s4]\n"); 451 } 452 453 pr_err("alienware-wmi: unknown deep sleep status: %d\n", ret); 454 return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); 455 } 456 457 static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr, 458 const char *buf, size_t count) 459 { 460 struct alienfx_platdata *pdata = dev_get_platdata(dev); 461 struct wmax_basic_args args; 462 int ret; 463 464 if (strcmp(buf, "disabled\n") == 0) 465 args.arg = 0; 466 else if (strcmp(buf, "s5\n") == 0) 467 args.arg = 1; 468 else 469 args.arg = 2; 470 pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); 471 472 ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL, 473 &args, sizeof(args), NULL); 474 if (!ret) 475 pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret); 476 477 return count; 478 } 479 480 static DEVICE_ATTR_RW(deepsleep); 481 482 static bool deepsleep_group_visible(struct kobject *kobj) 483 { 484 return alienware_interface == WMAX && alienfx->deepslp; 485 } 486 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep); 487 488 static struct attribute *deepsleep_attrs[] = { 489 &dev_attr_deepsleep.attr, 490 NULL, 491 }; 492 493 const struct attribute_group wmax_deepsleep_attribute_group = { 494 .name = "deepsleep", 495 .is_visible = SYSFS_GROUP_VISIBLE(deepsleep), 496 .attrs = deepsleep_attrs, 497 }; 498 499 /* 500 * Thermal Profile control 501 * - Provides thermal profile control through the Platform Profile API 502 */ 503 static bool is_wmax_thermal_code(u32 code) 504 { 505 if (code & WMAX_SENSOR_ID_MASK) 506 return false; 507 508 if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST) 509 return false; 510 511 if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC && 512 (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET) 513 return true; 514 515 if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT && 516 (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER) 517 return true; 518 519 return false; 520 } 521 522 static int wmax_thermal_information(struct wmi_device *wdev, u8 operation, 523 u8 arg, u32 *out_data) 524 { 525 struct wmax_u32_args in_args = { 526 .operation = operation, 527 .arg1 = arg, 528 .arg2 = 0, 529 .arg3 = 0, 530 }; 531 int ret; 532 533 ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION, 534 &in_args, sizeof(in_args), out_data); 535 if (ret < 0) 536 return ret; 537 538 if (*out_data == WMAX_FAILURE_CODE) 539 return -EBADRQC; 540 541 return 0; 542 } 543 544 static int wmax_thermal_control(struct wmi_device *wdev, u8 profile) 545 { 546 struct wmax_u32_args in_args = { 547 .operation = WMAX_OPERATION_ACTIVATE_PROFILE, 548 .arg1 = profile, 549 .arg2 = 0, 550 .arg3 = 0, 551 }; 552 u32 out_data; 553 int ret; 554 555 ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL, 556 &in_args, sizeof(in_args), &out_data); 557 if (ret) 558 return ret; 559 560 if (out_data == WMAX_FAILURE_CODE) 561 return -EBADRQC; 562 563 return 0; 564 } 565 566 static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation, 567 u32 *out_data) 568 { 569 struct wmax_u32_args in_args = { 570 .operation = operation, 571 .arg1 = 0, 572 .arg2 = 0, 573 .arg3 = 0, 574 }; 575 int ret; 576 577 ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS, 578 &in_args, sizeof(in_args), out_data); 579 if (ret < 0) 580 return ret; 581 582 if (*out_data == WMAX_FAILURE_CODE) 583 return -EOPNOTSUPP; 584 585 return 0; 586 } 587 588 static int thermal_profile_get(struct device *dev, 589 enum platform_profile_option *profile) 590 { 591 struct awcc_priv *priv = dev_get_drvdata(dev); 592 u32 out_data; 593 int ret; 594 595 ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE, 596 0, &out_data); 597 598 if (ret < 0) 599 return ret; 600 601 if (out_data == WMAX_THERMAL_MODE_GMODE) { 602 *profile = PLATFORM_PROFILE_PERFORMANCE; 603 return 0; 604 } 605 606 if (!is_wmax_thermal_code(out_data)) 607 return -ENODATA; 608 609 out_data &= WMAX_THERMAL_MODE_MASK; 610 *profile = wmax_mode_to_platform_profile[out_data]; 611 612 return 0; 613 } 614 615 static int thermal_profile_set(struct device *dev, 616 enum platform_profile_option profile) 617 { 618 struct awcc_priv *priv = dev_get_drvdata(dev); 619 620 if (awcc->gmode) { 621 u32 gmode_status; 622 int ret; 623 624 ret = wmax_game_shift_status(priv->wdev, 625 WMAX_OPERATION_GET_GAME_SHIFT_STATUS, 626 &gmode_status); 627 628 if (ret < 0) 629 return ret; 630 631 if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) || 632 (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) { 633 ret = wmax_game_shift_status(priv->wdev, 634 WMAX_OPERATION_TOGGLE_GAME_SHIFT, 635 &gmode_status); 636 637 if (ret < 0) 638 return ret; 639 } 640 } 641 642 return wmax_thermal_control(priv->wdev, 643 priv->supported_thermal_profiles[profile]); 644 } 645 646 static int thermal_profile_probe(void *drvdata, unsigned long *choices) 647 { 648 enum platform_profile_option profile; 649 struct awcc_priv *priv = drvdata; 650 enum wmax_thermal_mode mode; 651 u8 sys_desc[4]; 652 u32 first_mode; 653 u32 out_data; 654 int ret; 655 656 ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_SYS_DESCRIPTION, 657 0, (u32 *) &sys_desc); 658 if (ret < 0) 659 return ret; 660 661 first_mode = sys_desc[0] + sys_desc[1]; 662 663 for (u32 i = 0; i < sys_desc[3]; i++) { 664 ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_LIST_IDS, 665 i + first_mode, &out_data); 666 if (ret == -EBADRQC) 667 break; 668 if (ret) 669 return ret; 670 671 if (!is_wmax_thermal_code(out_data)) 672 continue; 673 674 mode = out_data & WMAX_THERMAL_MODE_MASK; 675 profile = wmax_mode_to_platform_profile[mode]; 676 priv->supported_thermal_profiles[profile] = out_data; 677 678 set_bit(profile, choices); 679 } 680 681 if (bitmap_empty(choices, PLATFORM_PROFILE_LAST)) 682 return -ENODEV; 683 684 if (awcc->gmode) { 685 priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] = 686 WMAX_THERMAL_MODE_GMODE; 687 688 set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); 689 } 690 691 return 0; 692 } 693 694 static const struct platform_profile_ops awcc_platform_profile_ops = { 695 .probe = thermal_profile_probe, 696 .profile_get = thermal_profile_get, 697 .profile_set = thermal_profile_set, 698 }; 699 700 static int awcc_platform_profile_init(struct wmi_device *wdev) 701 { 702 struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); 703 704 priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi", 705 priv, &awcc_platform_profile_ops); 706 707 return PTR_ERR_OR_ZERO(priv->ppdev); 708 } 709 710 static int alienware_awcc_setup(struct wmi_device *wdev) 711 { 712 struct awcc_priv *priv; 713 int ret; 714 715 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 716 if (!priv) 717 return -ENOMEM; 718 719 priv->wdev = wdev; 720 dev_set_drvdata(&wdev->dev, priv); 721 722 if (awcc->pprof) { 723 ret = awcc_platform_profile_init(wdev); 724 if (ret) 725 return ret; 726 } 727 728 return 0; 729 } 730 731 /* 732 * WMAX WMI driver 733 */ 734 static int wmax_wmi_update_led(struct alienfx_priv *priv, 735 struct wmi_device *wdev, u8 location) 736 { 737 struct wmax_led_args in_args = { 738 .led_mask = 1 << location, 739 .colors = priv->colors[location], 740 .state = priv->lighting_control_state, 741 }; 742 743 return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args, 744 sizeof(in_args), NULL); 745 } 746 747 static int wmax_wmi_update_brightness(struct alienfx_priv *priv, 748 struct wmi_device *wdev, u8 brightness) 749 { 750 struct wmax_brightness_args in_args = { 751 .led_mask = 0xFF, 752 .percentage = brightness, 753 }; 754 755 return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args, 756 sizeof(in_args), NULL); 757 } 758 759 static int wmax_wmi_probe(struct wmi_device *wdev, const void *context) 760 { 761 struct alienfx_platdata pdata = { 762 .wdev = wdev, 763 .ops = { 764 .upd_led = wmax_wmi_update_led, 765 .upd_brightness = wmax_wmi_update_brightness, 766 }, 767 }; 768 int ret; 769 770 if (awcc) 771 ret = alienware_awcc_setup(wdev); 772 else 773 ret = alienware_alienfx_setup(&pdata); 774 775 return ret; 776 } 777 778 static const struct wmi_device_id alienware_wmax_device_id_table[] = { 779 { WMAX_CONTROL_GUID, NULL }, 780 { }, 781 }; 782 MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table); 783 784 static struct wmi_driver alienware_wmax_wmi_driver = { 785 .driver = { 786 .name = "alienware-wmi-wmax", 787 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 788 }, 789 .id_table = alienware_wmax_device_id_table, 790 .probe = wmax_wmi_probe, 791 .no_singleton = true, 792 }; 793 794 int __init alienware_wmax_wmi_init(void) 795 { 796 const struct dmi_system_id *id; 797 798 id = dmi_first_match(awcc_dmi_table); 799 if (id) 800 awcc = id->driver_data; 801 802 if (force_platform_profile) { 803 if (!awcc) 804 awcc = &empty_quirks; 805 806 awcc->pprof = true; 807 } 808 809 if (force_gmode) { 810 if (awcc) 811 awcc->gmode = true; 812 else 813 pr_warn("force_gmode requires platform profile support\n"); 814 } 815 816 return wmi_driver_register(&alienware_wmax_wmi_driver); 817 } 818 819 void __exit alienware_wmax_wmi_exit(void) 820 { 821 wmi_driver_unregister(&alienware_wmax_wmi_driver); 822 } 823