Linux-LEDs Archive on lore.kernel.org
 help / color / Atom feed
From: "Vaittinen, Matti" <Matti.Vaittinen@fi.rohmeurope.com>
To: "mazziesaccount@gmail.com" <mazziesaccount@gmail.com>
Cc: "corbet@lwn.net" <corbet@lwn.net>,
	"linux-doc@vger.kernel.org" <linux-doc@vger.kernel.org>,
	"phil.edworthy@renesas.com" <phil.edworthy@renesas.com>,
	"linux-rtc@vger.kernel.org" <linux-rtc@vger.kernel.org>,
	"dmurphy@ti.com" <dmurphy@ti.com>,
	"linux-leds@vger.kernel.org" <linux-leds@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"jeffrey.t.kirsher@intel.com" <jeffrey.t.kirsher@intel.com>,
	"linux-gpio@vger.kernel.org" <linux-gpio@vger.kernel.org>,
	"mchehab+samsung@kernel.org" <mchehab+samsung@kernel.org>,
	"alexandre.belloni@bootlin.com" <alexandre.belloni@bootlin.com>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"mturquette@baylibre.com" <mturquette@baylibre.com>,
	"lgirdwood@gmail.com" <lgirdwood@gmail.com>,
	"jacek.anaszewski@gmail.com" <jacek.anaszewski@gmail.com>,
	"wsa+renesas@sang-engineering.com"
	<wsa+renesas@sang-engineering.com>,
	"a.zummo@towertech.it" <a.zummo@towertech.it>,
	"linus.walleij@linaro.org" <linus.walleij@linaro.org>,
	"m.szyprowski@samsung.com" <m.szyprowski@samsung.com>,
	"mark.rutland@arm.com" <mark.rutland@arm.com>,
	"robh+dt@kernel.org" <robh+dt@kernel.org>,
	"linux-clk@vger.kernel.org" <linux-clk@vger.kernel.org>,
	"hkallweit1@gmail.com" <hkallweit1@gmail.com>,
	"bgolaszewski@baylibre.com" <bgolaszewski@baylibre.com>,
	"hofrat@osadl.org" <hofrat@osadl.org>,
	"lee.jones@linaro.org" <lee.jones@linaro.org>,
	"pavel@ucw.cz" <pavel@ucw.cz>,
	"broonie@kernel.org" <broonie@kernel.org>,
	"sboyd@kernel.org" <sboyd@kernel.org>
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core
Date: Tue, 19 Nov 2019 14:23:09 +0000
Message-ID: <93aea8553f7ee96d699792b05892df991d2fe2dc.camel@fi.rohmeurope.com> (raw)
In-Reply-To: <258b5c9934e2b31a5f433a7dbb908dfe5da3d30c.1574059625.git.matti.vaittinen@fi.rohmeurope.com>


On Mon, 2019-11-18 at 09:03 +0200, Matti Vaittinen wrote:
> Qucik grep for 'for_each' or 'linux,default-trigger' or
> 'default-state' under drivers/leds can tell quite a lot. It seems
> multiple LED controller drivers implement the very similar looping
> through the child nodes in order to locate the LED nodes and read
> and support the common LED dt bindings. Implementing this same
> stuff for all LED controllers gets old pretty fast.
> 
> This commit adds support for locating the LED node (based on known
> node names - or linux,led-compatible property) and handling of
> few common LED properties.

This is actually not completely true. I originally thought of adding
led-compatible - but I changed my mind after I saw that at least some
of the existing drivers used value of "reg"-property to do the matching
in-driver. So, to make it simple for them, I did add property
name/value pair in init data for matching. This allows one to do led-
compatible, or to use existing fixed reg (or other) property value. I'd
better to change the commit message to reflect this too.

> 
> linux,default-trigger,
> default-state (with the exception of keep),
> 
> (in addition to already handled
> function-enumerator,
> function,
> color
> and label).
> 
> Regarding the node look-up: If no init_data is given, then no
> node-lookup is done and cdev name is used as such.
> 
> If init_data is goven but no starting point for node lookup - then
> (parent) device's own DT node is used. If no led-compatible is given,
> then of_match is searched for. If neither led-compatible not of_match
> is given then device's own node or passed starting point are used as
> such.

And same here.

> 
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
> 
> Changes from v4:
> Fixed issues reported by Dan Carpenter and kbuild-bot.
> (leftover kfree and uninitialized return value)
> 
>  drivers/leds/led-class.c |  88 ++++++++++++--
>  drivers/leds/led-core.c  | 246 +++++++++++++++++++++++++++++++----
> ----
>  include/linux/leds.h     |  90 ++++++++++++--
>  3 files changed, 359 insertions(+), 65 deletions(-)
> 
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 647b1263c579..98173b64a597 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -235,6 +235,29 @@ static int led_classdev_next_name(const char
> *init_name, char *name,
>  	return i;
>  }
>  
> +static void led_add_props(struct led_classdev *ld, struct
> led_properties *props)
> +{
> +	if (props->default_trigger)
> +		ld->default_trigger = props->default_trigger;
> +	/*
> +	 * According to binding docs the LED is by-default turned OFF
> unless
> +	 * default_state is used to indicate it should be ON or that
> state
> +	 * should be kept as is
> +	 */
> +	if (props->default_state) {
> +		ld->brightness = LED_OFF;
> +		if (!strcmp(props->default_state, "on"))
> +			ld->brightness = LED_FULL;
> +	/*
> +	 * We probably should not call the brightness_get prior calling
> +	 * the of_parse_cb if one is provided.
> +	 * Add a flag to advertice that state should be queried and
> kept as-is.
> +	 */
> +		else if (!strcmp(props->default_state, "keep"))
> +			props->brightness_keep = true;
> +	}
> +}
> +
>  /**
>   * led_classdev_register_ext - register a new object of led_classdev
> class
>   *			       with init data.
> @@ -251,22 +274,58 @@ int led_classdev_register_ext(struct device
> *parent,
>  	char final_name[LED_MAX_NAME_SIZE];
>  	const char *proposed_name = composed_name;
>  	int ret;
> -
> +	struct led_properties props = {0};
> +	struct fwnode_handle *fw;
> +
> +	/*
> +	 * We don't try getting the name based on DT node if init-data
> is not
> +	 * given. We could see if we find LED properties from the
> device's node
> +	 * but that might change LED names for old users of
> +	 * led_classdev_register who have been providing the LED name
> in
> +	 * cdev->name. So we seek fwnode for names only if init_data is
> given
> +	 */
>  	if (init_data) {
> +		led_cdev->init_data = init_data;
>  		if (init_data->devname_mandatory && !init_data-
> >devicename) {
>  			dev_err(parent, "Mandatory device name is
> missing");
>  			return -EINVAL;
>  		}
> -		ret = led_compose_name(parent, init_data,
> composed_name);
> +
> +		fw = led_find_fwnode(parent, init_data);
> +		if (!fw) {
> +			dev_err(parent, "No fwnode found\n");
> +			return -ENOENT;
> +		}
> +		/*
> +		 * We did increase refcount for fwnode. Let's set a
> flag so we
> +		 * can decrease it during deregistration
> +		 */
> +		led_cdev->fwnode_owned = true;
> +
> +		ret = led_parse_fwnode_props(parent, fw, &props);
> +		if (ret)
> +			goto err_out;
> +
> +		if (init_data->of_parse_cb)
> +			ret = init_data->of_parse_cb(led_cdev, fw,
> &props);
>  		if (ret < 0)
> -			return ret;
> +			goto err_out;
> +
> +		led_add_props(led_cdev, &props);
> +
> +		ret = led_compose_name(parent, init_data, &props,
> +				       composed_name);
> +		if (ret < 0)
> +			goto err_out;
>  	} else {
>  		proposed_name = led_cdev->name;
> +		led_cdev->fwnode_owned = false;
> +		fw = NULL;
>  	}
>  
>  	ret = led_classdev_next_name(proposed_name, final_name,
> sizeof(final_name));
>  	if (ret < 0)
> -		return ret;
> +		goto err_out;
>  
>  	mutex_init(&led_cdev->led_access);
>  	mutex_lock(&led_cdev->led_access);
> @@ -274,22 +333,28 @@ int led_classdev_register_ext(struct device
> *parent,
>  				led_cdev, led_cdev->groups, "%s",
> final_name);
>  	if (IS_ERR(led_cdev->dev)) {
>  		mutex_unlock(&led_cdev->led_access);
> -		return PTR_ERR(led_cdev->dev);
> +		ret = PTR_ERR(led_cdev->dev);
> +		goto err_out;
>  	}
> -	if (init_data && init_data->fwnode)
> -		led_cdev->dev->fwnode = init_data->fwnode;
> +	if (fw)
> +		led_cdev->dev->fwnode = fw;
>  
>  	if (ret)
>  		dev_warn(parent, "Led %s renamed to %s due to name
> collision",
>  				led_cdev->name, dev_name(led_cdev-
> >dev));
>  
> +	if (props.brightness_keep)
> +		if (led_cdev->brightness_get)
> +			led_cdev->brightness =
> +				 led_cdev->brightness_get(led_cdev);
> +
>  	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
>  		ret = led_add_brightness_hw_changed(led_cdev);
>  		if (ret) {
>  			device_unregister(led_cdev->dev);
>  			led_cdev->dev = NULL;
>  			mutex_unlock(&led_cdev->led_access);
> -			return ret;
> +			goto err_out;
>  		}
>  	}
>  
> @@ -322,6 +387,10 @@ int led_classdev_register_ext(struct device
> *parent,
>  			led_cdev->name);
>  
>  	return 0;
> +err_out:
> +	if (led_cdev->fwnode_owned)
> +		fwnode_handle_put(fw);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(led_classdev_register_ext);
>  
> @@ -355,6 +424,9 @@ void led_classdev_unregister(struct led_classdev
> *led_cdev)
>  	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
>  		led_remove_brightness_hw_changed(led_cdev);
>  
> +	if (led_cdev->fwnode_owned)
> +		fwnode_handle_put(led_cdev->dev->fwnode);
> +
>  	device_unregister(led_cdev->dev);
>  
>  	down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index f1f718dbe0f8..9369f03ee540 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -365,70 +365,214 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev)
>  }
>  EXPORT_SYMBOL_GPL(led_sysfs_enable);
>  
> -static void led_parse_fwnode_props(struct device *dev,
> -				   struct fwnode_handle *fwnode,
> -				   struct led_properties *props)
> +static int fw_is_match(struct fwnode_handle *fw,
> +		       struct led_fw_match_property *mp, void *val)
>  {
> -	int ret;
> +	void *cmp = mp->raw_val;
> +	int ret = -EINVAL;
> +
> +	if (mp->raw_val) {
> +		ret = fwnode_property_read_u8_array(fw, mp->name, val,
> +						    mp->size);
> +	} else if (mp->intval) {
> +		cmp = mp->intval;
> +		switch (mp->size) {
> +		case 1:
> +			ret = fwnode_property_read_u8_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 2:
> +			ret = fwnode_property_read_u16_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 4:
> +			ret = fwnode_property_read_u32_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 8:
> +			ret = fwnode_property_read_u64_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +	if (!ret && cmp)
> +		if (!memcmp(val, cmp, mp->size))
> +			return 1;
> +
> +	return 0;
> +}
> +/**
> + * led_find_fwnode - find fwnode for led
> + * @parent	LED controller device
> + * @init_data	led init data with match information
> + *
> + * Scans the firmware nodes and returns node matching the given
> init_data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> +				      struct led_init_data *init_data)
> +{
> +	struct fwnode_handle *fw;
> +
> +	/*
> +	 * This should never be called W/O init data. We could always
> return
> +	 * dev_fwnode() - but then we should pump-up the refcount
> +	 */
> +	if (!init_data)
> +		return NULL;
> +
> +	if (!init_data->fwnode)
> +		fw = dev_fwnode(parent);
> +	else
> +		fw = init_data->fwnode;
> +
> +	if (!fw)
> +		return NULL;
> +
> +	/*
> +	 * Simple things are pretty. I think simplest is to use DT
> node-name
> +	 * for matching the node with LED - same way regulators use the
> node
> +	 * name to match with desc.
> +	 *
> +	 * This may not work with existing LED DT entries if the node
> name has
> +	 * been freely selectible. In order to this to work the binding
> doc
> +	 * for LED driver should define usable node names.
> +	 *
> +	 * If this is not working we can define specific match property
> which
> +	 * value we scan and use for matching for LEDs connected to the
> +	 * controller.
> +	 */
> +	if (init_data->match_property.name && init_data-
> >match_property.size) {
> +		u8 *val;
> +		int ret;
> +		struct fwnode_handle *child;
> +		struct led_fw_match_property *mp;
> +
> +		mp = &init_data->match_property;
> +
> +		val = kzalloc(mp->size, GFP_KERNEL);
> +		if (!val)
> +			return ERR_PTR(-ENOMEM);
> +
> +		fwnode_for_each_child_node(fw, child) {
> +			ret = fw_is_match(child, mp, val);
> +			if (ret > 0) {
> +				kfree(val);
> +				return child;
> +			}
> +			if (ret < 0) {
> +				dev_err(parent,
> +					"invalid fw match. Use
> raw_val?\n");
> +				fwnode_handle_put(child);
> +				break;
> +			}
> +		}
> +		kfree(val);
> +	}
> +	if (init_data->of_match)
> +		fw = fwnode_get_named_child_node(fw, init_data-
> >of_match);
> +	else
> +		fw = fwnode_handle_get(fw);
> +
> +	return fw;
> +}
> +EXPORT_SYMBOL(led_find_fwnode);
> +
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> +			   struct led_properties *props)
> +{
> +	int ret = 0;
>  
>  	if (!fwnode)
> -		return;
> +		return 0;
>  
>  	if (fwnode_property_present(fwnode, "label")) {
>  		ret = fwnode_property_read_string(fwnode, "label",
> &props->label);
>  		if (ret)
>  			dev_err(dev, "Error parsing 'label' property
> (%d)\n", ret);
> -		return;
> +		return ret;
>  	}
>  
> +	/**
> +	 * Please note, logic changed - if invalid property is found we
> bail
> +	 * early out without parsing the rest of the properties.
> Originally
> +	 * this was the case only for 'label' property. I don't know
> the
> +	 * rationale behind original logic allowing invalid properties
> to be
> +	 * given. If there is a reason then we should reconsider this.
> +	 * Intuitively it feels correct to just yell and quit if we hit
> value we
> +	 * don't understand - but intuition may be wrong at times :)
> +	 */
>  	if (fwnode_property_present(fwnode, "color")) {
>  		ret = fwnode_property_read_u32(fwnode, "color", &props-
> >color);
> -		if (ret)
> +		if (ret) {
>  			dev_err(dev, "Error parsing 'color' property
> (%d)\n", ret);
> -		else if (props->color >= LED_COLOR_ID_MAX)
> +			return ret;
> +		} else if (props->color >= LED_COLOR_ID_MAX) {
>  			dev_err(dev, "LED color identifier out of
> range\n");
> -		else
> -			props->color_present = true;
> +			return ret;
> +		}
> +		props->color_present = true;
>  	}
>  
> +	if (fwnode_property_present(fwnode, "function")) {
> +		ret = fwnode_property_read_string(fwnode, "function",
> +						  &props->function);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'function' property
> (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
>  
> -	if (!fwnode_property_present(fwnode, "function"))
> -		return;
> -
> -	ret = fwnode_property_read_string(fwnode, "function", &props-
> >function);
> -	if (ret) {
> -		dev_err(dev,
> -			"Error parsing 'function' property (%d)\n",
> -			ret);
> +	if (fwnode_property_present(fwnode, "function-enumerator")) {
> +		ret = fwnode_property_read_u32(fwnode, "function-
> enumerator",
> +					       &props->func_enum);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'function-enumerator'
> property (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +		props->func_enum_present = true;
>  	}
>  
> -	if (!fwnode_property_present(fwnode, "function-enumerator"))
> -		return;
> +	if (fwnode_property_present(fwnode, "default-state")) {
> +		ret = fwnode_property_read_string(fwnode, "default-
> state",
> +						  &props-
> >default_state);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'default-state' property
> (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
>  
> -	ret = fwnode_property_read_u32(fwnode, "function-enumerator",
> -				       &props->func_enum);
> -	if (ret) {
> -		dev_err(dev,
> -			"Error parsing 'function-enumerator' property
> (%d)\n",
> -			ret);
> -	} else {
> -		props->func_enum_present = true;
> +	if (fwnode_property_present(fwnode, "linux,default-trigger")) {
> +		ret = fwnode_property_read_string(fwnode,
> +						  "linux,default-
> trigger",
> +						  &props-
> >default_trigger);
> +		if (ret)
> +			dev_err(dev,
> +				"Error parsing 'linux,default-trigger'
> property (%d)\n",
> +				ret);
>  	}
> +	return ret;
>  }
> +EXPORT_SYMBOL_GPL(led_parse_fwnode_props);
>  
>  int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> -		     char *led_classdev_name)
> +		     struct led_properties *props, char
> *led_classdev_name)
>  {
> -	struct led_properties props = {};
> -	struct fwnode_handle *fwnode = init_data->fwnode;
>  	const char *devicename = init_data->devicename;
>  
>  	if (!led_classdev_name)
>  		return -EINVAL;
>  
> -	led_parse_fwnode_props(dev, fwnode, &props);
> -
> -	if (props.label) {
> +	if (props->label) {
>  		/*
>  		 * If init_data.devicename is NULL, then it indicates
> that
>  		 * DT label should be used as-is for LED class device
> name.
> @@ -436,23 +580,23 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
>  		 * the final LED class device name.
>  		 */
>  		if (!devicename) {
> -			strscpy(led_classdev_name, props.label,
> +			strscpy(led_classdev_name, props->label,
>  				LED_MAX_NAME_SIZE);
>  		} else {
>  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> -				 devicename, props.label);
> +				 devicename, props->label);
>  		}
> -	} else if (props.function || props.color_present) {
> +	} else if (props->function || props->color_present) {
>  		char tmp_buf[LED_MAX_NAME_SIZE];
>  
> -		if (props.func_enum_present) {
> +		if (props->func_enum_present) {
>  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-
> %d",
> -				 props.color_present ?
> led_colors[props.color] : "",
> -				 props.function ?: "",
> props.func_enum);
> +				 props->color_present ?
> led_colors[props->color] : "",
> +				 props->function ?: "", props-
> >func_enum);
>  		} else {
>  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
> -				 props.color_present ?
> led_colors[props.color] : "",
> -				 props.function ?: "");
> +				 props->color_present ?
> led_colors[props->color] : "",
> +				 props->function ?: "");
>  		}
>  		if (init_data->devname_mandatory) {
>  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> @@ -468,11 +612,19 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
>  		}
>  		snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
>  			 devicename, init_data->default_label);
> -	} else if (is_of_node(fwnode)) {
> -		strscpy(led_classdev_name, to_of_node(fwnode)->name,
> -			LED_MAX_NAME_SIZE);
> -	} else
> -		return -EINVAL;
> +	} else {
> +		struct fwnode_handle *fwnode = led_find_fwnode(dev,
> init_data);
> +		int ret = -EINVAL;
> +
> +		if (is_of_node(fwnode)) {
> +			ret = 0;
> +			strscpy(led_classdev_name, to_of_node(fwnode)-
> >name,
> +				LED_MAX_NAME_SIZE);
> +		}
> +		fwnode_handle_put(fwnode);
> +
> +		return ret;
> +	}
>  
>  	return 0;
>  }
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index efb309dba914..aea7d6bc7294 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -13,6 +13,7 @@
>  #include <linux/kernfs.h>
>  #include <linux/list.h>
>  #include <linux/mutex.h>
> +#include <linux/property.h>
>  #include <linux/rwsem.h>
>  #include <linux/spinlock.h>
>  #include <linux/timer.h>
> @@ -20,6 +21,7 @@
>  
>  struct device;
>  struct led_pattern;
> +struct led_classdev;
>  /*
>   * LED Core
>   */
> @@ -31,8 +33,47 @@ enum led_brightness {
>  	LED_FULL	= 255,
>  };
>  
> +struct led_properties {
> +	u32		color;
> +	bool		color_present;
> +	const char	*function;
> +	u32		func_enum;
> +	bool		func_enum_present;
> +	const char	*label;
> +	const char	*default_trigger;
> +	const char	*default_state;
> +	bool		brightness_keep;
> +};
> +
> +struct led_fw_match_property {
> +	const char *name;	/* Name of the property to match */
> +	void *raw_val;		/* Raw property value as present in
> fwnode */
> +	void *intval;		/* Property value if 8,16,32 or 64bit
> integer */
> +	size_t size;		/* Size of value in bytes */
> +};
> +
>  struct led_init_data {
> -	/* device fwnode handle */
> +	/*
> +	 * If DT binding dictates the node name the driver can fill
> of_match
> +	 * corresponding to node name describing this LED. If fwnode is
> given
> +	 * the match is searched from it's child nodes. If not, the
> match is
> +	 * searched from device's own child nodes.
> +	 */
> +	const char *of_match;
> +	/*
> +	 * If fwnode contains property with known value the driver can
> specify
> +	 * correct propertty-value pair here to do the matching. This
> has higher
> +	 * priority than of_match. If fwnode is given the match is
> searched
> +	 * from it's child nodes. If not match is searched from
> device's
> +	 * own child nodes.
> +	 */
> +	struct led_fw_match_property match_property;
> +	/*
> +	 * device fwnode handle. If of_match or led_compatible are not
> given
> +	 * this is used for LED as given. If of_match or led_compatible
> are
> +	 * given then this is used as a parent node whose child nodes
> are
> +	 * scanned for given match.
> +	 */
>  	struct fwnode_handle *fwnode;
>  	/*
>  	 * default <color:function> tuple, for backward compatibility
> @@ -53,9 +94,17 @@ struct led_init_data {
>  	 * set it to true
>  	 */
>  	bool devname_mandatory;
> +	/*
> +	 * Callback which a LED driver can register if it has own non-
> standard
> +	 * DT properties. Core calls this with the located DT node
> during
> +	 * class_device registration
> +	 */
> +	int (*of_parse_cb)(struct led_classdev *ld, struct
> fwnode_handle *fw,
> +			    struct led_properties *props);
>  };
>  
>  struct led_classdev {
> +	struct led_init_data	*init_data;
>  	const char		*name;
>  	enum led_brightness	 brightness;
>  	enum led_brightness	 max_brightness;
> @@ -148,6 +197,7 @@ struct led_classdev {
>  
>  	/* Ensures consistent access to the LED Flash Class device */
>  	struct mutex		led_access;
> +	bool			fwnode_owned;
>  };
>  
>  /**
> @@ -302,6 +352,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
>   * led_compose_name - compose LED class device name
>   * @dev: LED controller device object
>   * @init_data: the LED class device initialization data
> + * @props: LED properties as parsed from fwnode.
>   * @led_classdev_name: composed LED class device name
>   *
>   * Create LED class device name basing on the provided init_data
> argument.
> @@ -311,6 +362,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
>   * Returns: 0 on success or negative error value on failure
>   */
>  extern int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> +			    struct led_properties *props,
>  			    char *led_classdev_name);
>  
>  /**
> @@ -324,6 +376,33 @@ static inline bool led_sysfs_is_disabled(struct
> led_classdev *led_cdev)
>  	return led_cdev->flags & LED_SYSFS_DISABLE;
>  }
>  
> +/**
> + * led_find_fwnode - find fwnode matching given LED init data
> + * @parent: LED controller device this LED is driven by
> + * @init_data: the LED class device initialization data
> + *
> + * Find the fw node matching given LED init data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + *
> + * Returns: node handle or NULL if matching fw node was not found
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> +				      struct led_init_data *init_data);
> +
> +/**
> + * led_parse_fwnode_props - parse LED common properties from fwnode
> + * @dev: pointer to LED device.
> + * @fwnode: LED node containing the properties
> + * @props: structure where found property data is stored.
> + *
> + * Parse the common LED properties from fwnode.
> + *
> + * Returns: 0 on success or negative error value on failure
> + */
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> +			   struct led_properties *props);
> +
>  /*
>   * LED Triggers
>   */
> @@ -490,15 +569,6 @@ struct led_platform_data {
>  	struct led_info	*leds;
>  };
>  
> -struct led_properties {
> -	u32		color;
> -	bool		color_present;
> -	const char	*function;
> -	u32		func_enum;
> -	bool		func_enum_present;
> -	const char	*label;
> -};
> -
>  struct gpio_desc;
>  typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
>  				unsigned long *delay_on,
> -- 
> 2.21.0
> 
> 


  parent reply index

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-18  6:53 [PATCH v5 00/16] Support ROHM BD71828 PMIC Matti Vaittinen
2019-11-18  6:53 ` [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings Matti Vaittinen
2019-11-18 16:25   ` Mark Brown
2019-11-18 18:03     ` Vaittinen, Matti
2019-11-19 18:13       ` Mark Brown
2019-11-19 18:51         ` Vaittinen, Matti
2019-11-19 19:36           ` Mark Brown
2019-11-29  7:48             ` Vaittinen, Matti
2019-11-29 12:09               ` Mark Brown
2019-12-02  7:57                 ` Vaittinen, Matti
2019-12-02 13:11                   ` Mark Brown
2019-12-02 14:02                     ` Vaittinen, Matti
2019-12-04 12:47                       ` Mark Brown
2019-12-04 13:13                         ` Vaittinen, Matti
2019-12-04 14:14                           ` Mark Brown
2019-12-10 10:39                             ` Vaittinen, Matti
2019-12-10 11:14                 ` Vaittinen, Matti
2019-12-10 12:11                   ` Mark Brown
2019-12-10 12:41                     ` Vaittinen, Matti
2019-12-10 12:45                       ` Mark Brown
2019-12-10 13:07                         ` Vaittinen, Matti
2019-11-22 22:48   ` Rob Herring
2019-11-18  6:54 ` [PATCH v5 02/16] dt-bindings: leds: ROHM BD71282 PMIC LED driver Matti Vaittinen
2019-11-22 23:00   ` Rob Herring
2019-11-18  6:54 ` [PATCH v5 03/16] dt-bindings: mfd: Document ROHM BD71828 bindings Matti Vaittinen
2019-11-22 23:05   ` Rob Herring
2019-11-18  6:55 ` [PATCH v5 04/16] mfd: rohm PMICs - use platform_device_id to match MFD sub-devices Matti Vaittinen
2019-11-18  6:55 ` [PATCH v5 05/16] mfd: bd71828: Support ROHM BD71828 PMIC - core Matti Vaittinen
2019-11-18  6:56 ` [PATCH v5 06/16] mfd: input: bd71828: Add power-key support Matti Vaittinen
2019-11-18  6:56 ` [PATCH v5 07/16] clk: bd718x7: Support ROHM BD71828 clk block Matti Vaittinen
2019-11-18  6:57 ` [PATCH v5 08/16] regulator: bd718x7: Split driver to common and bd718x7 specific parts Matti Vaittinen
2019-11-18  6:57 ` [PATCH v5 09/16] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators Matti Vaittinen
2019-11-18 16:20   ` Mark Brown
2019-11-19  9:12     ` Vaittinen, Matti
2019-11-18  6:58 ` [PATCH v5 10/16] gpio: devres: Add devm_gpiod_get_parent_array Matti Vaittinen
2019-11-19 14:43   ` Linus Walleij
2019-11-19 17:54     ` Vaittinen, Matti
2019-11-21 14:13       ` Linus Walleij
2019-11-18  6:59 ` [PATCH v5 11/16] docs: driver-model: Add missing managed GPIO array get functions Matti Vaittinen
2019-11-18  6:59 ` [PATCH v5 12/16] regulator: bd71828: Add GPIO based run-level control for regulators Matti Vaittinen
2019-11-18  7:00 ` [PATCH v5 13/16] rtc: bd70528 add BD71828 support Matti Vaittinen
2019-12-10 13:24   ` Alexandre Belloni
2019-11-18  7:01 ` [PATCH v5 14/16] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs Matti Vaittinen
2019-11-18  9:22   ` Bartosz Golaszewski
2019-11-18  7:03 ` [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core Matti Vaittinen
2019-11-18 21:55   ` Jacek Anaszewski
2019-11-19  7:21     ` Vaittinen, Matti
2019-11-19 19:30       ` Jacek Anaszewski
2019-11-20  7:31         ` Vaittinen, Matti
2019-11-19 14:23   ` Vaittinen, Matti [this message]
2019-11-18  7:03 ` [PATCH v5 16/16] led: bd71828: Support LED outputs on ROHM BD71828 PMIC Matti Vaittinen

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=93aea8553f7ee96d699792b05892df991d2fe2dc.camel@fi.rohmeurope.com \
    --to=matti.vaittinen@fi.rohmeurope.com \
    --cc=a.zummo@towertech.it \
    --cc=alexandre.belloni@bootlin.com \
    --cc=bgolaszewski@baylibre.com \
    --cc=broonie@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=dmurphy@ti.com \
    --cc=hkallweit1@gmail.com \
    --cc=hofrat@osadl.org \
    --cc=jacek.anaszewski@gmail.com \
    --cc=jeffrey.t.kirsher@intel.com \
    --cc=lee.jones@linaro.org \
    --cc=lgirdwood@gmail.com \
    --cc=linus.walleij@linaro.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=linux-rtc@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=mark.rutland@arm.com \
    --cc=mazziesaccount@gmail.com \
    --cc=mchehab+samsung@kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=pavel@ucw.cz \
    --cc=phil.edworthy@renesas.com \
    --cc=robh+dt@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=wsa+renesas@sang-engineering.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-LEDs Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-leds/0 linux-leds/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-leds linux-leds/ https://lore.kernel.org/linux-leds \
		linux-leds@vger.kernel.org
	public-inbox-index linux-leds

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-leds


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git