Commit 2d7b4cdb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'backlight-next-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight

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
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define KTD253_T_LOW_NS (200 + 10) /* Additional 10ns as safety factor */ #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_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 #define KTD253_T_OFF_MS 3
struct ktd253_backlight { struct ktd253_backlight {
...@@ -34,13 +35,50 @@ struct ktd253_backlight { ...@@ -34,13 +35,50 @@ struct ktd253_backlight {
u16 ratio; 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) static int ktd253_backlight_update_status(struct backlight_device *bl)
{ {
struct ktd253_backlight *ktd253 = bl_get_data(bl); struct ktd253_backlight *ktd253 = bl_get_data(bl);
int brightness = backlight_get_brightness(bl); int brightness = backlight_get_brightness(bl);
u16 target_ratio; u16 target_ratio;
u16 current_ratio = ktd253->ratio; u16 current_ratio = ktd253->ratio;
unsigned long flags; int ret;
dev_dbg(ktd253->dev, "new brightness/ratio: %d/32\n", brightness); 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) ...@@ -62,37 +100,34 @@ static int ktd253_backlight_update_status(struct backlight_device *bl)
} }
if (current_ratio == 0) { if (current_ratio == 0) {
gpiod_set_value_cansleep(ktd253->gpiod, 1); ktd253_backlight_set_max_ratio(ktd253);
ndelay(KTD253_T_HIGH_NS);
/* We always fall back to this when we power on */
current_ratio = KTD253_MAX_RATIO; 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) { while (current_ratio != target_ratio) {
/* /*
* These GPIO operations absolutely can NOT sleep so no * These GPIO operations absolutely can NOT sleep so no
* _cansleep suffixes, and no using GPIO expanders on * _cansleep suffixes, and no using GPIO expanders on
* slow buses for this! * slow buses for this!
*/ */
gpiod_set_value(ktd253->gpiod, 0); ret = ktd253_backlight_stepdown(ktd253);
ndelay(KTD253_T_LOW_NS); if (ret == -EAGAIN) {
gpiod_set_value(ktd253->gpiod, 1); /*
ndelay(KTD253_T_HIGH_NS); * 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 */ /* After 1/32 we loop back to 32/32 */
if (current_ratio == KTD253_MIN_RATIO)
current_ratio = KTD253_MAX_RATIO; current_ratio = KTD253_MAX_RATIO;
else } else {
current_ratio--; current_ratio--;
} }
local_irq_restore(flags); }
ktd253->ratio = current_ratio; ktd253->ratio = current_ratio;
dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio); dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio);
......
...@@ -409,6 +409,33 @@ static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data) ...@@ -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) static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
{ {
struct device_node *node = pb->dev->of_node; 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 */ /* Not booted with device tree or no phandle link to the node */
if (!node || !node->phandle) if (!node || !node->phandle)
...@@ -420,20 +447,7 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) ...@@ -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 * assume that another driver will enable the backlight at the
* appropriate time. Therefore, if it is disabled, keep it so. * appropriate time. Therefore, if it is disabled, keep it so.
*/ */
return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN;
/* 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;
} }
static int pwm_backlight_probe(struct platform_device *pdev) static int pwm_backlight_probe(struct platform_device *pdev)
...@@ -486,18 +500,6 @@ 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; 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"); pb->power_supply = devm_regulator_get(&pdev->dev, "power");
if (IS_ERR(pb->power_supply)) { if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply); ret = PTR_ERR(pb->power_supply);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment