162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/leds.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/platform_device.h>
662306a36Sopenharmony_ci#include <linux/regmap.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define A500_EC_LED_DELAY_USEC	(100 * 1000)
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cienum {
1162306a36Sopenharmony_ci	REG_RESET_LEDS = 0x40,
1262306a36Sopenharmony_ci	REG_POWER_LED_ON = 0x42,
1362306a36Sopenharmony_ci	REG_CHARGE_LED_ON = 0x43,
1462306a36Sopenharmony_ci	REG_ANDROID_LEDS_OFF = 0x5a,
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct a500_led {
1862306a36Sopenharmony_ci	struct led_classdev cdev;
1962306a36Sopenharmony_ci	const struct reg_sequence *enable_seq;
2062306a36Sopenharmony_ci	struct a500_led *other;
2162306a36Sopenharmony_ci	struct regmap *rmap;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic const struct reg_sequence a500_ec_leds_reset_seq[] = {
2562306a36Sopenharmony_ci	REG_SEQ(REG_RESET_LEDS, 0x0, A500_EC_LED_DELAY_USEC),
2662306a36Sopenharmony_ci	REG_SEQ(REG_ANDROID_LEDS_OFF, 0x0, A500_EC_LED_DELAY_USEC),
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const struct reg_sequence a500_ec_white_led_enable_seq[] = {
3062306a36Sopenharmony_ci	REG_SEQ(REG_POWER_LED_ON, 0x0, A500_EC_LED_DELAY_USEC),
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct reg_sequence a500_ec_orange_led_enable_seq[] = {
3462306a36Sopenharmony_ci	REG_SEQ(REG_CHARGE_LED_ON, 0x0, A500_EC_LED_DELAY_USEC),
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int a500_ec_led_brightness_set(struct led_classdev *led_cdev,
3862306a36Sopenharmony_ci				      enum led_brightness value)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct a500_led *led = container_of(led_cdev, struct a500_led, cdev);
4162306a36Sopenharmony_ci	struct reg_sequence control_seq[2];
4262306a36Sopenharmony_ci	unsigned int num_regs = 1;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (value) {
4562306a36Sopenharmony_ci		control_seq[0] = led->enable_seq[0];
4662306a36Sopenharmony_ci	} else {
4762306a36Sopenharmony_ci		/*
4862306a36Sopenharmony_ci		 * There is no separate controls which can disable LEDs
4962306a36Sopenharmony_ci		 * individually, there is only RESET_LEDS command that turns
5062306a36Sopenharmony_ci		 * off both LEDs.
5162306a36Sopenharmony_ci		 *
5262306a36Sopenharmony_ci		 * RESET_LEDS turns off both LEDs, thus restore other LED if
5362306a36Sopenharmony_ci		 * it's turned ON.
5462306a36Sopenharmony_ci		 */
5562306a36Sopenharmony_ci		if (led->other->cdev.brightness)
5662306a36Sopenharmony_ci			num_regs = 2;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		control_seq[0] = a500_ec_leds_reset_seq[0];
5962306a36Sopenharmony_ci		control_seq[1] = led->other->enable_seq[0];
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return regmap_multi_reg_write(led->rmap, control_seq, num_regs);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int a500_ec_leds_probe(struct platform_device *pdev)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct a500_led *white_led, *orange_led;
6862306a36Sopenharmony_ci	struct regmap *rmap;
6962306a36Sopenharmony_ci	int err;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	rmap = dev_get_regmap(pdev->dev.parent, "KB930");
7262306a36Sopenharmony_ci	if (!rmap)
7362306a36Sopenharmony_ci		return -EINVAL;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* reset and turn off LEDs */
7662306a36Sopenharmony_ci	regmap_multi_reg_write(rmap, a500_ec_leds_reset_seq, 2);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	white_led = devm_kzalloc(&pdev->dev, sizeof(*white_led), GFP_KERNEL);
7962306a36Sopenharmony_ci	if (!white_led)
8062306a36Sopenharmony_ci		return -ENOMEM;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	white_led->cdev.name = "power:white";
8362306a36Sopenharmony_ci	white_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set;
8462306a36Sopenharmony_ci	white_led->cdev.flags = LED_CORE_SUSPENDRESUME;
8562306a36Sopenharmony_ci	white_led->cdev.max_brightness = 1;
8662306a36Sopenharmony_ci	white_led->enable_seq = a500_ec_white_led_enable_seq;
8762306a36Sopenharmony_ci	white_led->rmap = rmap;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	orange_led = devm_kzalloc(&pdev->dev, sizeof(*orange_led), GFP_KERNEL);
9062306a36Sopenharmony_ci	if (!orange_led)
9162306a36Sopenharmony_ci		return -ENOMEM;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	orange_led->cdev.name = "power:orange";
9462306a36Sopenharmony_ci	orange_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set;
9562306a36Sopenharmony_ci	orange_led->cdev.flags = LED_CORE_SUSPENDRESUME;
9662306a36Sopenharmony_ci	orange_led->cdev.max_brightness = 1;
9762306a36Sopenharmony_ci	orange_led->enable_seq = a500_ec_orange_led_enable_seq;
9862306a36Sopenharmony_ci	orange_led->rmap = rmap;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	white_led->other = orange_led;
10162306a36Sopenharmony_ci	orange_led->other = white_led;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	err = devm_led_classdev_register(&pdev->dev, &white_led->cdev);
10462306a36Sopenharmony_ci	if (err) {
10562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register white LED\n");
10662306a36Sopenharmony_ci		return err;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	err = devm_led_classdev_register(&pdev->dev, &orange_led->cdev);
11062306a36Sopenharmony_ci	if (err) {
11162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register orange LED\n");
11262306a36Sopenharmony_ci		return err;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic struct platform_driver a500_ec_leds_driver = {
11962306a36Sopenharmony_ci	.driver = {
12062306a36Sopenharmony_ci		.name = "acer-a500-iconia-leds",
12162306a36Sopenharmony_ci	},
12262306a36Sopenharmony_ci	.probe = a500_ec_leds_probe,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_cimodule_platform_driver(a500_ec_leds_driver);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciMODULE_DESCRIPTION("LED driver for Acer Iconia Tab A500 Power Button");
12762306a36Sopenharmony_ciMODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
12862306a36Sopenharmony_ciMODULE_ALIAS("platform:acer-a500-iconia-leds");
12962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
130