On Thu, 2014-12-11 at 16:38 +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. Due to this, the > backlight core has some modifications. Also, to re-use some of the ACPI > video module's code, one function has been exported. > > Signed-off-by: Aaron Lu <aaron.lu@xxxxxxxxx> Jingoo and Lee, are you okay with the changes in drivers/video/backlight/backlight.c and include/linux/backlight.h? thanks, rui > --- > v6: Fix an issue that wrongly set error path return value as reported > by Olof Johansson. > > drivers/acpi/video.c | 77 ++++---- > drivers/thermal/Kconfig | 26 +-- > drivers/thermal/int340x_thermal/Kconfig | 41 ++++ > drivers/thermal/int340x_thermal/Makefile | 1 + > drivers/thermal/int340x_thermal/int3406_thermal.c | 229 ++++++++++++++++++++++ > drivers/video/backlight/backlight.c | 44 +++-- > include/acpi/video.h | 20 ++ > include/linux/backlight.h | 2 + > 8 files changed, 366 insertions(+), 74 deletions(-) > create mode 100644 drivers/thermal/int340x_thermal/Kconfig > create mode 100644 drivers/thermal/int340x_thermal/int3406_thermal.c > > diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c > index 807a88a0f394..32880e6c8da4 100644 > --- a/drivers/acpi/video.c > +++ b/drivers/acpi/video.c > @@ -186,19 +186,6 @@ struct acpi_video_device_cap { > u8 _DDC:1; /* Return the EDID for this device */ > }; > > -struct acpi_video_brightness_flags { > - u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ > - u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ > - u8 _BQC_use_index:1; /* _BQC returns an index value */ > -}; > - > -struct acpi_video_device_brightness { > - int curr; > - int count; > - int *levels; > - struct acpi_video_brightness_flags flags; > -}; > - > struct acpi_video_device { > unsigned long device_id; > struct acpi_video_device_flags flags; > @@ -344,7 +331,7 @@ static const struct thermal_cooling_device_ops video_cooling_ops = { > */ > > static int > -acpi_video_device_lcd_query_levels(struct acpi_video_device *device, > +acpi_video_device_lcd_query_levels(acpi_handle handle, > union acpi_object **levels) > { > int status; > @@ -354,7 +341,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device, > > *levels = NULL; > > - status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer); > + status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer); > if (!ACPI_SUCCESS(status)) > return status; > obj = (union acpi_object *)buffer.pointer; > @@ -727,29 +714,18 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device, > return 0; > } > > - > -/* > - * Arg: > - * device : video output device (LCD, CRT, ..) > - * > - * Return Value: > - * Maximum brightness level > - * > - * Allocate and initialize device->brightness. > - */ > - > -static int > -acpi_video_init_brightness(struct acpi_video_device *device) > +int acpi_video_get_levels(struct acpi_device *device, > + struct acpi_video_device_brightness **dev_br) > { > union acpi_object *obj = NULL; > int i, max_level = 0, count = 0, level_ac_battery = 0; > - unsigned long long level, level_old; > union acpi_object *o; > struct acpi_video_device_brightness *br = NULL; > int result = -EINVAL; > u32 value; > > - if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { > + if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle, > + &obj))) { > ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " > "LCD brightness level\n")); > goto out; > @@ -822,6 +798,39 @@ acpi_video_init_brightness(struct acpi_video_device *device) > "Found unordered _BCL package")); > > br->count = count; > + *dev_br = br; > + result = 0; > + > +out: > + kfree(obj); > + return result; > +out_free: > + kfree(br); > + goto out; > +} > +EXPORT_SYMBOL(acpi_video_get_levels); > + > +/* > + * Arg: > + * device : video output device (LCD, CRT, ..) > + * > + * Return Value: > + * Maximum brightness level > + * > + * Allocate and initialize device->brightness. > + */ > + > +static int > +acpi_video_init_brightness(struct acpi_video_device *device) > +{ > + int i, max_level = 0; > + unsigned long long level, level_old; > + struct acpi_video_device_brightness *br = NULL; > + int result = -EINVAL; > + > + result = acpi_video_get_levels(device->dev, &br); > + if (result) > + return result; > device->brightness = br; > > /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ > @@ -864,17 +873,13 @@ set_level: > goto out_free_levels; > > ACPI_DEBUG_PRINT((ACPI_DB_INFO, > - "found %d brightness levels\n", count - 2)); > - kfree(obj); > - return result; > + "found %d brightness levels\n", br->count - 2)); > + return 0; > > out_free_levels: > kfree(br->levels); > -out_free: > kfree(br); > -out: > device->brightness = NULL; > - kfree(obj); > return result; > } > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > index f554d25b4399..ac391d8d76b4 100644 > --- a/drivers/thermal/Kconfig > +++ b/drivers/thermal/Kconfig > @@ -229,29 +229,9 @@ config INTEL_SOC_DTS_THERMAL > notification methods.The other trip is a critical trip point, which > was set by the driver based on the TJ MAX temperature. > > -config INT340X_THERMAL > - tristate "ACPI INT340X thermal drivers" > - depends on X86 && ACPI > - select THERMAL_GOV_USER_SPACE > - select ACPI_THERMAL_REL > - select ACPI_FAN > - 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 > > menu "Texas Instruments thermal drivers" > source "drivers/thermal/ti-soc-thermal/Kconfig" > diff --git a/drivers/thermal/int340x_thermal/Kconfig b/drivers/thermal/int340x_thermal/Kconfig > new file mode 100644 > index 000000000000..b92892a6afe0 > --- /dev/null > +++ b/drivers/thermal/int340x_thermal/Kconfig > @@ -0,0 +1,41 @@ > +# > +# 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 > + 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. > + > +if INT340X_THERMAL > + > +config ACPI_THERMAL_REL > + tristate > + depends on ACPI > + > +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 ffe40bffaf1a..a9d0429be412 100644 > --- a/drivers/thermal/int340x_thermal/Makefile > +++ b/drivers/thermal/int340x_thermal/Makefile > @@ -1,4 +1,5 @@ > obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o > obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o > obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.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..2719e49a0af9 > --- /dev/null > +++ b/drivers/thermal/int340x_thermal/int3406_thermal.c > @@ -0,0 +1,229 @@ > +/* > + * INT3406 thermal driver for display participant device > + * > + * Copyright (C) 2014, Intel Corporation > + * Authors: Aaron Lu <aaron.lu@xxxxxxxxx> > + * > + * 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; > + > + 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 - 1; i++) { > + if (level >= d->br->levels[i] && level <= d->br->levels[i + 1]) > + break; > + } > + > + *state = 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(platform_get_drvdata(pdev)); > + 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", > + .acpi_match_table = int3406_thermal_match, > + }, > +}; > + > +module_platform_driver(int3406_thermal_driver); > + > +MODULE_DESCRIPTION("INT3406 Thermal driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c > index bddc8b17a4d8..bea749329236 100644 > --- a/drivers/video/backlight/backlight.c > +++ b/drivers/video/backlight/backlight.c > @@ -164,28 +164,19 @@ static ssize_t brightness_show(struct device *dev, > return sprintf(buf, "%d\n", bd->props.brightness); > } > > -static ssize_t brightness_store(struct device *dev, > - struct device_attribute *attr, const char *buf, size_t count) > +int backlight_device_set_brightness(struct backlight_device *bd, int brightness) > { > - int rc; > - struct backlight_device *bd = to_backlight_device(dev); > - unsigned long brightness; > - > - rc = kstrtoul(buf, 0, &brightness); > - if (rc) > - return rc; > - > - rc = -ENXIO; > + int rc = -ENXIO; > > mutex_lock(&bd->ops_lock); > if (bd->ops) { > if (brightness > bd->props.max_brightness) > rc = -EINVAL; > else { > - pr_debug("set brightness to %lu\n", brightness); > + pr_debug("set brightness to %u\n", brightness); > bd->props.brightness = brightness; > backlight_update_status(bd); > - rc = count; > + rc = 0; > } > } > mutex_unlock(&bd->ops_lock); > @@ -194,6 +185,23 @@ static ssize_t brightness_store(struct device *dev, > > return rc; > } > +EXPORT_SYMBOL(backlight_device_set_brightness); > + > +static ssize_t brightness_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t count) > +{ > + int rc; > + struct backlight_device *bd = to_backlight_device(dev); > + unsigned long brightness; > + > + rc = kstrtoul(buf, 0, &brightness); > + if (rc) > + return rc; > + > + rc = backlight_device_set_brightness(bd, brightness); > + > + return rc ? rc : count; > +} > static DEVICE_ATTR_RW(brightness); > > static ssize_t type_show(struct device *dev, struct device_attribute *attr, > @@ -380,7 +388,7 @@ struct backlight_device *backlight_device_register(const char *name, > } > EXPORT_SYMBOL(backlight_device_register); > > -bool backlight_device_registered(enum backlight_type type) > +struct backlight_device *backlight_device_get_by_type(enum backlight_type type) > { > bool found = false; > struct backlight_device *bd; > @@ -394,7 +402,13 @@ bool backlight_device_registered(enum backlight_type type) > } > mutex_unlock(&backlight_dev_list_mutex); > > - return found; > + return found ? bd : NULL; > +} > +EXPORT_SYMBOL(backlight_device_get_by_type); > + > +bool backlight_device_registered(enum backlight_type type) > +{ > + return backlight_device_get_by_type(type) ? true : false; > } > EXPORT_SYMBOL(backlight_device_registered); > > diff --git a/include/acpi/video.h b/include/acpi/video.h > index 843ef1adfbfa..956300d2f214 100644 > --- a/include/acpi/video.h > +++ b/include/acpi/video.h > @@ -3,6 +3,19 @@ > > #include <linux/errno.h> /* for ENODEV */ > > +struct acpi_video_brightness_flags { > + u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ > + u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ > + u8 _BQC_use_index:1; /* _BQC returns an index value */ > +}; > + > +struct acpi_video_device_brightness { > + int curr; > + int count; > + int *levels; > + struct acpi_video_brightness_flags flags; > +}; > + > struct acpi_device; > > #define ACPI_VIDEO_CLASS "video" > @@ -22,6 +35,8 @@ extern void acpi_video_unregister(void); > extern void acpi_video_unregister_backlight(void); > extern int acpi_video_get_edid(struct acpi_device *device, int type, > int device_id, void **edid); > +extern int acpi_video_get_levels(struct acpi_device *device, > + struct acpi_video_device_brightness **dev_br); > extern bool acpi_video_verify_backlight_support(void); > #else > static inline int acpi_video_register(void) { return 0; } > @@ -32,6 +47,11 @@ static inline int acpi_video_get_edid(struct acpi_device *device, int type, > { > return -ENODEV; > } > +static int acpi_video_get_levels(struct acpi_device *device, > + struct acpi_video_device_brightness **dev_br) > +{ > + return -ENODEV; > +} > static inline bool acpi_video_verify_backlight_support(void) { return false; } > #endif > > diff --git a/include/linux/backlight.h b/include/linux/backlight.h > index adb14a8616df..c59a020df3f8 100644 > --- a/include/linux/backlight.h > +++ b/include/linux/backlight.h > @@ -140,6 +140,8 @@ extern void backlight_force_update(struct backlight_device *bd, > extern bool backlight_device_registered(enum backlight_type type); > extern int backlight_register_notifier(struct notifier_block *nb); > extern int backlight_unregister_notifier(struct notifier_block *nb); > +extern struct backlight_device *backlight_device_get_by_type(enum backlight_type type); > +extern int backlight_device_set_brightness(struct backlight_device *bd, int brightness); > > #define to_backlight_device(obj) container_of(obj, struct backlight_device, dev) > -- To unsubscribe from this list: send the line "unsubscribe linux-next" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html