xref: /linux/drivers/leds/flash/leds-ktd2692.c (revision c8d430db8eec7d4fd13a6bea27b7086a54eda6da)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * LED driver : leds-ktd2692.c
4  *
5  * Copyright (C) 2015 Samsung Electronics
6  * Ingi Kim <ingi2.kim@samsung.com>
7  */
8 
9 #include <linux/cleanup.h>
10 #include <linux/err.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/leds-expresswire.h>
13 #include <linux/led-class-flash.h>
14 #include <linux/module.h>
15 #include <linux/mutex.h>
16 #include <linux/of.h>
17 #include <linux/platform_device.h>
18 #include <linux/regulator/consumer.h>
19 
20 /* Value related the movie mode */
21 #define KTD2692_MOVIE_MODE_CURRENT_LEVELS	16
22 #define KTD2692_MM_TO_FL_RATIO(x)		((x) / 3)
23 #define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE	8
24 
25 /* Value related the flash mode */
26 #define KTD2692_FLASH_MODE_TIMEOUT_LEVELS	8
27 #define KTD2692_FLASH_MODE_TIMEOUT_DISABLE	0
28 #define KTD2692_FLASH_MODE_CURR_PERCENT(x)	(((x) * 16) / 100)
29 
30 /* Macro for getting offset of flash timeout */
31 #define GET_TIMEOUT_OFFSET(timeout, step)	((timeout) / (step))
32 
33 /* Base register address */
34 #define KTD2692_REG_LVP_BASE			0x00
35 #define KTD2692_REG_FLASH_TIMEOUT_BASE		0x20
36 #define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE	0x40
37 #define KTD2692_REG_MOVIE_CURRENT_BASE		0x60
38 #define KTD2692_REG_FLASH_CURRENT_BASE		0x80
39 #define KTD2692_REG_MODE_BASE			0xA0
40 
41 /* KTD2692 default length of name */
42 #define KTD2692_NAME_LENGTH			20
43 
44 /* Movie / Flash Mode Control */
45 enum ktd2692_led_mode {
46 	KTD2692_MODE_DISABLE = 0,	/* default */
47 	KTD2692_MODE_MOVIE,
48 	KTD2692_MODE_FLASH,
49 };
50 
51 struct ktd2692_led_config_data {
52 	/* maximum LED current in movie mode */
53 	u32 movie_max_microamp;
54 	/* maximum LED current in flash mode */
55 	u32 flash_max_microamp;
56 	/* maximum flash timeout */
57 	u32 flash_max_timeout;
58 	/* max LED brightness level */
59 	enum led_brightness max_brightness;
60 };
61 
62 const struct expresswire_timing ktd2692_timing = {
63 	.poweroff_us = 700,
64 	.data_start_us = 10,
65 	.end_of_data_low_us = 10,
66 	.end_of_data_high_us = 350,
67 	.short_bitset_us = 4,
68 	.long_bitset_us = 12
69 };
70 
71 struct ktd2692_context {
72 	/* Common ExpressWire properties (ctrl GPIO and timing) */
73 	struct expresswire_common_props props;
74 
75 	/* Related LED Flash class device */
76 	struct led_classdev_flash fled_cdev;
77 
78 	/* secures access to the device */
79 	struct mutex lock;
80 	struct regulator *regulator;
81 
82 	struct gpio_desc *aux_gpio;
83 
84 	enum ktd2692_led_mode mode;
85 	enum led_brightness torch_brightness;
86 };
87 
88 static struct ktd2692_context *fled_cdev_to_led(
89 				struct led_classdev_flash *fled_cdev)
90 {
91 	return container_of(fled_cdev, struct ktd2692_context, fled_cdev);
92 }
93 
94 static int ktd2692_led_brightness_set(struct led_classdev *led_cdev,
95 				       enum led_brightness brightness)
96 {
97 	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
98 	struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
99 
100 	mutex_lock(&led->lock);
101 
102 	if (brightness == LED_OFF) {
103 		led->mode = KTD2692_MODE_DISABLE;
104 		gpiod_direction_output(led->aux_gpio, 0);
105 	} else {
106 		expresswire_write_u8(&led->props, brightness |
107 					KTD2692_REG_MOVIE_CURRENT_BASE);
108 		led->mode = KTD2692_MODE_MOVIE;
109 	}
110 
111 	expresswire_write_u8(&led->props, led->mode | KTD2692_REG_MODE_BASE);
112 	mutex_unlock(&led->lock);
113 
114 	return 0;
115 }
116 
117 static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
118 					bool state)
119 {
120 	struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
121 	struct led_flash_setting *timeout = &fled_cdev->timeout;
122 	u32 flash_tm_reg;
123 
124 	mutex_lock(&led->lock);
125 
126 	if (state) {
127 		flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step);
128 		expresswire_write_u8(&led->props, flash_tm_reg
129 				| KTD2692_REG_FLASH_TIMEOUT_BASE);
130 
131 		led->mode = KTD2692_MODE_FLASH;
132 		gpiod_direction_output(led->aux_gpio, 1);
133 	} else {
134 		led->mode = KTD2692_MODE_DISABLE;
135 		gpiod_direction_output(led->aux_gpio, 0);
136 	}
137 
138 	expresswire_write_u8(&led->props, led->mode | KTD2692_REG_MODE_BASE);
139 
140 	fled_cdev->led_cdev.brightness = LED_OFF;
141 	led->mode = KTD2692_MODE_DISABLE;
142 
143 	mutex_unlock(&led->lock);
144 
145 	return 0;
146 }
147 
148 static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
149 					 u32 timeout)
150 {
151 	return 0;
152 }
153 
154 static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg)
155 {
156 	u32 offset, step;
157 	u32 movie_current_microamp;
158 
159 	offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS;
160 	step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp)
161 		/ KTD2692_MOVIE_MODE_CURRENT_LEVELS;
162 
163 	do {
164 		movie_current_microamp = step * offset;
165 		offset--;
166 	} while ((movie_current_microamp > cfg->movie_max_microamp) &&
167 		(offset > 0));
168 
169 	cfg->max_brightness = offset;
170 }
171 
172 static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev,
173 				       struct ktd2692_led_config_data *cfg)
174 {
175 	struct led_flash_setting *setting;
176 
177 	setting = &fled_cdev->timeout;
178 	setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE;
179 	setting->max = cfg->flash_max_timeout;
180 	setting->step = cfg->flash_max_timeout
181 			/ (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1);
182 	setting->val = cfg->flash_max_timeout;
183 }
184 
185 static void ktd2692_setup(struct ktd2692_context *led)
186 {
187 	led->mode = KTD2692_MODE_DISABLE;
188 	expresswire_power_off(&led->props);
189 	gpiod_direction_output(led->aux_gpio, 0);
190 
191 	expresswire_write_u8(&led->props, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1)
192 				 | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE);
193 	expresswire_write_u8(&led->props, KTD2692_FLASH_MODE_CURR_PERCENT(45)
194 				 | KTD2692_REG_FLASH_CURRENT_BASE);
195 }
196 
197 static void regulator_disable_action(void *_data)
198 {
199 	struct device *dev = _data;
200 	struct ktd2692_context *led = dev_get_drvdata(dev);
201 	int ret;
202 
203 	ret = regulator_disable(led->regulator);
204 	if (ret)
205 		dev_err(dev, "Failed to disable supply: %d\n", ret);
206 }
207 
208 static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
209 			    struct ktd2692_led_config_data *cfg)
210 {
211 	struct device_node *np = dev_of_node(dev);
212 	int ret;
213 
214 	if (!np)
215 		return -ENXIO;
216 
217 	led->props.ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
218 	ret = PTR_ERR_OR_ZERO(led->props.ctrl_gpio);
219 	if (ret)
220 		return dev_err_probe(dev, ret, "cannot get ctrl-gpios\n");
221 
222 	led->aux_gpio = devm_gpiod_get_optional(dev, "aux", GPIOD_ASIS);
223 	if (IS_ERR(led->aux_gpio))
224 		return dev_err_probe(dev, PTR_ERR(led->aux_gpio), "cannot get aux-gpios\n");
225 
226 	led->regulator = devm_regulator_get(dev, "vin");
227 	if (IS_ERR(led->regulator))
228 		led->regulator = NULL;
229 
230 	if (led->regulator) {
231 		ret = regulator_enable(led->regulator);
232 		if (ret) {
233 			dev_err(dev, "Failed to enable supply: %d\n", ret);
234 		} else {
235 			ret = devm_add_action_or_reset(dev,
236 						regulator_disable_action, dev);
237 			if (ret)
238 				return ret;
239 		}
240 	}
241 
242 	struct device_node *child_node __free(device_node) =
243 		of_get_next_available_child(np, NULL);
244 	if (!child_node) {
245 		dev_err(dev, "No DT child node found for connected LED.\n");
246 		return -EINVAL;
247 	}
248 
249 	led->fled_cdev.led_cdev.name =
250 		of_get_property(child_node, "label", NULL) ? : child_node->name;
251 
252 	ret = of_property_read_u32(child_node, "led-max-microamp",
253 				   &cfg->movie_max_microamp);
254 	if (ret) {
255 		dev_err(dev, "failed to parse led-max-microamp\n");
256 		return ret;
257 	}
258 
259 	ret = of_property_read_u32(child_node, "flash-max-microamp",
260 				   &cfg->flash_max_microamp);
261 	if (ret) {
262 		dev_err(dev, "failed to parse flash-max-microamp\n");
263 		return ret;
264 	}
265 
266 	ret = of_property_read_u32(child_node, "flash-max-timeout-us",
267 				   &cfg->flash_max_timeout);
268 	if (ret) {
269 		dev_err(dev, "failed to parse flash-max-timeout-us\n");
270 		return ret;
271 	}
272 
273 	return 0;
274 }
275 
276 static const struct led_flash_ops flash_ops = {
277 	.strobe_set = ktd2692_led_flash_strobe_set,
278 	.timeout_set = ktd2692_led_flash_timeout_set,
279 };
280 
281 static int ktd2692_probe(struct platform_device *pdev)
282 {
283 	struct ktd2692_context *led;
284 	struct led_classdev *led_cdev;
285 	struct led_classdev_flash *fled_cdev;
286 	struct ktd2692_led_config_data led_cfg;
287 	int ret;
288 
289 	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
290 	if (!led)
291 		return -ENOMEM;
292 
293 	fled_cdev = &led->fled_cdev;
294 	led_cdev = &fled_cdev->led_cdev;
295 
296 	ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg);
297 	if (ret)
298 		return ret;
299 
300 	ktd2692_init_flash_timeout(fled_cdev, &led_cfg);
301 	ktd2692_init_movie_current_max(&led_cfg);
302 
303 	fled_cdev->ops = &flash_ops;
304 
305 	led_cdev->max_brightness = led_cfg.max_brightness;
306 	led_cdev->brightness_set_blocking = ktd2692_led_brightness_set;
307 	led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
308 
309 	mutex_init(&led->lock);
310 
311 	platform_set_drvdata(pdev, led);
312 
313 	ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
314 	if (ret) {
315 		dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name);
316 		mutex_destroy(&led->lock);
317 		return ret;
318 	}
319 
320 	ktd2692_setup(led);
321 
322 	return 0;
323 }
324 
325 static void ktd2692_remove(struct platform_device *pdev)
326 {
327 	struct ktd2692_context *led = platform_get_drvdata(pdev);
328 
329 	led_classdev_flash_unregister(&led->fled_cdev);
330 
331 	mutex_destroy(&led->lock);
332 }
333 
334 static const struct of_device_id ktd2692_match[] = {
335 	{ .compatible = "kinetic,ktd2692", },
336 	{ /* sentinel */ },
337 };
338 MODULE_DEVICE_TABLE(of, ktd2692_match);
339 
340 static struct platform_driver ktd2692_driver = {
341 	.driver = {
342 		.name  = "ktd2692",
343 		.of_match_table = ktd2692_match,
344 	},
345 	.probe  = ktd2692_probe,
346 	.remove_new = ktd2692_remove,
347 };
348 
349 module_platform_driver(ktd2692_driver);
350 
351 MODULE_IMPORT_NS(EXPRESSWIRE);
352 MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>");
353 MODULE_DESCRIPTION("Kinetic KTD2692 LED driver");
354 MODULE_LICENSE("GPL v2");
355