diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
| commit | cf9610f9116d55c5a66ef9f1faecacabd93a9322 (patch) | |
| tree | 757a3d280760c654767631cab5ad8af2d86d1848 /drivers/input/input-leds.c | |
| download | linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.tar.gz linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.zip | |
Merge tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrlgrafted
Pull pin control updates from Linus Walleij:
"Core changes:
- Use the non-blocking mux_state_try_select() in the generic MUX pin
control back-end
- Free pin maps on pinctrl_generic_to_map() failure in the core
helpers
New hardware pin control:
- Qualcomm Maili TLMM SoC, PMG1110 PMIC, and Eliza LPASS LPI TLMM SoC
(this is for the low power audio, LPASS = Low Power Audio SubSystem
portions)
- Upboard support on top of the Intel pin controller
- Apple T6030 and T6031 support (just compatible strings)
- Samsung Exynos 8855 SoC
- Mediatek MT6858 SoC
- Rockchip RK3308B and RV1106 SoCs
- Airoha EN7523 and AN7563 SoCs
Improvements:
- Qualcomm TLMM GPIOs are unconditionally marked as wakeup capable
- Qualcomm improvements on top of the Qualcomm PDC (power domain
controller) irqchip improvements from tglx:s tree
- Qualcomm IPQ806x and IPQ9650 non-urgent pin grop fixups
- The NPCM8xx driver has been put into shape fixing many pin and
group definitions
- Some non-urgent Tegra 264 pin assignments are fixed up
- Some non-urgent fixups to Airoha AN7581 and AN7583 registers and
pin assignments, missing features etc"
* tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (130 commits)
dt-bindings: pinctrl: Convert TI DA850 pupd to DT schema
pinctrl: meson: sync some modify from A4
pinctrl: meson: a4: Add input enable pin configuration
pinctrl: realtek: rtd1625: remove unused group name spdif_sel
pinctrl: realtek: rtd1625: fix base_bit for VE4 GPIO 13
dt-bindings: pinctrl: microchip,pic32mzda-pinctrl: Convert to DT schema
Revert "Merge branch 'ib-rsk7204' into devel"
pinctrl: rockchip: Add RV1106 pinctrl support
dt-bindings: pinctrl: rockchip: Add RV1106 compatible
pinctrl: rockchip: Decode drive strength in the get function
pinctrl: fix PINCTRL_GENERIC_MUX not always being selectable
pinctrl: airoha: add support of an7563 SoC
dt-bindings: pinctrl: airoha: add support of an7563 pin controller
pinctrl: airoha: try to find chip scu node by phandle first
pinctrl: airoha: add support of en7523 SoC
dt-bindings: pinctrl: airoha: add support of en7523 pin controller
pinctrl: airoha: an7583: add support for olt pinmux
pinctrl: airoha: an7583: add support for pon_alt pinmux
pinctrl: airoha: an7583: add support for npu_uart pinmux
dt-bindings: pinctrl: airoha: an7583: add missed features
...
Diffstat (limited to 'drivers/input/input-leds.c')
| -rw-r--r-- | drivers/input/input-leds.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c new file mode 100644 index 000000000..b08d1d08d --- /dev/null +++ b/drivers/input/input-leds.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LED support for the input layer + * + * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/input.h> + +#if IS_ENABLED(CONFIG_VT) +#define VT_TRIGGER(_name) .trigger = _name +#else +#define VT_TRIGGER(_name) .trigger = NULL +#endif + +#if IS_ENABLED(CONFIG_SND_CTL_LED) +#define AUDIO_TRIGGER(_name) .trigger = _name +#else +#define AUDIO_TRIGGER(_name) .trigger = NULL +#endif + +static const struct { + const char *name; + const char *trigger; +} input_led_info[LED_CNT] = { + [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") }, + [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") }, + [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") }, + [LED_COMPOSE] = { "compose" }, + [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, + [LED_SLEEP] = { "sleep" } , + [LED_SUSPEND] = { "suspend" }, + [LED_MUTE] = { "mute", AUDIO_TRIGGER("audio-mute") }, + [LED_MISC] = { "misc" }, + [LED_MAIL] = { "mail" }, + [LED_CHARGING] = { "charging" }, +}; + +struct input_led { + struct led_classdev cdev; + struct input_handle *handle; + unsigned int code; /* One of LED_* constants */ +}; + +struct input_leds { + struct input_handle handle; + unsigned int num_leds; + struct input_led leds[] __counted_by(num_leds); +}; + +static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + struct input_dev *input = led->handle->dev; + + return test_bit(led->code, input->led) ? cdev->max_brightness : 0; +} + +static void input_leds_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + + input_inject_event(led->handle, EV_LED, led->code, !!brightness); +} + +static void input_leds_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ +} + +static int input_leds_get_count(struct input_dev *dev) +{ + unsigned int led_code; + int count = 0; + + for_each_set_bit(led_code, dev->ledbit, LED_CNT) + if (input_led_info[led_code].name) + count++; + + return count; +} + +static int input_leds_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_leds *leds; + struct input_led *led; + unsigned int num_leds; + unsigned int led_code; + int led_no; + int error; + + num_leds = input_leds_get_count(dev); + if (!num_leds) + return -ENXIO; + + leds = kzalloc_flex(*leds, leds, num_leds); + if (!leds) + return -ENOMEM; + + leds->num_leds = num_leds; + + leds->handle.dev = dev; + leds->handle.handler = handler; + leds->handle.name = "leds"; + leds->handle.private = leds; + + error = input_register_handle(&leds->handle); + if (error) + goto err_free_mem; + + error = input_open_device(&leds->handle); + if (error) + goto err_unregister_handle; + + led_no = 0; + for_each_set_bit(led_code, dev->ledbit, LED_CNT) { + if (!input_led_info[led_code].name) + continue; + + led = &leds->leds[led_no]; + led->handle = &leds->handle; + led->code = led_code; + + led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", + dev_name(&dev->dev), + input_led_info[led_code].name); + if (!led->cdev.name) { + error = -ENOMEM; + goto err_unregister_leds; + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_get = input_leds_brightness_get; + led->cdev.brightness_set = input_leds_brightness_set; + led->cdev.default_trigger = input_led_info[led_code].trigger; + + error = led_classdev_register(&dev->dev, &led->cdev); + if (error) { + dev_err(&dev->dev, "failed to register LED %s: %d\n", + led->cdev.name, error); + kfree(led->cdev.name); + goto err_unregister_leds; + } + + led_no++; + } + + return 0; + +err_unregister_leds: + while (--led_no >= 0) { + struct input_led *led = &leds->leds[led_no]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(&leds->handle); + +err_unregister_handle: + input_unregister_handle(&leds->handle); + +err_free_mem: + kfree(leds); + return error; +} + +static void input_leds_disconnect(struct input_handle *handle) +{ + struct input_leds *leds = handle->private; + int i; + + for (i = 0; i < leds->num_leds; i++) { + struct input_led *led = &leds->leds[i]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(handle); + input_unregister_handle(handle); + + kfree(leds); +} + +static const struct input_device_id input_leds_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_LED) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, input_leds_ids); + +static struct input_handler input_leds_handler = { + .event = input_leds_event, + .connect = input_leds_connect, + .disconnect = input_leds_disconnect, + .name = "leds", + .id_table = input_leds_ids, +}; + +static int __init input_leds_init(void) +{ + return input_register_handler(&input_leds_handler); +} +module_init(input_leds_init); + +static void __exit input_leds_exit(void) +{ + input_unregister_handler(&input_leds_handler); +} +module_exit(input_leds_exit); + +MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_DESCRIPTION("Input -> LEDs Bridge"); +MODULE_LICENSE("GPL v2"); |
