1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * gpio_backlight.c - Simple GPIO-controlled backlight |
4 | */ |
5 | |
6 | #include <linux/backlight.h> |
7 | #include <linux/err.h> |
8 | #include <linux/fb.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/init.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/platform_data/gpio_backlight.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/property.h> |
17 | #include <linux/slab.h> |
18 | |
19 | struct gpio_backlight { |
20 | struct device *dev; |
21 | struct gpio_desc *gpiod; |
22 | }; |
23 | |
24 | static int gpio_backlight_update_status(struct backlight_device *bl) |
25 | { |
26 | struct gpio_backlight *gbl = bl_get_data(bl_dev: bl); |
27 | |
28 | gpiod_set_value_cansleep(desc: gbl->gpiod, value: backlight_get_brightness(bd: bl)); |
29 | |
30 | return 0; |
31 | } |
32 | |
33 | static int gpio_backlight_check_fb(struct backlight_device *bl, |
34 | struct fb_info *info) |
35 | { |
36 | struct gpio_backlight *gbl = bl_get_data(bl_dev: bl); |
37 | |
38 | return !gbl->dev || gbl->dev == info->device; |
39 | } |
40 | |
41 | static const struct backlight_ops gpio_backlight_ops = { |
42 | .options = BL_CORE_SUSPENDRESUME, |
43 | .update_status = gpio_backlight_update_status, |
44 | .check_fb = gpio_backlight_check_fb, |
45 | }; |
46 | |
47 | static int gpio_backlight_probe(struct platform_device *pdev) |
48 | { |
49 | struct device *dev = &pdev->dev; |
50 | struct gpio_backlight_platform_data *pdata = dev_get_platdata(dev); |
51 | struct device_node *of_node = dev->of_node; |
52 | struct backlight_properties props; |
53 | struct backlight_device *bl; |
54 | struct gpio_backlight *gbl; |
55 | int ret, init_brightness, def_value; |
56 | |
57 | gbl = devm_kzalloc(dev, size: sizeof(*gbl), GFP_KERNEL); |
58 | if (gbl == NULL) |
59 | return -ENOMEM; |
60 | |
61 | if (pdata) |
62 | gbl->dev = pdata->dev; |
63 | |
64 | def_value = device_property_read_bool(dev, propname: "default-on" ); |
65 | |
66 | gbl->gpiod = devm_gpiod_get(dev, NULL, flags: GPIOD_ASIS); |
67 | if (IS_ERR(ptr: gbl->gpiod)) |
68 | return dev_err_probe(dev, err: PTR_ERR(ptr: gbl->gpiod), |
69 | fmt: "The gpios parameter is missing or invalid\n" ); |
70 | |
71 | memset(&props, 0, sizeof(props)); |
72 | props.type = BACKLIGHT_RAW; |
73 | props.max_brightness = 1; |
74 | bl = devm_backlight_device_register(dev, name: dev_name(dev), parent: dev, devdata: gbl, |
75 | ops: &gpio_backlight_ops, props: &props); |
76 | if (IS_ERR(ptr: bl)) { |
77 | dev_err(dev, "failed to register backlight\n" ); |
78 | return PTR_ERR(ptr: bl); |
79 | } |
80 | |
81 | /* Set the initial power state */ |
82 | if (!of_node || !of_node->phandle) |
83 | /* Not booted with device tree or no phandle link to the node */ |
84 | bl->props.power = def_value ? FB_BLANK_UNBLANK |
85 | : FB_BLANK_POWERDOWN; |
86 | else if (gpiod_get_value_cansleep(desc: gbl->gpiod) == 0) |
87 | bl->props.power = FB_BLANK_POWERDOWN; |
88 | else |
89 | bl->props.power = FB_BLANK_UNBLANK; |
90 | |
91 | bl->props.brightness = 1; |
92 | |
93 | init_brightness = backlight_get_brightness(bd: bl); |
94 | ret = gpiod_direction_output(desc: gbl->gpiod, value: init_brightness); |
95 | if (ret) { |
96 | dev_err(dev, "failed to set initial brightness\n" ); |
97 | return ret; |
98 | } |
99 | |
100 | platform_set_drvdata(pdev, data: bl); |
101 | return 0; |
102 | } |
103 | |
104 | static struct of_device_id gpio_backlight_of_match[] = { |
105 | { .compatible = "gpio-backlight" }, |
106 | { /* sentinel */ } |
107 | }; |
108 | |
109 | MODULE_DEVICE_TABLE(of, gpio_backlight_of_match); |
110 | |
111 | static struct platform_driver gpio_backlight_driver = { |
112 | .driver = { |
113 | .name = "gpio-backlight" , |
114 | .of_match_table = gpio_backlight_of_match, |
115 | }, |
116 | .probe = gpio_backlight_probe, |
117 | }; |
118 | |
119 | module_platform_driver(gpio_backlight_driver); |
120 | |
121 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>" ); |
122 | MODULE_DESCRIPTION("GPIO-based Backlight Driver" ); |
123 | MODULE_LICENSE("GPL" ); |
124 | MODULE_ALIAS("platform:gpio-backlight" ); |
125 | |