Commit 447bbf76 authored by Sebastian Reichel's avatar Sebastian Reichel

Merge tag 'ib-leds-platform-power-v6.11'

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: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
parents ebacfa1f 9af12f57
......@@ -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);
......
......@@ -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>
......@@ -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;
......
......@@ -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,
......
......@@ -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
......
......@@ -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);
};
......@@ -381,16 +381,19 @@ 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;
......@@ -398,22 +401,22 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
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;
}
......@@ -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;
......@@ -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 = {};
......@@ -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) {
......@@ -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;
......@@ -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++;
......@@ -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);
......@@ -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;
......@@ -580,8 +588,6 @@ static int ktd202x_probe(struct i2c_client *client)
return ret;
}
mutex_init(&chip->mutex);
return 0;
}
......@@ -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)
......@@ -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);
......@@ -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);
......
......@@ -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;
......@@ -36,12 +38,20 @@ 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);
......@@ -49,6 +59,8 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
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;
}
}
......@@ -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,
......@@ -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:
......@@ -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. */
......
......@@ -107,6 +107,7 @@ struct led_classdev {
#define LED_BRIGHT_HW_CHANGED BIT(21)
#define LED_RETAIN_AT_SHUTDOWN BIT(22)
#define LED_INIT_DEFAULT_TRIGGER BIT(23)
#define LED_MULTI_COLOR BIT(24)
/* set_brightness_work / blink_timer flags, atomic, private. */
unsigned long work_flags;
......@@ -373,6 +374,25 @@ void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness);
*/
int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value);
/**
* led_mc_set_brightness - set mc LED color intensity values and brightness
* @led_cdev: the LED to set
* @intensity_value: array of per color intensity values to set
* @num_colors: amount of entries in intensity_value array
* @brightness: the brightness to set the LED to
*
* Set a multi-color LED's per color intensity values and brightness.
* If necessary, this cancels the software blink timer. This function is
* guaranteed not to sleep.
*
* Calling this function on a non multi-color led_classdev or with the wrong
* num_colors value is an error. In this case an error will be logged once
* and the call will do nothing.
*/
void led_mc_set_brightness(struct led_classdev *led_cdev,
unsigned int *intensity_value, unsigned int num_colors,
unsigned int brightness);
/**
* led_update_brightness - update LED brightness
* @led_cdev: the LED to query
......@@ -490,6 +510,9 @@ void led_trigger_register_simple(const char *name,
struct led_trigger **trigger);
void led_trigger_unregister_simple(struct led_trigger *trigger);
void led_trigger_event(struct led_trigger *trigger, enum led_brightness event);
void led_mc_trigger_event(struct led_trigger *trig,
unsigned int *intensity_value, unsigned int num_colors,
enum led_brightness brightness);
void led_trigger_blink(struct led_trigger *trigger, unsigned long delay_on,
unsigned long delay_off);
void led_trigger_blink_oneshot(struct led_trigger *trigger,
......@@ -532,6 +555,9 @@ static inline void led_trigger_register_simple(const char *name,
static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
static inline void led_trigger_event(struct led_trigger *trigger,
enum led_brightness event) {}
static inline void led_mc_trigger_event(struct led_trigger *trig,
unsigned int *intensity_value, unsigned int num_colors,
enum led_brightness brightness) {}
static inline void led_trigger_blink(struct led_trigger *trigger,
unsigned long delay_on,
unsigned long delay_off) {}
......
......@@ -319,6 +319,8 @@ struct power_supply {
char *online_trig_name;
struct led_trigger *charging_blink_full_solid_trig;
char *charging_blink_full_solid_trig_name;
struct led_trigger *charging_orange_full_green_trig;
char *charging_orange_full_green_trig_name;
#endif
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment