/* Convert an led.brightness level (0..255) to a TCA6507 level (0..15) */ staticinlineint TO_LEVEL(int brightness)
{ return brightness >> 4;
}
/* ...and convert back */ staticinlineint TO_BRIGHT(int level)
{ if (level) return (level << 4) | 0xf; return0;
}
#define NUM_LEDS 7 struct tca6507_chip { int reg_set; /* One bit per register where *a'1'meanstheregister
* should be written */
u8 reg_file[TCA6507_REG_CNT]; /* Bank 2 is Master Intensity and doesn't use times */ struct bank { int level; int ontime, offtime; int on_dflt, off_dflt; int time_use, level_use;
} bank[3]; struct i2c_client *client; struct work_struct work;
spinlock_t lock;
struct tca6507_led { struct tca6507_chip *chip; struct led_classdev led_cdev; int num; int ontime, offtime; int on_dflt, off_dflt; int bank; /* Bank used, or -1 */ int blink; /* Set if hardware-blinking */
} leds[NUM_LEDS]; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; int gpio_map[NUM_LEDS]; #endif
};
staticint choose_times(int msec, int *c1p, int *c2p)
{ /* *Choosetwotimecodeswhichaddto'msec'asnearas *possible.Thefirstreturnedisthe'on'or'off'time. *Thesecondistobeusedasa'fade-on'or'fade-off'time. *If'msec'iseven,thefirstwillnotbesmallerthanthe *second.If'msec'isodd,thefirstwillnotbelarger *thanthesecond. *Ifwecannotgetasumwithin1/8of'msec'failwith *-EINVAL,otherwisereturnthesumthatwasachieved,plus1 *ifthefirstissmaller. *Iftwopossibilitiesareequallygood(e.g.512+0, *256+256),choosethefirstpairsothereismore *change-timevisible(i.e.itissofter).
*/ int c1, c2; int tmax = msec * 9 / 8; int tmin = msec * 7 / 8; int diff = 65536;
/* We start at '1' to ensure we never even think of choosing a *totaltimeof'0'.
*/ for (c1 = 1; c1 < TIMECODES; c1++) { int t = time_codes[c1]; if (t*2 < tmin) continue; if (t > tmax) break; for (c2 = 0; c2 <= c1; c2++) { int tt = t + time_codes[c2]; int d; if (tt < tmin) continue; if (tt > tmax) break; /* This works! */
d = abs(msec - tt); if (d >= diff) continue; /* Best yet */
*c1p = c1;
*c2p = c2;
diff = d; if (d == 0) return msec;
}
} if (diff < 65536) { int actual; if (msec & 1) {
swap(*c2p, *c1p);
}
actual = time_codes[*c1p] + time_codes[*c2p]; if (*c1p < *c2p) return actual + 1; else return actual;
} /* No close match */ return -EINVAL;
}
/* *Updatetheregisterfilewiththeappropriate3-bitstateforthe *givenled.
*/ staticvoid set_select(struct tca6507_chip *tca, int led, int val)
{ int mask = (1 << led); int bit;
for (bit = 0; bit < 3; bit++) { int n = tca->reg_file[bit] & ~mask; if (val & (1 << bit))
n |= mask; if (tca->reg_file[bit] != n) {
tca->reg_file[bit] = n;
tca->reg_set |= (1 << bit);
}
}
}
/* Update the register file with the appropriate 4-bit code for one *bankorother.Thiscanbeusedfortimers,forlevels,orfor *initialization.
*/ staticvoid set_code(struct tca6507_chip *tca, int reg, int bank, intnew)
{ int mask = 0xF; int n; if (bank) {
mask <<= 4; new <<= 4;
}
n = tca->reg_file[reg] & ~mask;
n |= new; if (tca->reg_file[reg] != n) {
tca->reg_file[reg] = n;
tca->reg_set |= 1 << reg;
}
}
/* Update brightness level. */ staticvoid set_level(struct tca6507_chip *tca, int bank, int level)
{ switch (bank) { case BANK0: case BANK1:
set_code(tca, TCA6507_MAX_INTENSITY, bank, level); break; case MASTER:
set_code(tca, TCA6507_MASTER_INTENSITY, 0, level); break;
}
tca->bank[bank].level = level;
}
/* Record all relevant time codes for a given bank */ staticvoid set_times(struct tca6507_chip *tca, int bank)
{ int c1, c2; int result;
result = choose_times(tca->bank[bank].ontime, &c1, &c2); if (result < 0) return;
dev_dbg(&tca->client->dev, "Chose on times %d(%d) %d(%d) for %dms\n",
c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].ontime);
set_code(tca, TCA6507_FADE_ON, bank, c2);
set_code(tca, TCA6507_FULL_ON, bank, c1);
tca->bank[bank].ontime = result;
result = choose_times(tca->bank[bank].offtime, &c1, &c2);
dev_dbg(&tca->client->dev, "Chose off times %d(%d) %d(%d) for %dms\n",
c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].offtime);
set_code(tca, TCA6507_FADE_OFF, bank, c2);
set_code(tca, TCA6507_FIRST_OFF, bank, c1);
set_code(tca, TCA6507_SECOND_OFF, bank, c1);
tca->bank[bank].offtime = result;
set_code(tca, TCA6507_INITIALIZE, bank, INIT_CODE);
}
for (r = 0; r < TCA6507_REG_CNT; r++) if (set & (1<<r))
i2c_smbus_write_byte_data(cl, r, file[r]);
}
staticvoid led_release(struct tca6507_led *led)
{ /* If led owns any resource, release it. */ struct tca6507_chip *tca = led->chip; if (led->bank >= 0) { struct bank *b = tca->bank + led->bank; if (led->blink)
b->time_use--;
b->level_use--;
}
led->blink = 0;
led->bank = -1;
}
staticint led_prepare(struct tca6507_led *led)
{ /* Assign this led to a bank, configuring that bank if
* necessary. */ int level = TO_LEVEL(led->led_cdev.brightness); struct tca6507_chip *tca = led->chip; int c1, c2; int i; struct bank *b; int need_init = 0;
for (i = BANK0; i <= BANK1; i++) { if (tca->bank[i].level_use == 0) /* not in use - it is ours! */ break; if (tca->bank[i].level != level) /* Incompatible level - skip */ /* FIX: if timer matches we maybe should consider *thisanyway...
*/ continue;
if (tca->bank[i].time_use == 0) /* Timer not in use, and level matches - use it */ break;
if (!(tca->bank[i].on_dflt ||
led->on_dflt ||
tca->bank[i].ontime == led->ontime)) /* on time is incompatible */ continue;
if (!(tca->bank[i].off_dflt ||
led->off_dflt ||
tca->bank[i].offtime == led->offtime)) /* off time is incompatible */ continue;
/* looks like a suitable match */ break;
}
if (i > BANK1) /* Nothing matches - how sad */ return -EINVAL;
b = &tca->bank[i]; if (b->level_use == 0)
set_level(tca, i, level);
b->level_use++;
led->bank = i;
staticint tca6507_probe_gpios(struct device *dev, struct tca6507_chip *tca, struct tca6507_platform_data *pdata)
{ int err; int i = 0; int gpios = 0;
for (i = 0; i < NUM_LEDS; i++) if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) { /* Configure as a gpio */
tca->gpio_map[gpios] = i;
gpios++;
}
¤ 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.0.16Bemerkung:
(vorverarbeitet am 2026-09-29)
¤
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.