Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07abd432 authored by Dan Murphy's avatar Dan Murphy Committed by Jacek Anaszewski
Browse files

leds: lm3692x: Support LED sync configuration



The LM36922 has one output but can sync current to 2 LED
strings. The user may only use one sync so the other
syncs need to be disabled.

The LM36923 has 3 LED syncs.

Signed-off-by: default avatarDan Murphy <dmurphy@ti.com>
Signed-off-by: default avatarJacek Anaszewski <jacek.anaszewski@gmail.com>
parent 581e3ca3
Loading
Loading
Loading
Loading
+120 −38
Original line number Original line Diff line number Diff line
@@ -15,6 +15,9 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <uapi/linux/uleds.h>
#include <uapi/linux/uleds.h>


#define LM36922_MODEL	0
#define LM36923_MODEL	1

#define LM3692X_REV		0x0
#define LM3692X_REV		0x0
#define LM3692X_RESET		0x1
#define LM3692X_RESET		0x1
#define LM3692X_EN		0x10
#define LM3692X_EN		0x10
@@ -33,6 +36,9 @@
#define LM3692X_DEVICE_EN	BIT(0)
#define LM3692X_DEVICE_EN	BIT(0)
#define LM3692X_LED1_EN		BIT(1)
#define LM3692X_LED1_EN		BIT(1)
#define LM3692X_LED2_EN		BIT(2)
#define LM3692X_LED2_EN		BIT(2)
#define LM36923_LED3_EN		BIT(3)
#define LM3692X_ENABLE_MASK	(LM3692X_DEVICE_EN | LM3692X_LED1_EN | \
				 LM3692X_LED2_EN | LM36923_LED3_EN)


/* Brightness Control Bits */
/* Brightness Control Bits */
#define LM3692X_BL_ADJ_POL	BIT(0)
#define LM3692X_BL_ADJ_POL	BIT(0)
@@ -98,6 +104,8 @@
 * @enable_gpio - VDDIO/EN gpio to enable communication interface
 * @enable_gpio - VDDIO/EN gpio to enable communication interface
 * @regulator - LED supply regulator pointer
 * @regulator - LED supply regulator pointer
 * @label - LED label
 * @label - LED label
 * @led_enable - LED sync to be enabled
 * @model_id - Current device model ID enumerated
 */
 */
struct lm3692x_led {
struct lm3692x_led {
	struct mutex lock;
	struct mutex lock;
@@ -107,6 +115,8 @@ struct lm3692x_led {
	struct gpio_desc *enable_gpio;
	struct gpio_desc *enable_gpio;
	struct regulator *regulator;
	struct regulator *regulator;
	char label[LED_MAX_NAME_SIZE];
	char label[LED_MAX_NAME_SIZE];
	int led_enable;
	int model_id;
};
};


static const struct reg_default lm3692x_reg_defs[] = {
static const struct reg_default lm3692x_reg_defs[] = {
@@ -189,6 +199,7 @@ static int lm3692x_brightness_set(struct led_classdev *led_cdev,


static int lm3692x_init(struct lm3692x_led *led)
static int lm3692x_init(struct lm3692x_led *led)
{
{
	int enable_state;
	int ret;
	int ret;


	if (led->regulator) {
	if (led->regulator) {
@@ -215,9 +226,25 @@ static int lm3692x_init(struct lm3692x_led *led)


	/*
	/*
	 * For glitch free operation, the following data should
	 * For glitch free operation, the following data should
	 * only be written while device enable bit is 0
	 * only be written while LEDx enable bits are 0 and the device enable
	 * bit is set to 1.
	 * per Section 7.5.14 of the data sheet
	 * per Section 7.5.14 of the data sheet
	 */
	 */
	ret = regmap_write(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN);
	if (ret)
		goto out;

	/* Set the brightness to 0 so when enabled the LEDs do not come
	 * on with full brightness.
	 */
	ret = regmap_write(led->regmap, LM3692X_BRT_MSB, 0);
	if (ret)
		goto out;

	ret = regmap_write(led->regmap, LM3692X_BRT_LSB, 0);
	if (ret)
		goto out;

	ret = regmap_write(led->regmap, LM3692X_PWM_CTRL,
	ret = regmap_write(led->regmap, LM3692X_PWM_CTRL,
		LM3692X_PWM_FILTER_100 | LM3692X_PWM_SAMP_24MHZ);
		LM3692X_PWM_FILTER_100 | LM3692X_PWM_SAMP_24MHZ);
	if (ret)
	if (ret)
@@ -247,6 +274,38 @@ static int lm3692x_init(struct lm3692x_led *led)
	if (ret)
	if (ret)
		goto out;
		goto out;


	switch (led->led_enable) {
	case 0:
	default:
		if (led->model_id == LM36923_MODEL)
			enable_state = LM3692X_LED1_EN | LM3692X_LED2_EN |
			       LM36923_LED3_EN;
		else
			enable_state = LM3692X_LED1_EN | LM3692X_LED2_EN;

		break;
	case 1:
		enable_state = LM3692X_LED1_EN;
		break;
	case 2:
		enable_state = LM3692X_LED2_EN;
		break;

	case 3:
		if (led->model_id == LM36923_MODEL) {
			enable_state = LM36923_LED3_EN;
			break;
		}

		ret = -EINVAL;
		dev_err(&led->client->dev,
			"LED3 sync not available on this device\n");
		goto out;
	}

	ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK,
				 enable_state | LM3692X_DEVICE_EN);

	return ret;
	return ret;
out:
out:
	dev_err(&led->client->dev, "Fail writing initialization values\n");
	dev_err(&led->client->dev, "Fail writing initialization values\n");
@@ -263,55 +322,29 @@ static int lm3692x_init(struct lm3692x_led *led)


	return ret;
	return ret;
}
}

static int lm3692x_probe_dt(struct lm3692x_led *led)
static int lm3692x_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
{
	struct fwnode_handle *child = NULL;
	struct fwnode_handle *child = NULL;
	struct lm3692x_led *led;
	const char *name;
	const char *name;
	int ret;
	int ret;


	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
	led->enable_gpio = devm_gpiod_get_optional(&led->client->dev,
	if (!led)
		return -ENOMEM;

	led->enable_gpio = devm_gpiod_get_optional(&client->dev,
						   "enable", GPIOD_OUT_LOW);
						   "enable", GPIOD_OUT_LOW);
	if (IS_ERR(led->enable_gpio)) {
	if (IS_ERR(led->enable_gpio)) {
		ret = PTR_ERR(led->enable_gpio);
		ret = PTR_ERR(led->enable_gpio);
		dev_err(&client->dev, "Failed to get enable gpio: %d\n", ret);
		dev_err(&led->client->dev, "Failed to get enable gpio: %d\n",
			ret);
		return ret;
		return ret;
	}
	}


	led->regulator = devm_regulator_get(&client->dev, "vled");
	led->regulator = devm_regulator_get(&led->client->dev, "vled");
	if (IS_ERR(led->regulator))
	if (IS_ERR(led->regulator))
		led->regulator = NULL;
		led->regulator = NULL;


	led->client = client;
	led->led_dev.name = led->label;
	led->led_dev.brightness_set_blocking = lm3692x_brightness_set;

	mutex_init(&led->lock);

	i2c_set_clientdata(client, led);

	led->regmap = devm_regmap_init_i2c(client, &lm3692x_regmap_config);
	if (IS_ERR(led->regmap)) {
		ret = PTR_ERR(led->regmap);
		dev_err(&client->dev, "Failed to allocate register map: %d\n",
			ret);
		return ret;
	}

	ret = lm3692x_init(led);
	if (ret)
		return ret;

	child = device_get_next_child_node(&led->client->dev, child);
	child = device_get_next_child_node(&led->client->dev, child);
	if (!child) {
	if (!child) {
		dev_err(&led->client->dev, "No LED Child node\n");
		dev_err(&led->client->dev, "No LED Child node\n");
		return ret;
		return -ENODEV;
	}
	}


	fwnode_property_read_string(child, "linux,default-trigger",
	fwnode_property_read_string(child, "linux,default-trigger",
@@ -325,14 +358,57 @@ static int lm3692x_probe(struct i2c_client *client,
		snprintf(led->label, sizeof(led->label),
		snprintf(led->label, sizeof(led->label),
			 "%s:%s", led->client->name, name);
			 "%s:%s", led->client->name, name);


	led->led_dev.dev->of_node = to_of_node(child);
	ret = fwnode_property_read_u32(child, "reg", &led->led_enable);
	if (ret) {
		dev_err(&led->client->dev, "reg DT property missing\n");
		return ret;
	}


	ret = devm_led_classdev_register(&client->dev, &led->led_dev);
	led->led_dev.name = led->label;

	ret = devm_led_classdev_register(&led->client->dev, &led->led_dev);
	if (ret) {
	if (ret) {
		dev_err(&client->dev, "led register err: %d\n", ret);
		dev_err(&led->client->dev, "led register err: %d\n", ret);
		return ret;
		return ret;
	}
	}


	led->led_dev.dev->of_node = to_of_node(child);

	return 0;
}

static int lm3692x_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct lm3692x_led *led;
	int ret;

	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
	if (!led)
		return -ENOMEM;

	mutex_init(&led->lock);
	led->client = client;
	led->led_dev.brightness_set_blocking = lm3692x_brightness_set;
	led->model_id = id->driver_data;
	i2c_set_clientdata(client, led);

	led->regmap = devm_regmap_init_i2c(client, &lm3692x_regmap_config);
	if (IS_ERR(led->regmap)) {
		ret = PTR_ERR(led->regmap);
		dev_err(&client->dev, "Failed to allocate register map: %d\n",
			ret);
		return ret;
	}

	ret = lm3692x_probe_dt(led);
	if (ret)
		return ret;

	ret = lm3692x_init(led);
	if (ret)
		return ret;

	return 0;
	return 0;
}
}


@@ -341,6 +417,12 @@ static int lm3692x_remove(struct i2c_client *client)
	struct lm3692x_led *led = i2c_get_clientdata(client);
	struct lm3692x_led *led = i2c_get_clientdata(client);
	int ret;
	int ret;


	ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0);
	if (ret) {
		dev_err(&led->client->dev, "Failed to disable regulator\n");
		return ret;
	}

	if (led->enable_gpio)
	if (led->enable_gpio)
		gpiod_direction_output(led->enable_gpio, 0);
		gpiod_direction_output(led->enable_gpio, 0);


@@ -357,8 +439,8 @@ static int lm3692x_remove(struct i2c_client *client)
}
}


static const struct i2c_device_id lm3692x_id[] = {
static const struct i2c_device_id lm3692x_id[] = {
	{ "lm36922", 0 },
	{ "lm36922", LM36922_MODEL },
	{ "lm36923", 1 },
	{ "lm36923", LM36923_MODEL },
	{ }
	{ }
};
};
MODULE_DEVICE_TABLE(i2c, lm3692x_id);
MODULE_DEVICE_TABLE(i2c, lm3692x_id);