]> Pileus Git - ~andy/linux/blob - drivers/gpu/drm/nouveau/core/subdev/therm/base.c
drm/i915/vlv: use per-pipe backlight controls v2
[~andy/linux] / drivers / gpu / drm / nouveau / core / subdev / therm / base.c
1 /*
2  * Copyright 2012 The Nouveau community
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Martin Peres
23  */
24
25 #include <core/object.h>
26 #include <core/device.h>
27
28 #include <subdev/bios.h>
29
30 #include "priv.h"
31
32 static int
33 nouveau_therm_update_trip(struct nouveau_therm *therm)
34 {
35         struct nouveau_therm_priv *priv = (void *)therm;
36         struct nouveau_therm_trip_point *trip = priv->fan->bios.trip,
37                                         *cur_trip = NULL,
38                                         *last_trip = priv->last_trip;
39         u8  temp = therm->temp_get(therm);
40         u16 duty, i;
41
42         /* look for the trip point corresponding to the current temperature */
43         cur_trip = NULL;
44         for (i = 0; i < priv->fan->bios.nr_fan_trip; i++) {
45                 if (temp >= trip[i].temp)
46                         cur_trip = &trip[i];
47         }
48
49         /* account for the hysteresis cycle */
50         if (last_trip && temp <= (last_trip->temp) &&
51             temp > (last_trip->temp - last_trip->hysteresis))
52                 cur_trip = last_trip;
53
54         if (cur_trip) {
55                 duty = cur_trip->fan_duty;
56                 priv->last_trip = cur_trip;
57         } else {
58                 duty = 0;
59                 priv->last_trip = NULL;
60         }
61
62         return duty;
63 }
64
65 static int
66 nouveau_therm_update_linear(struct nouveau_therm *therm)
67 {
68         struct nouveau_therm_priv *priv = (void *)therm;
69         u8  linear_min_temp = priv->fan->bios.linear_min_temp;
70         u8  linear_max_temp = priv->fan->bios.linear_max_temp;
71         u8  temp = therm->temp_get(therm);
72         u16 duty;
73
74         /* handle the non-linear part first */
75         if (temp < linear_min_temp)
76                 return priv->fan->bios.min_duty;
77         else if (temp > linear_max_temp)
78                 return priv->fan->bios.max_duty;
79
80         /* we are in the linear zone */
81         duty  = (temp - linear_min_temp);
82         duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty);
83         duty /= (linear_max_temp - linear_min_temp);
84         duty += priv->fan->bios.min_duty;
85
86         return duty;
87 }
88
89 static void
90 nouveau_therm_update(struct nouveau_therm *therm, int mode)
91 {
92         struct nouveau_timer *ptimer = nouveau_timer(therm);
93         struct nouveau_therm_priv *priv = (void *)therm;
94         unsigned long flags;
95         int duty;
96
97         spin_lock_irqsave(&priv->lock, flags);
98         nv_debug(therm, "FAN speed check\n");
99         if (mode < 0)
100                 mode = priv->mode;
101         priv->mode = mode;
102
103         switch (mode) {
104         case NOUVEAU_THERM_CTRL_MANUAL:
105                 ptimer->alarm_cancel(ptimer, &priv->alarm);
106                 duty = nouveau_therm_fan_get(therm);
107                 if (duty < 0)
108                         duty = 100;
109                 break;
110         case NOUVEAU_THERM_CTRL_AUTO:
111                 if (priv->fan->bios.nr_fan_trip)
112                         duty = nouveau_therm_update_trip(therm);
113                 else
114                         duty = nouveau_therm_update_linear(therm);
115                 break;
116         case NOUVEAU_THERM_CTRL_NONE:
117         default:
118                 ptimer->alarm_cancel(ptimer, &priv->alarm);
119                 goto done;
120         }
121
122         nv_debug(therm, "FAN target request: %d%%\n", duty);
123         nouveau_therm_fan_set(therm, (mode != NOUVEAU_THERM_CTRL_AUTO), duty);
124
125 done:
126         if (list_empty(&priv->alarm.head) && (mode == NOUVEAU_THERM_CTRL_AUTO))
127                 ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm);
128         else if (!list_empty(&priv->alarm.head))
129                 nv_debug(therm, "therm fan alarm list is not empty\n");
130         spin_unlock_irqrestore(&priv->lock, flags);
131 }
132
133 static void
134 nouveau_therm_alarm(struct nouveau_alarm *alarm)
135 {
136         struct nouveau_therm_priv *priv =
137                container_of(alarm, struct nouveau_therm_priv, alarm);
138         nouveau_therm_update(&priv->base, -1);
139 }
140
141 int
142 nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode)
143 {
144         struct nouveau_therm_priv *priv = (void *)therm;
145         struct nouveau_device *device = nv_device(therm);
146         static const char *name[] = {
147                 "disabled",
148                 "manual",
149                 "automatic"
150         };
151
152         /* The default PDAEMON ucode interferes with fan management */
153         if ((mode >= ARRAY_SIZE(name)) ||
154             (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))
155                 return -EINVAL;
156
157         /* do not allow automatic fan management if the thermal sensor is
158          * not available */
159         if (priv->mode == 2 && therm->temp_get(therm) < 0)
160                 return -EINVAL;
161
162         if (priv->mode == mode)
163                 return 0;
164
165         nv_info(therm, "fan management: %s\n", name[mode]);
166         nouveau_therm_update(therm, mode);
167         return 0;
168 }
169
170 int
171 nouveau_therm_attr_get(struct nouveau_therm *therm,
172                        enum nouveau_therm_attr_type type)
173 {
174         struct nouveau_therm_priv *priv = (void *)therm;
175
176         switch (type) {
177         case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
178                 return priv->fan->bios.min_duty;
179         case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
180                 return priv->fan->bios.max_duty;
181         case NOUVEAU_THERM_ATTR_FAN_MODE:
182                 return priv->mode;
183         case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
184                 return priv->bios_sensor.thrs_fan_boost.temp;
185         case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
186                 return priv->bios_sensor.thrs_fan_boost.hysteresis;
187         case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK:
188                 return priv->bios_sensor.thrs_down_clock.temp;
189         case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST:
190                 return priv->bios_sensor.thrs_down_clock.hysteresis;
191         case NOUVEAU_THERM_ATTR_THRS_CRITICAL:
192                 return priv->bios_sensor.thrs_critical.temp;
193         case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST:
194                 return priv->bios_sensor.thrs_critical.hysteresis;
195         case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN:
196                 return priv->bios_sensor.thrs_shutdown.temp;
197         case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
198                 return priv->bios_sensor.thrs_shutdown.hysteresis;
199         }
200
201         return -EINVAL;
202 }
203
204 int
205 nouveau_therm_attr_set(struct nouveau_therm *therm,
206                        enum nouveau_therm_attr_type type, int value)
207 {
208         struct nouveau_therm_priv *priv = (void *)therm;
209
210         switch (type) {
211         case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
212                 if (value < 0)
213                         value = 0;
214                 if (value > priv->fan->bios.max_duty)
215                         value = priv->fan->bios.max_duty;
216                 priv->fan->bios.min_duty = value;
217                 return 0;
218         case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
219                 if (value < 0)
220                         value = 0;
221                 if (value < priv->fan->bios.min_duty)
222                         value = priv->fan->bios.min_duty;
223                 priv->fan->bios.max_duty = value;
224                 return 0;
225         case NOUVEAU_THERM_ATTR_FAN_MODE:
226                 return nouveau_therm_fan_mode(therm, value);
227         case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
228                 priv->bios_sensor.thrs_fan_boost.temp = value;
229                 priv->sensor.program_alarms(therm);
230                 return 0;
231         case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
232                 priv->bios_sensor.thrs_fan_boost.hysteresis = value;
233                 priv->sensor.program_alarms(therm);
234                 return 0;
235         case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK:
236                 priv->bios_sensor.thrs_down_clock.temp = value;
237                 priv->sensor.program_alarms(therm);
238                 return 0;
239         case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST:
240                 priv->bios_sensor.thrs_down_clock.hysteresis = value;
241                 priv->sensor.program_alarms(therm);
242                 return 0;
243         case NOUVEAU_THERM_ATTR_THRS_CRITICAL:
244                 priv->bios_sensor.thrs_critical.temp = value;
245                 priv->sensor.program_alarms(therm);
246                 return 0;
247         case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST:
248                 priv->bios_sensor.thrs_critical.hysteresis = value;
249                 priv->sensor.program_alarms(therm);
250                 return 0;
251         case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN:
252                 priv->bios_sensor.thrs_shutdown.temp = value;
253                 priv->sensor.program_alarms(therm);
254                 return 0;
255         case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
256                 priv->bios_sensor.thrs_shutdown.hysteresis = value;
257                 priv->sensor.program_alarms(therm);
258                 return 0;
259         }
260
261         return -EINVAL;
262 }
263
264 int
265 _nouveau_therm_init(struct nouveau_object *object)
266 {
267         struct nouveau_therm *therm = (void *)object;
268         struct nouveau_therm_priv *priv = (void *)therm;
269         int ret;
270
271         ret = nouveau_subdev_init(&therm->base);
272         if (ret)
273                 return ret;
274
275         if (priv->suspend >= 0) {
276                 /* restore the pwm value only when on manual or auto mode */
277                 if (priv->suspend > 0)
278                         nouveau_therm_fan_set(therm, true, priv->fan->percent);
279
280                 nouveau_therm_fan_mode(therm, priv->suspend);
281         }
282         nouveau_therm_sensor_init(therm);
283         nouveau_therm_fan_init(therm);
284         return 0;
285 }
286
287 int
288 _nouveau_therm_fini(struct nouveau_object *object, bool suspend)
289 {
290         struct nouveau_therm *therm = (void *)object;
291         struct nouveau_therm_priv *priv = (void *)therm;
292
293         nouveau_therm_fan_fini(therm, suspend);
294         nouveau_therm_sensor_fini(therm, suspend);
295         if (suspend) {
296                 priv->suspend = priv->mode;
297                 priv->mode = NOUVEAU_THERM_CTRL_NONE;
298         }
299
300         return nouveau_subdev_fini(&therm->base, suspend);
301 }
302
303 int
304 nouveau_therm_create_(struct nouveau_object *parent,
305                       struct nouveau_object *engine,
306                       struct nouveau_oclass *oclass,
307                       int length, void **pobject)
308 {
309         struct nouveau_therm_priv *priv;
310         int ret;
311
312         ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PTHERM",
313                                      "therm", length, pobject);
314         priv = *pobject;
315         if (ret)
316                 return ret;
317
318         nouveau_alarm_init(&priv->alarm, nouveau_therm_alarm);
319         spin_lock_init(&priv->lock);
320         spin_lock_init(&priv->sensor.alarm_program_lock);
321
322         priv->base.fan_get = nouveau_therm_fan_user_get;
323         priv->base.fan_set = nouveau_therm_fan_user_set;
324         priv->base.fan_sense = nouveau_therm_fan_sense;
325         priv->base.attr_get = nouveau_therm_attr_get;
326         priv->base.attr_set = nouveau_therm_attr_set;
327         priv->mode = priv->suspend = -1; /* undefined */
328         return 0;
329 }
330
331 int
332 nouveau_therm_preinit(struct nouveau_therm *therm)
333 {
334         nouveau_therm_sensor_ctor(therm);
335         nouveau_therm_ic_ctor(therm);
336         nouveau_therm_fan_ctor(therm);
337
338         nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_NONE);
339         nouveau_therm_sensor_preinit(therm);
340         return 0;
341 }
342
343 void
344 _nouveau_therm_dtor(struct nouveau_object *object)
345 {
346         struct nouveau_therm_priv *priv = (void *)object;
347         kfree(priv->fan);
348         nouveau_subdev_destroy(&priv->base.base);
349 }