aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Lu <aaron.lu@intel.com>2016-05-09 03:54:58 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-05-10 20:15:31 -0400
commita3c89334f06bacf7e2f23203ad64cfd6e78dbbf4 (patch)
tree58f16efc32d0e20fc883946bce2cfb8fd95de1bf
parent059500940defe285222d3b189b366dfe7f299cae (diff)
Thermal / ACPI / video: add INT3406 thermal driver
INT3406 ACPI device object resembles an ACPI video output device, but its _BCM is said to be deprecated and should not be used. So we will make use of the raw interface to do the actual cooling. Signed-off-by: Aaron Lu <aaron.lu@intel.com> Acked-by: Zhang Rui <rui.zhang@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/thermal/Kconfig28
-rw-r--r--drivers/thermal/int340x_thermal/Kconfig42
-rw-r--r--drivers/thermal/int340x_thermal/Makefile1
-rw-r--r--drivers/thermal/int340x_thermal/int3406_thermal.c236
4 files changed, 282 insertions, 25 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 3c3dc4a3d52c..d89d60c8b6cf 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -338,31 +338,9 @@ config INTEL_QUARK_DTS_THERMAL
338 hot & critical. The critical trip point default value is set by 338 hot & critical. The critical trip point default value is set by
339 underlying BIOS/Firmware. 339 underlying BIOS/Firmware.
340 340
341config INT340X_THERMAL 341menu "ACPI INT340X thermal drivers"
342 tristate "ACPI INT340X thermal drivers" 342source drivers/thermal/int340x_thermal/Kconfig
343 depends on X86 && ACPI 343endmenu
344 select THERMAL_GOV_USER_SPACE
345 select ACPI_THERMAL_REL
346 select ACPI_FAN
347 select INTEL_SOC_DTS_IOSF_CORE
348 select THERMAL_WRITABLE_TRIPS
349 help
350 Newer laptops and tablets that use ACPI may have thermal sensors and
351 other devices with thermal control capabilities outside the core
352 CPU/SOC, for thermal safety reasons.
353 They are exposed for the OS to use via the INT3400 ACPI device object
354 as the master, and INT3401~INT340B ACPI device objects as the slaves.
355 Enable this to expose the temperature information and cooling ability
356 from these objects to userspace via the normal thermal framework.
357 This means that a wide range of applications and GUI widgets can show
358 the information to the user or use this information for making
359 decisions. For example, the Intel Thermal Daemon can use this
360 information to allow the user to select his laptop to run without
361 turning on the fans.
362
363config ACPI_THERMAL_REL
364 tristate
365 depends on ACPI
366 344
367config INTEL_PCH_THERMAL 345config INTEL_PCH_THERMAL
368 tristate "Intel PCH Thermal Reporting Driver" 346 tristate "Intel PCH Thermal Reporting Driver"
diff --git a/drivers/thermal/int340x_thermal/Kconfig b/drivers/thermal/int340x_thermal/Kconfig
new file mode 100644
index 000000000000..0582bd12a239
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/Kconfig
@@ -0,0 +1,42 @@
1#
2# ACPI INT340x thermal drivers configuration
3#
4
5config INT340X_THERMAL
6 tristate "ACPI INT340X thermal drivers"
7 depends on X86 && ACPI
8 select THERMAL_GOV_USER_SPACE
9 select ACPI_THERMAL_REL
10 select ACPI_FAN
11 select INTEL_SOC_DTS_IOSF_CORE
12 help
13 Newer laptops and tablets that use ACPI may have thermal sensors and
14 other devices with thermal control capabilities outside the core
15 CPU/SOC, for thermal safety reasons.
16 They are exposed for the OS to use via the INT3400 ACPI device object
17 as the master, and INT3401~INT340B ACPI device objects as the slaves.
18 Enable this to expose the temperature information and cooling ability
19 from these objects to userspace via the normal thermal framework.
20 This means that a wide range of applications and GUI widgets can show
21 the information to the user or use this information for making
22 decisions. For example, the Intel Thermal Daemon can use this
23 information to allow the user to select his laptop to run without
24 turning on the fans.
25
26config ACPI_THERMAL_REL
27 tristate
28 depends on ACPI
29
30if INT340X_THERMAL
31
32config INT3406_THERMAL
33 tristate "ACPI INT3406 display thermal driver"
34 depends on ACPI_VIDEO
35 help
36 The display thermal device represents the LED/LCD display panel
37 that may or may not include touch support. The main function of
38 the display thermal device is to allow control of the display
39 brightness in order to address a thermal condition or to reduce
40 power consumed by display device.
41
42endif
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index ba77a34f659f..df0df055e7ff 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
3obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o 3obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
4obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o 4obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
5obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o 5obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
6obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
6obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o 7obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
diff --git a/drivers/thermal/int340x_thermal/int3406_thermal.c b/drivers/thermal/int340x_thermal/int3406_thermal.c
new file mode 100644
index 000000000000..13d431cbd29e
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int3406_thermal.c
@@ -0,0 +1,236 @@
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
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
16#include <linux/backlight.h>
17#include <linux/thermal.h>
18#include <acpi/video.h>
19
20#define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
21
22struct int3406_thermal_data {
23 int upper_limit;
24 int upper_limit_index;
25 int lower_limit;
26 int lower_limit_index;
27 acpi_handle handle;
28 struct acpi_video_device_brightness *br;
29 struct backlight_device *raw_bd;
30 struct thermal_cooling_device *cooling_dev;
31};
32
33static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d)
34{
35 int max_level = d->br->levels[d->br->count - 1];
36 int raw_max = d->raw_bd->props.max_brightness;
37
38 return level * raw_max / max_level;
39}
40
41static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d)
42{
43 int raw_max = d->raw_bd->props.max_brightness;
44 int max_level = d->br->levels[d->br->count - 1];
45
46 return level * max_level / raw_max;
47}
48
49static int
50int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
51 unsigned long *state)
52{
53 struct int3406_thermal_data *d = cooling_dev->devdata;
54 int index = d->lower_limit_index ? d->lower_limit_index : 2;
55
56 *state = d->br->count - 1 - index;
57 return 0;
58}
59
60static int
61int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
62 unsigned long state)
63{
64 struct int3406_thermal_data *d = cooling_dev->devdata;
65 int level, raw_level;
66
67 if (state > d->br->count - 3)
68 return -EINVAL;
69
70 state = d->br->count - 1 - state;
71 level = d->br->levels[state];
72
73 if ((d->upper_limit && level > d->upper_limit) ||
74 (d->lower_limit && level < d->lower_limit))
75 return -EINVAL;
76
77 raw_level = int3406_thermal_to_raw(level, d);
78 return backlight_device_set_brightness(d->raw_bd, raw_level);
79}
80
81static int
82int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
83 unsigned long *state)
84{
85 struct int3406_thermal_data *d = cooling_dev->devdata;
86 int raw_level, level, i;
87 int *levels = d->br->levels;
88
89 raw_level = d->raw_bd->props.brightness;
90 level = int3406_thermal_to_acpi(raw_level, d);
91
92 /*
93 * There is no 1:1 mapping between the firmware interface level with the
94 * raw interface level, we will have to find one that is close enough.
95 */
96 for (i = 2; i < d->br->count; i++) {
97 if (level < levels[i]) {
98 if (i == 2)
99 break;
100 if ((level - levels[i - 1]) < (levels[i] - level))
101 i--;
102 break;
103 }
104 }
105
106 *state = d->br->count - 1 - i;
107 return 0;
108}
109
110static const struct thermal_cooling_device_ops video_cooling_ops = {
111 .get_max_state = int3406_thermal_get_max_state,
112 .get_cur_state = int3406_thermal_get_cur_state,
113 .set_cur_state = int3406_thermal_set_cur_state,
114};
115
116static int int3406_thermal_get_index(int *array, int nr, int value)
117{
118 int i;
119
120 for (i = 0; i < nr; i++) {
121 if (array[i] == value)
122 break;
123 }
124 return i == nr ? -ENOENT : i;
125}
126
127static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
128{
129 acpi_status status;
130 unsigned long long lower_limit, upper_limit;
131 int index;
132
133 status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
134 if (ACPI_SUCCESS(status)) {
135 index = int3406_thermal_get_index(d->br->levels, d->br->count,
136 lower_limit);
137 if (index > 0) {
138 d->lower_limit = (int)lower_limit;
139 d->lower_limit_index = index;
140 }
141 }
142
143 status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
144 if (ACPI_SUCCESS(status)) {
145 index = int3406_thermal_get_index(d->br->levels, d->br->count,
146 upper_limit);
147 if (index > 0) {
148 d->upper_limit = (int)upper_limit;
149 d->upper_limit_index = index;
150 }
151 }
152}
153
154static void int3406_notify(acpi_handle handle, u32 event, void *data)
155{
156 if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
157 int3406_thermal_get_limit(data);
158}
159
160static int int3406_thermal_probe(struct platform_device *pdev)
161{
162 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
163 struct int3406_thermal_data *d;
164 struct backlight_device *bd;
165 int ret;
166
167 if (!ACPI_HANDLE(&pdev->dev))
168 return -ENODEV;
169
170 d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
171 if (!d)
172 return -ENOMEM;
173 d->handle = ACPI_HANDLE(&pdev->dev);
174
175 bd = backlight_device_get_by_type(BACKLIGHT_RAW);
176 if (!bd)
177 return -ENODEV;
178 d->raw_bd = bd;
179
180 ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br);
181 if (ret)
182 return ret;
183
184 int3406_thermal_get_limit(d);
185
186 d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
187 d, &video_cooling_ops);
188 if (IS_ERR(d->cooling_dev))
189 goto err;
190
191 ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
192 int3406_notify, d);
193 if (ret)
194 goto err_cdev;
195
196 platform_set_drvdata(pdev, d);
197
198 return 0;
199
200err_cdev:
201 thermal_cooling_device_unregister(d->cooling_dev);
202err:
203 kfree(d->br);
204 return -ENODEV;
205}
206
207static int int3406_thermal_remove(struct platform_device *pdev)
208{
209 struct int3406_thermal_data *d = platform_get_drvdata(pdev);
210
211 thermal_cooling_device_unregister(d->cooling_dev);
212 kfree(d->br);
213 return 0;
214}
215
216static const struct acpi_device_id int3406_thermal_match[] = {
217 {"INT3406", 0},
218 {}
219};
220
221MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
222
223static struct platform_driver int3406_thermal_driver = {
224 .probe = int3406_thermal_probe,
225 .remove = int3406_thermal_remove,
226 .driver = {
227 .name = "int3406 thermal",
228 .owner = THIS_MODULE,
229 .acpi_match_table = int3406_thermal_match,
230 },
231};
232
233module_platform_driver(int3406_thermal_driver);
234
235MODULE_DESCRIPTION("INT3406 Thermal driver");
236MODULE_LICENSE("GPL v2");