blob: c303893e1a28a37aaca789fd44fc8df58e580fbe [file] [log] [blame]
Fenglin Wu3b507ad2017-11-13 10:46:02 +08001/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/bitops.h>
14#include <linux/device.h>
15#include <linux/err.h>
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/leds.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/of.h>
22#include <linux/of_address.h>
23#include <linux/platform_device.h>
24#include <linux/pwm.h>
25#include <linux/regmap.h>
26#include <linux/types.h>
27
28#define TRILED_REG_TYPE 0x04
29#define TRILED_REG_SUBTYPE 0x05
30#define TRILED_REG_EN_CTL 0x46
31
32/* TRILED_REG_EN_CTL */
33#define TRILED_EN_CTL_MASK GENMASK(7, 5)
34#define TRILED_EN_CTL_MAX_BIT 7
35
36#define TRILED_TYPE 0x19
37#define TRILED_SUBTYPE_LED3H0L12 0x02
38#define TRILED_SUBTYPE_LED2H0L12 0x03
39#define TRILED_SUBTYPE_LED1H2L12 0x04
40
41#define TRILED_NUM_MAX 3
42
43#define PWM_PERIOD_DEFAULT_NS 1000000
Fenglin Wu3b507ad2017-11-13 10:46:02 +080044
45struct pwm_setting {
46 u32 pre_period_ns;
47 u32 period_ns;
48 u32 duty_ns;
49};
50
51struct led_setting {
52 u32 on_ms;
53 u32 off_ms;
54 enum led_brightness brightness;
55 bool blink;
Fenglin Wuc9c06e02018-01-28 13:57:43 +080056 bool breath;
Fenglin Wu3b507ad2017-11-13 10:46:02 +080057};
58
59struct qpnp_led_dev {
60 struct led_classdev cdev;
61 struct pwm_device *pwm_dev;
62 struct pwm_setting pwm_setting;
63 struct led_setting led_setting;
64 struct qpnp_tri_led_chip *chip;
65 struct mutex lock;
66 const char *label;
67 const char *default_trigger;
68 u8 id;
69 bool blinking;
Fenglin Wuc9c06e02018-01-28 13:57:43 +080070 bool breathing;
Fenglin Wu3b507ad2017-11-13 10:46:02 +080071};
72
73struct qpnp_tri_led_chip {
74 struct device *dev;
75 struct regmap *regmap;
76 struct qpnp_led_dev *leds;
77 struct mutex bus_lock;
78 int num_leds;
79 u16 reg_base;
80 u8 subtype;
81};
82
83static int qpnp_tri_led_read(struct qpnp_tri_led_chip *chip, u16 addr, u8 *val)
84{
85 int rc;
86 unsigned int tmp;
87
88 mutex_lock(&chip->bus_lock);
89 rc = regmap_read(chip->regmap, chip->reg_base + addr, &tmp);
90 if (rc < 0)
91 dev_err(chip->dev, "Read addr 0x%x failed, rc=%d\n", addr, rc);
92 else
93 *val = (u8)tmp;
94 mutex_unlock(&chip->bus_lock);
95
96 return rc;
97}
98
99static int qpnp_tri_led_masked_write(struct qpnp_tri_led_chip *chip,
100 u16 addr, u8 mask, u8 val)
101{
102 int rc;
103
104 mutex_lock(&chip->bus_lock);
105 rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
106 if (rc < 0)
107 dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
108 addr, val, mask, rc);
109 mutex_unlock(&chip->bus_lock);
110
111 return rc;
112}
113
114static int __tri_led_config_pwm(struct qpnp_led_dev *led,
115 struct pwm_setting *pwm)
116{
117 struct pwm_state pstate;
118 int rc;
119
120 pwm_get_state(led->pwm_dev, &pstate);
121 pstate.enabled = !!(pwm->duty_ns != 0);
122 pstate.period = pwm->period_ns;
123 pstate.duty_cycle = pwm->duty_ns;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800124 pstate.output_type = led->led_setting.breath ?
125 PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED;
126 /* Use default pattern in PWM device */
127 pstate.output_pattern = NULL;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800128 rc = pwm_apply_state(led->pwm_dev, &pstate);
129
130 if (rc < 0)
131 dev_err(led->chip->dev, "Apply PWM state for %s led failed, rc=%d\n",
132 led->cdev.name, rc);
133
134 return rc;
135}
136
137static int __tri_led_set(struct qpnp_led_dev *led)
138{
139 int rc = 0;
140 u8 val = 0, mask = 0;
141
142 rc = __tri_led_config_pwm(led, &led->pwm_setting);
143 if (rc < 0) {
144 dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
145 led->cdev.name, rc);
146 return rc;
147 }
148
149 mask |= 1 << (TRILED_EN_CTL_MAX_BIT - led->id);
150
151 if (led->pwm_setting.duty_ns == 0)
152 val = 0;
153 else
154 val = mask;
155
156 rc = qpnp_tri_led_masked_write(led->chip, TRILED_REG_EN_CTL,
157 mask, val);
158 if (rc < 0)
159 dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
160 TRILED_REG_EN_CTL, rc);
161
162 return rc;
163}
164
165static int qpnp_tri_led_set(struct qpnp_led_dev *led)
166{
167 u32 on_ms, off_ms, period_ns, duty_ns;
168 enum led_brightness brightness = led->led_setting.brightness;
169 int rc = 0;
170
171 if (led->led_setting.blink) {
172 on_ms = led->led_setting.on_ms;
173 off_ms = led->led_setting.off_ms;
174 if (on_ms > INT_MAX / NSEC_PER_MSEC)
175 duty_ns = INT_MAX - 1;
176 else
177 duty_ns = on_ms * NSEC_PER_MSEC;
178
179 if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
180 period_ns = INT_MAX;
181 duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
182 } else {
183 period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
184 }
185
186 if (period_ns < duty_ns && duty_ns != 0)
187 period_ns = duty_ns + 1;
188 } else {
189 /* Use initial period if no blinking is required */
190 period_ns = led->pwm_setting.pre_period_ns;
191
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800192 if (brightness == LED_OFF)
193 duty_ns = 0;
194 else if (period_ns > INT_MAX / brightness)
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800195 duty_ns = (period_ns / LED_FULL) * brightness;
196 else
197 duty_ns = (period_ns * brightness) / LED_FULL;
198
199 if (period_ns < duty_ns && duty_ns != 0)
200 period_ns = duty_ns + 1;
201 }
202 dev_dbg(led->chip->dev, "PWM settings for %s led: period = %dns, duty = %dns\n",
203 led->cdev.name, period_ns, duty_ns);
204
205 led->pwm_setting.duty_ns = duty_ns;
206 led->pwm_setting.period_ns = period_ns;
207
208 rc = __tri_led_set(led);
209 if (rc < 0) {
210 dev_err(led->chip->dev, "__tri_led_set %s failed, rc=%d\n",
211 led->cdev.name, rc);
212 return rc;
213 }
214
215 if (led->led_setting.blink) {
216 led->cdev.brightness = LED_FULL;
217 led->blinking = true;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800218 led->breathing = false;
219 } else if (led->led_setting.breath) {
220 led->cdev.brightness = LED_FULL;
221 led->blinking = false;
222 led->breathing = true;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800223 } else {
224 led->cdev.brightness = led->led_setting.brightness;
225 led->blinking = false;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800226 led->breathing = false;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800227 }
228
229 return rc;
230}
231
232static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev,
233 enum led_brightness brightness)
234{
235 struct qpnp_led_dev *led =
236 container_of(led_cdev, struct qpnp_led_dev, cdev);
237 int rc = 0;
238
239 mutex_lock(&led->lock);
240 if (brightness > LED_FULL)
241 brightness = LED_FULL;
242
243 if (brightness == led->led_setting.brightness &&
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800244 !led->blinking && !led->breathing) {
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800245 mutex_unlock(&led->lock);
246 return 0;
247 }
248
249 led->led_setting.brightness = brightness;
250 if (!!brightness)
251 led->led_setting.off_ms = 0;
252 else
253 led->led_setting.on_ms = 0;
254 led->led_setting.blink = false;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800255 led->led_setting.breath = false;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800256
257 rc = qpnp_tri_led_set(led);
258 if (rc)
259 dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
260 led->label, rc);
261
262 mutex_unlock(&led->lock);
263
264 return rc;
265}
266
267static enum led_brightness qpnp_tri_led_get_brightness(
268 struct led_classdev *led_cdev)
269{
270 return led_cdev->brightness;
271}
272
273static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev,
274 unsigned long *on_ms, unsigned long *off_ms)
275{
276 struct qpnp_led_dev *led =
277 container_of(led_cdev, struct qpnp_led_dev, cdev);
278 int rc = 0;
279
280 mutex_lock(&led->lock);
281 if (led->blinking && *on_ms == led->led_setting.on_ms &&
282 *off_ms == led->led_setting.off_ms) {
283 dev_dbg(led_cdev->dev, "Ignore, on/off setting is not changed: on %lums, off %lums\n",
284 *on_ms, *off_ms);
285 mutex_unlock(&led->lock);
286 return 0;
287 }
288
289 if (*on_ms == 0) {
290 led->led_setting.blink = false;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800291 led->led_setting.breath = false;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800292 led->led_setting.brightness = LED_OFF;
293 } else if (*off_ms == 0) {
294 led->led_setting.blink = false;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800295 led->led_setting.breath = false;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800296 led->led_setting.brightness = led->cdev.brightness;
297 } else {
298 led->led_setting.on_ms = *on_ms;
299 led->led_setting.off_ms = *off_ms;
300 led->led_setting.blink = true;
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800301 led->led_setting.breath = false;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800302 }
303
304 rc = qpnp_tri_led_set(led);
305 if (rc)
306 dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
307 led->label, rc);
308
309 mutex_unlock(&led->lock);
310 return rc;
311}
312
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800313static ssize_t breath_show(struct device *dev, struct device_attribute *attr,
314 char *buf)
315{
316 struct led_classdev *led_cdev = dev_get_drvdata(dev);
317 struct qpnp_led_dev *led =
318 container_of(led_cdev, struct qpnp_led_dev, cdev);
319
320 return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath);
321}
322
323static ssize_t breath_store(struct device *dev, struct device_attribute *attr,
324 const char *buf, size_t count)
325{
326 int rc;
327 bool breath;
328 struct led_classdev *led_cdev = dev_get_drvdata(dev);
329 struct qpnp_led_dev *led =
330 container_of(led_cdev, struct qpnp_led_dev, cdev);
331
332 rc = kstrtobool(buf, &breath);
333 if (rc < 0)
334 return rc;
335
336 mutex_lock(&led->lock);
337 if (led->breathing == breath)
338 goto unlock;
339
340 led->led_setting.blink = false;
341 led->led_setting.breath = breath;
342 led->led_setting.brightness = breath ? LED_FULL : LED_OFF;
343 rc = qpnp_tri_led_set(led);
344 if (rc < 0)
345 dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
346 led->label, rc);
347
348unlock:
349 mutex_unlock(&led->lock);
350 return (rc < 0) ? rc : count;
351}
352
353static DEVICE_ATTR(breath, 0644, breath_show, breath_store);
354static const struct attribute *breath_attrs[] = {
355 &dev_attr_breath.attr,
356 NULL
357};
358
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800359static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
360{
361 struct qpnp_led_dev *led;
362 int rc, i, j;
363
364 for (i = 0; i < chip->num_leds; i++) {
365 led = &chip->leds[i];
366 mutex_init(&led->lock);
367 led->cdev.name = led->label;
368 led->cdev.max_brightness = LED_FULL;
369 led->cdev.brightness_set_blocking = qpnp_tri_led_set_brightness;
370 led->cdev.brightness_get = qpnp_tri_led_get_brightness;
371 led->cdev.blink_set = qpnp_tri_led_set_blink;
372 led->cdev.default_trigger = led->default_trigger;
373 led->cdev.brightness = LED_OFF;
Fenglin Wu00699452018-02-28 09:21:38 +0800374 led->cdev.flags |= LED_KEEP_TRIGGER;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800375
376 rc = devm_led_classdev_register(chip->dev, &led->cdev);
377 if (rc < 0) {
378 dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
379 led->label, rc);
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800380 goto err_out;
381 }
382
383 if (pwm_get_output_type_supported(led->pwm_dev)
384 & PWM_OUTPUT_MODULATED) {
385 rc = sysfs_create_files(&led->cdev.dev->kobj,
386 breath_attrs);
387 if (rc < 0) {
388 dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n",
389 led->label, rc);
390 goto err_out;
391 }
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800392 }
393 }
394
395 return 0;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800396
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800397err_out:
398 for (j = 0; j <= i; j++) {
399 if (j < i)
400 sysfs_remove_files(&chip->leds[j].cdev.dev->kobj,
401 breath_attrs);
402 mutex_destroy(&chip->leds[j].lock);
403 }
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800404 return rc;
405}
406
407static int qpnp_tri_led_hw_init(struct qpnp_tri_led_chip *chip)
408{
409 int rc = 0;
410 u8 val;
411
412 rc = qpnp_tri_led_read(chip, TRILED_REG_TYPE, &val);
413 if (rc < 0) {
414 dev_err(chip->dev, "Read REG_TYPE failed, rc=%d\n", rc);
415 return rc;
416 }
417
418 if (val != TRILED_TYPE) {
419 dev_err(chip->dev, "invalid subtype(%d)\n", val);
420 return -ENODEV;
421 }
422
423 rc = qpnp_tri_led_read(chip, TRILED_REG_SUBTYPE, &val);
424 if (rc < 0) {
425 dev_err(chip->dev, "Read REG_SUBTYPE failed, rc=%d\n", rc);
426 return rc;
427 }
428
429 chip->subtype = val;
430
431 return 0;
432}
433
434static int qpnp_tri_led_parse_dt(struct qpnp_tri_led_chip *chip)
435{
436 struct device_node *node = chip->dev->of_node, *child_node;
437 struct qpnp_led_dev *led;
438 struct pwm_args pargs;
439 const __be32 *addr;
Kiran Gunda84d7c9e2018-03-15 18:03:06 +0530440 int rc = 0, id, i = 0;
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800441
442 addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
443 if (!addr) {
444 dev_err(chip->dev, "Getting address failed\n");
445 return -EINVAL;
446 }
447 chip->reg_base = be32_to_cpu(addr[0]);
448
449 chip->num_leds = of_get_available_child_count(node);
450 if (chip->num_leds == 0) {
451 dev_err(chip->dev, "No led child node defined\n");
452 return -ENODEV;
453 }
454
455 if (chip->num_leds > TRILED_NUM_MAX) {
456 dev_err(chip->dev, "can't support %d leds(max %d)\n",
457 chip->num_leds, TRILED_NUM_MAX);
458 return -EINVAL;
459 }
460
461 chip->leds = devm_kcalloc(chip->dev, chip->num_leds,
462 sizeof(struct qpnp_led_dev), GFP_KERNEL);
463 if (!chip->leds)
464 return -ENOMEM;
465
466 for_each_available_child_of_node(node, child_node) {
467 rc = of_property_read_u32(child_node, "led-sources", &id);
468 if (rc) {
469 dev_err(chip->dev, "Get led-sources failed, rc=%d\n",
470 rc);
471 return rc;
472 }
473
474 if (id >= TRILED_NUM_MAX) {
475 dev_err(chip->dev, "only support 0~%d current source\n",
476 TRILED_NUM_MAX - 1);
477 return -EINVAL;
478 }
479
480 led = &chip->leds[i++];
481 led->chip = chip;
482 led->id = id;
483 led->label =
484 of_get_property(child_node, "label", NULL) ? :
485 child_node->name;
486
487 led->pwm_dev =
488 devm_of_pwm_get(chip->dev, child_node, NULL);
489 if (IS_ERR(led->pwm_dev)) {
490 rc = PTR_ERR(led->pwm_dev);
491 if (rc != -EPROBE_DEFER)
492 dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
493 led->label, rc);
494 return rc;
495 }
496
497 pwm_get_args(led->pwm_dev, &pargs);
498 if (pargs.period == 0)
499 led->pwm_setting.pre_period_ns = PWM_PERIOD_DEFAULT_NS;
500 else
501 led->pwm_setting.pre_period_ns = pargs.period;
502
503 led->default_trigger = of_get_property(child_node,
504 "linux,default-trigger", NULL);
505 }
506
507 return rc;
508}
509
510static int qpnp_tri_led_probe(struct platform_device *pdev)
511{
512 struct qpnp_tri_led_chip *chip;
513 int rc = 0;
514
515 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
516 if (!chip)
517 return -ENOMEM;
518
519 chip->dev = &pdev->dev;
520 chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
521 if (!chip->regmap) {
522 dev_err(chip->dev, "Getting regmap failed\n");
523 return -EINVAL;
524 }
525
526 rc = qpnp_tri_led_parse_dt(chip);
527 if (rc < 0) {
528 dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
529 rc);
530 return rc;
531 }
532
533 mutex_init(&chip->bus_lock);
534
535 rc = qpnp_tri_led_hw_init(chip);
536 if (rc) {
537 dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
538 goto destroy;
539 }
540
541 dev_set_drvdata(chip->dev, chip);
542 rc = qpnp_tri_led_register(chip);
543 if (rc < 0) {
544 dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
545 rc);
546 goto destroy;
547 }
548
549 dev_dbg(chip->dev, "Tri-led module with subtype 0x%x is detected\n",
550 chip->subtype);
551 return 0;
552destroy:
553 mutex_destroy(&chip->bus_lock);
554 dev_set_drvdata(chip->dev, NULL);
555
556 return rc;
557}
558
559static int qpnp_tri_led_remove(struct platform_device *pdev)
560{
561 int i;
562 struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
563
564 mutex_destroy(&chip->bus_lock);
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800565 for (i = 0; i < chip->num_leds; i++) {
566 sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs);
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800567 mutex_destroy(&chip->leds[i].lock);
Fenglin Wuc9c06e02018-01-28 13:57:43 +0800568 }
Fenglin Wu3b507ad2017-11-13 10:46:02 +0800569 dev_set_drvdata(chip->dev, NULL);
570 return 0;
571}
572
573static const struct of_device_id qpnp_tri_led_of_match[] = {
574 { .compatible = "qcom,tri-led",},
575 { },
576};
577
578static struct platform_driver qpnp_tri_led_driver = {
579 .driver = {
580 .name = "qcom,tri-led",
581 .of_match_table = qpnp_tri_led_of_match,
582 },
583 .probe = qpnp_tri_led_probe,
584 .remove = qpnp_tri_led_remove,
585};
586module_platform_driver(qpnp_tri_led_driver);
587
588MODULE_DESCRIPTION("QTI TRI_LED driver");
589MODULE_LICENSE("GPL v2");
590MODULE_ALIAS("leds:qpnp-tri-led");