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