Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: Add driver for Acer Iconia Tab A500

Acer Iconia Tab A500 is an Android tablet device which has two LEDs
embedded into the Power Button. Orange LED indicates "battery charging"
status and white LED indicates "wake-up/charge-done" status. The new LED
driver provides control over both LEDs to userspace.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Pavel Machek <pavel@ucw.cz>

authored by

Dmitry Osipenko and committed by
Pavel Machek
1ece06ac c4241abc

+137
+7
drivers/leds/Kconfig
··· 921 921 This option enables support for the SGM3140 500mA Buck/Boost Charge 922 922 Pump LED Driver. 923 923 924 + config LEDS_ACER_A500 925 + tristate "Power button LED support for Acer Iconia Tab A500" 926 + depends on LEDS_CLASS && MFD_ACER_A500_EC 927 + help 928 + This option enables support for the Power Button LED of 929 + Acer Iconia Tab A500. 930 + 924 931 comment "LED Triggers" 925 932 source "drivers/leds/trigger/Kconfig" 926 933
+1
drivers/leds/Makefile
··· 10 10 # LED Platform Drivers (keep this sorted, M-| sort) 11 11 obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o 12 12 obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o 13 + obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o 13 14 obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o 14 15 obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o 15 16 obj-$(CONFIG_LEDS_APU) += leds-apu.o
+129
drivers/leds/leds-acer-a500.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + 3 + #include <linux/leds.h> 4 + #include <linux/module.h> 5 + #include <linux/platform_device.h> 6 + #include <linux/regmap.h> 7 + 8 + #define A500_EC_LED_DELAY_USEC (100 * 1000) 9 + 10 + enum { 11 + REG_RESET_LEDS = 0x40, 12 + REG_POWER_LED_ON = 0x42, 13 + REG_CHARGE_LED_ON = 0x43, 14 + REG_ANDROID_LEDS_OFF = 0x5a, 15 + }; 16 + 17 + struct a500_led { 18 + struct led_classdev cdev; 19 + const struct reg_sequence *enable_seq; 20 + struct a500_led *other; 21 + struct regmap *rmap; 22 + }; 23 + 24 + static const struct reg_sequence a500_ec_leds_reset_seq[] = { 25 + REG_SEQ(REG_RESET_LEDS, 0x0, A500_EC_LED_DELAY_USEC), 26 + REG_SEQ(REG_ANDROID_LEDS_OFF, 0x0, A500_EC_LED_DELAY_USEC), 27 + }; 28 + 29 + static const struct reg_sequence a500_ec_white_led_enable_seq[] = { 30 + REG_SEQ(REG_POWER_LED_ON, 0x0, A500_EC_LED_DELAY_USEC), 31 + }; 32 + 33 + static const struct reg_sequence a500_ec_orange_led_enable_seq[] = { 34 + REG_SEQ(REG_CHARGE_LED_ON, 0x0, A500_EC_LED_DELAY_USEC), 35 + }; 36 + 37 + static int a500_ec_led_brightness_set(struct led_classdev *led_cdev, 38 + enum led_brightness value) 39 + { 40 + struct a500_led *led = container_of(led_cdev, struct a500_led, cdev); 41 + struct reg_sequence control_seq[2]; 42 + unsigned int num_regs = 1; 43 + 44 + if (value) { 45 + control_seq[0] = led->enable_seq[0]; 46 + } else { 47 + /* 48 + * There is no separate controls which can disable LEDs 49 + * individually, there is only RESET_LEDS command that turns 50 + * off both LEDs. 51 + * 52 + * RESET_LEDS turns off both LEDs, thus restore other LED if 53 + * it's turned ON. 54 + */ 55 + if (led->other->cdev.brightness) 56 + num_regs = 2; 57 + 58 + control_seq[0] = a500_ec_leds_reset_seq[0]; 59 + control_seq[1] = led->other->enable_seq[0]; 60 + } 61 + 62 + return regmap_multi_reg_write(led->rmap, control_seq, num_regs); 63 + } 64 + 65 + static int a500_ec_leds_probe(struct platform_device *pdev) 66 + { 67 + struct a500_led *white_led, *orange_led; 68 + struct regmap *rmap; 69 + int err; 70 + 71 + rmap = dev_get_regmap(pdev->dev.parent, "KB930"); 72 + if (!rmap) 73 + return -EINVAL; 74 + 75 + /* reset and turn off LEDs */ 76 + regmap_multi_reg_write(rmap, a500_ec_leds_reset_seq, 2); 77 + 78 + white_led = devm_kzalloc(&pdev->dev, sizeof(*white_led), GFP_KERNEL); 79 + if (!white_led) 80 + return -ENOMEM; 81 + 82 + white_led->cdev.name = "power:white"; 83 + white_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set; 84 + white_led->cdev.flags = LED_CORE_SUSPENDRESUME; 85 + white_led->cdev.max_brightness = 1; 86 + white_led->enable_seq = a500_ec_white_led_enable_seq; 87 + white_led->rmap = rmap; 88 + 89 + orange_led = devm_kzalloc(&pdev->dev, sizeof(*orange_led), GFP_KERNEL); 90 + if (!orange_led) 91 + return -ENOMEM; 92 + 93 + orange_led->cdev.name = "power:orange"; 94 + orange_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set; 95 + orange_led->cdev.flags = LED_CORE_SUSPENDRESUME; 96 + orange_led->cdev.max_brightness = 1; 97 + orange_led->enable_seq = a500_ec_orange_led_enable_seq; 98 + orange_led->rmap = rmap; 99 + 100 + white_led->other = orange_led; 101 + orange_led->other = white_led; 102 + 103 + err = devm_led_classdev_register(&pdev->dev, &white_led->cdev); 104 + if (err) { 105 + dev_err(&pdev->dev, "failed to register white LED\n"); 106 + return err; 107 + } 108 + 109 + err = devm_led_classdev_register(&pdev->dev, &orange_led->cdev); 110 + if (err) { 111 + dev_err(&pdev->dev, "failed to register orange LED\n"); 112 + return err; 113 + } 114 + 115 + return 0; 116 + } 117 + 118 + static struct platform_driver a500_ec_leds_driver = { 119 + .driver = { 120 + .name = "acer-a500-iconia-leds", 121 + }, 122 + .probe = a500_ec_leds_probe, 123 + }; 124 + module_platform_driver(a500_ec_leds_driver); 125 + 126 + MODULE_DESCRIPTION("LED driver for Acer Iconia Tab A500 Power Button"); 127 + MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); 128 + MODULE_ALIAS("platform:acer-a500-iconia-leds"); 129 + MODULE_LICENSE("GPL");