[PATCH] pwm-backlight: add support for device tree gpio control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




This patch adds support for controlling an arbitrary number of gpios to the
pwm-backlight driver.  This was left as a TODO when initial device tree support
was added by Thierry a while back.  This functionality replaces the callbacks
that are passed in the platform data for non-DT cases.  Users can avail
themselves of this feature by adding a 'gpios' property to the 'backlight' node.
When the update_status() callback in backlight_ops runs, the gpios listed in the
property are asserted/deasserted if the specified brightness is non-zero/zero.

Tested on a pxa270-based Palm Treo 680.

Signed-off-by: Mike Dunn <mikedunn@xxxxxxxxxxx>
---

Thanks for looking!

 .../bindings/video/backlight/pwm-backlight.txt     |   4 +
 drivers/video/backlight/pwm_bl.c                   | 128 ++++++++++++++++++---
 2 files changed, 113 insertions(+), 19 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
index 1e4fc72..4583e68 100644
--- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
@@ -14,6 +14,9 @@ Required properties:
 Optional properties:
   - pwm-names: a list of names for the PWM devices specified in the
                "pwms" property (see PWM binding[0])
+  - gpios:     An arbitrary number of gpios that must be asserted when the
+               backlight is on, and de-asserted when off.  They will be asserted
+               in the order they appear, and de-asserted in reverse order.
 
 [0]: Documentation/devicetree/bindings/pwm/pwm.txt
 
@@ -25,4 +28,5 @@ Example:
 
 		brightness-levels = <0 4 8 16 32 64 128 255>;
 		default-brightness-level = <6>;
+		gpios = <&gpio 77 0>, <&gpio 25 1>;  /* gpio 25 is active low */
 	};
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 1fea627..1e2ab52 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -20,6 +20,12 @@
 #include <linux/pwm.h>
 #include <linux/pwm_backlight.h>
 #include <linux/slab.h>
+#include <linux/of_gpio.h>
+
+struct pwm_bl_gpio {
+	unsigned int gpio;
+	enum of_gpio_flags flags;
+};
 
 struct pwm_bl_data {
 	struct pwm_device	*pwm;
@@ -27,6 +33,8 @@ struct pwm_bl_data {
 	unsigned int		period;
 	unsigned int		lth_brightness;
 	unsigned int		*levels;
+	unsigned int		num_gpios;
+	struct pwm_bl_gpio	*gpios;
 	int			(*notify)(struct device *,
 					  int brightness);
 	void			(*notify_after)(struct device *,
@@ -94,14 +102,77 @@ static const struct backlight_ops pwm_backlight_ops = {
 };
 
 #ifdef CONFIG_OF
+static int pwm_backlight_dt_notify(struct device *dev, int brightness)
+{
+	struct backlight_device *bl = dev_get_drvdata(dev);
+	struct pwm_bl_data *pb = bl_get_data(bl);
+	int i;
+
+	if (brightness) {
+		for (i = 0; i < pb->num_gpios; i++) {
+			if (pb->gpios[i].flags == OF_GPIO_ACTIVE_LOW)
+				gpio_set_value(pb->gpios[i].gpio, 0);
+			else
+				gpio_set_value(pb->gpios[i].gpio, 1);
+		}
+		return 0;
+	}
+
+	/* de-assert gpios in reverse order, in case this is important */
+	for (i = pb->num_gpios - 1; i >= 0; i--) {
+		if (pb->gpios[i].flags == OF_GPIO_ACTIVE_LOW)
+			gpio_set_value(pb->gpios[i].gpio, 1);
+		else
+			gpio_set_value(pb->gpios[i].gpio, 0);
+	}
+	return 0;
+}
+
+static void pwm_backlight_dt_exit(struct pwm_bl_data *pb)
+{
+	int i;
+
+	for (i = 0; i < pb->num_gpios; i++)
+		gpio_free(pb->gpios[i].gpio);
+}
+
+static int pwm_backlight_dt_init(struct device *dev, struct pwm_bl_data *pb)
+{
+	int i, j, ret;
+
+	/* request gpios and drive in the inactive state */
+	for (i = 0; i < pb->num_gpios; i++) {
+		char gpio_name[32];
+		unsigned long flags;
+		if (pb->gpios[i].flags == OF_GPIO_ACTIVE_LOW)
+			flags = GPIOF_OUT_INIT_LOW;
+		else
+			flags = GPIOF_OUT_INIT_HIGH;
+		snprintf(gpio_name, 32, "%s.%d", dev_name(dev), i);
+		ret = gpio_request_one(pb->gpios[i].gpio, flags, gpio_name);
+		if (ret) {
+			dev_err(dev, "gpio #%d request failed\n", i);
+			goto gpio_err;
+		}
+	}
+	return 0;
+
+ gpio_err:
+	for (j = 0; j < i; j++)
+		gpio_free(pb->gpios[j].gpio);
+	return ret;
+}
+
 static int pwm_backlight_parse_dt(struct device *dev,
-				  struct platform_pwm_backlight_data *data)
+				  struct platform_pwm_backlight_data *data,
+				  struct pwm_bl_data *pb)
 {
 	struct device_node *node = dev->of_node;
 	struct property *prop;
 	int length;
 	u32 value;
-	int ret;
+	int ret, i, num_gpios;
+	size_t gpiosize;
 
 	if (!node)
 		return -ENODEV;
@@ -138,13 +209,29 @@ static int pwm_backlight_parse_dt(struct device *dev,
 		data->max_brightness--;
 	}
 
-	/*
-	 * TODO: Most users of this driver use a number of GPIOs to control
-	 *       backlight power. Support for specifying these needs to be
-	 *       added.
-	 */
+	/* read gpios from DT property */
+	num_gpios = of_gpio_count(node);
+	if (num_gpios == -ENOENT)
+		return 0;	/* no 'gpios' property present */
+	if (num_gpios < 0) {
+		dev_err(dev, "invalid DT node: gpios\n");
+		return -EINVAL;
+	}
+	gpiosize = sizeof(struct pwm_bl_gpio) * num_gpios;
+	pb->gpios = devm_kzalloc(dev, gpiosize, GFP_KERNEL);
+	if (!pb->gpios)
+		return -ENOMEM;
+	for (i = 0; i < num_gpios; i++) {
+		int gpio;
+		enum of_gpio_flags flags;
+		gpio = of_get_gpio_flags(node, i, &flags);
+		pb->gpios[i].gpio = (unsigned int)gpio;
+		pb->gpios[i].flags = flags;
+	}
+	pb->num_gpios = (unsigned int)num_gpios;
+	pb->notify = pwm_backlight_dt_notify;
 
-	return 0;
+	return pwm_backlight_dt_init(dev, pb);
 }
 
 static struct of_device_id pwm_backlight_of_match[] = {
@@ -155,10 +242,12 @@ static struct of_device_id pwm_backlight_of_match[] = {
 MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
 #else
 static int pwm_backlight_parse_dt(struct device *dev,
-				  struct platform_pwm_backlight_data *data)
+				  struct platform_pwm_backlight_data *data,
+				  struct pwm_bl_data *pb)
 {
 	return -ENODEV;
 }
+static void pwm_backlight_dt_exit(struct pwm_bl_data *pb) {}
 #endif
 
 static int pwm_backlight_probe(struct platform_device *pdev)
@@ -171,8 +260,14 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	unsigned int max;
 	int ret;
 
+	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
+	if (!pb) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
 	if (!data) {
-		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata, pb);
 		if (ret < 0) {
 			dev_err(&pdev->dev, "failed to find platform data\n");
 			return ret;
@@ -187,20 +282,14 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 			return ret;
 	}
 
-	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
-	if (!pb) {
-		dev_err(&pdev->dev, "no memory for state\n");
-		ret = -ENOMEM;
-		goto err_alloc;
-	}
-
 	if (data->levels) {
 		max = data->levels[data->max_brightness];
 		pb->levels = data->levels;
 	} else
 		max = data->max_brightness;
 
-	pb->notify = data->notify;
+	if (pb->notify == NULL)	/* not using DT and its built-in notify() */
+		pb->notify = data->notify;
 	pb->notify_after = data->notify_after;
 	pb->check_fb = data->check_fb;
 	pb->exit = data->exit;
@@ -250,9 +339,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	}
 
 	bl->props.brightness = data->dft_brightness;
+	platform_set_drvdata(pdev, bl);
 	backlight_update_status(bl);
 
-	platform_set_drvdata(pdev, bl);
 	return 0;
 
 err_alloc:
@@ -271,6 +360,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
 	pwm_disable(pb->pwm);
 	if (pb->exit)
 		pb->exit(&pdev->dev);
+	pwm_backlight_dt_exit(pb);
 	return 0;
 }
 
-- 
1.8.1.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux