From: "Zhang, Rui" <rui.zhang@intel.com>
To: "Lu, Aaron" <aaron.lu@intel.com>
Cc: "jingoohan1@gmail.com" <jingoohan1@gmail.com>,
"linux-fbdev@vger.kernel.org" <linux-fbdev@vger.kernel.org>,
"Pandruvada, Srinivas" <srinivas.pandruvada@intel.com>,
"linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>,
"rjw@rjwysocki.net" <rjw@rjwysocki.net>,
"linux-acpi@vger.kernel.org" <linux-acpi@vger.kernel.org>
Subject: Re: [PATCH v3 update 4/4] Thermal: add INT3406 thermal driver
Date: Tue, 10 May 2016 08:08:52 +0000 [thread overview]
Message-ID: <1462867730.25391.2.camel@rzhang1-mobl4> (raw)
In-Reply-To: <20160509075458.GA8945@aaronlu.sh.intel.com>
On Mon, 2016-05-09 at 15:54 +0800, Aaron Lu wrote:
> 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>
> ---
> update: change copyright to 2016 and license to GPL v2 as suggested by Rui.
>
> drivers/thermal/Kconfig | 28 +--
> drivers/thermal/int340x_thermal/Kconfig | 42 ++++
> drivers/thermal/int340x_thermal/Makefile | 1 +
> drivers/thermal/int340x_thermal/int3406_thermal.c | 236 ++++++++++++++++++++++
> 4 files changed, 282 insertions(+), 25 deletions(-)
> create mode 100644 drivers/thermal/int340x_thermal/Kconfig
> create mode 100644 drivers/thermal/int340x_thermal/int3406_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index c37eedc35a24..cb1ba70ceaa7 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -338,31 +338,9 @@ config INTEL_QUARK_DTS_THERMAL
> hot & critical. The critical trip point default value is set by
> underlying BIOS/Firmware.
>
> -config INT340X_THERMAL
> - tristate "ACPI INT340X thermal drivers"
> - depends on X86 && ACPI
> - select THERMAL_GOV_USER_SPACE
> - select ACPI_THERMAL_REL
> - select ACPI_FAN
> - select INTEL_SOC_DTS_IOSF_CORE
> - select THERMAL_WRITABLE_TRIPS
> - help
> - Newer laptops and tablets that use ACPI may have thermal sensors and
> - other devices with thermal control capabilities outside the core
> - CPU/SOC, for thermal safety reasons.
> - They are exposed for the OS to use via the INT3400 ACPI device object
> - as the master, and INT3401~INT340B ACPI device objects as the slaves.
> - Enable this to expose the temperature information and cooling ability
> - from these objects to userspace via the normal thermal framework.
> - This means that a wide range of applications and GUI widgets can show
> - the information to the user or use this information for making
> - decisions. For example, the Intel Thermal Daemon can use this
> - information to allow the user to select his laptop to run without
> - turning on the fans.
> -
> -config ACPI_THERMAL_REL
> - tristate
> - depends on ACPI
> +menu "ACPI INT340X thermal drivers"
> +source drivers/thermal/int340x_thermal/Kconfig
> +endmenu
>
> config INTEL_PCH_THERMAL
> 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 @@
> +#
> +# ACPI INT340x thermal drivers configuration
> +#
> +
> +config INT340X_THERMAL
> + tristate "ACPI INT340X thermal drivers"
> + depends on X86 && ACPI
> + select THERMAL_GOV_USER_SPACE
> + select ACPI_THERMAL_REL
> + select ACPI_FAN
> + select INTEL_SOC_DTS_IOSF_CORE
> + help
> + Newer laptops and tablets that use ACPI may have thermal sensors and
> + other devices with thermal control capabilities outside the core
> + CPU/SOC, for thermal safety reasons.
> + They are exposed for the OS to use via the INT3400 ACPI device object
> + as the master, and INT3401~INT340B ACPI device objects as the slaves.
> + Enable this to expose the temperature information and cooling ability
> + from these objects to userspace via the normal thermal framework.
> + This means that a wide range of applications and GUI widgets can show
> + the information to the user or use this information for making
> + decisions. For example, the Intel Thermal Daemon can use this
> + information to allow the user to select his laptop to run without
> + turning on the fans.
> +
> +config ACPI_THERMAL_REL
> + tristate
> + depends on ACPI
> +
> +if INT340X_THERMAL
> +
> +config INT3406_THERMAL
> + tristate "ACPI INT3406 display thermal driver"
> + depends on ACPI_VIDEO
> + help
> + The display thermal device represents the LED/LCD display panel
> + that may or may not include touch support. The main function of
> + the display thermal device is to allow control of the display
> + brightness in order to address a thermal condition or to reduce
> + power consumed by display device.
> +
> +endif
> 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
> obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
> obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
> obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
> +obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
> obj-$(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 @@
> +/*
> + * 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;
> + int upper_limit_index;
> + int lower_limit;
> + int lower_limit_index;
> + acpi_handle handle;
> + struct acpi_video_device_brightness *br;
> + struct backlight_device *raw_bd;
> + struct thermal_cooling_device *cooling_dev;
> +};
> +
> +static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d)
> +{
> + int max_level = d->br->levels[d->br->count - 1];
> + int raw_max = d->raw_bd->props.max_brightness;
> +
> + return level * raw_max / max_level;
> +}
> +
> +static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d)
> +{
> + int raw_max = d->raw_bd->props.max_brightness;
> + int max_level = d->br->levels[d->br->count - 1];
> +
> + return level * max_level / raw_max;
> +}
> +
> +static int
> +int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
> + unsigned long *state)
> +{
> + struct int3406_thermal_data *d = cooling_dev->devdata;
> + int index = d->lower_limit_index ? d->lower_limit_index : 2;
> +
> + *state = d->br->count - 1 - index;
> + 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;
> + int level, raw_level;
> +
> + if (state > d->br->count - 3)
> + return -EINVAL;
> +
> + state = d->br->count - 1 - state;
> + level = d->br->levels[state];
> +
> + if ((d->upper_limit && level > d->upper_limit) ||
> + (d->lower_limit && level < d->lower_limit))
> + return -EINVAL;
> +
> + raw_level = int3406_thermal_to_raw(level, d);
> + 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;
> + int raw_level, level, i;
> + int *levels = d->br->levels;
> +
> + raw_level = d->raw_bd->props.brightness;
> + level = int3406_thermal_to_acpi(raw_level, d);
> +
> + /*
> + * There is no 1:1 mapping between the firmware interface level with the
> + * raw interface level, we will have to find one that is close enough.
> + */
> + for (i = 2; i < d->br->count; i++) {
> + if (level < levels[i]) {
> + if (i == 2)
> + break;
> + if ((level - levels[i - 1]) < (levels[i] - level))
> + i--;
> + break;
> + }
> + }
> +
> + *state = d->br->count - 1 - i;
> + 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;
> +
> + for (i = 0; i < nr; i++) {
> + 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;
> + int index;
> +
> + status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
> + if (ACPI_SUCCESS(status)) {
> + index = int3406_thermal_get_index(d->br->levels, d->br->count,
> + lower_limit);
> + if (index > 0) {
> + d->lower_limit = (int)lower_limit;
> + d->lower_limit_index = index;
> + }
> + }
> +
> + status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
> + if (ACPI_SUCCESS(status)) {
> + index = int3406_thermal_get_index(d->br->levels, d->br->count,
> + upper_limit);
> + if (index > 0) {
> + d->upper_limit = (int)upper_limit;
> + d->upper_limit_index = index;
> + }
> + }
> +}
> +
> +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;
> +
> + ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br);
> + 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",
> + .owner = THIS_MODULE,
> + .acpi_match_table = int3406_thermal_match,
> + },
> +};
> +
> +module_platform_driver(int3406_thermal_driver);
> +
> +MODULE_DESCRIPTION("INT3406 Thermal driver");
> +MODULE_LICENSE("GPL v2");
next prev parent reply other threads:[~2016-05-10 8:09 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-04-27 12:45 [PATCH v3 0/4] Support INT3406 Display thermal device Aaron Lu
2016-04-27 12:45 ` [PATCH v3 1/4] video / backlight: add two APIs for drivers to use Aaron Lu
2016-04-27 12:45 ` [PATCH v3 2/4] video / backlight: remove the backlight_device_registered API Aaron Lu
2016-04-27 12:45 ` [PATCH v3 3/4] ACPI/video: export acpi_video_get_levels Aaron Lu
2016-04-27 12:45 ` [PATCH v3 4/4] Thermal: add INT3406 thermal driver Aaron Lu
2016-05-09 7:47 ` Zhang, Rui
2016-05-09 7:54 ` [PATCH v3 update " Aaron Lu
2016-05-10 8:08 ` Zhang, Rui [this message]
2016-05-13 22:05 ` Rafael J. Wysocki
2016-04-27 19:38 ` [PATCH v3 0/4] Support INT3406 Display thermal device Rafael J. Wysocki
2016-04-28 1:48 ` Aaron Lu
2016-05-04 21:47 ` Rafael J. Wysocki
2016-05-06 6:45 ` Aaron Lu
2016-05-09 7:49 ` Zhang, Rui
2016-05-09 12:23 ` Rafael J. Wysocki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1462867730.25391.2.camel@rzhang1-mobl4 \
--to=rui.zhang@intel.com \
--cc=aaron.lu@intel.com \
--cc=jingoohan1@gmail.com \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-fbdev@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=rjw@rjwysocki.net \
--cc=srinivas.pandruvada@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).