int3406_thermal.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * INT3406 thermal driver for display participant device
  3. *
  4. * Copyright (C) 2016, Intel Corporation
  5. * Authors: Aaron Lu <aaron.lu@intel.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. */
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/acpi.h>
  15. #include <linux/backlight.h>
  16. #include <linux/thermal.h>
  17. #include <acpi/video.h>
  18. #define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
  19. struct int3406_thermal_data {
  20. int upper_limit;
  21. int upper_limit_index;
  22. int lower_limit;
  23. int lower_limit_index;
  24. acpi_handle handle;
  25. struct acpi_video_device_brightness *br;
  26. struct backlight_device *raw_bd;
  27. struct thermal_cooling_device *cooling_dev;
  28. };
  29. static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d)
  30. {
  31. int max_level = d->br->levels[d->br->count - 1];
  32. int raw_max = d->raw_bd->props.max_brightness;
  33. return level * raw_max / max_level;
  34. }
  35. static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d)
  36. {
  37. int raw_max = d->raw_bd->props.max_brightness;
  38. int max_level = d->br->levels[d->br->count - 1];
  39. return level * max_level / raw_max;
  40. }
  41. static int
  42. int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
  43. unsigned long *state)
  44. {
  45. struct int3406_thermal_data *d = cooling_dev->devdata;
  46. int index = d->lower_limit_index ? d->lower_limit_index : 2;
  47. *state = d->br->count - 1 - index;
  48. return 0;
  49. }
  50. static int
  51. int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
  52. unsigned long state)
  53. {
  54. struct int3406_thermal_data *d = cooling_dev->devdata;
  55. int level, raw_level;
  56. if (state > d->br->count - 3)
  57. return -EINVAL;
  58. state = d->br->count - 1 - state;
  59. level = d->br->levels[state];
  60. if ((d->upper_limit && level > d->upper_limit) ||
  61. (d->lower_limit && level < d->lower_limit))
  62. return -EINVAL;
  63. raw_level = int3406_thermal_to_raw(level, d);
  64. return backlight_device_set_brightness(d->raw_bd, raw_level);
  65. }
  66. static int
  67. int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
  68. unsigned long *state)
  69. {
  70. struct int3406_thermal_data *d = cooling_dev->devdata;
  71. int raw_level, level, i;
  72. int *levels = d->br->levels;
  73. raw_level = d->raw_bd->props.brightness;
  74. level = int3406_thermal_to_acpi(raw_level, d);
  75. /*
  76. * There is no 1:1 mapping between the firmware interface level with the
  77. * raw interface level, we will have to find one that is close enough.
  78. */
  79. for (i = 2; i < d->br->count; i++) {
  80. if (level < levels[i]) {
  81. if (i == 2)
  82. break;
  83. if ((level - levels[i - 1]) < (levels[i] - level))
  84. i--;
  85. break;
  86. }
  87. }
  88. *state = d->br->count - 1 - i;
  89. return 0;
  90. }
  91. static const struct thermal_cooling_device_ops video_cooling_ops = {
  92. .get_max_state = int3406_thermal_get_max_state,
  93. .get_cur_state = int3406_thermal_get_cur_state,
  94. .set_cur_state = int3406_thermal_set_cur_state,
  95. };
  96. static int int3406_thermal_get_index(int *array, int nr, int value)
  97. {
  98. int i;
  99. for (i = 0; i < nr; i++) {
  100. if (array[i] == value)
  101. break;
  102. }
  103. return i == nr ? -ENOENT : i;
  104. }
  105. static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
  106. {
  107. acpi_status status;
  108. unsigned long long lower_limit, upper_limit;
  109. int index;
  110. status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
  111. if (ACPI_SUCCESS(status)) {
  112. index = int3406_thermal_get_index(d->br->levels, d->br->count,
  113. lower_limit);
  114. if (index > 0) {
  115. d->lower_limit = (int)lower_limit;
  116. d->lower_limit_index = index;
  117. }
  118. }
  119. status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
  120. if (ACPI_SUCCESS(status)) {
  121. index = int3406_thermal_get_index(d->br->levels, d->br->count,
  122. upper_limit);
  123. if (index > 0) {
  124. d->upper_limit = (int)upper_limit;
  125. d->upper_limit_index = index;
  126. }
  127. }
  128. }
  129. static void int3406_notify(acpi_handle handle, u32 event, void *data)
  130. {
  131. if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
  132. int3406_thermal_get_limit(data);
  133. }
  134. static int int3406_thermal_probe(struct platform_device *pdev)
  135. {
  136. struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
  137. struct int3406_thermal_data *d;
  138. struct backlight_device *bd;
  139. int ret;
  140. if (!ACPI_HANDLE(&pdev->dev))
  141. return -ENODEV;
  142. d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
  143. if (!d)
  144. return -ENOMEM;
  145. d->handle = ACPI_HANDLE(&pdev->dev);
  146. bd = backlight_device_get_by_type(BACKLIGHT_RAW);
  147. if (!bd)
  148. return -ENODEV;
  149. d->raw_bd = bd;
  150. ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br, NULL);
  151. if (ret)
  152. return ret;
  153. int3406_thermal_get_limit(d);
  154. d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
  155. d, &video_cooling_ops);
  156. if (IS_ERR(d->cooling_dev))
  157. goto err;
  158. ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
  159. int3406_notify, d);
  160. if (ret)
  161. goto err_cdev;
  162. platform_set_drvdata(pdev, d);
  163. return 0;
  164. err_cdev:
  165. thermal_cooling_device_unregister(d->cooling_dev);
  166. err:
  167. kfree(d->br);
  168. return -ENODEV;
  169. }
  170. static int int3406_thermal_remove(struct platform_device *pdev)
  171. {
  172. struct int3406_thermal_data *d = platform_get_drvdata(pdev);
  173. thermal_cooling_device_unregister(d->cooling_dev);
  174. kfree(d->br);
  175. return 0;
  176. }
  177. static const struct acpi_device_id int3406_thermal_match[] = {
  178. {"INT3406", 0},
  179. {}
  180. };
  181. MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
  182. static struct platform_driver int3406_thermal_driver = {
  183. .probe = int3406_thermal_probe,
  184. .remove = int3406_thermal_remove,
  185. .driver = {
  186. .name = "int3406 thermal",
  187. .acpi_match_table = int3406_thermal_match,
  188. },
  189. };
  190. module_platform_driver(int3406_thermal_driver);
  191. MODULE_DESCRIPTION("INT3406 Thermal driver");
  192. MODULE_LICENSE("GPL v2");