diff options
author | Linus Torvalds | 2017-05-01 13:12:49 -0700 |
---|---|---|
committer | Linus Torvalds | 2017-05-01 13:12:49 -0700 |
commit | 85724edecbdc19f53ed4b902fc3a32e4d1b61c9b (patch) | |
tree | ec44080accc2ea5571f28feef21488af97ee8d9a | |
parent | 477d7caeede0e3a933368440fc877b12c25dbb6d (diff) | |
parent | 28c5fe99016d28f15d1b825df8acb1558a3a63a1 (diff) |
Merge tag 'leds_for_4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds
Pull LED updates from Jacek Anaszewski:
"New drivers:
- add LED support for MT6323 PMIC
- add LED support for Motorola CPCAP PMIC
New features and improvements:
- add LED trigger for all CPUs aggregated which is useful on tiny
boards with more CPU cores than LED pins
- add OF variants of LED registering functions as a preparation for
adding generic support for Device Tree parsing
- dell-led improvements and cleanups, followed by moving it to the
x86 platform driver subsystem which is a more appropriate place for
it
- extend pca9532 Device Tree support by adding the LEDs
'default-state' property
- extend pca963x Device Tree support by adding nxp,inverted-out
property for inverting the polarity of the output
- remove ACPI support for lp3952 since it relied on a non-official
ACPI IDs"
* tag 'leds_for_4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds:
leds: pca9532: Extend pca9532 device tree support
leds: cpcap: new driver
mfd: cpcap: Add missing include dependencies
leds: lp3952: Use 'if (ret)' pattern
leds: lp3952: Remove ACPI support for lp3952
leds: mt6323: Fix an off by one bug in probe
dt-bindings: leds: Add document bindings for leds-mt6323
leds: Add LED support for MT6323 PMIC
leds: gpio: use OF variant of LED registering function
leds: core: add OF variants of LED registering functions
platform/x86: dell-wmi-led: fix coding style issues
dell-led: move driver to drivers/platform/x86/dell-wmi-led.c
dell-led: remove code related to mic mute LED
platform/x86: dell-laptop: import dell_micmute_led_set() from drivers/leds/dell-led.c
ALSA: hda - rename dell_led_set_func to dell_micmute_led_set_func
ALSA: hda - use dell_micmute_led_set() instead of dell_app_wmi_led_set()
dell-led: remove GUID check from dell_micmute_led_set()
leds/trigger/cpu: Add LED trigger for all CPUs aggregated
-rw-r--r-- | Documentation/devicetree/bindings/leds/leds-cpcap.txt | 29 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/leds/leds-mt6323.txt | 60 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/leds/leds-pca9532.txt | 10 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 27 | ||||
-rw-r--r-- | drivers/leds/Makefile | 3 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 26 | ||||
-rw-r--r-- | drivers/leds/leds-cpcap.c | 239 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 12 | ||||
-rw-r--r-- | drivers/leds/leds-lp3952.c | 18 | ||||
-rw-r--r-- | drivers/leds/leds-mt6323.c | 502 | ||||
-rw-r--r-- | drivers/leds/leds-pca9532.c | 27 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-cpu.c | 33 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 8 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 28 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi-led.c (renamed from drivers/leds/dell-led.c) | 141 | ||||
-rw-r--r-- | include/linux/dell-led.h | 6 | ||||
-rw-r--r-- | include/linux/leds-pca9532.h | 4 | ||||
-rw-r--r-- | include/linux/leds.h | 14 | ||||
-rw-r--r-- | include/linux/mfd/motorola-cpcap.h | 3 | ||||
-rw-r--r-- | sound/pci/hda/dell_wmi_helper.c | 30 |
21 files changed, 1043 insertions, 178 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-cpcap.txt b/Documentation/devicetree/bindings/leds/leds-cpcap.txt new file mode 100644 index 000000000000..ebf7cdc7f70c --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-cpcap.txt @@ -0,0 +1,29 @@ +Motorola CPCAP PMIC LEDs +------------------------ + +This module is part of the CPCAP. For more details about the whole +chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt. + +Requires node properties: +- compatible: should be one of + * "motorola,cpcap-led-mdl" (Main Display Lighting) + * "motorola,cpcap-led-kl" (Keyboard Lighting) + * "motorola,cpcap-led-adl" (Aux Display Lighting) + * "motorola,cpcap-led-red" (Red Triode) + * "motorola,cpcap-led-green" (Green Triode) + * "motorola,cpcap-led-blue" (Blue Triode) + * "motorola,cpcap-led-cf" (Camera Flash) + * "motorola,cpcap-led-bt" (Bluetooth) + * "motorola,cpcap-led-cp" (Camera Privacy LED) +- label: see Documentation/devicetree/bindings/leds/common.txt +- vdd-supply: A phandle to the regulator powering the LED + +Example: + +&cpcap { + cpcap_led_red: red-led { + compatible = "motorola,cpcap-led-red"; + label = "cpcap:red"; + vdd-supply = <&sw5>; + }; +}; diff --git a/Documentation/devicetree/bindings/leds/leds-mt6323.txt b/Documentation/devicetree/bindings/leds/leds-mt6323.txt new file mode 100644 index 000000000000..45bf9f7d85f3 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-mt6323.txt @@ -0,0 +1,60 @@ +Device Tree Bindings for LED support on MT6323 PMIC + +MT6323 LED controller is subfunction provided by MT6323 PMIC, so the LED +controllers are defined as the subnode of the function node provided by MT6323 +PMIC controller that is being defined as one kind of Muti-Function Device (MFD) +using shared bus called PMIC wrapper for each subfunction to access remote +MT6323 PMIC hardware. + +For MT6323 MFD bindings see: +Documentation/devicetree/bindings/mfd/mt6397.txt +For MediaTek PMIC wrapper bindings see: +Documentation/devicetree/bindings/soc/mediatek/pwrap.txt + +Required properties: +- compatible : Must be "mediatek,mt6323-led" +- address-cells : Must be 1 +- size-cells : Must be 0 + +Each led is represented as a child node of the mediatek,mt6323-led that +describes the initial behavior for each LED physically and currently only four +LED child nodes can be supported. + +Required properties for the LED child node: +- reg : LED channel number (0..3) + +Optional properties for the LED child node: +- label : See Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : See Documentation/devicetree/bindings/leds/common.txt +- default-state: See Documentation/devicetree/bindings/leds/common.txt + +Example: + + mt6323: pmic { + compatible = "mediatek,mt6323"; + + ... + + mt6323led: leds { + compatible = "mediatek,mt6323-led"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + label = "LED0"; + linux,default-trigger = "timer"; + default-state = "on"; + }; + led@1 { + reg = <1>; + label = "LED1"; + default-state = "off"; + }; + led@2 { + reg = <2>; + label = "LED2"; + default-state = "on"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-pca9532.txt b/Documentation/devicetree/bindings/leds/leds-pca9532.txt index 198f3ba0e01f..f769c52e3643 100644 --- a/Documentation/devicetree/bindings/leds/leds-pca9532.txt +++ b/Documentation/devicetree/bindings/leds/leds-pca9532.txt @@ -17,6 +17,8 @@ Optional sub-node properties: - label: see Documentation/devicetree/bindings/leds/common.txt - type: Output configuration, see dt-bindings/leds/leds-pca9532.h (default NONE) - linux,default-trigger: see Documentation/devicetree/bindings/leds/common.txt + - default-state: see Documentation/devicetree/bindings/leds/common.txt + This property is only valid for sub-nodes of type <PCA9532_TYPE_LED>. Example: #include <dt-bindings/leds/leds-pca9532.h> @@ -33,6 +35,14 @@ Example: label = "pca:green:power"; type = <PCA9532_TYPE_LED>; }; + kernel-booting { + type = <PCA9532_TYPE_LED>; + default-state = "on"; + }; + sys-stat { + type = <PCA9532_TYPE_LED>; + default-state = "keep"; // don't touch, was set by U-Boot + }; }; For more product information please see the link below: diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 275f467956ee..6c2999872090 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -76,6 +76,15 @@ config LEDS_BCM6358 This option enables support for LEDs connected to the BCM6358 LED HW controller accessed via MMIO registers. +config LEDS_CPCAP + tristate "LED Support for Motorola CPCAP" + depends on LEDS_CLASS + depends on MFD_CPCAP + depends on OF + help + This option enables support for LEDs offered by Motorola's + CPCAP PMIC. + config LEDS_LM3530 tristate "LCD Backlight driver for LM3530" depends on LEDS_CLASS @@ -126,6 +135,14 @@ config LEDS_MIKROTIK_RB532 This option enables support for the so called "User LED" of Mikrotik's Routerboard 532. +config LEDS_MT6323 + tristate "LED Support for Mediatek MT6323 PMIC" + depends on LEDS_CLASS + depends on MFD_MT6397 + help + This option enables support for on-chip LED drivers found on + Mediatek MT6323 PMIC. + config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS @@ -241,7 +258,6 @@ config LEDS_LP3952 tristate "LED Support for TI LP3952 2 channel LED driver" depends on LEDS_CLASS depends on I2C - depends on ACPI depends on GPIOLIB select REGMAP_I2C help @@ -463,15 +479,6 @@ config LEDS_ADP5520 To compile this driver as a module, choose M here: the module will be called leds-adp5520. -config LEDS_DELL_NETBOOKS - tristate "External LED on Dell Business Netbooks" - depends on LEDS_CLASS - depends on X86 && ACPI_WMI - depends on DELL_SMBIOS - help - This adds support for the Latitude 2100 and similar - notebooks that have an external LED. - config LEDS_MC13783 tristate "LED Support for MC13XXX PMIC" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 6b8273736478..45f133962ed8 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o +obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o @@ -52,7 +53,6 @@ obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o -obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o @@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o +obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f2b0a80a62b4..b0e2d55acbd6 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -244,11 +244,14 @@ static int led_classdev_next_name(const char *init_name, char *name, } /** - * led_classdev_register - register a new object of led_classdev class. - * @parent: The device to register. + * of_led_classdev_register - register a new object of led_classdev class. + * + * @parent: parent of LED device * @led_cdev: the led_classdev structure for this device. + * @np: DT node describing this LED */ -int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) +int of_led_classdev_register(struct device *parent, struct device_node *np, + struct led_classdev *led_cdev) { char name[LED_MAX_NAME_SIZE]; int ret; @@ -261,6 +264,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_cdev, led_cdev->groups, "%s", name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); + led_cdev->dev->of_node = np; if (ret) dev_warn(parent, "Led %s renamed to %s due to name collision", @@ -303,7 +307,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) return 0; } -EXPORT_SYMBOL_GPL(led_classdev_register); +EXPORT_SYMBOL_GPL(of_led_classdev_register); /** * led_classdev_unregister - unregisters a object of led_properties class. @@ -348,12 +352,14 @@ static void devm_led_classdev_release(struct device *dev, void *res) } /** - * devm_led_classdev_register - resource managed led_classdev_register() - * @parent: The device to register. + * devm_of_led_classdev_register - resource managed led_classdev_register() + * + * @parent: parent of LED device * @led_cdev: the led_classdev structure for this device. */ -int devm_led_classdev_register(struct device *parent, - struct led_classdev *led_cdev) +int devm_of_led_classdev_register(struct device *parent, + struct device_node *np, + struct led_classdev *led_cdev) { struct led_classdev **dr; int rc; @@ -362,7 +368,7 @@ int devm_led_classdev_register(struct device *parent, if (!dr) return -ENOMEM; - rc = led_classdev_register(parent, led_cdev); + rc = of_led_classdev_register(parent, np, led_cdev); if (rc) { devres_free(dr); return rc; @@ -373,7 +379,7 @@ int devm_led_classdev_register(struct device *parent, return 0; } -EXPORT_SYMBOL_GPL(devm_led_classdev_register); +EXPORT_SYMBOL_GPL(devm_of_led_classdev_register); static int devm_led_classdev_match(struct device *dev, void *res, void *data) { diff --git a/drivers/leds/leds-cpcap.c b/drivers/leds/leds-cpcap.c new file mode 100644 index 000000000000..f0f28c442807 --- /dev/null +++ b/drivers/leds/leds-cpcap.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2017 Sebastian Reichel <sre@kernel.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * later as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/leds.h> +#include <linux/mfd/motorola-cpcap.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#define CPCAP_LED_NO_CURRENT 0x0001 + +struct cpcap_led_info { + u16 reg; + u16 mask; + u16 limit; + u16 init_mask; + u16 init_val; +}; + +static const struct cpcap_led_info cpcap_led_red = { + .reg = CPCAP_REG_REDC, + .mask = 0x03FF, + .limit = 31, +}; + +static const struct cpcap_led_info cpcap_led_green = { + .reg = CPCAP_REG_GREENC, + .mask = 0x03FF, + .limit = 31, +}; + +static const struct cpcap_led_info cpcap_led_blue = { + .reg = CPCAP_REG_BLUEC, + .mask = 0x03FF, + .limit = 31, +}; + +/* aux display light */ +static const struct cpcap_led_info cpcap_led_adl = { + .reg = CPCAP_REG_ADLC, + .mask = 0x000F, + .limit = 1, + .init_mask = 0x7FFF, + .init_val = 0x5FF0, +}; + +/* camera privacy led */ +static const struct cpcap_led_info cpcap_led_cp = { + .reg = CPCAP_REG_CLEDC, + .mask = 0x0007, + .limit = 1, + .init_mask = 0x03FF, + .init_val = 0x0008, +}; + +struct cpcap_led { + struct led_classdev led; + const struct cpcap_led_info *info; + struct device *dev; + struct regmap *regmap; + struct mutex update_lock; + struct regulator *vdd; + bool powered; + + u32 current_limit; +}; + +static u16 cpcap_led_val(u8 current_limit, u8 duty_cycle) +{ + current_limit &= 0x1f; /* 5 bit */ + duty_cycle &= 0x0f; /* 4 bit */ + + return current_limit << 4 | duty_cycle; +} + +static int cpcap_led_set_power(struct cpcap_led *led, bool status) +{ + int err; + + if (status == led->powered) + return 0; + + if (status) + err = regulator_enable(led->vdd); + else + err = regulator_disable(led->vdd); + + if (err) { + dev_err(led->dev, "regulator failure: %d", err); + return err; + } + + led->powered = status; + + return 0; +} + +static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value) +{ + struct cpcap_led *led = container_of(ledc, struct cpcap_led, led); + int brightness; + int err; + + mutex_lock(&led->update_lock); + + if (value > LED_OFF) { + err = cpcap_led_set_power(led, true); + if (err) + goto exit; + } + + if (value == LED_OFF) { + /* Avoid HW issue by turning off current before duty cycle */ + err = regmap_update_bits(led->regmap, + led->info->reg, led->info->mask, CPCAP_LED_NO_CURRENT); + if (err) { + dev_err(led->dev, "regmap failed: %d", err); + goto exit; + } + + brightness = cpcap_led_val(value, LED_OFF); + } else { + brightness = cpcap_led_val(value, LED_ON); + } + + err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask, + brightness); + if (err) { + dev_err(led->dev, "regmap failed: %d", err); + goto exit; + } + + if (value == LED_OFF) { + err = cpcap_led_set_power(led, false); + if (err) + goto exit; + } + +exit: + mutex_unlock(&led->update_lock); + return err; +} + +static const struct of_device_id cpcap_led_of_match[] = { + { .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red }, + { .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green }, + { .compatible = "motorola,cpcap-led-blue", .data = &cpcap_led_blue }, + { .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl }, + { .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpcap_led_of_match); + +static int cpcap_led_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct cpcap_led *led; + int err; + + match = of_match_device(of_match_ptr(cpcap_led_of_match), &pdev->dev); + if (!match || !match->data) + return -EINVAL; + + led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + platform_set_drvdata(pdev, led); + led->info = match->data; + led->dev = &pdev->dev; + + if (led->info->reg == 0x0000) { + dev_err(led->dev, "Unsupported LED"); + return -ENODEV; + } + + led->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!led->regmap) + return -ENODEV; + + led->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(led->vdd)) { + err = PTR_ERR(led->vdd); + dev_err(led->dev, "Couldn't get regulator: %d", err); + return err; + } + + err = device_property_read_string(&pdev->dev, "label", &led->led.name); + if (err) { + dev_err(led->dev, "Couldn't read LED label: %d", err); + return err; + } + + if (led->info->init_mask) { + err = regmap_update_bits(led->regmap, led->info->reg, + led->info->init_mask, led->info->init_val); + if (err) { + dev_err(led->dev, "regmap failed: %d", err); + return err; + } + } + + mutex_init(&led->update_lock); + + led->led.max_brightness = led->info->limit; + led->led.brightness_set_blocking = cpcap_led_set; + err = devm_led_classdev_register(&pdev->dev, &led->led); + if (err) { + dev_err(led->dev, "Couldn't register LED: %d", err); + return err; + } + + return 0; +} + +static struct platform_driver cpcap_led_driver = { + .probe = cpcap_led_probe, + .driver = { + .name = "cpcap-led", + .of_match_table = cpcap_led_of_match, + }, +}; +module_platform_driver(cpcap_led_driver); + +MODULE_DESCRIPTION("CPCAP LED driver"); +MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 066fc7590729..e753ba93ba1e 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -77,7 +77,7 @@ static int gpio_blink_set(struct led_classdev *led_cdev, static int create_gpio_led(const struct gpio_led *template, struct gpio_led_data *led_dat, struct device *parent, - gpio_blink_set_t blink_set) + struct device_node *np, gpio_blink_set_t blink_set) { int ret, state; @@ -139,7 +139,7 @@ static int create_gpio_led(const struct gpio_led *template, if (ret < 0) return ret; - return devm_led_classdev_register(parent, &led_dat->cdev); + return devm_of_led_classdev_register(parent, np, &led_dat->cdev); } struct gpio_leds_priv { @@ -208,7 +208,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) if (fwnode_property_present(child, "panic-indicator")) led.panic_indicator = 1; - ret = create_gpio_led(&led, led_dat, dev, NULL); + ret = create_gpio_led(&led, led_dat, dev, np, NULL); if (ret < 0) { fwnode_handle_put(child); return ERR_PTR(ret); @@ -242,9 +242,9 @@ static int gpio_led_probe(struct platform_device *pdev) priv->num_leds = pdata->num_leds; for (i = 0; i < priv->num_leds; i++) { - ret = create_gpio_led(&pdata->leds[i], - &priv->leds[i], - &pdev->dev, pdata->gpio_blink_set); + ret = create_gpio_led(&pdata->leds[i], &priv->leds[i], + &pdev->dev, NULL, + pdata->gpio_blink_set); if (ret < 0) return ret; } diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 4847e89883a7..847f7f282126 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -10,7 +10,6 @@ * */ -#include <linux/acpi.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/i2c.h> @@ -103,10 +102,11 @@ static int lp3952_get_label(struct device *dev, const char *label, char *dest) const char *str; ret = device_property_read_string(dev, label, &str); - if (!ret) - strncpy(dest, str, LP3952_LABEL_MAX_LEN); + if (ret) + return ret; - return ret; + strncpy(dest, str, LP3952_LABEL_MAX_LEN); + return 0; } static int lp3952_register_led_classdev(struct lp3952_led_array *priv) @@ -276,19 +276,9 @@ static const struct i2c_device_id lp3952_id[] = { }; MODULE_DEVICE_TABLE(i2c, lp3952_id); -#ifdef CONFIG_ACPI -static const struct acpi_device_id lp3952_acpi_match[] = { - {"TXNW3952", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, lp3952_acpi_match); -#endif - static struct i2c_driver lp3952_i2c_driver = { .driver = { .name = LP3952_NAME, - .acpi_match_table = ACPI_PTR(lp3952_acpi_match), }, .probe = lp3952_probe, .remove = lp3952_remove, diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c new file mode 100644 index 000000000000..8893c74e9a1f --- /dev/null +++ b/drivers/leds/leds-mt6323.c @@ -0,0 +1,502 @@ +/* + * LED driver for Mediatek MT6323 PMIC + * + * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/mfd/mt6323/registers.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* + * Register field for MT6323_TOP_CKPDN0 to enable + * 32K clock common for LED device. + */ +#define MT6323_RG_DRV_32K_CK_PDN BIT(11) +#define MT6323_RG_DRV_32K_CK_PDN_MASK BIT(11) + +/* + * Register field for MT6323_TOP_CKPDN2 to enable + * individual clock for LED device. + */ +#define MT6323_RG_ISINK_CK_PDN(i) BIT(i) +#define MT6323_RG_ISINK_CK_PDN_MASK(i) BIT(i) + +/* + * Register field for MT6323_TOP_CKCON1 to select + * clock source. + */ +#define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i)) + +/* + * Register for MT6323_ISINK_CON0 to setup the + * duty cycle of the blink. + */ +#define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i)) +#define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8) +#define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \ + MT6323_ISINK_DIM_DUTY_MASK) + +/* Register to setup the period of the blink. */ +#define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i)) +#define MT6323_ISINK_DIM_FSEL_MASK (0xffff) +#define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK) + +/* Register to control the brightness. */ +#define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i)) +#define MT6323_ISINK_CH_STEP_SHIFT 12 +#define MT6323_ISINK_CH_STEP_MASK (0x7 << 12) +#define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \ + MT6323_ISINK_CH_STEP_MASK) +#define MT6323_ISINK_SFSTR0_TC_MASK (0x3 << 1) +#define MT6323_ISINK_SFSTR0_TC(i) (((i) << 1) & \ + MT6323_ISINK_SFSTR0_TC_MASK) +#define MT6323_ISINK_SFSTR0_EN_MASK BIT(0) +#define MT6323_ISINK_SFSTR0_EN BIT(0) + +/* Register to LED channel enablement. */ +#define MT6323_ISINK_CH_EN_MASK(i) BIT(i) +#define MT6323_ISINK_CH_EN(i) BIT(i) + +#define MT6323_MAX_PERIOD 10000 +#define MT6323_MAX_LEDS 4 +#define MT6323_MAX_BRIGHTNESS 6 +#define MT6323_UNIT_DUTY 3125 +#define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\ + (p) * MT6323_UNIT_DUTY) + +struct mt6323_leds; + +/** + * struct mt6323_led - state container for the LED device + * @id: the identifier in MT6323 LED device + * @parent: the pointer to MT6323 LED controller + * @cdev: LED class device for this LED device + * @current_brightness: current state of the LED device + */ +struct mt6323_led { + int id; + struct mt6323_leds *parent; + struct led_classdev cdev; + enum led_brightness current_brightness; +}; + +/** + * struct mt6323_leds - state container for holding LED controller + * of the driver + * @dev: the device pointer + * @hw: the underlying hardware providing shared + * bus for the register operations + * @lock: the lock among process context + * @led: the array that contains the state of individual + * LED device + */ +struct mt6323_leds { + struct device *dev; + struct mt6397_chip *hw; + /* protect among process context */ + struct mutex lock; + struct mt6323_led *led[MT6323_MAX_LEDS]; +}; + +static int mt6323_led_hw_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + struct regmap *regmap = leds->hw->regmap; + u32 con2_mask = 0, con2_val = 0; + int ret; + + /* + * Setup current output for the corresponding + * brightness level. + */ + con2_mask |= MT6323_ISINK_CH_STEP_MASK | + MT6323_ISINK_SFSTR0_TC_MASK | + MT6323_ISINK_SFSTR0_EN_MASK; + con2_val |= MT6323_ISINK_CH_STEP(brightness - 1) | + MT6323_ISINK_SFSTR0_TC(2) | + MT6323_ISINK_SFSTR0_EN; + + ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id), + con2_mask, con2_val); + return ret; +} + +static int mt6323_led_hw_off(struct led_classdev *cdev) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + struct regmap *regmap = leds->hw->regmap; + unsigned int status; + int ret; + + status = MT6323_ISINK_CH_EN(led->id); + ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, + MT6323_ISINK_CH_EN_MASK(led->id), ~status); + if (ret < 0) + return ret; + + usleep_range(100, 300); + ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, + MT6323_RG_ISINK_CK_PDN_MASK(led->id), + MT6323_RG_ISINK_CK_PDN(led->id)); + if (ret < 0) + return ret; + + return 0; +} + +static enum led_brightness +mt6323_get_led_hw_brightness(struct led_classdev *cdev) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + struct regmap *regmap = leds->hw->regmap; + unsigned int status; + int ret; + + ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status); + if (ret < 0) + return ret; + + if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id)) + return 0; + + ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status); + if (ret < 0) + return ret; + + if (!(status & MT6323_ISINK_CH_EN(led->id))) + return 0; + + ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status); + if (ret < 0) + return ret; + + return ((status & MT6323_ISINK_CH_STEP_MASK) + >> MT6323_ISINK_CH_STEP_SHIFT) + 1; +} + +static int mt6323_led_hw_on(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + struct regmap *regmap = leds->hw->regmap; + unsigned int status; + int ret; + + /* + * Setup required clock source, enable the corresponding + * clock and channel and let work with continuous blink as + * the default. + */ + ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1, + MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0); + if (ret < 0) + return ret; + + status = MT6323_RG_ISINK_CK_PDN(led->id); + ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, + MT6323_RG_ISINK_CK_PDN_MASK(led->id), + ~status); + if (ret < 0) + return ret; + + usleep_range(100, 300); + + ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, + MT6323_ISINK_CH_EN_MASK(led->id), + MT6323_ISINK_CH_EN(led->id)); + if (ret < 0) + return ret; + + ret = mt6323_led_hw_brightness(cdev, brightness); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), + MT6323_ISINK_DIM_DUTY_MASK, + MT6323_ISINK_DIM_DUTY(31)); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), + MT6323_ISINK_DIM_FSEL_MASK, + MT6323_ISINK_DIM_FSEL(1000)); + if (ret < 0) + return ret; + + return 0; +} + +static int mt6323_led_set_blink(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + struct regmap *regmap = leds->hw->regmap; + unsigned long period; + u8 duty_hw; + int ret; + + /* + * Units are in ms, if over the hardware able + * to support, fallback into software blink + */ + period = *delay_on + *delay_off; + + if (period > MT6323_MAX_PERIOD) + return -EINVAL; + + /* + * LED subsystem requires a default user + * friendly blink pattern for the LED so using + * 1Hz duty cycle 50% here if without specific + * value delay_on and delay off being assigned. + */ + if (!*delay_on && !*delay_off) { + *delay_on = 500; + *delay_off = 500; + } + + /* + * Calculate duty_hw based on the percentage of period during + * which the led is ON. + */ + duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period); + + /* hardware doesn't support zero duty cycle. */ + if (!duty_hw) + return -EINVAL; + + mutex_lock(&leds->lock); + /* + * Set max_brightness as the software blink behavior + * when no blink brightness. + */ + if (!led->current_brightness) { + ret = mt6323_led_hw_on(cdev, cdev->max_brightness); + if (ret < 0) + goto out; + led->current_brightness = cdev->max_brightness; + } + + ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), + MT6323_ISINK_DIM_DUTY_MASK, + MT6323_ISINK_DIM_DUTY(duty_hw - 1)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), + MT6323_ISINK_DIM_FSEL_MASK, + MT6323_ISINK_DIM_FSEL(period - 1)); +out: + mutex_unlock(&leds->lock); + + return ret; +} + +static int mt6323_led_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + struct mt6323_leds *leds = led->parent; + int ret; + + mutex_lock(&leds->lock); + + if (!led->current_brightness && brightness) { + ret = mt6323_led_hw_on(cdev, brightness); + if (ret < 0) + goto out; + } else if (brightness) { + ret = mt6323_led_hw_brightness(cdev, brightness); + if (ret < 0) + goto out; + } else { + ret = mt6323_led_hw_off(cdev); + if (ret < 0) + goto out; + } + + led->current_brightness = brightness; +out: + mutex_unlock(&leds->lock); + + return ret; +} + +static int mt6323_led_set_dt_default(struct led_classdev *cdev, + struct device_node *np) +{ + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); + const char *state; + int ret = 0; + + led->cdev.name = of_get_property(np, "label", NULL) ? : np->name; + led->cdev.default_trigger = of_get_property(np, + "linux,default-trigger", + NULL); + + state = of_get_property(np, "default-state", NULL); + if (state) { + if (!strcmp(state, "keep")) { + ret = mt6323_get_led_hw_brightness(cdev); + if (ret < 0) + return ret; + led->current_brightness = ret; + ret = 0; + } else if (!strcmp(state, "on")) { + ret = + mt6323_led_set_brightness(cdev, cdev->max_brightness); + } else { + ret = mt6323_led_set_brightness(cdev, LED_OFF); + } + } + + return ret; +} + +static int mt6323_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct mt6397_chip *hw = dev_get_drvdata(pdev->dev.parent); + struct mt6323_leds *leds; + struct mt6323_led *led; + int ret; + unsigned int status; + u32 reg; + + leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); + if (!leds) + return -ENOMEM; + + platform_set_drvdata(pdev, leds); + leds->dev = dev; + + /* + * leds->hw points to the underlying bus for the register + * controlled. + */ + leds->hw = hw; + mutex_init(&leds->lock); + + status = MT6323_RG_DRV_32K_CK_PDN; + ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, + MT6323_RG_DRV_32K_CK_PDN_MASK, ~status); + if (ret < 0) { + dev_err(leds->dev, + "Failed to update MT6323_TOP_CKPDN0 Register\n"); + return ret; + } + + for_each_available_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, "Failed to read led 'reg' property\n"); + goto put_child_node; + } + + if (reg >= MT6323_MAX_LEDS || leds->led[reg]) { + dev_err(dev, "Invalid led reg %u\n", reg); + ret = -EINVAL; + goto put_child_node; + } + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) { + ret = -ENOMEM; + goto put_child_node; + } + + leds->led[reg] = led; + leds->led[reg]->id = reg; + leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS; + leds->led[reg]->cdev.brightness_set_blocking = + mt6323_led_set_brightness; + leds->led[reg]->cdev.blink_set = mt6323_led_set_blink; + leds->led[reg]->cdev.brightness_get = + mt6323_get_led_hw_brightness; + leds->led[reg]->parent = leds; + + ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child); + if (ret < 0) { + dev_err(leds->dev, + "Failed to LED set default from devicetree\n"); + goto put_child_node; + } + + ret = devm_led_classdev_register(dev, &leds->led[reg]->cdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register LED: %d\n", + ret); + goto put_child_node; + } + leds->led[reg]->cdev.dev->of_node = child; + } + + return 0; + +put_child_node: + of_node_put(child); + return ret; +} + +static int mt6323_led_remove(struct platform_device *pdev) +{ + struct mt6323_leds *leds = platform_get_drvdata(pdev); + int i; + + /* Turn the LEDs off on driver removal. */ + for (i = 0 ; leds->led[i] ; i++) + mt6323_led_hw_off(&leds->led[i]->cdev); + + regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, + MT6323_RG_DRV_32K_CK_PDN_MASK, + MT6323_RG_DRV_32K_CK_PDN); + + mutex_destroy(&leds->lock); + + return 0; +} + +static const struct of_device_id mt6323_led_dt_match[] = { + { .compatible = "mediatek,mt6323-led" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); + +static struct platform_driver mt6323_led_driver = { + .probe = mt6323_led_probe, + .remove = mt6323_led_remove, + .driver = { + .name = "mt6323-led", + .of_match_table = mt6323_led_dt_match, + }, +}; + +module_platform_driver(mt6323_led_driver); + +MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC"); +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 06e63106ae1e..7fea18b0c15d 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -254,6 +254,21 @@ static void pca9532_input_work(struct work_struct *work) mutex_unlock(&data->update_lock); } +static enum pca9532_state pca9532_getled(struct pca9532_led *led) +{ + struct i2c_client *client = led->client; + struct pca9532_data *data = i2c_get_clientdata(client); + u8 maxleds = data->chip_info->num_leds; + char reg; + enum pca9532_state ret; + + mutex_lock(&data->update_lock); + reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id)); + ret = reg >> LED_NUM(led->id)/2; + mutex_unlock(&data->update_lock); + return ret; +} + #ifdef CONFIG_LEDS_PCA9532_GPIO static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset) { @@ -366,7 +381,10 @@ static int pca9532_configure(struct i2c_client *client, gpios++; break; case PCA9532_TYPE_LED: - led->state = pled->state; + if (pled->state == PCA9532_KEEP) + led->state = pca9532_getled(led); + else + led->state = pled->state; led->name = pled->name; led->ldev.name = led->name; led->ldev.default_trigger = pled->default_trigger; @@ -456,6 +474,7 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np) const struct of_device_id *match; int devid, maxleds; int i = 0; + const char *state; match = of_match_device(of_pca9532_leds_match, dev); if (!match) @@ -475,6 +494,12 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np) of_property_read_u32(child, "type", &pdata->leds[i].type); of_property_read_string(child, "linux,default-trigger", &pdata->leds[i].default_trigger); + if (!of_property_read_string(child, "default-state", &state)) { + if (!strcmp(state, "on")) + pdata->leds[i].state = PCA9532_ON; + else if (!strcmp(state, "keep")) + pdata->leds[i].state = PCA9532_KEEP; + } if (++i >= maxleds) { of_node_put(child); break; diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c index a41896468cb3..66a626091936 100644 --- a/drivers/leds/trigger/ledtrig-cpu.c +++ b/drivers/leds/trigger/ledtrig-cpu.c @@ -31,12 +31,16 @@ #define MAX_NAME_LEN 8 struct led_trigger_cpu { + bool is_active; char name[MAX_NAME_LEN]; struct led_trigger *_trig; }; static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); +static struct led_trigger *trig_cpu_all; +static atomic_t num_active_cpus = ATOMIC_INIT(0); + /** * ledtrig_cpu - emit a CPU event as a trigger * @evt: CPU event to be emitted @@ -47,26 +51,46 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); void ledtrig_cpu(enum cpu_led_event ledevt) { struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig); + bool is_active = trig->is_active; /* Locate the correct CPU LED */ switch (ledevt) { case CPU_LED_IDLE_END: case CPU_LED_START: /* Will turn the LED on, max brightness */ - led_trigger_event(trig->_trig, LED_FULL); + is_active = true; break; case CPU_LED_IDLE_START: case CPU_LED_STOP: case CPU_LED_HALTED: /* Will turn the LED off */ - led_trigger_event(trig->_trig, LED_OFF); + is_active = false; break; default: /* Will leave the LED as it is */ break; } + + if (is_active != trig->is_active) { + unsigned int active_cpus; + unsigned int total_cpus; + + /* Update trigger state */ + trig->is_active = is_active; + atomic_add(is_active ? 1 : -1, &num_active_cpus); + active_cpus = atomic_read(&num_active_cpus); + total_cpus = num_present_cpus(); + + led_trigger_event(trig->_trig, + is_active ? LED_FULL : LED_OFF); + + + led_trigger_event(trig_cpu_all, + DIV_ROUND_UP(LED_FULL * active_cpus, total_cpus)); + + } } EXPORT_SYMBOL(ledtrig_cpu); @@ -113,6 +137,11 @@ static int __init ledtrig_cpu_init(void) BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); /* + * Registering a trigger for all CPUs. + */ + led_trigger_register_simple("cpu", &trig_cpu_all); + + /* * Registering CPU led trigger for each CPU core here * ignores CPU hotplug, but after this CPU hotplug works * fine with this trigger. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4bc88eb52712..e1bffc9bb194 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -141,6 +141,14 @@ config DELL_WMI_AIO To compile this driver as a module, choose M here: the module will be called dell-wmi-aio. +config DELL_WMI_LED + tristate "External LED on Dell Business Netbooks" + depends on LEDS_CLASS + depends on ACPI_WMI + help + This adds support for the Latitude 2100 and similar + notebooks that have an external LED. + config DELL_SMO8800 tristate "Dell Latitude freefall driver (ACPI SMO88XX)" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 299d0f9e40f7..776b3a7a4984 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o +obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index f57dd282a002..2e237bad4995 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -29,6 +29,7 @@ #include <linux/mm.h> #include <linux/i8042.h> #include <linux/debugfs.h> +#include <linux/dell-led.h> #include <linux/seq_file.h> #include <acpi/video.h> #include "dell-rbtn.h" @@ -42,6 +43,8 @@ #define KBD_LED_AUTO_50_TOKEN 0x02EB #define KBD_LED_AUTO_75_TOKEN 0x02EC #define KBD_LED_AUTO_100_TOKEN 0x02F6 +#define GLOBAL_MIC_MUTE_ENABLE 0x0364 +#define GLOBAL_MIC_MUTE_DISABLE 0x0365 struct quirk_entry { u8 touchpad_led; @@ -1978,6 +1981,31 @@ static void kbd_led_exit(void) led_classdev_unregister(&kbd_led); } +int dell_micmute_led_set(int state) +{ + struct calling_interface_buffer *buffer; + struct calling_interface_token *token; + + if (state == 0) + token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE); + else if (state == 1) + token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE); + else + return -EINVAL; + + if (!token) + return -ENODEV; + + buffer = dell_smbios_get_buffer(); + buffer->input[0] = token->location; + buffer->input[1] = token->value; + dell_smbios_send_request(1, 0); + dell_smbios_release_buffer(); + + return state; +} +EXPORT_SYMBOL_GPL(dell_micmute_led_set); + static int __init dell_init(void) { struct calling_interface_buffer *buffer; diff --git a/drivers/leds/dell-led.c b/drivers/platform/x86/dell-wmi-led.c index b3d6e9c15cf9..a0c7e99530ef 100644 --- a/drivers/leds/dell-led.c +++ b/drivers/platform/x86/dell-wmi-led.c @@ -1,6 +1,4 @@ /* - * dell_led.c - Dell LED Driver - * * Copyright (C) 2010 Dell Inc. * Louis Davis <louis_davis@dell.com> * Jim Dailey <jim_dailey@dell.com> @@ -15,16 +13,12 @@ #include <linux/leds.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/dmi.h> -#include <linux/dell-led.h> -#include "../platform/x86/dell-smbios.h" MODULE_AUTHOR("Louis Davis/Jim Dailey"); MODULE_DESCRIPTION("Dell LED Control Driver"); MODULE_LICENSE("GPL"); #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396" -#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); /* Error Result Codes: */ @@ -43,53 +37,6 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); #define CMD_LED_OFF 17 #define CMD_LED_BLINK 18 -#define GLOBAL_MIC_MUTE_ENABLE 0x364 -#define GLOBAL_MIC_MUTE_DISABLE 0x365 - -static int dell_micmute_led_set(int state) -{ - struct calling_interface_buffer *buffer; - struct calling_interface_token *token; - - if (!wmi_has_guid(DELL_APP_GUID)) - return -ENODEV; - - if (state == 0) - token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE); - else if (state == 1) - token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE); - else - return -EINVAL; - - if (!token) - return -ENODEV; - - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = token->value; - dell_smbios_send_request(1, 0); - dell_smbios_release_buffer(); - - return state; -} - -int dell_app_wmi_led_set(int whichled, int on) -{ - int state = 0; - - switch (whichled) { - case DELL_LED_MICMUTE: - state = dell_micmute_led_set(on); - break; - default: - pr_warn("led type %x is not supported\n", whichled); - break; - } - - return state; -} -EXPORT_SYMBOL_GPL(dell_app_wmi_led_set); - struct bios_args { unsigned char length; unsigned char result_code; @@ -99,37 +46,29 @@ struct bios_args { unsigned char off_time; }; -static int dell_led_perform_fn(u8 length, - u8 result_code, - u8 device_id, - u8 command, - u8 on_time, - u8 off_time) +static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id, + u8 command, u8 on_time, u8 off_time) { - struct bios_args *bios_return; - u8 return_code; - union acpi_object *obj; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct bios_args *bios_return; struct acpi_buffer input; + union acpi_object *obj; acpi_status status; + u8 return_code; - struct bios_args args; - args.length = length; - args.result_code = result_code; - args.device_id = device_id; - args.command = command; - args.on_time = on_time; - args.off_time = off_time; + struct bios_args args = { + .length = length, + .result_code = result_code, + .device_id = device_id, + .command = command, + .on_time = on_time, + .off_time = off_time + }; input.length = sizeof(struct bios_args); input.pointer = &args; - status = wmi_evaluate_method(DELL_LED_BIOS_GUID, - 1, - 1, - &input, - &output); - + status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 1, 1, &input, &output); if (ACPI_FAILURE(status)) return status; @@ -137,7 +76,7 @@ static int dell_led_perform_fn(u8 length, if (!obj) return -EINVAL; - else if (obj->type != ACPI_TYPE_BUFFER) { + if (obj->type != ACPI_TYPE_BUFFER) { kfree(obj); return -EINVAL; } @@ -170,8 +109,7 @@ static int led_off(void) 0); /* not used */ } -static int led_blink(unsigned char on_eighths, - unsigned char off_eighths) +static int led_blink(unsigned char on_eighths, unsigned char off_eighths) { return dell_led_perform_fn(5, /* Length of command */ INTERFACE_ERROR, /* Init to INTERFACE_ERROR */ @@ -182,7 +120,7 @@ static int led_blink(unsigned char on_eighths, } static void dell_led_set(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { if (value == LED_OFF) led_off(); @@ -191,27 +129,22 @@ static void dell_led_set(struct led_classdev *led_cdev, } static int dell_led_blink(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) + unsigned long *delay_on, unsigned long *delay_off) { unsigned long on_eighths; unsigned long off_eighths; - /* The Dell LED delay is based on 125ms intervals. - Need to round up to next interval. */ + /* + * The Dell LED delay is based on 125ms intervals. + * Need to round up to next interval. + */ - on_eighths = (*delay_on + 124) / 125; - if (0 == on_eighths) - on_eighths = 1; - if (on_eighths > 255) - on_eighths = 255; + on_eighths = DIV_ROUND_UP(*delay_on, 125); + on_eighths = clamp_t(unsigned long, on_eighths, 1, 255); *delay_on = on_eighths * 125; - off_eighths = (*delay_off + 124) / 125; - if (0 == off_eighths) - off_eighths = 1; - if (off_eighths > 255) - off_eighths = 255; + off_eighths = DIV_ROUND_UP(*delay_off, 125); + off_eighths = clamp_t(unsigned long, off_eighths, 1, 255); *delay_off = off_eighths * 125; led_blink(on_eighths, off_eighths); @@ -232,29 +165,21 @@ static int __init dell_led_init(void) { int error = 0; - if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID)) + if (!wmi_has_guid(DELL_LED_BIOS_GUID)) return -ENODEV; - if (wmi_has_guid(DELL_LED_BIOS_GUID)) { - error = led_off(); - if (error != 0) - return -ENODEV; - - error = led_classdev_register(NULL, &dell_led); - } + error = led_off(); + if (error != 0) + return -ENODEV; - return error; + return led_classdev_register(NULL, &dell_led); } static void __exit dell_led_exit(void) { - int error = 0; + led_classdev_unregister(&dell_led); - if (wmi_has_guid(DELL_LED_BIOS_GUID)) { - error = led_off(); - if (error == 0) - led_classdev_unregister(&dell_led); - } + led_off(); } module_init(dell_led_init); diff --git a/include/linux/dell-led.h b/include/linux/dell-led.h index 7009b8bec77b..3f033c48071e 100644 --- a/include/linux/dell-led.h +++ b/include/linux/dell-led.h @@ -1,10 +1,6 @@ #ifndef __DELL_LED_H__ #define __DELL_LED_H__ -enum { - DELL_LED_MICMUTE, -}; - -int dell_app_wmi_led_set(int whichled, int on); +int dell_micmute_led_set(int on); #endif diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h index d215b4561180..5e240b2b4d58 100644 --- a/include/linux/leds-pca9532.h +++ b/include/linux/leds-pca9532.h @@ -22,7 +22,8 @@ enum pca9532_state { PCA9532_OFF = 0x0, PCA9532_ON = 0x1, PCA9532_PWM0 = 0x2, - PCA9532_PWM1 = 0x3 + PCA9532_PWM1 = 0x3, + PCA9532_KEEP = 0xff, }; struct pca9532_led { @@ -44,4 +45,3 @@ struct pca9532_platform_data { }; #endif /* __LINUX_PCA9532_H */ - diff --git a/include/linux/leds.h b/include/linux/leds.h index 38c0bd7ca107..64c56d454f7d 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -122,10 +122,16 @@ struct led_classdev { struct mutex led_access; }; -extern int led_classdev_register(struct device *parent, - struct led_classdev *led_cdev); -extern int devm_led_classdev_register(struct device *parent, - struct led_classdev *led_cdev); +extern int of_led_classdev_register(struct device *parent, + struct device_node *np, + struct led_classdev *led_cdev); +#define led_classdev_register(parent, led_cdev) \ + of_led_classdev_register(parent, NULL, led_cdev) +extern int devm_of_led_classdev_register(struct device *parent, + struct device_node *np, + struct led_classdev *led_cdev); +#define devm_led_classdev_register(parent, led_cdev) \ + devm_of_led_classdev_register(parent, NULL, led_cdev) extern void led_classdev_unregister(struct led_classdev *led_cdev); extern void devm_led_classdev_unregister(struct device *parent, struct led_classdev *led_cdev); diff --git a/include/linux/mfd/motorola-cpcap.h b/include/linux/mfd/motorola-cpcap.h index b4031c2b2214..53758a7d7c32 100644 --- a/include/linux/mfd/motorola-cpcap.h +++ b/include/linux/mfd/motorola-cpcap.h @@ -14,6 +14,9 @@ * published by the Free Software Foundation. */ +#include <linux/device.h> +#include <linux/regmap.h> + #define CPCAP_VENDOR_ST 0 #define CPCAP_VENDOR_TI 1 diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c index 19d41da79f93..7efa7bd7acb2 100644 --- a/sound/pci/hda/dell_wmi_helper.c +++ b/sound/pci/hda/dell_wmi_helper.c @@ -2,11 +2,11 @@ * to be included from codec driver */ -#if IS_ENABLED(CONFIG_LEDS_DELL_NETBOOKS) +#if IS_ENABLED(CONFIG_DELL_LAPTOP) #include <linux/dell-led.h> static int dell_led_value; -static int (*dell_led_set_func)(int, int); +static int (*dell_micmute_led_set_func)(int); static void (*dell_old_cap_hook)(struct hda_codec *, struct snd_kcontrol *, struct snd_ctl_elem_value *); @@ -18,7 +18,7 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec, if (dell_old_cap_hook) dell_old_cap_hook(codec, kcontrol, ucontrol); - if (!ucontrol || !dell_led_set_func) + if (!ucontrol || !dell_micmute_led_set_func) return; if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { /* TODO: How do I verify if it's a mono or stereo here? */ @@ -26,8 +26,8 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec, if (val == dell_led_value) return; dell_led_value = val; - if (dell_led_set_func) - dell_led_set_func(DELL_LED_MICMUTE, dell_led_value); + if (dell_micmute_led_set_func) + dell_micmute_led_set_func(dell_led_value); } } @@ -39,15 +39,15 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, bool removefunc = false; if (action == HDA_FIXUP_ACT_PROBE) { - if (!dell_led_set_func) - dell_led_set_func = symbol_request(dell_app_wmi_led_set); - if (!dell_led_set_func) { - codec_warn(codec, "Failed to find dell wmi symbol dell_app_wmi_led_set\n"); + if (!dell_micmute_led_set_func) + dell_micmute_led_set_func = symbol_request(dell_micmute_led_set); + if (!dell_micmute_led_set_func) { + codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n"); return; } removefunc = true; - if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) { + if (dell_micmute_led_set_func(false) >= 0) { dell_led_value = 0; if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch) codec_dbg(codec, "Skipping micmute LED control due to several ADCs"); @@ -60,17 +60,17 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, } - if (dell_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { - symbol_put(dell_app_wmi_led_set); - dell_led_set_func = NULL; + if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { + symbol_put(dell_micmute_led_set); + dell_micmute_led_set_func = NULL; dell_old_cap_hook = NULL; } } -#else /* CONFIG_LEDS_DELL_NETBOOKS */ +#else /* CONFIG_DELL_LAPTOP */ static void alc_fixup_dell_wmi(struct hda_codec *codec, const struct hda_fixup *fix, int action) { } -#endif /* CONFIG_LEDS_DELL_NETBOOKS */ +#endif /* CONFIG_DELL_LAPTOP */ |