diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst index ce57254cb871..3ade16c18328 100644 --- a/Documentation/leds/index.rst +++ b/Documentation/leds/index.rst @@ -17,6 +17,7 @@ LEDs uleds leds-blinkm + leds-cht-wcove leds-el15203000 leds-lm3556 leds-lp3944 diff --git a/Documentation/leds/leds-cht-wcove.rst b/Documentation/leds/leds-cht-wcove.rst new file mode 100644 index 000000000000..5ec7cb60c4aa --- /dev/null +++ b/Documentation/leds/leds-cht-wcove.rst @@ -0,0 +1,38 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========================================================== +Kernel driver for Intel Cherry Trail Whiskey Cove PMIC LEDs +=========================================================== + +/sys/class/leds//hw_pattern +-------------------------------- + +Specify a hardware pattern for the Whiskey Cove PMIC LEDs. + +The only supported pattern is hardware breathing mode:: + + "0 2000 1 2000" + + ^ + | + Max-| --- + | / \ + | / \ + | / \ / + | / \ / + Min-|- --- + | + 0------2------4--> time (sec) + +The rise and fall times must be the same value. +Supported values are 2000, 1000, 500 and 250 for +breathing frequencies of 1/4, 1/2, 1 and 2 Hz. + +The set pattern only controls the timing. For max brightness the last +set brightness is used and the max brightness can be changed +while breathing by writing the brightness attribute. + +This is just like how blinking works in the LED subsystem, +for both sw and hw blinking the brightness can also be changed +while blinking. Breathing on this hw really is just a variant +mode of blinking. diff --git a/drivers/leds/leds-cht-wcove.c b/drivers/leds/leds-cht-wcove.c index 166b140e132a..36fb4c2c3c13 100644 --- a/drivers/leds/leds-cht-wcove.c +++ b/drivers/leds/leds-cht-wcove.c @@ -217,9 +217,10 @@ static int cht_wc_leds_find_freq(unsigned long period) return -1; } -static int cht_wc_leds_blink_set(struct led_classdev *cdev, - unsigned long *delay_on, - unsigned long *delay_off) +static int cht_wc_leds_set_effect(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off, + u8 effect) { struct cht_wc_led *led = container_of(cdev, struct cht_wc_led, cdev); unsigned int ctrl; @@ -246,7 +247,7 @@ static int cht_wc_leds_blink_set(struct led_classdev *cdev, } ret = regmap_update_bits(led->regmap, led->regs->fsm, - CHT_WC_LED_EFF_MASK, CHT_WC_LED_EFF_BLINKING); + CHT_WC_LED_EFF_MASK, effect); if (ret < 0) dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret); @@ -265,6 +266,37 @@ done: return ret; } +static int cht_wc_leds_blink_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + return cht_wc_leds_set_effect(cdev, delay_on, delay_off, CHT_WC_LED_EFF_BLINKING); +} + +static int cht_wc_leds_pattern_set(struct led_classdev *cdev, + struct led_pattern *pattern, + u32 len, int repeat) +{ + unsigned long delay_off, delay_on; + + if (repeat > 0 || len != 2 || + pattern[0].brightness != 0 || pattern[1].brightness != 1 || + pattern[0].delta_t != pattern[1].delta_t || + (pattern[0].delta_t != 250 && pattern[0].delta_t != 500 && + pattern[0].delta_t != 1000 && pattern[0].delta_t != 2000)) + return -EINVAL; + + delay_off = pattern[0].delta_t; + delay_on = pattern[1].delta_t; + + return cht_wc_leds_set_effect(cdev, &delay_on, &delay_off, CHT_WC_LED_EFF_BREATHING); +} + +static int cht_wc_leds_pattern_clear(struct led_classdev *cdev) +{ + return cht_wc_leds_brightness_set(cdev, 0); +} + static int cht_wc_led_save_regs(struct cht_wc_led *led, struct cht_wc_led_saved_regs *saved_regs) { @@ -329,6 +361,8 @@ static int cht_wc_leds_probe(struct platform_device *pdev) led->cdev.brightness_set_blocking = cht_wc_leds_brightness_set; led->cdev.brightness_get = cht_wc_leds_brightness_get; led->cdev.blink_set = cht_wc_leds_blink_set; + led->cdev.pattern_set = cht_wc_leds_pattern_set; + led->cdev.pattern_clear = cht_wc_leds_pattern_clear; led->cdev.max_brightness = 255; ret = led_classdev_register(&pdev->dev, &led->cdev);