mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
LED updates for 4.12
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJZB3/ZAAoJEL1qUBy3i3wm79YP/0lw2TXXpyrVImo9WjR7SUEB XJCQdjvmbxVfPhyb5qzUIRLImQj4RPUzQ/qwnXmj9wmAKd8tlV/3ldg/xopDZXUd XbDrvzsALIXryh6Ajfx4IMRoI7gQcQM82PBETNej2dS+1d1EUwAgoLQm99D6Q66Y mtxdHsgZZkC/yENoFg4C+RS1+29Mb9lCfIJyug/Ram2aytHqkVfHpMQpp233UWgb kkssbvMGCZJWgCS582w+S2MLFsStb1WYiDZcQt5SJSCnzzkvMEbuwHshL+5mQZpT wquY5rgqIkRPt98In+Qj/rMnXetFzbczdIUEtbyyC+PRPElYgQIehzsS8cdaPSCS 98LruhiRoTeYO1gooeGhgUjZkTDH7xOXsDpvCCXlvcwFCCjoYoKHI6Ti/krWEgo5 RAMz5BRGHvGVEAit6faGbpUT0L643g4onT52e+omcfyvzOmOsGO4BN69O9IrvTvd Jxbcj4Zh0uMZyeBvX2L4cg/9BYROhAvGXiuDr8JwOqvCOwCGrTffrFWCb0hOFGZB /JeCyplCDpLHRu4ybPtE1Ele1ylnbJ9ywCoN+VZkpsIpRD+OK4XHiKFjnt55TVXY GDt75r7oeahPXF9/sD31qzxrKCpBw/wkyEB62AxwbBWF9xo2fAv3q7mNGMQztkUy rKgb4bGIRbGFuevXoo9j =gJJi -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
85724edecb
29
Documentation/devicetree/bindings/leds/leds-cpcap.txt
Normal file
29
Documentation/devicetree/bindings/leds/leds-cpcap.txt
Normal file
@ -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>;
|
||||
};
|
||||
};
|
60
Documentation/devicetree/bindings/leds/leds-mt6323.txt
Normal file
60
Documentation/devicetree/bindings/leds/leds-mt6323.txt
Normal file
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
239
drivers/leds/leds-cpcap.c
Normal file
239
drivers/leds/leds-cpcap.c
Normal file
@ -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");
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
502
drivers/leds/leds-mt6323.c
Normal file
502
drivers/leds/leds-mt6323.c
Normal file
@ -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");
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
@ -112,6 +136,11 @@ static int __init ledtrig_cpu_init(void)
|
||||
/* Supports up to 9999 cpu cores */
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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_off();
|
||||
if (error != 0)
|
||||
return -ENODEV;
|
||||
|
||||
error = led_classdev_register(NULL, &dell_led);
|
||||
}
|
||||
|
||||
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);
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user