nvidia-wmi-ec-backlight.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
  4. */
  5. #include <linux/acpi.h>
  6. #include <linux/backlight.h>
  7. #include <linux/mod_devicetable.h>
  8. #include <linux/module.h>
  9. #include <linux/platform_data/x86/nvidia-wmi-ec-backlight.h>
  10. #include <linux/types.h>
  11. #include <linux/wmi.h>
  12. #include <acpi/video.h>
  13. static bool force;
  14. module_param(force, bool, 0444);
  15. MODULE_PARM_DESC(force, "Force loading (disable acpi_backlight=xxx checks");
  16. /**
  17. * wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method
  18. * @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID
  19. * @id: The WMI method ID to call (e.g. %WMI_BRIGHTNESS_METHOD_LEVEL or
  20. * %WMI_BRIGHTNESS_METHOD_SOURCE)
  21. * @mode: The operation to perform on the method (e.g. %WMI_BRIGHTNESS_MODE_SET
  22. * or %WMI_BRIGHTNESS_MODE_GET)
  23. * @val: Pointer to a value passed in by the caller when @mode is
  24. * %WMI_BRIGHTNESS_MODE_SET, or a value passed out to caller when @mode
  25. * is %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL.
  26. *
  27. * Returns 0 on success, or a negative error number on failure.
  28. */
  29. static int wmi_brightness_notify(struct wmi_device *w, enum wmi_brightness_method id, enum wmi_brightness_mode mode, u32 *val)
  30. {
  31. struct wmi_brightness_args args = {
  32. .mode = mode,
  33. .val = 0,
  34. .ret = 0,
  35. };
  36. struct acpi_buffer buf = { (acpi_size)sizeof(args), &args };
  37. acpi_status status;
  38. if (id < WMI_BRIGHTNESS_METHOD_LEVEL ||
  39. id >= WMI_BRIGHTNESS_METHOD_MAX ||
  40. mode < WMI_BRIGHTNESS_MODE_GET || mode >= WMI_BRIGHTNESS_MODE_MAX)
  41. return -EINVAL;
  42. if (mode == WMI_BRIGHTNESS_MODE_SET)
  43. args.val = *val;
  44. status = wmidev_evaluate_method(w, 0, id, &buf, &buf);
  45. if (ACPI_FAILURE(status)) {
  46. dev_err(&w->dev, "EC backlight control failed: %s\n",
  47. acpi_format_exception(status));
  48. return -EIO;
  49. }
  50. if (mode != WMI_BRIGHTNESS_MODE_SET)
  51. *val = args.ret;
  52. return 0;
  53. }
  54. static int nvidia_wmi_ec_backlight_update_status(struct backlight_device *bd)
  55. {
  56. struct wmi_device *wdev = bl_get_data(bd);
  57. return wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
  58. WMI_BRIGHTNESS_MODE_SET,
  59. &bd->props.brightness);
  60. }
  61. static int nvidia_wmi_ec_backlight_get_brightness(struct backlight_device *bd)
  62. {
  63. struct wmi_device *wdev = bl_get_data(bd);
  64. u32 level;
  65. int ret;
  66. ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
  67. WMI_BRIGHTNESS_MODE_GET, &level);
  68. if (ret < 0)
  69. return ret;
  70. return level;
  71. }
  72. static const struct backlight_ops nvidia_wmi_ec_backlight_ops = {
  73. .update_status = nvidia_wmi_ec_backlight_update_status,
  74. .get_brightness = nvidia_wmi_ec_backlight_get_brightness,
  75. };
  76. static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ctx)
  77. {
  78. struct backlight_properties props = {};
  79. struct backlight_device *bdev;
  80. int ret;
  81. /* drivers/acpi/video_detect.c also checks that SOURCE == EC */
  82. if (!force && acpi_video_get_backlight_type() != acpi_backlight_nvidia_wmi_ec)
  83. return -ENODEV;
  84. /*
  85. * Identify this backlight device as a firmware device so that it can
  86. * be prioritized over any exposed GPU-driven raw device(s).
  87. */
  88. props.type = BACKLIGHT_FIRMWARE;
  89. ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
  90. WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL,
  91. &props.max_brightness);
  92. if (ret)
  93. return ret;
  94. ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
  95. WMI_BRIGHTNESS_MODE_GET, &props.brightness);
  96. if (ret)
  97. return ret;
  98. bdev = devm_backlight_device_register(&wdev->dev,
  99. "nvidia_wmi_ec_backlight",
  100. &wdev->dev, wdev,
  101. &nvidia_wmi_ec_backlight_ops,
  102. &props);
  103. return PTR_ERR_OR_ZERO(bdev);
  104. }
  105. static const struct wmi_device_id nvidia_wmi_ec_backlight_id_table[] = {
  106. { .guid_string = WMI_BRIGHTNESS_GUID },
  107. { }
  108. };
  109. MODULE_DEVICE_TABLE(wmi, nvidia_wmi_ec_backlight_id_table);
  110. static struct wmi_driver nvidia_wmi_ec_backlight_driver = {
  111. .driver = {
  112. .name = "nvidia-wmi-ec-backlight",
  113. },
  114. .probe = nvidia_wmi_ec_backlight_probe,
  115. .id_table = nvidia_wmi_ec_backlight_id_table,
  116. };
  117. module_wmi_driver(nvidia_wmi_ec_backlight_driver);
  118. MODULE_AUTHOR("Daniel Dadap <[email protected]>");
  119. MODULE_DESCRIPTION("NVIDIA WMI EC Backlight driver");
  120. MODULE_LICENSE("GPL");