diff options
author | Bjorn Andersson | 2022-05-23 16:37:19 -0700 |
---|---|---|
committer | Pavel Machek | 2022-05-24 22:08:10 +0200 |
commit | e98a860f65428a3cae7ed7b3e8ebcf6320d7fc5e (patch) | |
tree | a63712ae933948a5b27eca0daba1f706cbac3c4c /drivers/leds | |
parent | 73bce575ed90c752eaa4b2b9a70860481d58d240 (diff) |
leds: qcom-lpg: Require pattern to follow documentation
The leds-trigger-pattern documentation describes how the brightness of
the LED should transition linearly from one brightness value to the
next, over the given delta_t.
But the pattern engine in the Qualcomm LPG hardware only supports
holding the brightness for each entry for the period.
This subset of patterns can be represented in the leds-trigger-pattern
by injecting zero-time transitions after each entry in the pattern,
resulting in a pattern that pattern that can be rendered by the LPG.
Rework LPG pattern interface to require these zero-time transitions, to
make it comply with this subset of patterns and reject the patterns it
can't render.
Fixes: 24e2d05d1b68 ("leds: Add driver for Qualcomm LPG")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/rgb/leds-qcom-lpg.c | 43 |
1 files changed, 38 insertions, 5 deletions
diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index cfa3362b2457..02f51cc61837 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -704,11 +704,12 @@ static int lpg_blink_mc_set(struct led_classdev *cdev, return ret; } -static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *pattern, +static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, u32 len, int repeat) { struct lpg_channel *chan; struct lpg *lpg = led->lpg; + struct led_pattern *pattern; unsigned int brightness_a; unsigned int brightness_b; unsigned int actual_len; @@ -719,18 +720,48 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *pattern, unsigned int hi_idx; unsigned int i; bool ping_pong = true; - int ret; + int ret = -EINVAL; /* Hardware only support oneshot or indefinite loops */ if (repeat != -1 && repeat != 1) return -EINVAL; /* + * The standardized leds-trigger-pattern format defines that the + * brightness of the LED follows a linear transition from one entry + * in the pattern to the next, over the given delta_t time. It + * describes that the way to perform instant transitions a zero-length + * entry should be added following a pattern entry. + * + * The LPG hardware is only able to perform the latter (no linear + * transitions), so require each entry in the pattern to be followed by + * a zero-length transition. + */ + if (len % 2) + return -EINVAL; + + pattern = kcalloc(len / 2, sizeof(*pattern), GFP_KERNEL); + if (!pattern) + return -ENOMEM; + + for (i = 0; i < len; i += 2) { + if (led_pattern[i].brightness != led_pattern[i + 1].brightness) + goto out_free_pattern; + if (led_pattern[i + 1].delta_t != 0) + goto out_free_pattern; + + pattern[i / 2].brightness = led_pattern[i].brightness; + pattern[i / 2].delta_t = led_pattern[i].delta_t; + } + + len /= 2; + + /* * Specifying a pattern of length 1 causes the hardware to iterate * through the entire LUT, so prohibit this. */ if (len < 2) - return -EINVAL; + goto out_free_pattern; /* * The LPG plays patterns with at a fixed pace, a "low pause" can be @@ -781,13 +812,13 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *pattern, * specify hi pause. Reject other variations. */ if (i != actual_len - 1) - return -EINVAL; + goto out_free_pattern; } } /* LPG_RAMP_DURATION_REG is a 9bit */ if (delta_t >= BIT(9)) - return -EINVAL; + goto out_free_pattern; /* Find "low pause" and "high pause" in the pattern */ lo_pause = pattern[0].delta_t; @@ -814,6 +845,8 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *pattern, out_unlock: mutex_unlock(&lpg->lock); +out_free_pattern: + kfree(pattern); return ret; } |