]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * gpio_backlight.c - Simple GPIO-controlled backlight | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/backlight.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/fb.h> | |
12 | #include <linux/gpio.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/of_gpio.h> | |
18 | #include <linux/platform_data/gpio_backlight.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/slab.h> | |
21 | ||
22 | struct gpio_backlight { | |
23 | struct device *dev; | |
24 | struct device *fbdev; | |
25 | ||
26 | int gpio; | |
27 | int active; | |
28 | int def_value; | |
29 | }; | |
30 | ||
31 | static int gpio_backlight_update_status(struct backlight_device *bl) | |
32 | { | |
33 | struct gpio_backlight *gbl = bl_get_data(bl); | |
34 | int brightness = bl->props.brightness; | |
35 | ||
36 | if (bl->props.power != FB_BLANK_UNBLANK || | |
37 | bl->props.fb_blank != FB_BLANK_UNBLANK || | |
38 | bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) | |
39 | brightness = 0; | |
40 | ||
41 | gpio_set_value_cansleep(gbl->gpio, | |
42 | brightness ? gbl->active : !gbl->active); | |
43 | ||
44 | return 0; | |
45 | } | |
46 | ||
47 | static int gpio_backlight_check_fb(struct backlight_device *bl, | |
48 | struct fb_info *info) | |
49 | { | |
50 | struct gpio_backlight *gbl = bl_get_data(bl); | |
51 | ||
52 | return gbl->fbdev == NULL || gbl->fbdev == info->dev; | |
53 | } | |
54 | ||
55 | static const struct backlight_ops gpio_backlight_ops = { | |
56 | .options = BL_CORE_SUSPENDRESUME, | |
57 | .update_status = gpio_backlight_update_status, | |
58 | .check_fb = gpio_backlight_check_fb, | |
59 | }; | |
60 | ||
61 | static int gpio_backlight_probe_dt(struct platform_device *pdev, | |
62 | struct gpio_backlight *gbl) | |
63 | { | |
64 | struct device_node *np = pdev->dev.of_node; | |
65 | enum of_gpio_flags gpio_flags; | |
66 | ||
67 | gbl->gpio = of_get_gpio_flags(np, 0, &gpio_flags); | |
68 | ||
69 | if (!gpio_is_valid(gbl->gpio)) { | |
70 | if (gbl->gpio != -EPROBE_DEFER) { | |
71 | dev_err(&pdev->dev, | |
72 | "Error: The gpios parameter is missing or invalid.\n"); | |
73 | } | |
74 | return gbl->gpio; | |
75 | } | |
76 | ||
77 | gbl->active = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; | |
78 | ||
79 | gbl->def_value = of_property_read_bool(np, "default-on"); | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int gpio_backlight_probe(struct platform_device *pdev) | |
85 | { | |
86 | struct gpio_backlight_platform_data *pdata = | |
87 | dev_get_platdata(&pdev->dev); | |
88 | struct backlight_properties props; | |
89 | struct backlight_device *bl; | |
90 | struct gpio_backlight *gbl; | |
91 | struct device_node *np = pdev->dev.of_node; | |
92 | unsigned long flags = GPIOF_DIR_OUT; | |
93 | int ret; | |
94 | ||
95 | if (!pdata && !np) { | |
96 | dev_err(&pdev->dev, | |
97 | "failed to find platform data or device tree node.\n"); | |
98 | return -ENODEV; | |
99 | } | |
100 | ||
101 | gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); | |
102 | if (gbl == NULL) | |
103 | return -ENOMEM; | |
104 | ||
105 | gbl->dev = &pdev->dev; | |
106 | ||
107 | if (np) { | |
108 | ret = gpio_backlight_probe_dt(pdev, gbl); | |
109 | if (ret) | |
110 | return ret; | |
111 | } else { | |
112 | gbl->fbdev = pdata->fbdev; | |
113 | gbl->gpio = pdata->gpio; | |
114 | gbl->active = pdata->active_low ? 0 : 1; | |
115 | gbl->def_value = pdata->def_value; | |
116 | } | |
117 | ||
118 | if (gbl->active) | |
119 | flags |= gbl->def_value ? GPIOF_INIT_HIGH : GPIOF_INIT_LOW; | |
120 | else | |
121 | flags |= gbl->def_value ? GPIOF_INIT_LOW : GPIOF_INIT_HIGH; | |
122 | ||
123 | ret = devm_gpio_request_one(gbl->dev, gbl->gpio, flags, | |
124 | pdata ? pdata->name : "backlight"); | |
125 | if (ret < 0) { | |
126 | dev_err(&pdev->dev, "unable to request GPIO\n"); | |
127 | return ret; | |
128 | } | |
129 | ||
130 | memset(&props, 0, sizeof(props)); | |
131 | props.type = BACKLIGHT_RAW; | |
132 | props.max_brightness = 1; | |
133 | bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), | |
134 | &pdev->dev, gbl, &gpio_backlight_ops, | |
135 | &props); | |
136 | if (IS_ERR(bl)) { | |
137 | dev_err(&pdev->dev, "failed to register backlight\n"); | |
138 | return PTR_ERR(bl); | |
139 | } | |
140 | ||
141 | bl->props.brightness = gbl->def_value; | |
142 | backlight_update_status(bl); | |
143 | ||
144 | platform_set_drvdata(pdev, bl); | |
145 | return 0; | |
146 | } | |
147 | ||
148 | #ifdef CONFIG_OF | |
149 | static struct of_device_id gpio_backlight_of_match[] = { | |
150 | { .compatible = "gpio-backlight" }, | |
151 | { /* sentinel */ } | |
152 | }; | |
153 | ||
154 | MODULE_DEVICE_TABLE(of, gpio_backlight_of_match); | |
155 | #endif | |
156 | ||
157 | static struct platform_driver gpio_backlight_driver = { | |
158 | .driver = { | |
159 | .name = "gpio-backlight", | |
160 | .of_match_table = of_match_ptr(gpio_backlight_of_match), | |
161 | }, | |
162 | .probe = gpio_backlight_probe, | |
163 | }; | |
164 | ||
165 | module_platform_driver(gpio_backlight_driver); | |
166 | ||
167 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | |
168 | MODULE_DESCRIPTION("GPIO-based Backlight Driver"); | |
169 | MODULE_LICENSE("GPL"); | |
170 | MODULE_ALIAS("platform:gpio-backlight"); |