]>
Commit | Line | Data |
---|---|---|
8b770e3c LP |
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> | |
9a6adb33 DC |
16 | #include <linux/of.h> |
17 | #include <linux/of_gpio.h> | |
8b770e3c LP |
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; | |
9a6adb33 | 28 | int def_value; |
8b770e3c LP |
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 | ||
ab1e59b4 TL |
41 | gpio_set_value_cansleep(gbl->gpio, |
42 | brightness ? gbl->active : !gbl->active); | |
8b770e3c LP |
43 | |
44 | return 0; | |
45 | } | |
46 | ||
47 | static int gpio_backlight_get_brightness(struct backlight_device *bl) | |
48 | { | |
49 | return bl->props.brightness; | |
50 | } | |
51 | ||
52 | static int gpio_backlight_check_fb(struct backlight_device *bl, | |
53 | struct fb_info *info) | |
54 | { | |
55 | struct gpio_backlight *gbl = bl_get_data(bl); | |
56 | ||
57 | return gbl->fbdev == NULL || gbl->fbdev == info->dev; | |
58 | } | |
59 | ||
60 | static const struct backlight_ops gpio_backlight_ops = { | |
61 | .options = BL_CORE_SUSPENDRESUME, | |
62 | .update_status = gpio_backlight_update_status, | |
63 | .get_brightness = gpio_backlight_get_brightness, | |
64 | .check_fb = gpio_backlight_check_fb, | |
65 | }; | |
66 | ||
9a6adb33 DC |
67 | static int gpio_backlight_probe_dt(struct platform_device *pdev, |
68 | struct gpio_backlight *gbl) | |
69 | { | |
70 | struct device_node *np = pdev->dev.of_node; | |
71 | enum of_gpio_flags gpio_flags; | |
72 | ||
73 | gbl->gpio = of_get_gpio_flags(np, 0, &gpio_flags); | |
74 | ||
75 | if (!gpio_is_valid(gbl->gpio)) { | |
76 | if (gbl->gpio != -EPROBE_DEFER) { | |
77 | dev_err(&pdev->dev, | |
78 | "Error: The gpios parameter is missing or invalid.\n"); | |
79 | } | |
80 | return gbl->gpio; | |
81 | } | |
82 | ||
83 | gbl->active = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; | |
84 | ||
85 | gbl->def_value = of_property_read_bool(np, "default-on"); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
8b770e3c LP |
90 | static int gpio_backlight_probe(struct platform_device *pdev) |
91 | { | |
c512794c JH |
92 | struct gpio_backlight_platform_data *pdata = |
93 | dev_get_platdata(&pdev->dev); | |
8b770e3c LP |
94 | struct backlight_properties props; |
95 | struct backlight_device *bl; | |
96 | struct gpio_backlight *gbl; | |
9a6adb33 | 97 | struct device_node *np = pdev->dev.of_node; |
8b770e3c LP |
98 | int ret; |
99 | ||
9a6adb33 DC |
100 | if (!pdata && !np) { |
101 | dev_err(&pdev->dev, | |
102 | "failed to find platform data or device tree node.\n"); | |
8b770e3c LP |
103 | return -ENODEV; |
104 | } | |
105 | ||
106 | gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); | |
107 | if (gbl == NULL) | |
108 | return -ENOMEM; | |
109 | ||
110 | gbl->dev = &pdev->dev; | |
9a6adb33 DC |
111 | |
112 | if (np) { | |
113 | ret = gpio_backlight_probe_dt(pdev, gbl); | |
114 | if (ret) | |
115 | return ret; | |
116 | } else { | |
117 | gbl->fbdev = pdata->fbdev; | |
118 | gbl->gpio = pdata->gpio; | |
119 | gbl->active = pdata->active_low ? 0 : 1; | |
120 | gbl->def_value = pdata->def_value; | |
121 | } | |
8b770e3c LP |
122 | |
123 | ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT | | |
124 | (gbl->active ? GPIOF_INIT_LOW | |
125 | : GPIOF_INIT_HIGH), | |
9a6adb33 | 126 | pdata ? pdata->name : "backlight"); |
8b770e3c LP |
127 | if (ret < 0) { |
128 | dev_err(&pdev->dev, "unable to request GPIO\n"); | |
129 | return ret; | |
130 | } | |
131 | ||
132 | memset(&props, 0, sizeof(props)); | |
133 | props.type = BACKLIGHT_RAW; | |
134 | props.max_brightness = 1; | |
ff472015 JH |
135 | bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), |
136 | &pdev->dev, gbl, &gpio_backlight_ops, | |
137 | &props); | |
8b770e3c LP |
138 | if (IS_ERR(bl)) { |
139 | dev_err(&pdev->dev, "failed to register backlight\n"); | |
140 | return PTR_ERR(bl); | |
141 | } | |
142 | ||
9a6adb33 | 143 | bl->props.brightness = gbl->def_value; |
8b770e3c LP |
144 | backlight_update_status(bl); |
145 | ||
146 | platform_set_drvdata(pdev, bl); | |
147 | return 0; | |
148 | } | |
149 | ||
9a6adb33 DC |
150 | #ifdef CONFIG_OF |
151 | static struct of_device_id gpio_backlight_of_match[] = { | |
152 | { .compatible = "gpio-backlight" }, | |
153 | { /* sentinel */ } | |
154 | }; | |
155 | #endif | |
156 | ||
8b770e3c LP |
157 | static struct platform_driver gpio_backlight_driver = { |
158 | .driver = { | |
159 | .name = "gpio-backlight", | |
160 | .owner = THIS_MODULE, | |
9a6adb33 | 161 | .of_match_table = of_match_ptr(gpio_backlight_of_match), |
8b770e3c LP |
162 | }, |
163 | .probe = gpio_backlight_probe, | |
8b770e3c LP |
164 | }; |
165 | ||
166 | module_platform_driver(gpio_backlight_driver); | |
167 | ||
168 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | |
169 | MODULE_DESCRIPTION("GPIO-based Backlight Driver"); | |
170 | MODULE_LICENSE("GPL"); | |
171 | MODULE_ALIAS("platform:gpio-backlight"); |