/* 4 bits per LED selector register */ staticinline u8 pca955x_num_led_regs(u8 bits)
{ return (bits + 3) / 4;
}
/* * Return an LED selector register value based on an existing one, with * the appropriate 2-bit state value set for the given LED number (0-3).
*/ staticinline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
{ return (oldval & (~(0x3 << (led_num << 1)))) |
((state & 0x3) << (led_num << 1));
}
/* * Write to frequency prescaler register, used to program the * period of the PWM output. period = (PSCx + 1) / coeff * Where for pca9551 chips coeff = 38 and for all other chips coeff = 44
*/ staticint pca955x_write_psc(struct pca955x *pca955x, int n, u8 val)
{
u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + (2 * n); int ret;
ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val); if (ret < 0)
dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
val, ret); return ret;
}
/* * Write to PWM register, which determines the duty cycle of the * output. LED is OFF when the count is less than the value of this * register, and ON when it is greater. If PWMx == 0, LED is always OFF. * * Duty cycle is (256 - PWMx) / 256
*/ staticint pca955x_write_pwm(struct pca955x *pca955x, int n, u8 val)
{
u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); int ret;
ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val); if (ret < 0)
dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
val, ret); return ret;
}
/* * Write to LED selector register, which determines the source that * drives the LED output.
*/ staticint pca955x_write_ls(struct pca955x *pca955x, int n, u8 val)
{
u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret;
ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val); if (ret < 0)
dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
val, ret); return ret;
}
/* * Read the LED selector register, which determines the source that * drives the LED output.
*/ staticint pca955x_read_ls(struct pca955x *pca955x, int n, u8 *val)
{
u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret;
ret = i2c_smbus_read_byte_data(pca955x->client, cmd); if (ret < 0) {
dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret); return ret;
}
*val = (u8)ret; return 0;
}
staticint pca955x_read_pwm(struct pca955x *pca955x, int n, u8 *val)
{
u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); int ret;
ret = i2c_smbus_read_byte_data(pca955x->client, cmd); if (ret < 0) {
dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret); return ret;
}
*val = (u8)ret; return 0;
}
staticint pca955x_read_psc(struct pca955x *pca955x, int n, u8 *val)
{ int ret;
u8 cmd;
ret = pca955x_read_ls(pca955x, pca955x_led->led_num / 4, &ls); if (ret) return ret;
switch (pca955x_ledstate(ls, pca955x_led->led_num % 4)) { case PCA955X_LS_LED_ON: case PCA955X_LS_BLINK0:
ret = LED_FULL; break; case PCA955X_LS_LED_OFF:
ret = LED_OFF; break; case PCA955X_LS_BLINK1:
ret = pca955x_read_pwm(pca955x, 1, &pwm); if (ret) return ret;
ret = 255 - pwm; break;
}
return ret;
}
staticint pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{ struct pca955x_led *pca955x_led = led_to_pca955x(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 ret;
mutex_lock(&pca955x->lock);
ret = pca955x_read_ls(pca955x, reg, &ls); if (ret) goto out;
if (test_bit(pca955x_led->led_num, &pca955x->active_blink)) { if (value == LED_OFF) {
clear_bit(pca955x_led->led_num, &pca955x->active_blink);
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
} else { /* No variable brightness for blinking LEDs */ goto out;
}
} else { switch (value) { case LED_FULL:
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_ON); break; case LED_OFF:
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF); break; default: /* * Use PWM1 for all other values. This has the unwanted * side effect of making all LEDs on the chip share the * same brightness level if set to a value other than * OFF or FULL. But, this is probably better than just * turning off for all other values.
*/
ret = pca955x_write_pwm(pca955x, 1, 255 - value); if (ret) goto out;
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK1); break;
}
}
ret = pca955x_write_ls(pca955x, reg, ls);
out:
mutex_unlock(&pca955x->lock);
return ret;
}
static u8 pca955x_period_to_psc(struct pca955x *pca955x, unsignedlong period)
{ /* psc register value = (blink period * coeff) - 1 */
period *= pca955x->chipdef->blink_div;
period /= MSEC_PER_SEC;
period -= 1;
if (!test_and_set_bit(pca955x_led->led_num,
&pca955x->active_blink)) {
u8 ls; int reg = pca955x_led->led_num / 4; int bit = pca955x_led->led_num % 4;
ret = pca955x_read_ls(pca955x, reg, &ls); if (ret) goto out;
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK0);
ret = pca955x_write_ls(pca955x, reg, ls); if (ret) goto out;
/* * Force 50% duty cycle to maintain the specified * blink rate.
*/
ret = pca955x_write_pwm(pca955x, 0, 128); if (ret) goto out;
}
if (pca955x->blink_period != period) {
pca955x->blink_period = period;
ret = pca955x_write_psc(pca955x, 0, psc); if (ret) goto out;
}
period = pca955x_psc_to_period(pca955x, psc);
period /= 2;
*delay_on = period;
*delay_off = period;
} else {
ret = -EBUSY;
}
out:
mutex_unlock(&pca955x->lock);
return ret;
}
#ifdef CONFIG_LEDS_PCA955X_GPIO /* * Read the INPUT register, which contains the state of LEDs.
*/ staticint pca955x_read_input(struct i2c_client *client, int n, u8 *val)
{ int ret = i2c_smbus_read_byte_data(client, n);
chip = i2c_get_match_data(client); if (!chip) return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n");
adapter = client->adapter;
pdata = dev_get_platdata(&client->dev); if (!pdata) {
pdata = pca955x_get_pdata(client, chip); if (IS_ERR(pdata)) return PTR_ERR(pdata);
}
/* Make sure the slave address / chip type combo given is possible */ if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
chip->slv_addr) {
dev_err(&client->dev, "invalid slave address %02x\n",
client->addr); return -ENODEV;
}
dev_info(&client->dev, "Using %s %u-bit LED driver at slave address 0x%02x\n",
client->name, chip->bits, client->addr);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO;
if (pdata->num_leds != chip->bits) {
dev_err(&client->dev, "board info claims %d LEDs on a %u-bit chip\n",
pdata->num_leds, chip->bits); return -ENODEV;
}
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL); if (!pca955x) return -ENOMEM;
pca955x->leds = devm_kcalloc(&client->dev, chip->bits, sizeof(*pca955x_led), GFP_KERNEL); if (!pca955x->leds) return -ENOMEM;
nls = pca955x_num_led_regs(chip->bits); /* Use auto-increment feature to read all the LED selectors at once. */
err = i2c_smbus_read_i2c_block_data(client,
0x10 | (pca955x_num_input_regs(chip->bits) + 4), nls,
ls1); if (err < 0) return err;
for (i = 0; i < nls; i++)
ls2[i] = ls1[i];
for (i = 0; i < chip->bits; i++) {
pca955x_led = &pca955x->leds[i];
pca955x_led->led_num = i;
pca955x_led->pca955x = pca955x;
pca955x_led->type = pdata->leds[i].type;
switch (pca955x_led->type) { case PCA955X_TYPE_NONE: 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;
led->blink_set = pca955x_led_blink;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.