Skip to content

Commit

Permalink
Merge tag 'ib-leds-platform-power-v6.11'
Browse files Browse the repository at this point in the history
Immutable branch between LEDs, Power and RGB due for the v6.11 merge
window.

Merge it to provide functionality required by power-supply specific
LED handler cleanups depending on the newly added (multi-colour) LED
features.

Signed-off-by: Sebastian Reichel <[email protected]>
  • Loading branch information
sre committed Jun 2, 2024
2 parents ebacfa1 + 9af12f5 commit 447bbf7
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 35 deletions.
1 change: 1 addition & 0 deletions drivers/leds/led-class-multicolor.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ int led_classdev_multicolor_register_ext(struct device *parent,
return -EINVAL;

led_cdev = &mcled_cdev->led_cdev;
led_cdev->flags |= LED_MULTI_COLOR;
mcled_cdev->led_cdev.groups = led_multicolor_groups;

return led_classdev_register_ext(parent, led_cdev, init_data);
Expand Down
31 changes: 31 additions & 0 deletions drivers/leds/led-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include <linux/kernel.h>
#include <linux/led-class-multicolor.h>
#include <linux/leds.h>
#include <linux/list.h>
#include <linux/module.h>
Expand Down Expand Up @@ -362,6 +363,36 @@ int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value)
}
EXPORT_SYMBOL_GPL(led_set_brightness_sync);

/*
* This is a led-core function because just like led_set_brightness()
* it is used in the kernel by e.g. triggers.
*/
void led_mc_set_brightness(struct led_classdev *led_cdev,
unsigned int *intensity_value, unsigned int num_colors,
unsigned int brightness)
{
struct led_classdev_mc *mcled_cdev;
unsigned int i;

if (!(led_cdev->flags & LED_MULTI_COLOR)) {
dev_err_once(led_cdev->dev, "error not a multi-color LED\n");
return;
}

mcled_cdev = lcdev_to_mccdev(led_cdev);
if (num_colors != mcled_cdev->num_colors) {
dev_err_once(led_cdev->dev, "error num_colors mismatch %u != %u\n",
num_colors, mcled_cdev->num_colors);
return;
}

for (i = 0; i < mcled_cdev->num_colors; i++)
mcled_cdev->subled_info[i].intensity = intensity_value[i];

led_set_brightness(led_cdev, brightness);
}
EXPORT_SYMBOL_GPL(led_mc_set_brightness);

int led_update_brightness(struct led_classdev *led_cdev)
{
int ret;
Expand Down
20 changes: 20 additions & 0 deletions drivers/leds/led-triggers.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,26 @@ void led_trigger_event(struct led_trigger *trig,
}
EXPORT_SYMBOL_GPL(led_trigger_event);

void led_mc_trigger_event(struct led_trigger *trig,
unsigned int *intensity_value, unsigned int num_colors,
enum led_brightness brightness)
{
struct led_classdev *led_cdev;

if (!trig)
return;

rcu_read_lock();
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
if (!(led_cdev->flags & LED_MULTI_COLOR))
continue;

led_mc_set_brightness(led_cdev, intensity_value, num_colors, brightness);
}
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(led_mc_trigger_event);

static void led_trigger_blink_setup(struct led_trigger *trig,
unsigned long delay_on,
unsigned long delay_off,
Expand Down
1 change: 0 additions & 1 deletion drivers/leds/rgb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ config LEDS_GROUP_MULTICOLOR
config LEDS_KTD202X
tristate "LED support for KTD202x Chips"
depends on I2C
depends on OF
select REGMAP_I2C
help
This option enables support for the Kinetic KTD2026/KTD2027
Expand Down
80 changes: 46 additions & 34 deletions drivers/leds/rgb/leds-ktd202x.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ struct ktd202x {
struct device *dev;
struct regmap *regmap;
bool enabled;
int num_leds;
unsigned long num_leds;
struct ktd202x_led leds[] __counted_by(num_leds);
};

Expand Down Expand Up @@ -381,39 +381,42 @@ static int ktd202x_blink_mc_set(struct led_classdev *cdev,
mc->num_colors);
}

static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwnode,
struct ktd202x_led *led, struct led_init_data *init_data)
{
struct fwnode_handle *child;
struct led_classdev *cdev;
struct device_node *child;
struct mc_subled *info;
int num_channels;
int i = 0;

num_channels = of_get_available_child_count(np);
num_channels = 0;
fwnode_for_each_available_child_node(fwnode, child)
num_channels++;

if (!num_channels || num_channels > chip->num_leds)
return -EINVAL;

info = devm_kcalloc(chip->dev, num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;

for_each_available_child_of_node(np, child) {
fwnode_for_each_available_child_node(fwnode, child) {
u32 mono_color;
u32 reg;
int ret;

ret = of_property_read_u32(child, "reg", &reg);
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret != 0 || reg >= chip->num_leds) {
dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child);
of_node_put(child);
return -EINVAL;
dev_err(chip->dev, "invalid 'reg' of %pfw\n", child);
fwnode_handle_put(child);
return ret;
}

ret = of_property_read_u32(child, "color", &mono_color);
ret = fwnode_property_read_u32(child, "color", &mono_color);
if (ret < 0 && ret != -EINVAL) {
dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child);
of_node_put(child);
dev_err(chip->dev, "failed to parse 'color' of %pfw\n", child);
fwnode_handle_put(child);
return ret;
}

Expand All @@ -433,16 +436,16 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data);
}

static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np,
static int ktd202x_setup_led_single(struct ktd202x *chip, struct fwnode_handle *fwnode,
struct ktd202x_led *led, struct led_init_data *init_data)
{
struct led_classdev *cdev;
u32 reg;
int ret;

ret = of_property_read_u32(np, "reg", &reg);
ret = fwnode_property_read_u32(fwnode, "reg", &reg);
if (ret != 0 || reg >= chip->num_leds) {
dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np);
dev_err(chip->dev, "invalid 'reg' of %pfw\n", fwnode);
return -EINVAL;
}
led->index = reg;
Expand All @@ -454,7 +457,7 @@ static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np
return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data);
}

static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index)
static int ktd202x_add_led(struct ktd202x *chip, struct fwnode_handle *fwnode, unsigned int index)
{
struct ktd202x_led *led = &chip->leds[index];
struct led_init_data init_data = {};
Expand All @@ -463,21 +466,21 @@ static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigne
int ret;

/* Color property is optional in single color case */
ret = of_property_read_u32(np, "color", &color);
ret = fwnode_property_read_u32(fwnode, "color", &color);
if (ret < 0 && ret != -EINVAL) {
dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np);
dev_err(chip->dev, "failed to parse 'color' of %pfw\n", fwnode);
return ret;
}

led->chip = chip;
init_data.fwnode = of_fwnode_handle(np);
init_data.fwnode = fwnode;

if (color == LED_COLOR_ID_RGB) {
cdev = &led->mcdev.led_cdev;
ret = ktd202x_setup_led_rgb(chip, np, led, &init_data);
ret = ktd202x_setup_led_rgb(chip, fwnode, led, &init_data);
} else {
cdev = &led->cdev;
ret = ktd202x_setup_led_single(chip, np, led, &init_data);
ret = ktd202x_setup_led_single(chip, fwnode, led, &init_data);
}

if (ret) {
Expand All @@ -490,15 +493,14 @@ static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigne
return 0;
}

static int ktd202x_probe_dt(struct ktd202x *chip)
static int ktd202x_probe_fw(struct ktd202x *chip)
{
struct device_node *np = dev_of_node(chip->dev), *child;
struct fwnode_handle *child;
struct device *dev = chip->dev;
int count;
int i = 0;

chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev);

count = of_get_available_child_count(np);
count = device_get_child_node_count(dev);
if (!count || count > chip->num_leds)
return -EINVAL;

Expand All @@ -507,11 +509,11 @@ static int ktd202x_probe_dt(struct ktd202x *chip)
/* Allow the device to execute the complete reset */
usleep_range(200, 300);

for_each_available_child_of_node(np, child) {
device_for_each_child_node(dev, child) {
int ret = ktd202x_add_led(chip, child, i);

if (ret) {
of_node_put(child);
fwnode_handle_put(child);
return ret;
}
i++;
Expand Down Expand Up @@ -554,6 +556,12 @@ static int ktd202x_probe(struct i2c_client *client)
return ret;
}

ret = devm_mutex_init(dev, &chip->mutex);
if (ret)
return ret;

chip->num_leds = (unsigned long)i2c_get_match_data(client);

chip->regulators[0].supply = "vin";
chip->regulators[1].supply = "vio";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators);
Expand All @@ -568,7 +576,7 @@ static int ktd202x_probe(struct i2c_client *client)
return ret;
}

ret = ktd202x_probe_dt(chip);
ret = ktd202x_probe_fw(chip);
if (ret < 0) {
regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
return ret;
Expand All @@ -580,8 +588,6 @@ static int ktd202x_probe(struct i2c_client *client)
return ret;
}

mutex_init(&chip->mutex);

return 0;
}

Expand All @@ -590,8 +596,6 @@ static void ktd202x_remove(struct i2c_client *client)
struct ktd202x *chip = i2c_get_clientdata(client);

ktd202x_chip_disable(chip);

mutex_destroy(&chip->mutex);
}

static void ktd202x_shutdown(struct i2c_client *client)
Expand All @@ -602,10 +606,17 @@ static void ktd202x_shutdown(struct i2c_client *client)
regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
}

static const struct i2c_device_id ktd202x_id[] = {
{"ktd2026", KTD2026_NUM_LEDS},
{"ktd2027", KTD2027_NUM_LEDS},
{}
};
MODULE_DEVICE_TABLE(i2c, ktd202x_id);

static const struct of_device_id ktd202x_match_table[] = {
{ .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
{ .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
{},
{}
};
MODULE_DEVICE_TABLE(of, ktd202x_match_table);

Expand All @@ -617,6 +628,7 @@ static struct i2c_driver ktd202x_driver = {
.probe = ktd202x_probe,
.remove = ktd202x_remove,
.shutdown = ktd202x_shutdown,
.id_table = ktd202x_id,
};
module_i2c_driver(ktd202x_driver);

Expand Down
23 changes: 23 additions & 0 deletions drivers/power/supply/power_supply_leds.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
static void power_supply_update_bat_leds(struct power_supply *psy)
{
union power_supply_propval status;
unsigned int intensity_green[3] = { 0, 255, 0 };
unsigned int intensity_orange[3] = { 255, 128, 0 };

if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
return;
Expand All @@ -36,19 +38,29 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
/* Going from blink to LED on requires a LED_OFF event to stop blink */
led_trigger_event(psy->charging_blink_full_solid_trig, LED_OFF);
led_trigger_event(psy->charging_blink_full_solid_trig, LED_FULL);
led_mc_trigger_event(psy->charging_orange_full_green_trig,
intensity_green,
ARRAY_SIZE(intensity_green),
LED_FULL);
break;
case POWER_SUPPLY_STATUS_CHARGING:
led_trigger_event(psy->charging_full_trig, LED_FULL);
led_trigger_event(psy->charging_trig, LED_FULL);
led_trigger_event(psy->full_trig, LED_OFF);
led_trigger_blink(psy->charging_blink_full_solid_trig, 0, 0);
led_mc_trigger_event(psy->charging_orange_full_green_trig,
intensity_orange,
ARRAY_SIZE(intensity_orange),
LED_FULL);
break;
default:
led_trigger_event(psy->charging_full_trig, LED_OFF);
led_trigger_event(psy->charging_trig, LED_OFF);
led_trigger_event(psy->full_trig, LED_OFF);
led_trigger_event(psy->charging_blink_full_solid_trig,
LED_OFF);
led_trigger_event(psy->charging_orange_full_green_trig,
LED_OFF);
break;
}
}
Expand All @@ -74,6 +86,11 @@ static int power_supply_create_bat_triggers(struct power_supply *psy)
if (!psy->charging_blink_full_solid_trig_name)
goto charging_blink_full_solid_failed;

psy->charging_orange_full_green_trig_name = kasprintf(GFP_KERNEL,
"%s-charging-orange-full-green", psy->desc->name);
if (!psy->charging_orange_full_green_trig_name)
goto charging_red_full_green_failed;

led_trigger_register_simple(psy->charging_full_trig_name,
&psy->charging_full_trig);
led_trigger_register_simple(psy->charging_trig_name,
Expand All @@ -82,9 +99,13 @@ static int power_supply_create_bat_triggers(struct power_supply *psy)
&psy->full_trig);
led_trigger_register_simple(psy->charging_blink_full_solid_trig_name,
&psy->charging_blink_full_solid_trig);
led_trigger_register_simple(psy->charging_orange_full_green_trig_name,
&psy->charging_orange_full_green_trig);

return 0;

charging_red_full_green_failed:
kfree(psy->charging_blink_full_solid_trig_name);
charging_blink_full_solid_failed:
kfree(psy->full_trig_name);
full_failed:
Expand All @@ -101,10 +122,12 @@ static void power_supply_remove_bat_triggers(struct power_supply *psy)
led_trigger_unregister_simple(psy->charging_trig);
led_trigger_unregister_simple(psy->full_trig);
led_trigger_unregister_simple(psy->charging_blink_full_solid_trig);
led_trigger_unregister_simple(psy->charging_orange_full_green_trig);
kfree(psy->charging_blink_full_solid_trig_name);
kfree(psy->full_trig_name);
kfree(psy->charging_trig_name);
kfree(psy->charging_full_trig_name);
kfree(psy->charging_orange_full_green_trig_name);
}

/* Generated power specific LEDs triggers. */
Expand Down
Loading

0 comments on commit 447bbf7

Please sign in to comment.