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