Loading drivers/leds/leds-pwm.c +136 −13 Original line number Diff line number Diff line Loading @@ -22,12 +22,29 @@ #include <linux/leds_pwm.h> #include <linux/slab.h> #define PWM_PERIOD_DEFAULT_NS 1000000 struct pwm_setting { u64 period_ns; u64 duty_ns; }; struct led_setting { u64 on_ms; u64 off_ms; enum led_brightness brightness; bool blink; }; struct led_pwm_data { struct led_classdev cdev; struct pwm_device *pwm; struct pwm_setting pwm_setting; struct led_setting led_setting; unsigned int active_low; unsigned int period; int duty; bool blinking; }; struct led_pwm_priv { Loading @@ -35,35 +52,139 @@ struct led_pwm_priv { struct led_pwm_data leds[0]; }; static void __led_pwm_set(struct led_pwm_data *led_dat) static int __led_blink_config_pwm(struct led_pwm_data *led_data) { struct pwm_state pstate; int rc; pwm_get_state(led_data->pwm, &pstate); pstate.enabled = !!(led_data->pwm_setting.duty_ns != 0); pstate.period = led_data->pwm_setting.period_ns; pstate.duty_cycle = led_data->pwm_setting.duty_ns; /* Use default pattern in PWM device */ pstate.output_pattern = NULL; rc = pwm_apply_state(led_data->pwm, &pstate); if (rc < 0) pr_err("Apply PWM state for %s led failed, rc=%d\n", led_data->cdev.name, rc); return rc; } static int led_pwm_set_blink(struct led_pwm_data *led_data) { u64 on_ms, off_ms, period_ns, duty_ns; enum led_brightness brightness = led_data->led_setting.brightness; int rc = 0; if (led_data->led_setting.blink) { on_ms = led_data->led_setting.on_ms; off_ms = led_data->led_setting.off_ms; duty_ns = on_ms * NSEC_PER_MSEC; period_ns = (on_ms + off_ms) * NSEC_PER_MSEC; if (period_ns < duty_ns && duty_ns != 0) period_ns = duty_ns + 1; } else { /* Use initial period if no blinking is required */ period_ns = PWM_PERIOD_DEFAULT_NS; duty_ns = period_ns * brightness; do_div(duty_ns, LED_FULL); if (period_ns < duty_ns && duty_ns != 0) period_ns = duty_ns + 1; } pr_debug("BLINK: PWM settings for %s led: period = %lluns, duty = %lluns brightness = %d\n", led_data->cdev.name, period_ns, duty_ns, brightness); led_data->pwm_setting.duty_ns = duty_ns; led_data->pwm_setting.period_ns = period_ns; rc = __led_blink_config_pwm(led_data); if (rc < 0) { pr_err("failed to config pwm for blink %s failed, rc=%d\n", led_data->cdev.name, rc); return rc; } if (led_data->led_setting.blink) { led_data->cdev.brightness = LED_FULL; led_data->blinking = true; } else { led_data->cdev.brightness = led_data->led_setting.brightness; led_data->blinking = false; } return rc; } static int led_pwm_blink_set(struct led_classdev *led_cdev, unsigned long *on_ms, unsigned long *off_ms) { struct led_pwm_data *led_data = container_of(led_cdev, struct led_pwm_data, cdev); int rc = 0; if (led_data->blinking && *on_ms == led_data->led_setting.on_ms && *off_ms == led_data->led_setting.off_ms) { pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n", *on_ms, *off_ms); return 0; } if (*on_ms == 0) { led_data->led_setting.blink = false; led_data->led_setting.brightness = LED_OFF; } else if (*off_ms == 0) { led_data->led_setting.blink = false; led_data->led_setting.brightness = led_data->cdev.brightness; } else { led_data->led_setting.on_ms = *on_ms; led_data->led_setting.off_ms = *off_ms; led_data->led_setting.blink = true; } rc = led_pwm_set_blink(led_data); if (rc < 0) pr_err("blink led failed for rc=%d\n", rc); return rc; } static void __led_pwm_set(struct led_pwm_data *led_data) { int new_duty = led_dat->duty; int new_duty = led_data->duty; pwm_config(led_dat->pwm, new_duty, led_dat->period); pwm_config(led_data->pwm, new_duty, led_data->period); if (new_duty == 0) pwm_disable(led_dat->pwm); pwm_disable(led_data->pwm); else pwm_enable(led_dat->pwm); pwm_enable(led_data->pwm); } static int led_pwm_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct led_pwm_data *led_dat = struct led_pwm_data *led_data = container_of(led_cdev, struct led_pwm_data, cdev); unsigned int max = led_dat->cdev.max_brightness; unsigned long long duty = led_dat->period; unsigned int max = led_data->cdev.max_brightness; unsigned long long duty = led_data->period; duty *= brightness; do_div(duty, max); if (led_dat->active_low) duty = led_dat->period - duty; if (led_data->active_low) duty = led_data->period - duty; led_dat->duty = duty; led_data->duty = duty; __led_pwm_set(led_dat); __led_pwm_set(led_data); return 0; } Loading Loading @@ -92,7 +213,8 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, led_data->cdev.default_trigger = led->default_trigger; led_data->cdev.brightness = LED_OFF; led_data->cdev.max_brightness = led->max_brightness; led_data->cdev.flags = LED_CORE_SUSPENDRESUME; /* Set a flag to keep the trigger always */ led_data->cdev.flags |= LED_KEEP_TRIGGER; if (child) led_data->pwm = devm_of_pwm_get(dev, child, NULL); Loading @@ -107,6 +229,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, } led_data->cdev.brightness_set_blocking = led_pwm_set; led_data->cdev.blink_set = led_pwm_blink_set; /* * FIXME: pwm_apply_args() should be removed when switching to the Loading Loading
drivers/leds/leds-pwm.c +136 −13 Original line number Diff line number Diff line Loading @@ -22,12 +22,29 @@ #include <linux/leds_pwm.h> #include <linux/slab.h> #define PWM_PERIOD_DEFAULT_NS 1000000 struct pwm_setting { u64 period_ns; u64 duty_ns; }; struct led_setting { u64 on_ms; u64 off_ms; enum led_brightness brightness; bool blink; }; struct led_pwm_data { struct led_classdev cdev; struct pwm_device *pwm; struct pwm_setting pwm_setting; struct led_setting led_setting; unsigned int active_low; unsigned int period; int duty; bool blinking; }; struct led_pwm_priv { Loading @@ -35,35 +52,139 @@ struct led_pwm_priv { struct led_pwm_data leds[0]; }; static void __led_pwm_set(struct led_pwm_data *led_dat) static int __led_blink_config_pwm(struct led_pwm_data *led_data) { struct pwm_state pstate; int rc; pwm_get_state(led_data->pwm, &pstate); pstate.enabled = !!(led_data->pwm_setting.duty_ns != 0); pstate.period = led_data->pwm_setting.period_ns; pstate.duty_cycle = led_data->pwm_setting.duty_ns; /* Use default pattern in PWM device */ pstate.output_pattern = NULL; rc = pwm_apply_state(led_data->pwm, &pstate); if (rc < 0) pr_err("Apply PWM state for %s led failed, rc=%d\n", led_data->cdev.name, rc); return rc; } static int led_pwm_set_blink(struct led_pwm_data *led_data) { u64 on_ms, off_ms, period_ns, duty_ns; enum led_brightness brightness = led_data->led_setting.brightness; int rc = 0; if (led_data->led_setting.blink) { on_ms = led_data->led_setting.on_ms; off_ms = led_data->led_setting.off_ms; duty_ns = on_ms * NSEC_PER_MSEC; period_ns = (on_ms + off_ms) * NSEC_PER_MSEC; if (period_ns < duty_ns && duty_ns != 0) period_ns = duty_ns + 1; } else { /* Use initial period if no blinking is required */ period_ns = PWM_PERIOD_DEFAULT_NS; duty_ns = period_ns * brightness; do_div(duty_ns, LED_FULL); if (period_ns < duty_ns && duty_ns != 0) period_ns = duty_ns + 1; } pr_debug("BLINK: PWM settings for %s led: period = %lluns, duty = %lluns brightness = %d\n", led_data->cdev.name, period_ns, duty_ns, brightness); led_data->pwm_setting.duty_ns = duty_ns; led_data->pwm_setting.period_ns = period_ns; rc = __led_blink_config_pwm(led_data); if (rc < 0) { pr_err("failed to config pwm for blink %s failed, rc=%d\n", led_data->cdev.name, rc); return rc; } if (led_data->led_setting.blink) { led_data->cdev.brightness = LED_FULL; led_data->blinking = true; } else { led_data->cdev.brightness = led_data->led_setting.brightness; led_data->blinking = false; } return rc; } static int led_pwm_blink_set(struct led_classdev *led_cdev, unsigned long *on_ms, unsigned long *off_ms) { struct led_pwm_data *led_data = container_of(led_cdev, struct led_pwm_data, cdev); int rc = 0; if (led_data->blinking && *on_ms == led_data->led_setting.on_ms && *off_ms == led_data->led_setting.off_ms) { pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n", *on_ms, *off_ms); return 0; } if (*on_ms == 0) { led_data->led_setting.blink = false; led_data->led_setting.brightness = LED_OFF; } else if (*off_ms == 0) { led_data->led_setting.blink = false; led_data->led_setting.brightness = led_data->cdev.brightness; } else { led_data->led_setting.on_ms = *on_ms; led_data->led_setting.off_ms = *off_ms; led_data->led_setting.blink = true; } rc = led_pwm_set_blink(led_data); if (rc < 0) pr_err("blink led failed for rc=%d\n", rc); return rc; } static void __led_pwm_set(struct led_pwm_data *led_data) { int new_duty = led_dat->duty; int new_duty = led_data->duty; pwm_config(led_dat->pwm, new_duty, led_dat->period); pwm_config(led_data->pwm, new_duty, led_data->period); if (new_duty == 0) pwm_disable(led_dat->pwm); pwm_disable(led_data->pwm); else pwm_enable(led_dat->pwm); pwm_enable(led_data->pwm); } static int led_pwm_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct led_pwm_data *led_dat = struct led_pwm_data *led_data = container_of(led_cdev, struct led_pwm_data, cdev); unsigned int max = led_dat->cdev.max_brightness; unsigned long long duty = led_dat->period; unsigned int max = led_data->cdev.max_brightness; unsigned long long duty = led_data->period; duty *= brightness; do_div(duty, max); if (led_dat->active_low) duty = led_dat->period - duty; if (led_data->active_low) duty = led_data->period - duty; led_dat->duty = duty; led_data->duty = duty; __led_pwm_set(led_dat); __led_pwm_set(led_data); return 0; } Loading Loading @@ -92,7 +213,8 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, led_data->cdev.default_trigger = led->default_trigger; led_data->cdev.brightness = LED_OFF; led_data->cdev.max_brightness = led->max_brightness; led_data->cdev.flags = LED_CORE_SUSPENDRESUME; /* Set a flag to keep the trigger always */ led_data->cdev.flags |= LED_KEEP_TRIGGER; if (child) led_data->pwm = devm_of_pwm_get(dev, child, NULL); Loading @@ -107,6 +229,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, } led_data->cdev.brightness_set_blocking = led_pwm_set; led_data->cdev.blink_set = led_pwm_blink_set; /* * FIXME: pwm_apply_args() should be removed when switching to the Loading