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