1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Backlight Driver for Dialog DA9052 PMICs |
4 | * |
5 | * Copyright(c) 2012 Dialog Semiconductor Ltd. |
6 | * |
7 | * Author: David Dajun Chen <dchen@diasemi.com> |
8 | */ |
9 | |
10 | #include <linux/backlight.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/fb.h> |
13 | #include <linux/module.h> |
14 | #include <linux/platform_device.h> |
15 | |
16 | #include <linux/mfd/da9052/da9052.h> |
17 | #include <linux/mfd/da9052/reg.h> |
18 | |
19 | #define DA9052_MAX_BRIGHTNESS 0xFF |
20 | |
21 | enum { |
22 | DA9052_WLEDS_OFF, |
23 | DA9052_WLEDS_ON, |
24 | }; |
25 | |
26 | enum { |
27 | DA9052_TYPE_WLED1, |
28 | DA9052_TYPE_WLED2, |
29 | DA9052_TYPE_WLED3, |
30 | }; |
31 | |
32 | static const unsigned char wled_bank[] = { |
33 | DA9052_LED1_CONF_REG, |
34 | DA9052_LED2_CONF_REG, |
35 | DA9052_LED3_CONF_REG, |
36 | }; |
37 | |
38 | struct da9052_bl { |
39 | struct da9052 *da9052; |
40 | uint brightness; |
41 | uint state; |
42 | uint led_reg; |
43 | }; |
44 | |
45 | static int da9052_adjust_wled_brightness(struct da9052_bl *wleds) |
46 | { |
47 | unsigned char boost_en; |
48 | unsigned char i_sink; |
49 | int ret; |
50 | |
51 | boost_en = 0x3F; |
52 | i_sink = 0xFF; |
53 | if (wleds->state == DA9052_WLEDS_OFF) { |
54 | boost_en = 0x00; |
55 | i_sink = 0x00; |
56 | } |
57 | |
58 | ret = da9052_reg_write(da9052: wleds->da9052, DA9052_BOOST_REG, val: boost_en); |
59 | if (ret < 0) |
60 | return ret; |
61 | |
62 | ret = da9052_reg_write(da9052: wleds->da9052, DA9052_LED_CONT_REG, val: i_sink); |
63 | if (ret < 0) |
64 | return ret; |
65 | |
66 | ret = da9052_reg_write(da9052: wleds->da9052, reg: wled_bank[wleds->led_reg], val: 0x0); |
67 | if (ret < 0) |
68 | return ret; |
69 | |
70 | usleep_range(min: 10000, max: 11000); |
71 | |
72 | if (wleds->brightness) { |
73 | ret = da9052_reg_write(da9052: wleds->da9052, reg: wled_bank[wleds->led_reg], |
74 | val: wleds->brightness); |
75 | if (ret < 0) |
76 | return ret; |
77 | } |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static int da9052_backlight_update_status(struct backlight_device *bl) |
83 | { |
84 | int brightness = bl->props.brightness; |
85 | struct da9052_bl *wleds = bl_get_data(bl_dev: bl); |
86 | |
87 | wleds->brightness = brightness; |
88 | wleds->state = DA9052_WLEDS_ON; |
89 | |
90 | return da9052_adjust_wled_brightness(wleds); |
91 | } |
92 | |
93 | static int da9052_backlight_get_brightness(struct backlight_device *bl) |
94 | { |
95 | struct da9052_bl *wleds = bl_get_data(bl_dev: bl); |
96 | |
97 | return wleds->brightness; |
98 | } |
99 | |
100 | static const struct backlight_ops da9052_backlight_ops = { |
101 | .update_status = da9052_backlight_update_status, |
102 | .get_brightness = da9052_backlight_get_brightness, |
103 | }; |
104 | |
105 | static int da9052_backlight_probe(struct platform_device *pdev) |
106 | { |
107 | struct backlight_device *bl; |
108 | struct backlight_properties props; |
109 | struct da9052_bl *wleds; |
110 | |
111 | wleds = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct da9052_bl), GFP_KERNEL); |
112 | if (!wleds) |
113 | return -ENOMEM; |
114 | |
115 | wleds->da9052 = dev_get_drvdata(dev: pdev->dev.parent); |
116 | wleds->brightness = 0; |
117 | wleds->led_reg = platform_get_device_id(pdev)->driver_data; |
118 | wleds->state = DA9052_WLEDS_OFF; |
119 | |
120 | memset(&props, 0, sizeof(struct backlight_properties)); |
121 | props.type = BACKLIGHT_RAW; |
122 | props.max_brightness = DA9052_MAX_BRIGHTNESS; |
123 | |
124 | bl = devm_backlight_device_register(dev: &pdev->dev, name: pdev->name, |
125 | parent: wleds->da9052->dev, devdata: wleds, |
126 | ops: &da9052_backlight_ops, props: &props); |
127 | if (IS_ERR(ptr: bl)) { |
128 | dev_err(&pdev->dev, "Failed to register backlight\n" ); |
129 | return PTR_ERR(ptr: bl); |
130 | } |
131 | |
132 | bl->props.max_brightness = DA9052_MAX_BRIGHTNESS; |
133 | bl->props.brightness = 0; |
134 | platform_set_drvdata(pdev, data: bl); |
135 | |
136 | return da9052_adjust_wled_brightness(wleds); |
137 | } |
138 | |
139 | static void da9052_backlight_remove(struct platform_device *pdev) |
140 | { |
141 | struct backlight_device *bl = platform_get_drvdata(pdev); |
142 | struct da9052_bl *wleds = bl_get_data(bl_dev: bl); |
143 | |
144 | wleds->brightness = 0; |
145 | wleds->state = DA9052_WLEDS_OFF; |
146 | da9052_adjust_wled_brightness(wleds); |
147 | } |
148 | |
149 | static const struct platform_device_id da9052_wled_ids[] = { |
150 | { |
151 | .name = "da9052-wled1" , |
152 | .driver_data = DA9052_TYPE_WLED1, |
153 | }, |
154 | { |
155 | .name = "da9052-wled2" , |
156 | .driver_data = DA9052_TYPE_WLED2, |
157 | }, |
158 | { |
159 | .name = "da9052-wled3" , |
160 | .driver_data = DA9052_TYPE_WLED3, |
161 | }, |
162 | { }, |
163 | }; |
164 | MODULE_DEVICE_TABLE(platform, da9052_wled_ids); |
165 | |
166 | static struct platform_driver da9052_wled_driver = { |
167 | .probe = da9052_backlight_probe, |
168 | .remove_new = da9052_backlight_remove, |
169 | .id_table = da9052_wled_ids, |
170 | .driver = { |
171 | .name = "da9052-wled" , |
172 | }, |
173 | }; |
174 | |
175 | module_platform_driver(da9052_wled_driver); |
176 | |
177 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>" ); |
178 | MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC" ); |
179 | MODULE_LICENSE("GPL" ); |
180 | |