From: Tomi Valkeinen <hidden> Date: 2015-09-08 11:19:56
This series aims to add a led-backlight driver, similar to pwm-backlight, but
using a LED class device underneath.
LED framework has no support for DT or getting a LED class driver from another
kernel driver, so I added minimal functionality to led-class to get
led-backlight working.
Changes to v1:
- Split LED OF parts into separate .h and .c files
- Check for CONFIG_OF and CONFIG_LEDS_CLASS where relevant to leave unused code
out.
- Improved error prints and comments a bit
- Added put_device() into led_put(), as the device was gotten from
class_find_device() which requires a put_device() call.
Tomi
Tomi Valkeinen (3):
leds: Add of_led_get() and led_put()
backlight: add led-backlight driver
devicetree: Add led-backlight binding
.../bindings/video/backlight/led-backlight.txt | 30 +++
drivers/leds/Makefile | 6 +-
drivers/leds/led-class.c | 13 +-
drivers/leds/led-of.c | 82 +++++++
drivers/leds/leds.h | 1 +
drivers/video/backlight/Kconfig | 7 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/led_bl.c | 235 +++++++++++++++++++++
include/linux/leds-of.h | 26 +++
include/linux/leds.h | 2 +
10 files changed, 401 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/video/backlight/led-backlight.txt
create mode 100644 drivers/leds/led-of.c
create mode 100644 drivers/video/backlight/led_bl.c
create mode 100644 include/linux/leds-of.h
--
2.1.4
From: Tomi Valkeinen <hidden> Date: 2015-09-08 11:19:58
This patch adds basic support for a kernel driver to get a LED device.
This will be used by the led-backlight driver.
Only OF version is implemented for now, and the behavior is similar to
PWM's of_pwm_get() and pwm_put().
Signed-off-by: Tomi Valkeinen <redacted>
---
drivers/leds/Makefile | 6 +++-
drivers/leds/led-class.c | 13 +++++++-
drivers/leds/led-of.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/leds/leds.h | 1 +
include/linux/leds-of.h | 26 +++++++++++++++
include/linux/leds.h | 2 ++
6 files changed, 128 insertions(+), 2 deletions(-)
create mode 100644 drivers/leds/led-of.c
create mode 100644 include/linux/leds-of.h
@@ -0,0 +1,82 @@+/*+*LEDClassCoreOFsupport+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#include<linux/leds.h>+#include<linux/of.h>+#include<linux/leds-of.h>+#include<linux/module.h>++#include"leds.h"++/* find OF node for the given led_cdev */+staticstructdevice_node*find_led_of_node(structled_classdev*led_cdev)+{+structdevice*led_dev=led_cdev->dev;+structdevice_node*child;++for_each_child_of_node(led_dev->parent->of_node,child){+if(of_property_match_string(child,"label",led_cdev->name)=0)+returnchild;+}++returnNULL;+}++staticintled_match_led_node(structdevice*led_dev,constvoid*data)+{+structled_classdev*led_cdev=dev_get_drvdata(led_dev);+conststructdevice_node*target_node=data;+structdevice_node*led_node;++led_node=find_led_of_node(led_cdev);+if(!led_node)+return0;++of_node_put(led_node);++returnled_node=target_node?1:0;+}++/**+*of_led_get()-requestaLEDdeviceviatheLEDframework+*@np:devicenodetogettheLEDdevicefrom+*+*ReturnstheLEDdeviceparsedfromthephandlespecifiedinthe"leds"+*propertyofadevicetreenodeoranegativeerror-codeonfailure.+*+*Thecallermustuseled_put()toreleasethedeviceafteruse.+*/+structled_classdev*of_led_get(structdevice_node*np)+{+structdevice*led_dev;+structled_classdev*led_cdev;+structdevice_node*led_node;++led_node=of_parse_phandle(np,"leds",0);+if(!led_node)+returnERR_PTR(-ENODEV);++led_dev=class_find_device(leds_class,NULL,led_node,+led_match_led_node);++of_node_put(led_node);++if(!led_dev){+pr_err("failed to find led device for node %s, deferring probe\n",+of_node_full_name(led_node));+returnERR_PTR(-EPROBE_DEFER);+}++led_cdev=dev_get_drvdata(led_dev);++if(!try_module_get(led_cdev->dev->parent->driver->owner))+returnERR_PTR(-ENODEV);++returnled_cdev;+}+EXPORT_SYMBOL_GPL(of_led_get);
@@ -0,0 +1,30 @@+led-backlight bindings++Required properties:+ - compatible: "led-backlight"+ - leds: phandle to a led OF node [0]+ - brightness-levels: Array of distinct LED brightness levels. These+ are in the range from 0 to 255, passed to the LED class driver.+ - default-brightness-level: the default brightness level (index into the+ array defined by the "brightness-levels" property)+ - power-supply: regulator for supply voltage++Optional properties:+ - enable-gpios: contains a single GPIO specifier for the GPIO which enables+ and disables the backlight (see GPIO binding[1])++[0]: Documentation/devicetree/bindings/leds/common.txt+[1]: Documentation/devicetree/bindings/gpio/gpio.txt++Example:++ backlight {+ compatible = "led-backlight";+ leds = <&backlight_led>;++ brightness-levels = <0 4 8 16 32 64 128 255>;+ default-brightness-level = <6>;++ power-supply = <&vdd_bl_reg>;+ enable-gpios = <&gpio 58 0>;+ };
From: Tomi Valkeinen <hidden> Date: 2015-09-08 11:20:02
This patch adds a led-backlight driver (led_bl), which is similar to
pwm_bl except the driver uses a LED class driver to adjust the
brightness in the HW.
Signed-off-by: Tomi Valkeinen <redacted>
---
drivers/video/backlight/Kconfig | 7 ++
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/led_bl.c | 235 +++++++++++++++++++++++++++++++++++++++
3 files changed, 243 insertions(+)
create mode 100644 drivers/video/backlight/led_bl.c
@@ -0,0 +1,235 @@+/*+*Copyright2015TexasInstruments+*+*Author:TomiValkeinen<tomi.valkeinen@ti.com>+*+*Basedonpwm_bl.c+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;version2oftheLicense.+*/+#include<linux/backlight.h>+#include<linux/gpio/consumer.h>+#include<linux/leds.h>+#include<linux/leds-of.h>+#include<linux/module.h>+#include<linux/platform_device.h>+#include<linux/regulator/consumer.h>+#include<linux/slab.h>++structled_bl_data{+structdevice*dev;+structbacklight_device*bl_dev;++unsignedint*levels;+boolenabled;+structregulator*power_supply;+structgpio_desc*enable_gpio;++structled_classdev*led_cdev;++unsignedintmax_brightness;+unsignedintdefault_brightness;+};++staticvoidled_bl_set_brightness(structled_bl_data*priv,intbrightness)+{+interr;++if(!priv->enabled){+err=regulator_enable(priv->power_supply);+if(err<0)+dev_err(priv->dev,"failed to enable power supply\n");++if(priv->enable_gpio)+gpiod_set_value_cansleep(priv->enable_gpio,1);+}++led_set_brightness(priv->led_cdev,priv->levels[brightness]);++priv->enabled=true;+}++staticvoidled_bl_power_off(structled_bl_data*priv)+{+if(!priv->enabled)+return;++led_set_brightness(priv->led_cdev,LED_OFF);++if(priv->enable_gpio)+gpiod_set_value_cansleep(priv->enable_gpio,0);++regulator_disable(priv->power_supply);++priv->enabled=false;+}++staticintled_bl_update_status(structbacklight_device*bl)+{+structled_bl_data*priv=bl_get_data(bl);+intbrightness=bl->props.brightness;++if(bl->props.power!=FB_BLANK_UNBLANK||+bl->props.fb_blank!=FB_BLANK_UNBLANK||+bl->props.state&BL_CORE_FBBLANK)+brightness=0;++if(brightness>0)+led_bl_set_brightness(priv,brightness);+else+led_bl_power_off(priv);++return0;+}++staticconststructbacklight_opsled_bl_ops={+.update_status=led_bl_update_status,+};++staticintled_bl_parse_dt(structdevice*dev,+structled_bl_data*priv)+{+structdevice_node*node=dev->of_node;+intnum_levels;+u32*levels;+u32value;+intret;++if(!node)+return-ENODEV;++num_levels=of_property_count_u32_elems(node,"brightness-levels");+if(num_levels<0){+dev_err(dev,"failed to find 'brightness-levels'\n");+returnnum_levels;+}++levels=devm_kzalloc(dev,sizeof(u32)*num_levels,GFP_KERNEL);+if(!levels)+return-ENOMEM;++ret=of_property_read_u32_array(node,"brightness-levels",+levels,+num_levels);+if(ret<0){+dev_err(dev,"failed to parse 'brightness-levels'\n");+returnret;+}++ret=of_property_read_u32(node,"default-brightness-level",&value);+if(ret<0){+dev_err(dev,"failed to parse 'default-brightness-level'\n");+returnret;+}++if(value>=num_levels){+dev_err(dev,"invalid default-brightness-level\n");+return-EINVAL;+}++priv->levels=levels;+priv->max_brightness=num_levels-1;+priv->default_brightness=value;++priv->led_cdev=of_led_get(node);+if(IS_ERR(priv->led_cdev)){+dev_err(dev,"failed to get LED device\n");+returnPTR_ERR(priv->led_cdev);+}++return0;+}++staticintled_bl_probe(structplatform_device*pdev)+{+structbacklight_propertiesprops;+structled_bl_data*priv;+intret;++priv=devm_kzalloc(&pdev->dev,sizeof(*priv),GFP_KERNEL);+if(!priv)+return-ENOMEM;++platform_set_drvdata(pdev,priv);++priv->dev=&pdev->dev;++ret=led_bl_parse_dt(&pdev->dev,priv);+if(ret<0){+dev_err(&pdev->dev,"failed to parse DT data\n");+returnret;+}++priv->enable_gpio=devm_gpiod_get_optional(&pdev->dev,"enable",+GPIOD_OUT_LOW);+if(IS_ERR(priv->enable_gpio)){+ret=PTR_ERR(priv->enable_gpio);+gotoerr;+}++priv->power_supply=devm_regulator_get(&pdev->dev,"power");+if(IS_ERR(priv->power_supply)){+ret=PTR_ERR(priv->power_supply);+gotoerr;+}++memset(&props,0,sizeof(structbacklight_properties));+props.type=BACKLIGHT_RAW;+props.max_brightness=priv->max_brightness;+priv->bl_dev=backlight_device_register(dev_name(&pdev->dev),+&pdev->dev,priv,&led_bl_ops,&props);+if(IS_ERR(priv->bl_dev)){+dev_err(&pdev->dev,"failed to register backlight\n");+ret=PTR_ERR(priv->bl_dev);+gotoerr;+}++priv->bl_dev->props.brightness=priv->default_brightness;+backlight_update_status(priv->bl_dev);++return0;++err:+if(priv->led_cdev)+led_put(priv->led_cdev);++returnret;+}++staticintled_bl_remove(structplatform_device*pdev)+{+structled_bl_data*priv=platform_get_drvdata(pdev);+structbacklight_device*bl=priv->bl_dev;++backlight_device_unregister(bl);++led_bl_power_off(priv);++led_put(priv->led_cdev);++return0;+}++staticconststructof_device_idled_bl_of_match[]={+{.compatible="led-backlight"},+{}+};++MODULE_DEVICE_TABLE(of,led_bl_of_match);++staticstructplatform_driverled_bl_driver={+.driver={+.name="led-backlight",+.of_match_table=of_match_ptr(led_bl_of_match),+},+.probe=led_bl_probe,+.remove=led_bl_remove,+};++module_platform_driver(led_bl_driver);++MODULE_DESCRIPTION("LED based Backlight Driver");+MODULE_LICENSE("GPL");+MODULE_ALIAS("platform:led-backlight");
From: Jacek Anaszewski <hidden> Date: 2015-09-08 13:20:38
Hi Tomi,
Thanks for the update.
On 09/08/2015 01:19 PM, Tomi Valkeinen wrote:
This patch adds basic support for a kernel driver to get a LED device.
This will be used by the led-backlight driver.
Only OF version is implemented for now, and the behavior is similar to
PWM's of_pwm_get() and pwm_put().
Signed-off-by: Tomi Valkeinen <redacted>
---
drivers/leds/Makefile | 6 +++-
drivers/leds/led-class.c | 13 +++++++-
drivers/leds/led-of.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/leds/leds.h | 1 +
include/linux/leds-of.h | 26 +++++++++++++++
According to existing naming convention this should be "of_leds.h".
+}
+
+/**
+ * of_led_get() - request a LED device via the LED framework
+ * @np: device node to get the LED device from
+ *
+ * Returns the LED device parsed from the phandle specified in the "leds"
+ * property of a device tree node or a negative error-code on failure.
+ *
+ * The caller must use led_put() to release the device after use.
+ */
+struct led_classdev *of_led_get(struct device_node *np)
+{
+ struct device *led_dev;
+ struct led_classdev *led_cdev;
+ struct device_node *led_node;
+
+ led_node = of_parse_phandle(np, "leds", 0);
+ if (!led_node)
+ return ERR_PTR(-ENODEV);
+
+ led_dev = class_find_device(leds_class, NULL, led_node,
+ led_match_led_node);
+
+ of_node_put(led_node);
+
+ if (!led_dev) {
+ pr_err("failed to find led device for node %s, deferring probe\n",
+ of_node_full_name(led_node));
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ led_cdev = dev_get_drvdata(led_dev);
+
+ if (!try_module_get(led_cdev->dev->parent->driver->owner))
+ return ERR_PTR(-ENODEV);
+
+ return led_cdev;
+}
+EXPORT_SYMBOL_GPL(of_led_get);
@@ -0,0 +1,30 @@+led-backlight bindings++Required properties:+ - compatible: "led-backlight"+ - leds: phandle to a led OF node [0]+ - brightness-levels: Array of distinct LED brightness levels. These+ are in the range from 0 to 255, passed to the LED class driver.+ - default-brightness-level: the default brightness level (index into the+ array defined by the "brightness-levels" property)+ - power-supply: regulator for supply voltage
Hi Tomi
Maybe this regulator should be optional? I could imagine blacklights
without one, in none power sensitive cases, e.g. industrial PC with a
touch screen display, train ticket machine, etc.
Andrew
From: Jacek Anaszewski <hidden> Date: 2015-09-08 14:04:56
On 09/08/2015 01:19 PM, Tomi Valkeinen wrote:
quoted
This patch adds basic support for a kernel driver to get a LED device.
This will be used by the led-backlight driver.
Only OF version is implemented for now, and the behavior is similar to
PWM's of_pwm_get() and pwm_put().
Signed-off-by: Tomi Valkeinen <redacted>
---
drivers/leds/Makefile | 6 +++-
drivers/leds/led-class.c | 13 +++++++-
drivers/leds/led-of.c | 82
++++++++++++++++++++++++++++++++++++++++++++++++
drivers/leds/leds.h | 1 +
include/linux/leds-of.h | 26 +++++++++++++++
@@ -0,0 +1,30 @@+led-backlight bindings++Required properties:+ - compatible: "led-backlight"+ - leds: phandle to a led OF node [0]+ - brightness-levels: Array of distinct LED brightness levels. These+ are in the range from 0 to 255, passed to the LED class driver.+ - default-brightness-level: the default brightness level (index into the+ array defined by the "brightness-levels" property)+ - power-supply: regulator for supply voltage
Hi Tomi
Maybe this regulator should be optional? I could imagine blacklights
without one, in none power sensitive cases, e.g. industrial PC with a
touch screen display, train ticket machine, etc.
Yes, I think so. Especially in this case as this led-backlight device is
not exactly a specific HW device, but more of a virtual device.
Tomi
From: Tomi Valkeinen <hidden> Date: 2015-09-09 12:00:47
On 08/09/15 16:20, Jacek Anaszewski wrote:
Hi Tomi,
Thanks for the update.
On 09/08/2015 01:19 PM, Tomi Valkeinen wrote:
quoted
This patch adds basic support for a kernel driver to get a LED device.
This will be used by the led-backlight driver.
Only OF version is implemented for now, and the behavior is similar to
PWM's of_pwm_get() and pwm_put().
Signed-off-by: Tomi Valkeinen <redacted>
---
drivers/leds/Makefile | 6 +++-
drivers/leds/led-class.c | 13 +++++++-
drivers/leds/led-of.c | 82
++++++++++++++++++++++++++++++++++++++++++++++++
drivers/leds/leds.h | 1 +
include/linux/leds-of.h | 26 +++++++++++++++
According to existing naming convention this should be "of_leds.h".
Right. I was thinking it's "leds" first, and "of" second, but I see
of_*.h is the convention.
+#include <linux/module.h>
+
+#include "leds.h"
+
+/* find OF node for the given led_cdev */
+static struct device_node *find_led_of_node(struct led_classdev
*led_cdev)
+{
+ struct device *led_dev = led_cdev->dev;
+ struct device_node *child;
+
+ for_each_child_of_node(led_dev->parent->of_node, child) {
+ if (of_property_match_string(child, "label", led_cdev->name)
== 0)
Line over 80 characters.
I don't like to split lines to exact 80 chars, when it makes the code
more difficult to read. In this case it's 3 chars over 80, and splitting
the function call above to two lines doesn't look nice to me.
I'll do the func call separately, then it stays under 80 chars.
Ok, but... I think other already existing functions need no-ops also. If
there's a driver that uses of_led_get and led_put, it's sure to use some
other led_* functions also.
So if we want that driver to be compilable when LED support is disabled
in the kernel, we need to provide no-ops for all those functions.
Probably:
led_set_brightness
led_blink_set_oneshot
led_blink_set
Tomi
Ok, but... I think other already existing functions need no-ops also. If
there's a driver that uses of_led_get and led_put, it's sure to use some
other led_* functions also.
So if we want that driver to be compilable when LED support is disabled
in the kernel, we need to provide no-ops for all those functions.
Probably:
led_set_brightness
led_blink_set_oneshot
led_blink_set
That's right. It needs to be addressed soon too. Potentially
this could show up by breaking randconfig build.
--
Best Regards,
Jacek Anaszewski