linux/drivers/leds/leds-tca6507.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * leds-tca6507
 *
 * The TCA6507 is a programmable LED controller that can drive 7
 * separate lines either by holding them low, or by pulsing them
 * with modulated width.
 * The modulation can be varied in a simple pattern to produce a
 * blink or double-blink.
 *
 * This driver can configure each line either as a 'GPIO' which is
 * out-only (pull-up resistor required) or as an LED with variable
 * brightness and hardware-assisted blinking.
 *
 * Apart from OFF and ON there are three programmable brightness
 * levels which can be programmed from 0 to 15 and indicate how many
 * 500usec intervals in each 8msec that the led is 'on'.  The levels
 * are named MASTER, BANK0 and BANK1.
 *
 * There are two different blink rates that can be programmed, each
 * with separate time for rise, on, fall, off and second-off.  Thus if
 * 3 or more different non-trivial rates are required, software must
 * be used for the extra rates. The two different blink rates must
 * align with the two levels BANK0 and BANK1.  This driver does not
 * support double-blink so 'second-off' always matches 'off'.
 *
 * Only 16 different times can be programmed in a roughly logarithmic
 * scale from 64ms to 16320ms.  To be precise the possible times are:
 *    0, 64, 128, 192, 256, 384, 512, 768,
 *    1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320
 *
 * Times that cannot be closely matched with these must be handled in
 * software.  This driver allows 12.5% error in matching.
 *
 * This driver does not allow rise/fall rates to be set explicitly.
 * When trying to match a given 'on' or 'off' period, an appropriate
 * pair of 'change' and 'hold' times are chosen to get a close match.
 * If the target delay is even, the 'change' number will be the
 * smaller; if odd, the 'hold' number will be the smaller.

 * Choosing pairs of delays with 12.5% errors allows us to match
 * delays in the ranges: 56-72, 112-144, 168-216, 224-27504,
 * 28560-36720.
 * 26% of the achievable sums can be matched by multiple pairings.
 * For example 1536 == 1536+0, 1024+512, or 768+768.
 * This driver will always choose the pairing with the least
 * maximum - 768+768 in this case.  Other pairings are not available.
 *
 * Access to the 3 levels and 2 blinks are on a first-come,
 * first-served basis.  Access can be shared by multiple leds if they
 * have the same level and either same blink rates, or some don't
 * blink.  When a led changes, it relinquishes access and tries again,
 * so it might lose access to hardware blink.
 *
 * If a blink engine cannot be allocated, software blink is used.  If
 * the desired brightness cannot be allocated, the closest available
 * non-zero brightness is used.  As 'full' is always available, the
 * worst case would be to have two different blink rates at '1', with
 * Max at '2', then other leds will have to choose between '2' and
 * '16'.  Hopefully this is not likely.
 *
 * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the
 * brightness and LEDs using the blink.  It can only be reprogrammed
 * when the appropriate counter is zero.  The MASTER level has a
 * single usage count.
 *
 * Each LED has programmable 'on' and 'off' time as milliseconds.
 * With each there is a flag saying if it was explicitly requested or
 * defaulted.  Similarly the banks know if each time was explicit or a
 * default.  Defaults are permitted to be changed freely - they are
 * not recognised when matching.
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/gpio/driver.h>
#include <linux/property.h>
#include <linux/workqueue.h>

/* LED select registers determine the source that drives LED outputs */
#define TCA6507_LS_LED_OFF
#define TCA6507_LS_LED_OFF1
#define TCA6507_LS_LED_PWM0
#define TCA6507_LS_LED_PWM1
#define TCA6507_LS_LED_ON
#define TCA6507_LS_LED_MIR
#define TCA6507_LS_BLINK0
#define TCA6507_LS_BLINK1

struct tca6507_platform_data {};

#define TCA6507_MAKE_GPIO

enum {};
static int bank_source[3] =;
static int blink_source[2] =;

/* PWM registers */
#define TCA6507_REG_CNT

/*
 * 0x00, 0x01, 0x02 encode the TCA6507_LS_* values, each output
 * owns one bit in each register
 */
#define TCA6507_FADE_ON
#define TCA6507_FULL_ON
#define TCA6507_FADE_OFF
#define TCA6507_FIRST_OFF
#define TCA6507_SECOND_OFF
#define TCA6507_MAX_INTENSITY
#define TCA6507_MASTER_INTENSITY
#define TCA6507_INITIALIZE

#define INIT_CODE

#define TIMECODES
static int time_codes[TIMECODES] =;

/* Convert an led.brightness level (0..255) to a TCA6507 level (0..15) */
static inline int TO_LEVEL(int brightness)
{}

/* ...and convert back */
static inline int TO_BRIGHT(int level)
{}

#define NUM_LEDS
struct tca6507_chip {};

static const struct i2c_device_id tca6507_id[] =;
MODULE_DEVICE_TABLE(i2c, tca6507_id);

static int choose_times(int msec, int *c1p, int *c2p)
{}

/*
 * Update the register file with the appropriate 3-bit state for the
 * given led.
 */
static void set_select(struct tca6507_chip *tca, int led, int val)
{}

/* Update the register file with the appropriate 4-bit code for one
 * bank or other.  This can be used for timers, for levels, or for
 * initialization.
 */
static void set_code(struct tca6507_chip *tca, int reg, int bank, int new)
{}

/* Update brightness level. */
static void set_level(struct tca6507_chip *tca, int bank, int level)
{}

/* Record all relevant time codes for a given bank */
static void set_times(struct tca6507_chip *tca, int bank)
{}

/* Write all needed register of tca6507 */

static void tca6507_work(struct work_struct *work)
{}

static void led_release(struct tca6507_led *led)
{}

static int led_prepare(struct tca6507_led *led)
{}

static int led_assign(struct tca6507_led *led)
{}

static void tca6507_brightness_set(struct led_classdev *led_cdev,
				   enum led_brightness brightness)
{}

static int tca6507_blink_set(struct led_classdev *led_cdev,
			     unsigned long *delay_on,
			     unsigned long *delay_off)
{}

#ifdef CONFIG_GPIOLIB
static void tca6507_gpio_set_value(struct gpio_chip *gc,
				   unsigned offset, int val)
{}

static int tca6507_gpio_direction_output(struct gpio_chip *gc,
					  unsigned offset, int val)
{}

static int tca6507_probe_gpios(struct device *dev,
			       struct tca6507_chip *tca,
			       struct tca6507_platform_data *pdata)
{}
#else /* CONFIG_GPIOLIB */
static int tca6507_probe_gpios(struct device *dev,
			       struct tca6507_chip *tca,
			       struct tca6507_platform_data *pdata)
{
	return 0;
}
#endif /* CONFIG_GPIOLIB */

static struct tca6507_platform_data *
tca6507_led_dt_init(struct device *dev)
{}

static const struct of_device_id __maybe_unused of_tca6507_leds_match[] =;
MODULE_DEVICE_TABLE(of, of_tca6507_leds_match);

static int tca6507_probe(struct i2c_client *client)
{}

static void tca6507_remove(struct i2c_client *client)
{}

static struct i2c_driver tca6507_driver =;

module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();