Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

backlight: ktd253: Stabilize backlight

Remove interrupt disablement during backlight setting. It is
way to dangerous and makes platforms instable by having it
miss vblank IRQs leading to the graphics derailing.

The code is using ndelay() which is not available on
platforms such as ARM and will result in 32 * udelay(1)
which is substantial.

Add some code to detect if an interrupt occurs during the
tight loop and in that case just redo it from the top.

Fixes: 5317f37e48b9 ("backlight: Add Kinetic KTD253 backlight driver")
Cc: Stephan Gerhold <stephan@gerhold.net>
Reported-by: newbyte@disroot.org
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Linus Walleij and committed by
Lee Jones
daa37361 e73f0f0e

+55 -20
+55 -20
drivers/video/backlight/ktd253-backlight.c
··· 25 25 26 26 #define KTD253_T_LOW_NS (200 + 10) /* Additional 10ns as safety factor */ 27 27 #define KTD253_T_HIGH_NS (200 + 10) /* Additional 10ns as safety factor */ 28 + #define KTD253_T_OFF_CRIT_NS 100000 /* 100 us, now it doesn't look good */ 28 29 #define KTD253_T_OFF_MS 3 29 30 30 31 struct ktd253_backlight { ··· 35 34 u16 ratio; 36 35 }; 37 36 37 + static void ktd253_backlight_set_max_ratio(struct ktd253_backlight *ktd253) 38 + { 39 + gpiod_set_value_cansleep(ktd253->gpiod, 1); 40 + ndelay(KTD253_T_HIGH_NS); 41 + /* We always fall back to this when we power on */ 42 + } 43 + 44 + static int ktd253_backlight_stepdown(struct ktd253_backlight *ktd253) 45 + { 46 + /* 47 + * These GPIO operations absolutely can NOT sleep so no _cansleep 48 + * suffixes, and no using GPIO expanders on slow buses for this! 49 + * 50 + * The maximum number of cycles of the loop is 32 so the time taken 51 + * should nominally be: 52 + * (T_LOW_NS + T_HIGH_NS + loop_time) * 32 53 + * 54 + * Architectures do not always support ndelay() and we will get a few us 55 + * instead. If we get to a critical time limit an interrupt has likely 56 + * occured in the low part of the loop and we need to restart from the 57 + * top so we have the backlight in a known state. 58 + */ 59 + u64 ns; 60 + 61 + ns = ktime_get_ns(); 62 + gpiod_set_value(ktd253->gpiod, 0); 63 + ndelay(KTD253_T_LOW_NS); 64 + gpiod_set_value(ktd253->gpiod, 1); 65 + ns = ktime_get_ns() - ns; 66 + if (ns >= KTD253_T_OFF_CRIT_NS) { 67 + dev_err(ktd253->dev, "PCM on backlight took too long (%llu ns)\n", ns); 68 + return -EAGAIN; 69 + } 70 + ndelay(KTD253_T_HIGH_NS); 71 + return 0; 72 + } 73 + 38 74 static int ktd253_backlight_update_status(struct backlight_device *bl) 39 75 { 40 76 struct ktd253_backlight *ktd253 = bl_get_data(bl); 41 77 int brightness = backlight_get_brightness(bl); 42 78 u16 target_ratio; 43 79 u16 current_ratio = ktd253->ratio; 44 - unsigned long flags; 80 + int ret; 45 81 46 82 dev_dbg(ktd253->dev, "new brightness/ratio: %d/32\n", brightness); 47 83 ··· 100 62 } 101 63 102 64 if (current_ratio == 0) { 103 - gpiod_set_value_cansleep(ktd253->gpiod, 1); 104 - ndelay(KTD253_T_HIGH_NS); 105 - /* We always fall back to this when we power on */ 65 + ktd253_backlight_set_max_ratio(ktd253); 106 66 current_ratio = KTD253_MAX_RATIO; 107 67 } 108 68 109 - /* 110 - * WARNING: 111 - * The loop to set the correct current level is performed 112 - * with interrupts disabled as it is timing critical. 113 - * The maximum number of cycles of the loop is 32 114 - * so the time taken will be (T_LOW_NS + T_HIGH_NS + loop_time) * 32, 115 - */ 116 - local_irq_save(flags); 117 69 while (current_ratio != target_ratio) { 118 70 /* 119 71 * These GPIO operations absolutely can NOT sleep so no 120 72 * _cansleep suffixes, and no using GPIO expanders on 121 73 * slow buses for this! 122 74 */ 123 - gpiod_set_value(ktd253->gpiod, 0); 124 - ndelay(KTD253_T_LOW_NS); 125 - gpiod_set_value(ktd253->gpiod, 1); 126 - ndelay(KTD253_T_HIGH_NS); 127 - /* After 1/32 we loop back to 32/32 */ 128 - if (current_ratio == KTD253_MIN_RATIO) 75 + ret = ktd253_backlight_stepdown(ktd253); 76 + if (ret == -EAGAIN) { 77 + /* 78 + * Something disturbed the backlight setting code when 79 + * running so we need to bring the PWM back to a known 80 + * state. This shouldn't happen too much. 81 + */ 82 + gpiod_set_value_cansleep(ktd253->gpiod, 0); 83 + msleep(KTD253_T_OFF_MS); 84 + ktd253_backlight_set_max_ratio(ktd253); 129 85 current_ratio = KTD253_MAX_RATIO; 130 - else 86 + } else if (current_ratio == KTD253_MIN_RATIO) { 87 + /* After 1/32 we loop back to 32/32 */ 88 + current_ratio = KTD253_MAX_RATIO; 89 + } else { 131 90 current_ratio--; 91 + } 132 92 } 133 - local_irq_restore(flags); 134 93 ktd253->ratio = current_ratio; 135 94 136 95 dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio);