Lines Matching +full:gpio +full:- +full:leds

1 // SPDX-License-Identifier: GPL-2.0-only
3 * leds-tca6507
9 * blink or double-blink.
11 * This driver can configure each line either as a 'GPIO' which is
12 * out-only (pull-up resistor required) or as an LED with variable
13 * brightness and hardware-assisted blinking.
21 * with separate time for rise, on, fall, off and second-off. Thus if
22 * 3 or more different non-trivial rates are required, software must
25 * support double-blink so 'second-off' always matches 'off'.
42 * delays in the ranges: 56-72, 112-144, 168-216, 224-27504,
43 * 28560-36720.
47 * maximum - 768+768 in this case. Other pairings are not available.
49 * Access to the 3 levels and 2 blinks are on a first-come,
50 * first-served basis. Access can be shared by multiple leds if they
57 * non-zero brightness is used. As 'full' is always available, the
59 * Max at '2', then other leds will have to choose between '2' and
62 * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the
63 * brightness and LEDs using the blink. It can only be reprogrammed
70 * default. Defaults are permitted to be changed freely - they are
76 #include <linux/leds.h>
79 #include <linux/gpio/driver.h>
84 #define TCA6507_LS_LED_OFF 0x0 /* Output HI-Z (off) */
85 #define TCA6507_LS_LED_OFF1 0x1 /* Output HI-Z (off) - not used */
94 struct led_platform_data leds; member
178 int bank; /* Bank used, or -1 */
179 int blink; /* Set if hardware-blinking */
180 } leds[NUM_LEDS]; member
182 struct gpio_chip gpio; member
198 * The second is to be used as a 'fade-on' or 'fade-off' time. in choose_times()
203 * -EINVAL, otherwise return the sum that was achieved, plus 1 in choose_times()
207 * change-time visible (i.e. it is softer). in choose_times()
231 d = abs(msec - tt); in choose_times()
256 return -EINVAL; in choose_times()
260 * Update the register file with the appropriate 3-bit state for the
269 int n = tca->reg_file[bit] & ~mask; in set_select()
272 if (tca->reg_file[bit] != n) { in set_select()
273 tca->reg_file[bit] = n; in set_select()
274 tca->reg_set |= (1 << bit); in set_select()
279 /* Update the register file with the appropriate 4-bit code for one
291 n = tca->reg_file[reg] & ~mask; in set_code()
293 if (tca->reg_file[reg] != n) { in set_code()
294 tca->reg_file[reg] = n; in set_code()
295 tca->reg_set |= 1 << reg; in set_code()
311 tca->bank[bank].level = level; in set_level()
320 result = choose_times(tca->bank[bank].ontime, &c1, &c2); in set_times()
323 dev_dbg(&tca->client->dev, in set_times()
326 c2, time_codes[c2], tca->bank[bank].ontime); in set_times()
329 tca->bank[bank].ontime = result; in set_times()
331 result = choose_times(tca->bank[bank].offtime, &c1, &c2); in set_times()
332 dev_dbg(&tca->client->dev, in set_times()
335 c2, time_codes[c2], tca->bank[bank].offtime); in set_times()
339 tca->bank[bank].offtime = result; in set_times()
350 struct i2c_client *cl = tca->client; in tca6507_work()
355 spin_lock_irq(&tca->lock); in tca6507_work()
356 set = tca->reg_set; in tca6507_work()
357 memcpy(file, tca->reg_file, TCA6507_REG_CNT); in tca6507_work()
358 tca->reg_set = 0; in tca6507_work()
359 spin_unlock_irq(&tca->lock); in tca6507_work()
369 struct tca6507_chip *tca = led->chip; in led_release()
370 if (led->bank >= 0) { in led_release()
371 struct bank *b = tca->bank + led->bank; in led_release()
372 if (led->blink) in led_release()
373 b->time_use--; in led_release()
374 b->level_use--; in led_release()
376 led->blink = 0; in led_release()
377 led->bank = -1; in led_release()
384 int level = TO_LEVEL(led->led_cdev.brightness); in led_prepare()
385 struct tca6507_chip *tca = led->chip; in led_prepare()
391 led->led_cdev.brightness = TO_BRIGHT(level); in led_prepare()
393 set_select(tca, led->num, TCA6507_LS_LED_OFF); in led_prepare()
397 if (led->ontime == 0 || led->offtime == 0) { in led_prepare()
404 int best = -1;/* full-on */ in led_prepare()
405 int diff = 15-level; in led_prepare()
408 set_select(tca, led->num, TCA6507_LS_LED_ON); in led_prepare()
412 for (i = MASTER; i >= BANK0; i--) { in led_prepare()
414 if (tca->bank[i].level == level || in led_prepare()
415 tca->bank[i].level_use == 0) { in led_prepare()
419 d = abs(level - tca->bank[i].level); in led_prepare()
425 if (best == -1) { in led_prepare()
426 /* Best brightness is full-on */ in led_prepare()
427 set_select(tca, led->num, TCA6507_LS_LED_ON); in led_prepare()
428 led->led_cdev.brightness = LED_FULL; in led_prepare()
432 if (!tca->bank[best].level_use) in led_prepare()
435 tca->bank[best].level_use++; in led_prepare()
436 led->bank = best; in led_prepare()
437 set_select(tca, led->num, bank_source[best]); in led_prepare()
438 led->led_cdev.brightness = TO_BRIGHT(tca->bank[best].level); in led_prepare()
447 if (choose_times(led->ontime, &c1, &c2) < 0) in led_prepare()
448 return -EINVAL; in led_prepare()
449 if (choose_times(led->offtime, &c1, &c2) < 0) in led_prepare()
450 return -EINVAL; in led_prepare()
453 if (tca->bank[i].level_use == 0) in led_prepare()
454 /* not in use - it is ours! */ in led_prepare()
456 if (tca->bank[i].level != level) in led_prepare()
457 /* Incompatible level - skip */ in led_prepare()
463 if (tca->bank[i].time_use == 0) in led_prepare()
464 /* Timer not in use, and level matches - use it */ in led_prepare()
467 if (!(tca->bank[i].on_dflt || in led_prepare()
468 led->on_dflt || in led_prepare()
469 tca->bank[i].ontime == led->ontime)) in led_prepare()
473 if (!(tca->bank[i].off_dflt || in led_prepare()
474 led->off_dflt || in led_prepare()
475 tca->bank[i].offtime == led->offtime)) in led_prepare()
484 /* Nothing matches - how sad */ in led_prepare()
485 return -EINVAL; in led_prepare()
487 b = &tca->bank[i]; in led_prepare()
488 if (b->level_use == 0) in led_prepare()
490 b->level_use++; in led_prepare()
491 led->bank = i; in led_prepare()
493 if (b->on_dflt || in led_prepare()
494 !led->on_dflt || in led_prepare()
495 b->time_use == 0) { in led_prepare()
496 b->ontime = led->ontime; in led_prepare()
497 b->on_dflt = led->on_dflt; in led_prepare()
501 if (b->off_dflt || in led_prepare()
502 !led->off_dflt || in led_prepare()
503 b->time_use == 0) { in led_prepare()
504 b->offtime = led->offtime; in led_prepare()
505 b->off_dflt = led->off_dflt; in led_prepare()
512 led->ontime = b->ontime; in led_prepare()
513 led->offtime = b->offtime; in led_prepare()
515 b->time_use++; in led_prepare()
516 led->blink = 1; in led_prepare()
517 led->led_cdev.brightness = TO_BRIGHT(b->level); in led_prepare()
518 set_select(tca, led->num, blink_source[i]); in led_prepare()
524 struct tca6507_chip *tca = led->chip; in led_assign()
528 spin_lock_irqsave(&tca->lock, flags); in led_assign()
534 * to re-establish as steady level. in led_assign()
536 led->ontime = 0; in led_assign()
537 led->offtime = 0; in led_assign()
540 spin_unlock_irqrestore(&tca->lock, flags); in led_assign()
542 if (tca->reg_set) in led_assign()
543 schedule_work(&tca->work); in led_assign()
552 led->led_cdev.brightness = brightness; in tca6507_brightness_set()
553 led->ontime = 0; in tca6507_brightness_set()
554 led->offtime = 0; in tca6507_brightness_set()
566 led->on_dflt = 1; in tca6507_blink_set()
567 else if (delay_on != &led_cdev->blink_delay_on) in tca6507_blink_set()
568 led->on_dflt = 0; in tca6507_blink_set()
569 led->ontime = *delay_on; in tca6507_blink_set()
572 led->off_dflt = 1; in tca6507_blink_set()
573 else if (delay_off != &led_cdev->blink_delay_off) in tca6507_blink_set()
574 led->off_dflt = 0; in tca6507_blink_set()
575 led->offtime = *delay_off; in tca6507_blink_set()
577 if (led->ontime == 0) in tca6507_blink_set()
578 led->ontime = 512; in tca6507_blink_set()
579 if (led->offtime == 0) in tca6507_blink_set()
580 led->offtime = 512; in tca6507_blink_set()
582 if (led->led_cdev.brightness == LED_OFF) in tca6507_blink_set()
583 led->led_cdev.brightness = LED_FULL; in tca6507_blink_set()
585 led->ontime = 0; in tca6507_blink_set()
586 led->offtime = 0; in tca6507_blink_set()
587 led->led_cdev.brightness = LED_OFF; in tca6507_blink_set()
588 return -EINVAL; in tca6507_blink_set()
590 *delay_on = led->ontime; in tca6507_blink_set()
591 *delay_off = led->offtime; in tca6507_blink_set()
602 spin_lock_irqsave(&tca->lock, flags); in tca6507_gpio_set_value()
607 set_select(tca, tca->gpio_map[offset], in tca6507_gpio_set_value()
609 spin_unlock_irqrestore(&tca->lock, flags); in tca6507_gpio_set_value()
610 if (tca->reg_set) in tca6507_gpio_set_value()
611 schedule_work(&tca->work); in tca6507_gpio_set_value()
630 if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) { in tca6507_probe_gpios()
631 /* Configure as a gpio */ in tca6507_probe_gpios()
632 tca->gpio_map[gpios] = i; in tca6507_probe_gpios()
639 tca->gpio.label = "gpio-tca6507"; in tca6507_probe_gpios()
640 tca->gpio.ngpio = gpios; in tca6507_probe_gpios()
641 tca->gpio.base = pdata->gpio_base; in tca6507_probe_gpios()
642 tca->gpio.owner = THIS_MODULE; in tca6507_probe_gpios()
643 tca->gpio.direction_output = tca6507_gpio_direction_output; in tca6507_probe_gpios()
644 tca->gpio.set = tca6507_gpio_set_value; in tca6507_probe_gpios()
645 tca->gpio.parent = dev; in tca6507_probe_gpios()
647 tca->gpio.of_node = of_node_get(dev_of_node(dev)); in tca6507_probe_gpios()
649 err = gpiochip_add_data(&tca->gpio, tca); in tca6507_probe_gpios()
651 tca->gpio.ngpio = 0; in tca6507_probe_gpios()
659 if (tca->gpio.ngpio) in tca6507_remove_gpio()
660 gpiochip_remove(&tca->gpio); in tca6507_remove_gpio()
684 return ERR_PTR(-ENODEV); in tca6507_led_dt_init()
689 return ERR_PTR(-ENOMEM); in tca6507_led_dt_init()
699 fwnode_property_read_string(child, "linux,default-trigger", in tca6507_led_dt_init()
704 "gpio") >= 0) in tca6507_led_dt_init()
710 return ERR_PTR(ret ? : -EINVAL); in tca6507_led_dt_init()
719 return ERR_PTR(-ENOMEM); in tca6507_led_dt_init()
721 pdata->leds.leds = tca_leds; in tca6507_led_dt_init()
722 pdata->leds.num_leds = NUM_LEDS; in tca6507_led_dt_init()
724 pdata->gpio_base = -1; in tca6507_led_dt_init()
739 struct device *dev = &client->dev; in tca6507_probe()
746 adapter = client->adapter; in tca6507_probe()
749 return -EIO; in tca6507_probe()
753 dev_err(dev, "Need %d entries in platform-data list\n", NUM_LEDS); in tca6507_probe()
758 return -ENOMEM; in tca6507_probe()
760 tca->client = client; in tca6507_probe()
761 INIT_WORK(&tca->work, tca6507_work); in tca6507_probe()
762 spin_lock_init(&tca->lock); in tca6507_probe()
766 struct tca6507_led *l = tca->leds + i; in tca6507_probe()
768 l->chip = tca; in tca6507_probe()
769 l->num = i; in tca6507_probe()
770 if (pdata->leds.leds[i].name && !pdata->leds.leds[i].flags) { in tca6507_probe()
771 l->led_cdev.name = pdata->leds.leds[i].name; in tca6507_probe()
772 l->led_cdev.default_trigger in tca6507_probe()
773 = pdata->leds.leds[i].default_trigger; in tca6507_probe()
774 l->led_cdev.brightness_set = tca6507_brightness_set; in tca6507_probe()
775 l->led_cdev.blink_set = tca6507_blink_set; in tca6507_probe()
776 l->bank = -1; in tca6507_probe()
777 err = led_classdev_register(dev, &l->led_cdev); in tca6507_probe()
785 /* set all registers to known state - zero */ in tca6507_probe()
786 tca->reg_set = 0x7f; in tca6507_probe()
787 schedule_work(&tca->work); in tca6507_probe()
791 while (i--) { in tca6507_probe()
792 if (tca->leds[i].led_cdev.name) in tca6507_probe()
793 led_classdev_unregister(&tca->leds[i].led_cdev); in tca6507_probe()
802 struct tca6507_led *tca_leds = tca->leds; in tca6507_remove()
809 cancel_work_sync(&tca->work); in tca6507_remove()
816 .name = "leds-tca6507",