mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
leds: leds-gpio: Add support for GPIO descriptors
GPIO descriptors are the preferred way over legacy GPIO numbers nowadays. Convert the driver to use GPIO descriptors internally but still allow passing legacy GPIO numbers from platform data to support existing platforms. Signed-off-by: Mika Westerberg <[email protected]> Acked-by: Alexandre Courbot <[email protected]> Acked-by: Bryan Wu <[email protected]> Acked-by: Arnd Bergmann <[email protected]> Acked-by: Grant Likely <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
This commit is contained in:
parent
c479ff0933
commit
5c51277a9a
2 changed files with 46 additions and 35 deletions
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -24,11 +25,10 @@
|
|||
|
||||
struct gpio_led_data {
|
||||
struct led_classdev cdev;
|
||||
unsigned gpio;
|
||||
struct gpio_desc *gpiod;
|
||||
struct work_struct work;
|
||||
u8 new_level;
|
||||
u8 can_sleep;
|
||||
u8 active_low;
|
||||
u8 blinking;
|
||||
int (*platform_gpio_blink_set)(unsigned gpio, int state,
|
||||
unsigned long *delay_on, unsigned long *delay_off);
|
||||
|
|
@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work)
|
|||
container_of(work, struct gpio_led_data, work);
|
||||
|
||||
if (led_dat->blinking) {
|
||||
led_dat->platform_gpio_blink_set(led_dat->gpio,
|
||||
led_dat->new_level,
|
||||
NULL, NULL);
|
||||
int gpio = desc_to_gpio(led_dat->gpiod);
|
||||
int level = led_dat->new_level;
|
||||
|
||||
if (gpiod_is_active_low(led_dat->gpiod))
|
||||
level = !level;
|
||||
|
||||
led_dat->platform_gpio_blink_set(gpio, level, NULL, NULL);
|
||||
led_dat->blinking = 0;
|
||||
} else
|
||||
gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
|
||||
gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
|
||||
}
|
||||
|
||||
static void gpio_led_set(struct led_classdev *led_cdev,
|
||||
|
|
@ -60,9 +64,6 @@ static void gpio_led_set(struct led_classdev *led_cdev,
|
|||
else
|
||||
level = 1;
|
||||
|
||||
if (led_dat->active_low)
|
||||
level = !level;
|
||||
|
||||
/* Setting GPIOs with I2C/etc requires a task context, and we don't
|
||||
* seem to have a reliable way to know if we're already in one; so
|
||||
* let's just assume the worst.
|
||||
|
|
@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev,
|
|||
schedule_work(&led_dat->work);
|
||||
} else {
|
||||
if (led_dat->blinking) {
|
||||
led_dat->platform_gpio_blink_set(led_dat->gpio, level,
|
||||
NULL, NULL);
|
||||
int gpio = desc_to_gpio(led_dat->gpiod);
|
||||
|
||||
if (gpiod_is_active_low(led_dat->gpiod))
|
||||
level = !level;
|
||||
|
||||
led_dat->platform_gpio_blink_set(gpio, level, NULL,
|
||||
NULL);
|
||||
led_dat->blinking = 0;
|
||||
} else
|
||||
gpio_set_value(led_dat->gpio, level);
|
||||
gpiod_set_value(led_dat->gpiod, level);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,9 +91,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
|
|||
{
|
||||
struct gpio_led_data *led_dat =
|
||||
container_of(led_cdev, struct gpio_led_data, cdev);
|
||||
int gpio = desc_to_gpio(led_dat->gpiod);
|
||||
|
||||
led_dat->blinking = 1;
|
||||
return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
|
||||
return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK,
|
||||
delay_on, delay_off);
|
||||
}
|
||||
|
||||
|
|
@ -97,24 +104,33 @@ static int create_gpio_led(const struct gpio_led *template,
|
|||
{
|
||||
int ret, state;
|
||||
|
||||
led_dat->gpio = -1;
|
||||
if (!template->gpiod) {
|
||||
unsigned long flags = 0;
|
||||
|
||||
/* skip leds that aren't available */
|
||||
if (!gpio_is_valid(template->gpio)) {
|
||||
dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
|
||||
template->gpio, template->name);
|
||||
return 0;
|
||||
/* skip leds that aren't available */
|
||||
if (!gpio_is_valid(template->gpio)) {
|
||||
dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
|
||||
template->gpio, template->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (template->active_low)
|
||||
flags |= GPIOF_ACTIVE_LOW;
|
||||
|
||||
ret = devm_gpio_request_one(parent, template->gpio, flags,
|
||||
template->name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
led_dat->gpiod = gpio_to_desc(template->gpio);
|
||||
if (IS_ERR(led_dat->gpiod))
|
||||
return PTR_ERR(led_dat->gpiod);
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(parent, template->gpio, template->name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
led_dat->cdev.name = template->name;
|
||||
led_dat->cdev.default_trigger = template->default_trigger;
|
||||
led_dat->gpio = template->gpio;
|
||||
led_dat->can_sleep = gpio_cansleep(template->gpio);
|
||||
led_dat->active_low = template->active_low;
|
||||
led_dat->gpiod = template->gpiod;
|
||||
led_dat->can_sleep = gpiod_cansleep(template->gpiod);
|
||||
led_dat->blinking = 0;
|
||||
if (blink_set) {
|
||||
led_dat->platform_gpio_blink_set = blink_set;
|
||||
|
|
@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template,
|
|||
}
|
||||
led_dat->cdev.brightness_set = gpio_led_set;
|
||||
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
|
||||
state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
|
||||
state = !!gpiod_get_value_cansleep(led_dat->gpiod);
|
||||
else
|
||||
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
|
||||
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
|
||||
if (!template->retain_state_suspended)
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
|
||||
ret = gpiod_direction_output(led_dat->gpiod, state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
INIT_WORK(&led_dat->work, gpio_led_work);
|
||||
|
||||
ret = led_classdev_register(parent, &led_dat->cdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return led_classdev_register(parent, &led_dat->cdev);
|
||||
}
|
||||
|
||||
static void delete_gpio_led(struct gpio_led_data *led)
|
||||
{
|
||||
if (!gpio_is_valid(led->gpio))
|
||||
return;
|
||||
led_classdev_unregister(&led->cdev);
|
||||
cancel_work_sync(&led->work);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue