drivers/leds/leds-pwm.c

Source file repositories/reference/linux-study-clean/drivers/leds/leds-pwm.c

File Facts

System
Linux kernel
Corpus path
drivers/leds/leds-pwm.c
Extension
.c
Size
6195 bytes
Lines
250
Domain
Driver Families
Bucket
drivers/leds
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct led_pwm {
	const char	*name;
	u8		active_low;
	u8		default_state;
	unsigned int	max_brightness;
};

struct led_pwm_data {
	struct gpio_desc	*enable_gpio;
	struct led_classdev	cdev;
	struct pwm_device	*pwm;
	struct pwm_state	pwmstate;
	unsigned int		active_low;
};

struct led_pwm_priv {
	int num_leds;
	struct led_pwm_data leds[];
};

static int led_pwm_set(struct led_classdev *led_cdev,
		       enum led_brightness brightness)
{
	struct led_pwm_data *led_dat =
		container_of(led_cdev, struct led_pwm_data, cdev);
	unsigned int max = led_dat->cdev.max_brightness;
	unsigned long long duty = led_dat->pwmstate.period;

	duty *= brightness;
	do_div(duty, max);

	if (led_dat->active_low)
		duty = led_dat->pwmstate.period - duty;

	gpiod_set_value_cansleep(led_dat->enable_gpio, !!brightness);

	led_dat->pwmstate.duty_cycle = duty;
	/*
	 * Disabling a PWM doesn't guarantee that it emits the inactive level.
	 * So keep it on. Only for suspending the PWM should be disabled because
	 * otherwise it refuses to suspend. The possible downside is that the
	 * LED might stay (or even go) on.
	 */
	led_dat->pwmstate.enabled = !(led_cdev->flags & LED_SUSPENDED);
	return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate);
}

static int led_pwm_default_brightness_get(struct fwnode_handle *fwnode,
					  int max_brightness)
{
	unsigned int default_brightness;
	int ret;

	ret = fwnode_property_read_u32(fwnode, "default-brightness",
				       &default_brightness);
	if (ret < 0 || default_brightness > max_brightness)
		default_brightness = max_brightness;

	return default_brightness;
}

__attribute__((nonnull))
static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
		       struct led_pwm *led, struct fwnode_handle *fwnode)
{
	struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
	struct led_init_data init_data = { .fwnode = fwnode };
	int ret;

	led_data->active_low = led->active_low;
	led_data->cdev.name = led->name;
	led_data->cdev.brightness = LED_OFF;
	led_data->cdev.max_brightness = led->max_brightness;
	led_data->cdev.flags = LED_CORE_SUSPENDRESUME;

	led_data->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL);
	if (IS_ERR(led_data->pwm))
		return dev_err_probe(dev, PTR_ERR(led_data->pwm),
				     "unable to request PWM for %s\n",
				     led->name);

	led_data->cdev.brightness_set_blocking = led_pwm_set;

	/* init PWM state */
	switch (led->default_state) {
	case LEDS_DEFSTATE_KEEP:
		pwm_get_state(led_data->pwm, &led_data->pwmstate);
		if (led_data->pwmstate.period)
			break;
		led->default_state = LEDS_DEFSTATE_OFF;

Annotation

Implementation Notes