drivers/leds/leds-turris-omnia.c

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

File Facts

System
Linux kernel
Corpus path
drivers/leds/leds-turris-omnia.c
Extension
.c
Size
15398 bytes
Lines
556
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 omnia_led {
	struct led_classdev_mc mc_cdev;
	struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
	u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
	bool on, hwtrig;
	int reg;
};

#define to_omnia_led(l)		container_of(l, struct omnia_led, mc_cdev)

/**
 * struct omnia_leds - driver private data structure
 * @client:			I2C client device
 * @lock:			mutex to protect cached state
 * @has_gamma_correction:	whether the MCU firmware supports gamma correction
 * @brightness_knode:		kernel node of the "brightness" device sysfs attribute (this is the
 *				driver specific global brightness, not the LED classdev brightness)
 * @leds:			flexible array of per-LED data
 */
struct omnia_leds {
	struct i2c_client *client;
	struct mutex lock;
	bool has_gamma_correction;
	struct kernfs_node *brightness_knode;
	struct omnia_led leds[];
};

static int omnia_cmd_set_color(const struct i2c_client *client, u8 led, u8 r, u8 g, u8 b)
{
	u8 buf[5] = { OMNIA_CMD_LED_COLOR, led, r, g, b };

	return omnia_cmd_write(client, buf, sizeof(buf));
}

static int omnia_led_send_color_cmd(const struct i2c_client *client,
				    struct omnia_led *led)
{
	int ret;

	/* Send the color change command */
	ret = omnia_cmd_set_color(client, led->reg, led->subled_info[0].brightness,
				  led->subled_info[1].brightness, led->subled_info[2].brightness);
	if (ret < 0)
		return ret;

	/* Cache the RGB channel brightnesses */
	for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
		led->cached_channels[i] = led->subled_info[i].brightness;

	return 0;
}

/* Determine if the computed RGB channels are different from the cached ones */
static bool omnia_led_channels_changed(struct omnia_led *led)
{
	for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
		if (led->subled_info[i].brightness != led->cached_channels[i])
			return true;

	return false;
}

static int omnia_led_brightness_set_blocking(struct led_classdev *cdev,
					     enum led_brightness brightness)
{
	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
	struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
	struct omnia_led *led = to_omnia_led(mc_cdev);
	int err = 0;

	mutex_lock(&leds->lock);

	/*
	 * Only recalculate RGB brightnesses from intensities if brightness is
	 * non-zero (if it is zero and the LED is in HW blinking mode, we use
	 * max_brightness as brightness). Otherwise we won't be using them and
	 * we can save ourselves some software divisions (Omnia's CPU does not
	 * implement the division instruction).
	 */
	if (brightness || led->hwtrig) {
		led_mc_calc_color_components(mc_cdev, brightness ?:
						      cdev->max_brightness);

		/*
		 * Send color command only if brightness is non-zero and the RGB
		 * channel brightnesses changed.
		 */
		if (omnia_led_channels_changed(led))
			err = omnia_led_send_color_cmd(leds->client, led);
	}

Annotation

Implementation Notes