[PATCH v2 1/2] leds: pca955x: Clean up and optimize
From: Eddie James
Date: Thu Apr 07 2022 - 14:41:16 EST
Clean up the I2C access functions to avoid fetching the pca955x
driver data again. Optimize the probe to do at most 4 reads and
4 writes of the LED selector regs, rather than 16 of each.
Rename some functions and variables to be more consistent and
descriptive.
Signed-off-by: Eddie James <eajames@xxxxxxxxxxxxx>
---
drivers/leds/leds-pca955x.c | 148 ++++++++++++++++++++----------------
1 file changed, 82 insertions(+), 66 deletions(-)
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 81aaf21212d7..dd51f40973d8 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -140,13 +140,13 @@ struct pca955x_platform_data {
};
/* 8 bits per input register */
-static inline int pca95xx_num_input_regs(int bits)
+static inline int pca955x_num_input_regs(int bits)
{
return (bits + 7) / 8;
}
/* 4 bits per LED selector register */
-static inline int pca95xx_num_led_regs(int bits)
+static inline int pca955x_num_led_regs(int bits)
{
return (bits + 3) / 4;
}
@@ -161,20 +161,25 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
((state & 0x3) << (led_num << 1));
}
+static inline int pca955x_ledstate(u8 ls, int led_num)
+{
+ return (ls >> (led_num << 1)) & 0x3;
+}
+
/*
* Write to frequency prescaler register, used to program the
* period of the PWM output. period = (PSCx + 1) / 38
*/
-static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_psc(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + (2 * n);
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev,
+ "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val,
+ ret);
return ret;
}
@@ -185,16 +190,16 @@ static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
*
* Duty cycle is (256 - PWMx) / 256
*/
-static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_pwm(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev,
+ "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val,
+ ret);
return ret;
}
@@ -202,16 +207,16 @@ static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
* Write to LED selector register, which determines the source that
* drives the LED output.
*/
-static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_ls(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n;
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev,
+ "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val,
+ ret);
return ret;
}
@@ -219,15 +224,14 @@ static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)
* Read the LED selector register, which determines the source that
* drives the LED output.
*/
-static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)
+static int pca955x_read_ls(struct pca955x *pca955x, int n, u8 *val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n;
int ret;
- ret = i2c_smbus_read_byte_data(client, cmd);
+ ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
if (ret < 0) {
- dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n",
__func__, n, ret);
return ret;
}
@@ -235,15 +239,14 @@ static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)
return 0;
}
-static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val)
+static int pca955x_read_pwm(struct pca955x *pca955x, int n, u8 *val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
int ret;
- ret = i2c_smbus_read_byte_data(client, cmd);
+ ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
if (ret < 0) {
- dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n",
__func__, n, ret);
return ret;
}
@@ -260,12 +263,11 @@ static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
u8 ls, pwm;
int ret;
- ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls);
+ ret = pca955x_read_ls(pca955x, pca955x_led->led_num / 4, &ls);
if (ret)
return ret;
- ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3;
- switch (ls) {
+ switch (pca955x_ledstate(ls, pca955x_led->led_num % 4)) {
case PCA955X_LS_LED_ON:
ret = LED_FULL;
break;
@@ -276,7 +278,7 @@ static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
ret = LED_HALF;
break;
case PCA955X_LS_BLINK1:
- ret = pca955x_read_pwm(pca955x->client, 1, &pwm);
+ ret = pca955x_read_pwm(pca955x, 1, &pwm);
if (ret)
return ret;
ret = 255 - pwm;
@@ -289,34 +291,30 @@ static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
static int pca955x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct pca955x_led *pca955x_led;
- struct pca955x *pca955x;
+ struct pca955x_led *pca955x_led = container_of(led_cdev,
+ struct pca955x_led,
+ led_cdev);
+ struct pca955x *pca955x = pca955x_led->pca955x;
+ int reg = pca955x_led->led_num / 4;
+ int bit = pca955x_led->led_num % 4;
u8 ls;
- int chip_ls; /* which LSx to use (0-3 potentially) */
- int ls_led; /* which set of bits within LSx to use (0-3) */
int ret;
- pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);
- pca955x = pca955x_led->pca955x;
-
- chip_ls = pca955x_led->led_num / 4;
- ls_led = pca955x_led->led_num % 4;
-
mutex_lock(&pca955x->lock);
- ret = pca955x_read_ls(pca955x->client, chip_ls, &ls);
+ ret = pca955x_read_ls(pca955x, reg, &ls);
if (ret)
goto out;
switch (value) {
case LED_FULL:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_ON);
break;
case LED_OFF:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF);
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
break;
case LED_HALF:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0);
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK0);
break;
default:
/*
@@ -326,14 +324,14 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
* OFF, HALF, or FULL. But, this is probably better than
* just turning off for all other values.
*/
- ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);
+ ret = pca955x_write_pwm(pca955x, 1, 255 - value);
if (ret)
goto out;
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK1);
break;
}
- ret = pca955x_write_ls(pca955x->client, chip_ls, ls);
+ ret = pca955x_write_ls(pca955x, reg, ls);
out:
mutex_unlock(&pca955x->lock);
@@ -492,7 +490,9 @@ static int pca955x_probe(struct i2c_client *client)
struct led_classdev *led;
struct led_init_data init_data;
struct i2c_adapter *adapter;
- int i, err;
+ int i, bit, err, nls, reg;
+ u8 ls1[4];
+ u8 ls2[4];
struct pca955x_platform_data *pdata;
bool set_default_label = false;
bool keep_pwm = false;
@@ -563,6 +563,15 @@ static int pca955x_probe(struct i2c_client *client)
init_data.devname_mandatory = false;
init_data.devicename = "pca955x";
+ nls = pca955x_num_led_regs(chip->bits);
+ for (i = 0; i < nls; ++i) {
+ err = pca955x_read_ls(pca955x, i, &ls1[i]);
+ if (err)
+ return err;
+
+ ls2[i] = ls1[i];
+ }
+
for (i = 0; i < chip->bits; i++) {
pca955x_led = &pca955x->leds[i];
pca955x_led->led_num = i;
@@ -574,21 +583,20 @@ static int pca955x_probe(struct i2c_client *client)
case PCA955X_TYPE_GPIO:
break;
case PCA955X_TYPE_LED:
+ bit = i % 4;
+ reg = i / 4;
led = &pca955x_led->led_cdev;
led->brightness_set_blocking = pca955x_led_set;
led->brightness_get = pca955x_led_get;
if (pdata->leds[i].default_state ==
- LEDS_GPIO_DEFSTATE_OFF) {
- err = pca955x_led_set(led, LED_OFF);
- if (err)
- return err;
- } else if (pdata->leds[i].default_state ==
- LEDS_GPIO_DEFSTATE_ON) {
- err = pca955x_led_set(led, LED_FULL);
- if (err)
- return err;
- }
+ LEDS_GPIO_DEFSTATE_OFF)
+ ls2[reg] = pca955x_ledsel(ls2[reg], bit,
+ PCA955X_LS_LED_OFF);
+ else if (pdata->leds[i].default_state ==
+ LEDS_GPIO_DEFSTATE_ON)
+ ls2[reg] = pca955x_ledsel(ls2[reg], bit,
+ PCA955X_LS_LED_ON);
init_data.fwnode = pdata->leds[i].fwnode;
@@ -633,23 +641,31 @@ static int pca955x_probe(struct i2c_client *client)
}
}
+ for (i = 0; i < nls; ++i) {
+ if (ls1[i] != ls2[i]) {
+ err = pca955x_write_ls(pca955x, i, ls2[i]);
+ if (err)
+ return err;
+ }
+ }
+
/* PWM0 is used for half brightness or 50% duty cycle */
- err = pca955x_write_pwm(client, 0, 255 - LED_HALF);
+ err = pca955x_write_pwm(pca955x, 0, 255 - LED_HALF);
if (err)
return err;
if (!keep_pwm) {
/* PWM1 is used for variable brightness, default to OFF */
- err = pca955x_write_pwm(client, 1, 0);
+ err = pca955x_write_pwm(pca955x, 1, 0);
if (err)
return err;
}
/* Set to fast frequency so we do not see flashing */
- err = pca955x_write_psc(client, 0, 0);
+ err = pca955x_write_psc(pca955x, 0, 0);
if (err)
return err;
- err = pca955x_write_psc(client, 1, 0);
+ err = pca955x_write_psc(pca955x, 1, 0);
if (err)
return err;
--
2.27.0