Skip to content

Commit 48ca7f3

Browse files
bastien-curutchetlag-linaro
authored andcommitted
leds: pca9532: Use PWM1 for hardware blinking
PSC0/PWM0 are used by all LEDs for brightness and blinking. This causes conflicts when you set a brightness of a new LED while an other one is already using PWM0 for blinking. Use PSC1/PWM1 for blinking. Check that no other LED is already blinking with a different PSC1/PWM1 configuration to avoid conflict. Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com> Link: https://lore.kernel.org/r/20240617143910.154546-3-bastien.curutchet@bootlin.com Signed-off-by: Lee Jones <lee@kernel.org>
1 parent 0e69c90 commit 48ca7f3

1 file changed

Lines changed: 43 additions & 10 deletions

File tree

drivers/leds/leds-pca9532.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#define LED_SHIFT(led) (LED_NUM(led) * 2)
3030
#define LED_MASK(led) (0x3 << LED_SHIFT(led))
3131

32+
#define PCA9532_PWM_PERIOD_DIV 152
33+
#define PCA9532_PWM_DUTY_DIV 256
34+
3235
#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)
3336

3437
struct pca9532_chip_info {
@@ -194,29 +197,59 @@ static int pca9532_set_brightness(struct led_classdev *led_cdev,
194197
return err;
195198
}
196199

200+
static int pca9532_update_hw_blink(struct pca9532_led *led,
201+
unsigned long delay_on, unsigned long delay_off)
202+
{
203+
struct pca9532_data *data = i2c_get_clientdata(led->client);
204+
unsigned int psc;
205+
int i;
206+
207+
/* Look for others LEDs that already use PWM1 */
208+
for (i = 0; i < data->chip_info->num_leds; i++) {
209+
struct pca9532_led *other = &data->leds[i];
210+
211+
if (other == led)
212+
continue;
213+
214+
if (other->state == PCA9532_PWM1) {
215+
if (other->ldev.blink_delay_on != delay_on ||
216+
other->ldev.blink_delay_off != delay_off) {
217+
dev_err(&led->client->dev,
218+
"HW can handle only one blink configuration at a time\n");
219+
return -EINVAL;
220+
}
221+
}
222+
}
223+
224+
psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000;
225+
if (psc > U8_MAX) {
226+
dev_err(&led->client->dev, "Blink period too long to be handled by hardware\n");
227+
return -EINVAL;
228+
}
229+
230+
led->state = PCA9532_PWM1;
231+
data->psc[PCA9532_PWM_ID_1] = psc;
232+
data->pwm[PCA9532_PWM_ID_1] = (delay_on * PCA9532_PWM_DUTY_DIV) / (delay_on + delay_off);
233+
234+
return pca9532_setpwm(data->client, PCA9532_PWM_ID_1);
235+
}
236+
197237
static int pca9532_set_blink(struct led_classdev *led_cdev,
198238
unsigned long *delay_on, unsigned long *delay_off)
199239
{
200240
struct pca9532_led *led = ldev_to_led(led_cdev);
201-
struct i2c_client *client = led->client;
202-
int psc;
203-
int err = 0;
241+
int err;
204242

205243
if (*delay_on == 0 && *delay_off == 0) {
206244
/* led subsystem ask us for a blink rate */
207245
*delay_on = 1000;
208246
*delay_off = 1000;
209247
}
210-
if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6)
211-
return -EINVAL;
212248

213-
/* Thecus specific: only use PSC/PWM 0 */
214-
psc = (*delay_on * 152-1)/1000;
215-
err = pca9532_calcpwm(client, PCA9532_PWM_ID_0, psc, led_cdev->brightness);
249+
err = pca9532_update_hw_blink(led, *delay_on, *delay_off);
216250
if (err)
217251
return err;
218-
if (led->state == PCA9532_PWM0)
219-
pca9532_setpwm(led->client, PCA9532_PWM_ID_0);
252+
220253
pca9532_setled(led);
221254

222255
return 0;

0 commit comments

Comments
 (0)