0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/acpi.h>
0012 #include <linux/backlight.h>
0013 #include <linux/thermal.h>
0014 #include <acpi/video.h>
0015
0016 #define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
0017
0018 struct int3406_thermal_data {
0019 int upper_limit;
0020 int lower_limit;
0021 acpi_handle handle;
0022 struct acpi_video_device_brightness *br;
0023 struct backlight_device *raw_bd;
0024 struct thermal_cooling_device *cooling_dev;
0025 };
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 #define ACPI_TO_RAW(v, d) (d->raw_bd->props.max_brightness * v / 100)
0038 #define RAW_TO_ACPI(v, d) (v * 100 / d->raw_bd->props.max_brightness)
0039
0040 static int
0041 int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
0042 unsigned long *state)
0043 {
0044 struct int3406_thermal_data *d = cooling_dev->devdata;
0045
0046 *state = d->upper_limit - d->lower_limit;
0047 return 0;
0048 }
0049
0050 static int
0051 int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
0052 unsigned long state)
0053 {
0054 struct int3406_thermal_data *d = cooling_dev->devdata;
0055 int acpi_level, raw_level;
0056
0057 if (state > d->upper_limit - d->lower_limit)
0058 return -EINVAL;
0059
0060 acpi_level = d->br->levels[d->upper_limit - state];
0061
0062 raw_level = ACPI_TO_RAW(acpi_level, d);
0063
0064 return backlight_device_set_brightness(d->raw_bd, raw_level);
0065 }
0066
0067 static int
0068 int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
0069 unsigned long *state)
0070 {
0071 struct int3406_thermal_data *d = cooling_dev->devdata;
0072 int acpi_level;
0073 int index;
0074
0075 acpi_level = RAW_TO_ACPI(d->raw_bd->props.brightness, d);
0076
0077
0078
0079
0080
0081
0082 for (index = d->lower_limit; index < d->upper_limit; index++) {
0083 if (acpi_level <= d->br->levels[index])
0084 break;
0085 }
0086
0087 *state = d->upper_limit - index;
0088 return 0;
0089 }
0090
0091 static const struct thermal_cooling_device_ops video_cooling_ops = {
0092 .get_max_state = int3406_thermal_get_max_state,
0093 .get_cur_state = int3406_thermal_get_cur_state,
0094 .set_cur_state = int3406_thermal_set_cur_state,
0095 };
0096
0097 static int int3406_thermal_get_index(int *array, int nr, int value)
0098 {
0099 int i;
0100
0101 for (i = 2; i < nr; i++) {
0102 if (array[i] == value)
0103 break;
0104 }
0105 return i == nr ? -ENOENT : i;
0106 }
0107
0108 static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
0109 {
0110 acpi_status status;
0111 unsigned long long lower_limit, upper_limit;
0112
0113 status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
0114 if (ACPI_SUCCESS(status))
0115 d->lower_limit = int3406_thermal_get_index(d->br->levels,
0116 d->br->count, lower_limit);
0117
0118 status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
0119 if (ACPI_SUCCESS(status))
0120 d->upper_limit = int3406_thermal_get_index(d->br->levels,
0121 d->br->count, upper_limit);
0122
0123
0124 d->lower_limit = d->lower_limit > 0 ? d->lower_limit : 2;
0125 d->upper_limit = d->upper_limit > 0 ? d->upper_limit : d->br->count - 1;
0126 }
0127
0128 static void int3406_notify(acpi_handle handle, u32 event, void *data)
0129 {
0130 if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
0131 int3406_thermal_get_limit(data);
0132 }
0133
0134 static int int3406_thermal_probe(struct platform_device *pdev)
0135 {
0136 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
0137 struct int3406_thermal_data *d;
0138 struct backlight_device *bd;
0139 int ret;
0140
0141 if (!ACPI_HANDLE(&pdev->dev))
0142 return -ENODEV;
0143
0144 d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
0145 if (!d)
0146 return -ENOMEM;
0147 d->handle = ACPI_HANDLE(&pdev->dev);
0148
0149 bd = backlight_device_get_by_type(BACKLIGHT_RAW);
0150 if (!bd)
0151 return -ENODEV;
0152 d->raw_bd = bd;
0153
0154 ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br, NULL);
0155 if (ret)
0156 return ret;
0157
0158 int3406_thermal_get_limit(d);
0159
0160 d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
0161 d, &video_cooling_ops);
0162 if (IS_ERR(d->cooling_dev))
0163 goto err;
0164
0165 ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
0166 int3406_notify, d);
0167 if (ret)
0168 goto err_cdev;
0169
0170 platform_set_drvdata(pdev, d);
0171
0172 return 0;
0173
0174 err_cdev:
0175 thermal_cooling_device_unregister(d->cooling_dev);
0176 err:
0177 kfree(d->br);
0178 return -ENODEV;
0179 }
0180
0181 static int int3406_thermal_remove(struct platform_device *pdev)
0182 {
0183 struct int3406_thermal_data *d = platform_get_drvdata(pdev);
0184
0185 thermal_cooling_device_unregister(d->cooling_dev);
0186 kfree(d->br);
0187 return 0;
0188 }
0189
0190 static const struct acpi_device_id int3406_thermal_match[] = {
0191 {"INT3406", 0},
0192 {}
0193 };
0194
0195 MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
0196
0197 static struct platform_driver int3406_thermal_driver = {
0198 .probe = int3406_thermal_probe,
0199 .remove = int3406_thermal_remove,
0200 .driver = {
0201 .name = "int3406 thermal",
0202 .acpi_match_table = int3406_thermal_match,
0203 },
0204 };
0205
0206 module_platform_driver(int3406_thermal_driver);
0207
0208 MODULE_DESCRIPTION("INT3406 Thermal driver");
0209 MODULE_LICENSE("GPL v2");