drivers/video/backlight/ktd253-backlight.c

Source file repositories/reference/linux-study-clean/drivers/video/backlight/ktd253-backlight.c

File Facts

System
Linux kernel
Corpus path
drivers/video/backlight/ktd253-backlight.c
Extension
.c
Size
6433 bytes
Lines
225
Domain
Driver Families
Bucket
drivers/video
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 ktd253_backlight {
	struct device *dev;
	struct backlight_device *bl;
	struct gpio_desc *gpiod;
	u16 ratio;
};

static void ktd253_backlight_set_max_ratio(struct ktd253_backlight *ktd253)
{
	gpiod_set_value_cansleep(ktd253->gpiod, 1);
	ndelay(KTD253_T_HIGH_NS);
	/* We always fall back to this when we power on */
}

static int ktd253_backlight_stepdown(struct ktd253_backlight *ktd253)
{
	/*
	 * These GPIO operations absolutely can NOT sleep so no _cansleep
	 * suffixes, and no using GPIO expanders on slow buses for this!
	 *
	 * The maximum number of cycles of the loop is 32  so the time taken
	 * should nominally be:
	 * (T_LOW_NS + T_HIGH_NS + loop_time) * 32
	 *
	 * Architectures do not always support ndelay() and we will get a few us
	 * instead. If we get to a critical time limit an interrupt has likely
	 * occured in the low part of the loop and we need to restart from the
	 * top so we have the backlight in a known state.
	 */
	u64 ns;

	ns = ktime_get_ns();
	gpiod_set_value(ktd253->gpiod, 0);
	ndelay(KTD253_T_LOW_NS);
	gpiod_set_value(ktd253->gpiod, 1);
	ns = ktime_get_ns() - ns;
	if (ns >= KTD253_T_OFF_CRIT_NS) {
		dev_err(ktd253->dev, "PCM on backlight took too long (%llu ns)\n", ns);
		return -EAGAIN;
	}
	ndelay(KTD253_T_HIGH_NS);
	return 0;
}

static int ktd253_backlight_update_status(struct backlight_device *bl)
{
	struct ktd253_backlight *ktd253 = bl_get_data(bl);
	int brightness = backlight_get_brightness(bl);
	u16 target_ratio;
	u16 current_ratio = ktd253->ratio;
	int ret;

	dev_dbg(ktd253->dev, "new brightness/ratio: %d/32\n", brightness);

	target_ratio = brightness;

	if (target_ratio == current_ratio)
		/* This is already right */
		return 0;

	if (target_ratio == 0) {
		gpiod_set_value_cansleep(ktd253->gpiod, 0);
		/*
		 * We need to keep the GPIO low for at least this long
		 * to actually switch the KTD253 off.
		 */
		msleep(KTD253_T_OFF_MS);
		ktd253->ratio = 0;
		return 0;
	}

	if (current_ratio == 0) {
		ktd253_backlight_set_max_ratio(ktd253);
		current_ratio = KTD253_MAX_RATIO;
	}

	while (current_ratio != target_ratio) {
		/*
		 * These GPIO operations absolutely can NOT sleep so no
		 * _cansleep suffixes, and no using GPIO expanders on
		 * slow buses for this!
		 */
		ret = ktd253_backlight_stepdown(ktd253);
		if (ret == -EAGAIN) {
			/*
			 * Something disturbed the backlight setting code when
			 * running so we need to bring the PWM back to a known
			 * state. This shouldn't happen too much.
			 */
			gpiod_set_value_cansleep(ktd253->gpiod, 0);

Annotation

Implementation Notes