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/platform_data/gpio_backlight.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 
20 struct gpio_backlight {
21 	struct device *dev;
22 	struct device *fbdev;
23 
24 	int gpio;
25 	int active;
26 };
27 
28 static int gpio_backlight_update_status(struct backlight_device *bl)
29 {
30 	struct gpio_backlight *gbl = bl_get_data(bl);
31 	int brightness = bl->props.brightness;
32 
33 	if (bl->props.power != FB_BLANK_UNBLANK ||
34 	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
35 	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
36 		brightness = 0;
37 
38 	gpio_set_value(gbl->gpio, brightness ? gbl->active : !gbl->active);
39 
40 	return 0;
41 }
42 
43 static int gpio_backlight_get_brightness(struct backlight_device *bl)
44 {
45 	return bl->props.brightness;
46 }
47 
48 static int gpio_backlight_check_fb(struct backlight_device *bl,
49 				   struct fb_info *info)
50 {
51 	struct gpio_backlight *gbl = bl_get_data(bl);
52 
53 	return gbl->fbdev == NULL || gbl->fbdev == info->dev;
54 }
55 
56 static const struct backlight_ops gpio_backlight_ops = {
57 	.options	= BL_CORE_SUSPENDRESUME,
58 	.update_status	= gpio_backlight_update_status,
59 	.get_brightness	= gpio_backlight_get_brightness,
60 	.check_fb	= gpio_backlight_check_fb,
61 };
62 
63 static int gpio_backlight_probe(struct platform_device *pdev)
64 {
65 	struct gpio_backlight_platform_data *pdata =
66 		dev_get_platdata(&pdev->dev);
67 	struct backlight_properties props;
68 	struct backlight_device *bl;
69 	struct gpio_backlight *gbl;
70 	int ret;
71 
72 	if (!pdata) {
73 		dev_err(&pdev->dev, "failed to find platform data\n");
74 		return -ENODEV;
75 	}
76 
77 	gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL);
78 	if (gbl == NULL)
79 		return -ENOMEM;
80 
81 	gbl->dev = &pdev->dev;
82 	gbl->fbdev = pdata->fbdev;
83 	gbl->gpio = pdata->gpio;
84 	gbl->active = pdata->active_low ? 0 : 1;
85 
86 	ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT |
87 				    (gbl->active ? GPIOF_INIT_LOW
88 						 : GPIOF_INIT_HIGH),
89 				    pdata->name);
90 	if (ret < 0) {
91 		dev_err(&pdev->dev, "unable to request GPIO\n");
92 		return ret;
93 	}
94 
95 	memset(&props, 0, sizeof(props));
96 	props.type = BACKLIGHT_RAW;
97 	props.max_brightness = 1;
98 	bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev),
99 					&pdev->dev, gbl, &gpio_backlight_ops,
100 					&props);
101 	if (IS_ERR(bl)) {
102 		dev_err(&pdev->dev, "failed to register backlight\n");
103 		return PTR_ERR(bl);
104 	}
105 
106 	bl->props.brightness = pdata->def_value;
107 	backlight_update_status(bl);
108 
109 	platform_set_drvdata(pdev, bl);
110 	return 0;
111 }
112 
113 static struct platform_driver gpio_backlight_driver = {
114 	.driver		= {
115 		.name		= "gpio-backlight",
116 		.owner		= THIS_MODULE,
117 	},
118 	.probe		= gpio_backlight_probe,
119 };
120 
121 module_platform_driver(gpio_backlight_driver);
122 
123 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
124 MODULE_DESCRIPTION("GPIO-based Backlight Driver");
125 MODULE_LICENSE("GPL");
126 MODULE_ALIAS("platform:gpio-backlight");
127