Commit 2d7b4cdb authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull backlight updates from Lee Jones:
 "Fix-ups:
   - Improve bootloader/kernel device handover

  Bug Fixes:
   - Stabilise backlight in ktd253 driver"

* tag 'backlight-next-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
  backlight: pwm_bl: Improve bootloader/kernel device handover
  backlight: ktd253: Stabilize backlight
parents 86406a9e 79fad92f
Loading
Loading
Loading
Loading
+55 −20
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@

#define KTD253_T_LOW_NS (200 + 10) /* Additional 10ns as safety factor */
#define KTD253_T_HIGH_NS (200 + 10) /* Additional 10ns as safety factor */
#define KTD253_T_OFF_CRIT_NS 100000 /* 100 us, now it doesn't look good */
#define KTD253_T_OFF_MS 3

struct ktd253_backlight {
@@ -34,13 +35,50 @@ struct ktd253_backlight {
	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;
	unsigned long flags;
	int ret;

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

@@ -62,37 +100,34 @@ static int ktd253_backlight_update_status(struct backlight_device *bl)
	}

	if (current_ratio == 0) {
		gpiod_set_value_cansleep(ktd253->gpiod, 1);
		ndelay(KTD253_T_HIGH_NS);
		/* We always fall back to this when we power on */
		ktd253_backlight_set_max_ratio(ktd253);
		current_ratio = KTD253_MAX_RATIO;
	}

	/*
	 * WARNING:
	 * The loop to set the correct current level is performed
	 * with interrupts disabled as it is timing critical.
	 * The maximum number of cycles of the loop is 32
	 * so the time taken will be (T_LOW_NS + T_HIGH_NS + loop_time) * 32,
	 */
	local_irq_save(flags);
	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!
		 */
		gpiod_set_value(ktd253->gpiod, 0);
		ndelay(KTD253_T_LOW_NS);
		gpiod_set_value(ktd253->gpiod, 1);
		ndelay(KTD253_T_HIGH_NS);
		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);
			msleep(KTD253_T_OFF_MS);
			ktd253_backlight_set_max_ratio(ktd253);
			current_ratio = KTD253_MAX_RATIO;
		} else if (current_ratio == KTD253_MIN_RATIO) {
			/* After 1/32 we loop back to 32/32 */
		if (current_ratio == KTD253_MIN_RATIO)
			current_ratio = KTD253_MAX_RATIO;
		else
		} else {
			current_ratio--;
		}
	local_irq_restore(flags);
	}
	ktd253->ratio = current_ratio;

	dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio);
+28 −26
Original line number Diff line number Diff line
@@ -409,6 +409,33 @@ static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data)
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
{
	struct device_node *node = pb->dev->of_node;
	bool active = true;

	/*
	 * If the enable GPIO is present, observable (either as input
	 * or output) and off then the backlight is not currently active.
	 * */
	if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
		active = false;

	if (!regulator_is_enabled(pb->power_supply))
		active = false;

	if (!pwm_is_enabled(pb->pwm))
		active = false;

	/*
	 * Synchronize the enable_gpio with the observed state of the
	 * hardware.
	 */
	if (pb->enable_gpio)
		gpiod_direction_output(pb->enable_gpio, active);

	/*
	 * Do not change pb->enabled here! pb->enabled essentially
	 * tells us if we own one of the regulator's use counts and
	 * right now we do not.
	 */

	/* Not booted with device tree or no phandle link to the node */
	if (!node || !node->phandle)
@@ -420,20 +447,7 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
	 * assume that another driver will enable the backlight at the
	 * appropriate time. Therefore, if it is disabled, keep it so.
	 */

	/* if the enable GPIO is disabled, do not enable the backlight */
	if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
		return FB_BLANK_POWERDOWN;

	/* The regulator is disabled, do not enable the backlight */
	if (!regulator_is_enabled(pb->power_supply))
		return FB_BLANK_POWERDOWN;

	/* The PWM is disabled, keep it like this */
	if (!pwm_is_enabled(pb->pwm))
		return FB_BLANK_POWERDOWN;

	return FB_BLANK_UNBLANK;
	return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN;
}

static int pwm_backlight_probe(struct platform_device *pdev)
@@ -486,18 +500,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
		goto err_alloc;
	}

	/*
	 * If the GPIO is not known to be already configured as output, that
	 * is, if gpiod_get_direction returns either 1 or -EINVAL, change the
	 * direction to output and set the GPIO as active.
	 * Do not force the GPIO to active when it was already output as it
	 * could cause backlight flickering or we would enable the backlight too
	 * early. Leave the decision of the initial backlight state for later.
	 */
	if (pb->enable_gpio &&
	    gpiod_get_direction(pb->enable_gpio) != 0)
		gpiod_direction_output(pb->enable_gpio, 1);

	pb->power_supply = devm_regulator_get(&pdev->dev, "power");
	if (IS_ERR(pb->power_supply)) {
		ret = PTR_ERR(pb->power_supply);