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 <miked...@newsguy.com>
---

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 linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to