diff mbox series

[v2,2/4] leds: pca9532: Use PWM1 for hardware blinking

Message ID 20240617143910.154546-3-bastien.curutchet@bootlin.com
State New
Headers show
Series leds: pca9532: Use hardware for blinking LEDs | expand

Commit Message

Bastien Curutchet June 17, 2024, 2:39 p.m. UTC
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>
---
 drivers/leds/leds-pca9532.c | 53 ++++++++++++++++++++++++++++++-------
 1 file changed, 43 insertions(+), 10 deletions(-)

Comments

Bastien Curutchet July 10, 2024, 9:17 a.m. UTC | #1
Hi Lee,

On 6/17/24 16:39, Bastien Curutchet wrote:

> +static int pca9532_update_hw_blink(struct pca9532_led *led,
> +				   unsigned long delay_on, unsigned long delay_off)
> +{
> +	struct pca9532_data *data = i2c_get_clientdata(led->client);
> +	unsigned int psc;
> +	int i;
> +
> +	/* Look for others LEDs that already use PWM1 */
> +	for (i = 0; i < data->chip_info->num_leds; i++) {
> +		struct pca9532_led *other = &data->leds[i];
> +
> +		if (other == led)
> +			continue;
> +
> +		if (other->state == PCA9532_PWM1) {
> +			if (other->ldev.blink_delay_on != delay_on ||
> +			    other->ldev.blink_delay_off != delay_off) {
> +				dev_err(&led->client->dev,
> +					"HW can handle only one blink configuration at a time\n");

I'm having some second thoughts about this dev_err().

It was dev_dbg() in V1, but based on your suggestion, I changed it to 
dev_err() because an error was returned after.

I've worked more with this patch since it got applied, these error 
messages appear frequently, though they don’t seem to be 'real' errors 
to me as the software callback is used afterwards and the LED blinks at 
the expected period.

Don't you think a dev_dbg() would be more appropriate in this case ? Or 
perhaps a comment instead of a message ?

> +				return -EINVAL;
> +			}
> +		}
> +	}
> +
> +	psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000;
> +	if (psc > U8_MAX) {
> +		dev_err(&led->client->dev, "Blink period too long to be handled by hardware\n");

Same comments for this dev_err()

> +		return -EINVAL;
> +	}


Best regards,
Bastien
Lee Jones July 11, 2024, 8:30 a.m. UTC | #2
On Wed, 10 Jul 2024, Bastien Curutchet wrote:

> Hi Lee,
> 
> On 6/17/24 16:39, Bastien Curutchet wrote:
> 
> > +static int pca9532_update_hw_blink(struct pca9532_led *led,
> > +				   unsigned long delay_on, unsigned long delay_off)
> > +{
> > +	struct pca9532_data *data = i2c_get_clientdata(led->client);
> > +	unsigned int psc;
> > +	int i;
> > +
> > +	/* Look for others LEDs that already use PWM1 */
> > +	for (i = 0; i < data->chip_info->num_leds; i++) {
> > +		struct pca9532_led *other = &data->leds[i];
> > +
> > +		if (other == led)
> > +			continue;
> > +
> > +		if (other->state == PCA9532_PWM1) {
> > +			if (other->ldev.blink_delay_on != delay_on ||
> > +			    other->ldev.blink_delay_off != delay_off) {
> > +				dev_err(&led->client->dev,
> > +					"HW can handle only one blink configuration at a time\n");
> 
> I'm having some second thoughts about this dev_err().
> 
> It was dev_dbg() in V1, but based on your suggestion, I changed it to
> dev_err() because an error was returned after.
> 
> I've worked more with this patch since it got applied, these error messages
> appear frequently, though they don’t seem to be 'real' errors to me as the
> software callback is used afterwards and the LED blinks at the expected
> period.
> 
> Don't you think a dev_dbg() would be more appropriate in this case ? Or
> perhaps a comment instead of a message ?

If it's not an error, then don't return an error message.

Maybe drop the message for a comment and return -EBUSY instead?

> > +				return -EINVAL;
> > +			}
> > +		}
> > +	}
> > +
> > +	psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000;
> > +	if (psc > U8_MAX) {
> > +		dev_err(&led->client->dev, "Blink period too long to be handled by hardware\n");
> 
> Same comments for this dev_err()
> 
> > +		return -EINVAL;
> > +	}
> 
> 
> Best regards,
> Bastien
Bastien Curutchet July 11, 2024, 8:40 a.m. UTC | #3
Hi Lee,

On 7/11/24 10:30, Lee Jones wrote:
> On Wed, 10 Jul 2024, Bastien Curutchet wrote:
> 
>> Hi Lee,
>>
>> On 6/17/24 16:39, Bastien Curutchet wrote:
>>
>>> +static int pca9532_update_hw_blink(struct pca9532_led *led,
>>> +				   unsigned long delay_on, unsigned long delay_off)
>>> +{
>>> +	struct pca9532_data *data = i2c_get_clientdata(led->client);
>>> +	unsigned int psc;
>>> +	int i;
>>> +
>>> +	/* Look for others LEDs that already use PWM1 */
>>> +	for (i = 0; i < data->chip_info->num_leds; i++) {
>>> +		struct pca9532_led *other = &data->leds[i];
>>> +
>>> +		if (other == led)
>>> +			continue;
>>> +
>>> +		if (other->state == PCA9532_PWM1) {
>>> +			if (other->ldev.blink_delay_on != delay_on ||
>>> +			    other->ldev.blink_delay_off != delay_off) {
>>> +				dev_err(&led->client->dev,
>>> +					"HW can handle only one blink configuration at a time\n");
>>
>> I'm having some second thoughts about this dev_err().
>>
>> It was dev_dbg() in V1, but based on your suggestion, I changed it to
>> dev_err() because an error was returned after.
>>
>> I've worked more with this patch since it got applied, these error messages
>> appear frequently, though they don’t seem to be 'real' errors to me as the
>> software callback is used afterwards and the LED blinks at the expected
>> period.
>>
>> Don't you think a dev_dbg() would be more appropriate in this case ? Or
>> perhaps a comment instead of a message ?
> 
> If it's not an error, then don't return an error message.
> 
> Maybe drop the message for a comment and return -EBUSY instead?
> 

OK I'll do this, thank you.

>>> +				return -EINVAL;
>>> +			}
>>> +		}
>>> +	}
>>> +

Best regards,
Bastien
diff mbox series

Patch

diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index b6e5f48bffe7..244ae3ff79b5 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -29,6 +29,9 @@ 
 #define LED_SHIFT(led)		(LED_NUM(led) * 2)
 #define LED_MASK(led)		(0x3 << LED_SHIFT(led))
 
+#define PCA9532_PWM_PERIOD_DIV	152
+#define PCA9532_PWM_DUTY_DIV	256
+
 #define ldev_to_led(c)       container_of(c, struct pca9532_led, ldev)
 
 struct pca9532_chip_info {
@@ -194,29 +197,59 @@  static int pca9532_set_brightness(struct led_classdev *led_cdev,
 	return err;
 }
 
+static int pca9532_update_hw_blink(struct pca9532_led *led,
+				   unsigned long delay_on, unsigned long delay_off)
+{
+	struct pca9532_data *data = i2c_get_clientdata(led->client);
+	unsigned int psc;
+	int i;
+
+	/* Look for others LEDs that already use PWM1 */
+	for (i = 0; i < data->chip_info->num_leds; i++) {
+		struct pca9532_led *other = &data->leds[i];
+
+		if (other == led)
+			continue;
+
+		if (other->state == PCA9532_PWM1) {
+			if (other->ldev.blink_delay_on != delay_on ||
+			    other->ldev.blink_delay_off != delay_off) {
+				dev_err(&led->client->dev,
+					"HW can handle only one blink configuration at a time\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000;
+	if (psc > U8_MAX) {
+		dev_err(&led->client->dev, "Blink period too long to be handled by hardware\n");
+		return -EINVAL;
+	}
+
+	led->state = PCA9532_PWM1;
+	data->psc[PCA9532_PWM_ID_1] = psc;
+	data->pwm[PCA9532_PWM_ID_1] = (delay_on * PCA9532_PWM_DUTY_DIV) / (delay_on + delay_off);
+
+	return pca9532_setpwm(data->client, PCA9532_PWM_ID_1);
+}
+
 static int pca9532_set_blink(struct led_classdev *led_cdev,
 	unsigned long *delay_on, unsigned long *delay_off)
 {
 	struct pca9532_led *led = ldev_to_led(led_cdev);
-	struct i2c_client *client = led->client;
-	int psc;
-	int err = 0;
+	int err;
 
 	if (*delay_on == 0 && *delay_off == 0) {
 		/* led subsystem ask us for a blink rate */
 		*delay_on = 1000;
 		*delay_off = 1000;
 	}
-	if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6)
-		return -EINVAL;
 
-	/* Thecus specific: only use PSC/PWM 0 */
-	psc = (*delay_on * 152-1)/1000;
-	err = pca9532_calcpwm(client, PCA9532_PWM_ID_0, psc, led_cdev->brightness);
+	err = pca9532_update_hw_blink(led, *delay_on, *delay_off);
 	if (err)
 		return err;
-	if (led->state == PCA9532_PWM0)
-		pca9532_setpwm(led->client, PCA9532_PWM_ID_0);
+
 	pca9532_setled(led);
 
 	return 0;