Merge branches 'ib-leds-mfd-6.11', 'ib-leds-platform-power-6.11' and 'ib-mfd-leds-platform-6.11' into ibs-for-leds-merged

This commit is contained in:
Lee Jones 2024-06-21 11:54:35 +01:00
commit 59561ccd90
12 changed files with 203 additions and 42 deletions

View File

@ -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);

View File

@ -503,6 +503,11 @@ int led_classdev_register_ext(struct device *parent,
ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
if (ret < 0)
return ret;
else if (ret && led_cdev->flags & LED_REJECT_NAME_CONFLICT)
return -EEXIST;
else if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision\n",
proposed_name, final_name);
if (led_cdev->color >= LED_COLOR_ID_MAX)
dev_warn(parent, "LED %s color identifier out of range\n", final_name);
@ -518,10 +523,6 @@ int led_classdev_register_ext(struct device *parent,
if (init_data && init_data->fwnode)
device_set_node(led_cdev->dev, init_data->fwnode);
if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision",
proposed_name, dev_name(led_cdev->dev));
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ret = led_add_brightness_hw_changed(led_cdev);
if (ret) {

View File

@ -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>
@ -361,6 +362,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;

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -103,6 +103,10 @@ static const struct mfd_cell cros_ec_led_cells[] = {
{ .name = "cros-ec-led", },
};
static const struct mfd_cell cros_ec_keyboard_leds_cells[] = {
{ .name = "cros-keyboard-leds", },
};
static const struct cros_feature_to_cells cros_subdevices[] = {
{
.id = EC_FEATURE_CEC,
@ -134,6 +138,11 @@ static const struct cros_feature_to_cells cros_subdevices[] = {
.mfd_cells = cros_ec_led_cells,
.num_cells = ARRAY_SIZE(cros_ec_led_cells),
},
{
.id = EC_FEATURE_PWM_KEYB,
.mfd_cells = cros_ec_keyboard_leds_cells,
.num_cells = ARRAY_SIZE(cros_ec_keyboard_leds_cells),
},
};
static const struct mfd_cell cros_ec_platform_cells[] = {

View File

@ -150,7 +150,7 @@ config CROS_EC_PROTO
config CROS_KBD_LED_BACKLIGHT
tristate "Backlight LED support for Chrome OS keyboards"
depends on LEDS_CLASS && (ACPI || CROS_EC)
depends on LEDS_CLASS && (ACPI || CROS_EC || MFD_CROS_EC_DEV)
help
This option enables support for the keyboard backlight LEDs on
select Chrome OS systems.

View File

@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/mfd/core.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
@ -194,13 +195,46 @@ static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_
#endif /* IS_ENABLED(CONFIG_CROS_EC) */
#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV)
static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct keyboard_led *keyboard_led = platform_get_drvdata(pdev);
keyboard_led->ec = cros_ec;
return 0;
}
static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {
.init = keyboard_led_init_ec_pwm_mfd,
.brightness_set_blocking = keyboard_led_set_brightness_ec_pwm,
.brightness_get = keyboard_led_get_brightness_ec_pwm,
.max_brightness = KEYBOARD_BACKLIGHT_MAX,
};
#else /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */
static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {};
#endif /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */
static int keyboard_led_is_mfd_device(struct platform_device *pdev)
{
return IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) && mfd_get_cell(pdev);
}
static int keyboard_led_probe(struct platform_device *pdev)
{
const struct keyboard_led_drvdata *drvdata;
struct keyboard_led *keyboard_led;
int error;
drvdata = device_get_match_data(&pdev->dev);
if (keyboard_led_is_mfd_device(pdev))
drvdata = &keyboard_led_drvdata_ec_pwm_mfd;
else
drvdata = device_get_match_data(&pdev->dev);
if (!drvdata)
return -EINVAL;
@ -216,13 +250,15 @@ static int keyboard_led_probe(struct platform_device *pdev)
}
keyboard_led->cdev.name = "chromeos::kbd_backlight";
keyboard_led->cdev.flags |= LED_CORE_SUSPENDRESUME;
keyboard_led->cdev.flags |= LED_CORE_SUSPENDRESUME | LED_REJECT_NAME_CONFLICT;
keyboard_led->cdev.max_brightness = drvdata->max_brightness;
keyboard_led->cdev.brightness_set = drvdata->brightness_set;
keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking;
keyboard_led->cdev.brightness_get = drvdata->brightness_get;
error = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev);
if (error == -EEXIST) /* Already bound via other mechanism */
return -ENODEV;
if (error)
return error;

View File

@ -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. */

View File

@ -107,6 +107,8 @@ 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_REJECT_NAME_CONFLICT BIT(24)
#define LED_MULTI_COLOR BIT(25)
/* set_brightness_work / blink_timer flags, atomic, private. */
unsigned long work_flags;
@ -373,6 +375,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
@ -500,6 +521,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,
@ -542,6 +566,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) {}

View File

@ -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
};