root/drivers/thermal/intel/int340x_thermal/int3406_thermal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. int3406_thermal_get_max_state
  2. int3406_thermal_set_cur_state
  3. int3406_thermal_get_cur_state
  4. int3406_thermal_get_index
  5. int3406_thermal_get_limit
  6. int3406_notify
  7. int3406_thermal_probe
  8. int3406_thermal_remove

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

/* [<][>][^][v][top][bottom][index][help] */