From 8ad003e7348ecd59f457de9bbf06b30535d734f0 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Tue, 10 Sep 2019 17:23:59 +0200 Subject: backlight: lm3630a: Fix module aliases Devicetree aliases are missing, so that module autoloading does not work properly. Signed-off-by: Andreas Kemnade Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/lm3630a_bl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 2d8e8192e4e2..3614a4edbcd1 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -598,12 +598,14 @@ static const struct i2c_device_id lm3630a_id[] = { {} }; +MODULE_DEVICE_TABLE(i2c, lm3630a_id); + static const struct of_device_id lm3630a_match_table[] = { { .compatible = "ti,lm3630a", }, { }, }; -MODULE_DEVICE_TABLE(i2c, lm3630a_id); +MODULE_DEVICE_TABLE(of, lm3630a_match_table); static struct i2c_driver lm3630a_i2c_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From 7050a7c3747787c3f9ba94df59eb7e9e018bbdf2 Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 13 Aug 2019 14:28:55 +0530 Subject: backlight: ipaq_micro: Make structure micro_bl_props constant Static structure micro_bl_props, having type backlight_properties, is used only once, when it is passed as the last argument to function devm_backlight_device_register(). devm_backlight_device_register() is defined with its last parameter being declared constant. Hence make micro_bl_props itself constant as well. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/ipaq_micro_bl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/ipaq_micro_bl.c b/drivers/video/backlight/ipaq_micro_bl.c index 1123f67c12b3..85b16cc82878 100644 --- a/drivers/video/backlight/ipaq_micro_bl.c +++ b/drivers/video/backlight/ipaq_micro_bl.c @@ -44,7 +44,7 @@ static const struct backlight_ops micro_bl_ops = { .update_status = micro_bl_update_status, }; -static struct backlight_properties micro_bl_props = { +static const struct backlight_properties micro_bl_props = { .type = BACKLIGHT_RAW, .max_brightness = 255, .power = FB_BLANK_UNBLANK, -- cgit v1.2.3-59-g8ed1b From 0e0e78e32e723a1ee48207a2ad091afb30f6f1c5 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 12 Sep 2019 23:32:57 +0200 Subject: backlight: lm3630a: Add an enable gpio for the HWEN pin For now just enable it in the probe function to allow I2C access. Disabling also means resetting the register values to default and according to the datasheet does not give power savings. Tested on Kobo Clara HD. Signed-off-by: Andreas Kemnade Reviewed-by: Dan Murphy Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/lm3630a_bl.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 3614a4edbcd1..ee320883b710 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ struct lm3630a_chip { struct lm3630a_platform_data *pdata; struct backlight_device *bleda; struct backlight_device *bledb; + struct gpio_desc *enable_gpio; struct regmap *regmap; struct pwm_device *pwmd; }; @@ -534,6 +536,13 @@ static int lm3630a_probe(struct i2c_client *client, } pchip->pdata = pdata; + pchip->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(pchip->enable_gpio)) { + rval = PTR_ERR(pchip->enable_gpio); + return rval; + } + /* chip initialize */ rval = lm3630a_chip_init(pchip); if (rval < 0) { -- cgit v1.2.3-59-g8ed1b From de6f2a7fa2c9563266e2a3239e16b117daf22b47 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 2 Oct 2019 09:56:01 -0700 Subject: backlight: pwm_bl: Don't assign levels table repeatedly pwm_backlight_probe() re-assigns pb->levels for every brightness level. This is not needed and was likely not intended, since neither side of the assignment changes during the loop. Assign the field only once. Signed-off-by: Matthias Kaehlcke Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 746eebc411df..05d3b3802658 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -564,18 +564,17 @@ static int pwm_backlight_probe(struct platform_device *pdev) memset(&props, 0, sizeof(struct backlight_properties)); if (data->levels) { + pb->levels = data->levels; + /* * For the DT case, only when brightness levels is defined * data->levels is filled. For the non-DT case, data->levels * can come from platform data, however is not usual. */ - for (i = 0; i <= data->max_brightness; i++) { + for (i = 0; i <= data->max_brightness; i++) if (data->levels[i] > pb->scale) pb->scale = data->levels[i]; - pb->levels = data->levels; - } - if (pwm_backlight_is_linear(data)) props.scale = BACKLIGHT_SCALE_LINEAR; else -- cgit v1.2.3-59-g8ed1b From 349ee1228729df5c2a0cb33506cad2661e40e750 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 3 Oct 2019 14:35:02 -0700 Subject: backlight: pwm_bl: Add missing curly branches in else branch Add curly braces to an 'else' branch in pwm_backlight_update_status() to match the corresponding 'if' branch. Signed-off-by: Matthias Kaehlcke Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 05d3b3802658..9c4216f08c5a 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -125,8 +125,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl) state.duty_cycle = compute_duty_cycle(pb, brightness); pwm_apply_state(pb->pwm, &state); pwm_backlight_power_on(pb); - } else + } else { pwm_backlight_power_off(pb); + } if (pb->notify_after) pb->notify_after(pb->dev, brightness); -- cgit v1.2.3-59-g8ed1b From efdf690e159ab340486dd6d42f387bbb8f03a579 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Oct 2019 14:03:24 +0200 Subject: backlight: pwm_bl: Fix cie1913 comments and constant The "break-even" point for the two formulas is L==8, which is also what the code actually implements. [Incidentally, at that point one has Y=0.008856, not 0.08856]. Moreover, all the sources I can find say the linear factor is 903.3 rather than 902.3, which makes sense since then the formulas agree at L==8, both yielding the 0.008856 figure to four significant digits. Signed-off-by: Rasmus Villemoes Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 9c4216f08c5a..3525e04791ce 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -156,8 +156,8 @@ static const struct backlight_ops pwm_backlight_ops = { * * The CIE 1931 lightness formula is what actually describes how we perceive * light: - * Y = (L* / 902.3) if L* ≤ 0.08856 - * Y = ((L* + 16) / 116)^3 if L* > 0.08856 + * Y = (L* / 903.3) if L* ≤ 8 + * Y = ((L* + 16) / 116)^3 if L* > 8 * * Where Y is the luminance, the amount of light coming out of the screen, and * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human @@ -170,9 +170,15 @@ static u64 cie1931(unsigned int lightness, unsigned int scale) { u64 retval; + /* + * @lightness is given as a number between 0 and 1, expressed + * as a fixed-point number in scale @scale. Convert to a + * percentage, still expressed as a fixed-point number, so the + * above formulas can be applied. + */ lightness *= 100; if (lightness <= (8 * scale)) { - retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9023); + retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9033); } else { retval = int_pow((lightness + (16 * scale)) / 116, 3); retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale)); -- cgit v1.2.3-59-g8ed1b From e802cbafcbd250a88cbd4ea7f9afb9c0d4267c7a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Oct 2019 14:03:25 +0200 Subject: backlight: pwm_bl: Eliminate a 64/32 division lightness*1000 is nowhere near overflowing 32 bits, so we can just use an ordinary 32/32 division, which is much cheaper than the 64/32 done via do_div(). Signed-off-by: Rasmus Villemoes Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 3525e04791ce..15d84da77ecd 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -178,7 +178,7 @@ static u64 cie1931(unsigned int lightness, unsigned int scale) */ lightness *= 100; if (lightness <= (8 * scale)) { - retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9033); + retval = DIV_ROUND_CLOSEST(lightness * 10, 9033); } else { retval = int_pow((lightness + (16 * scale)) / 116, 3); retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale)); -- cgit v1.2.3-59-g8ed1b From 407feae1cacaa5d00ebe686532a73ad6de747409 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Oct 2019 14:03:26 +0200 Subject: backlight: pwm_bl: Drop use of int_pow() For a fixed small exponent of 3, it is more efficient to simply use two explicit multiplications rather than calling the int_pow() library function: Aside from the function call overhead, its implementation using repeated squaring means it ends up doing four 64x64 multiplications. Signed-off-by: Rasmus Villemoes Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 15d84da77ecd..ee85055146dd 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -180,7 +180,8 @@ static u64 cie1931(unsigned int lightness, unsigned int scale) if (lightness <= (8 * scale)) { retval = DIV_ROUND_CLOSEST(lightness * 10, 9033); } else { - retval = int_pow((lightness + (16 * scale)) / 116, 3); + retval = (lightness + (16 * scale)) / 116; + retval *= retval * retval; retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale)); } -- cgit v1.2.3-59-g8ed1b From ca58b37034453e690e5278f95f32ea050951cf9f Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Oct 2019 14:03:27 +0200 Subject: backlight: pwm_bl: Switch to power-of-2 base for fixed-point math Using a power-of-2 instead of power-of-10 base makes the computations much cheaper. 2^16 is safe; retval never becomes more than 2^48 + 2^32/2. On a 32 bit platform, the very expensive 64/32 division at the end of cie1931() instead becomes essentially free (a shift by 32 is just a register rename). Signed-off-by: Rasmus Villemoes Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index ee85055146dd..efb4efc2a13d 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -149,7 +149,8 @@ static const struct backlight_ops pwm_backlight_ops = { }; #ifdef CONFIG_OF -#define PWM_LUMINANCE_SCALE 10000 /* luminance scale */ +#define PWM_LUMINANCE_SHIFT 16 +#define PWM_LUMINANCE_SCALE (1 << PWM_LUMINANCE_SHIFT) /* luminance scale */ /* * CIE lightness to PWM conversion. @@ -166,23 +167,25 @@ static const struct backlight_ops pwm_backlight_ops = { * The following function does the fixed point maths needed to implement the * above formula. */ -static u64 cie1931(unsigned int lightness, unsigned int scale) +static u64 cie1931(unsigned int lightness) { u64 retval; /* * @lightness is given as a number between 0 and 1, expressed - * as a fixed-point number in scale @scale. Convert to a - * percentage, still expressed as a fixed-point number, so the - * above formulas can be applied. + * as a fixed-point number in scale + * PWM_LUMINANCE_SCALE. Convert to a percentage, still + * expressed as a fixed-point number, so the above formulas + * can be applied. */ lightness *= 100; - if (lightness <= (8 * scale)) { + if (lightness <= (8 * PWM_LUMINANCE_SCALE)) { retval = DIV_ROUND_CLOSEST(lightness * 10, 9033); } else { - retval = (lightness + (16 * scale)) / 116; + retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116; retval *= retval * retval; - retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale)); + retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1); + retval >>= 2*PWM_LUMINANCE_SHIFT; } return retval; @@ -216,8 +219,7 @@ int pwm_backlight_brightness_default(struct device *dev, /* Fill the table using the cie1931 algorithm */ for (i = 0; i < data->max_brightness; i++) { retval = cie1931((i * PWM_LUMINANCE_SCALE) / - data->max_brightness, PWM_LUMINANCE_SCALE) * - period; + data->max_brightness) * period; retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE); if (retval > UINT_MAX) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 53e4929150610149aac3453e2030c59b871984bb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 15 Oct 2019 21:18:16 +0200 Subject: backlight: Kconfig: jornada720: Use CONFIG_PREEMPTION CONFIG_PREEMPTION is selected by CONFIG_PREEMPT and by CONFIG_PREEMPT_RT. Both PREEMPT and PREEMPT_RT require the same functionality which today depends on CONFIG_PREEMPT. Switch the Kconfig dependency to CONFIG_PREEMPTION. Signed-off-by: Thomas Gleixner [Sebastian: +LCD_HP700] Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 40676be2e46a..d09396393724 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -99,7 +99,7 @@ config LCD_TOSA config LCD_HP700 tristate "HP Jornada 700 series LCD Driver" - depends on SA1100_JORNADA720_SSP && !PREEMPT + depends on SA1100_JORNADA720_SSP && !PREEMPTION default y help If you have an HP Jornada 700 series handheld (710/720/728) @@ -228,7 +228,7 @@ config BACKLIGHT_HP680 config BACKLIGHT_HP700 tristate "HP Jornada 700 series Backlight Driver" - depends on SA1100_JORNADA720_SSP && !PREEMPT + depends on SA1100_JORNADA720_SSP && !PREEMPTION default y help If you have an HP Jornada 700 series, -- cgit v1.2.3-59-g8ed1b From 51c0ddc7583494ac6ecd1f5d1688f1aced7ea2f9 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:56:57 +0530 Subject: backlight: qcom-wled: Rename pm8941-wled.c to qcom-wled.c pm8941-wled.c driver is supporting the WLED peripheral on pm8941. Rename it to qcom-wled.c so that it can support WLED on multiple PMICs. Signed-off-by: Kiran Gunda Reviewed-by: Bjorn Andersson Acked-by: Rob Herring Acked-by: Daniel Thompson Acked-by: Pavel Machek Signed-off-by: Lee Jones --- .../bindings/leds/backlight/pm8941-wled.txt | 42 -- .../bindings/leds/backlight/qcom-wled.txt | 42 ++ drivers/video/backlight/Kconfig | 8 +- drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/pm8941-wled.c | 424 --------------------- drivers/video/backlight/qcom-wled.c | 424 +++++++++++++++++++++ 6 files changed, 471 insertions(+), 471 deletions(-) delete mode 100644 Documentation/devicetree/bindings/leds/backlight/pm8941-wled.txt create mode 100644 Documentation/devicetree/bindings/leds/backlight/qcom-wled.txt delete mode 100644 drivers/video/backlight/pm8941-wled.c create mode 100644 drivers/video/backlight/qcom-wled.c (limited to 'drivers/video') diff --git a/Documentation/devicetree/bindings/leds/backlight/pm8941-wled.txt b/Documentation/devicetree/bindings/leds/backlight/pm8941-wled.txt deleted file mode 100644 index e5b294dafc58..000000000000 --- a/Documentation/devicetree/bindings/leds/backlight/pm8941-wled.txt +++ /dev/null @@ -1,42 +0,0 @@ -Binding for Qualcomm PM8941 WLED driver - -Required properties: -- compatible: should be "qcom,pm8941-wled" -- reg: slave address - -Optional properties: -- default-brightness: brightness value on boot, value from: 0-4095 - default: 2048 -- label: The name of the backlight device -- qcom,cs-out: bool; enable current sink output -- qcom,cabc: bool; enable content adaptive backlight control -- qcom,ext-gen: bool; use externally generated modulator signal to dim -- qcom,current-limit: mA; per-string current limit; value from 0 to 25 - default: 20mA -- qcom,current-boost-limit: mA; boost current limit; one of: - 105, 385, 525, 805, 980, 1260, 1400, 1680 - default: 805mA -- qcom,switching-freq: kHz; switching frequency; one of: - 600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371, - 1600, 1920, 2400, 3200, 4800, 9600, - default: 1600kHz -- qcom,ovp: V; Over-voltage protection limit; one of: - 27, 29, 32, 35 - default: 29V -- qcom,num-strings: #; number of led strings attached; value from 1 to 3 - default: 2 - -Example: - -pm8941-wled@d800 { - compatible = "qcom,pm8941-wled"; - reg = <0xd800>; - label = "backlight"; - - qcom,cs-out; - qcom,current-limit = <20>; - qcom,current-boost-limit = <805>; - qcom,switching-freq = <1600>; - qcom,ovp = <29>; - qcom,num-strings = <2>; -}; diff --git a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.txt b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.txt new file mode 100644 index 000000000000..fb39e329c9ce --- /dev/null +++ b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.txt @@ -0,0 +1,42 @@ +Binding for Qualcomm Technologies, Inc. WLED driver + +Required properties: +- compatible: should be "qcom,pm8941-wled" +- reg: slave address + +Optional properties: +- default-brightness: brightness value on boot, value from: 0-4095 + default: 2048 +- label: The name of the backlight device +- qcom,cs-out: bool; enable current sink output +- qcom,cabc: bool; enable content adaptive backlight control +- qcom,ext-gen: bool; use externally generated modulator signal to dim +- qcom,current-limit: mA; per-string current limit; value from 0 to 25 + default: 20mA +- qcom,current-boost-limit: mA; boost current limit; one of: + 105, 385, 525, 805, 980, 1260, 1400, 1680 + default: 805mA +- qcom,switching-freq: kHz; switching frequency; one of: + 600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371, + 1600, 1920, 2400, 3200, 4800, 9600, + default: 1600kHz +- qcom,ovp: V; Over-voltage protection limit; one of: + 27, 29, 32, 35 + default: 29V +- qcom,num-strings: #; number of led strings attached; value from 1 to 3 + default: 2 + +Example: + +pm8941-wled@d800 { + compatible = "qcom,pm8941-wled"; + reg = <0xd800>; + label = "backlight"; + + qcom,cs-out; + qcom,current-limit = <20>; + qcom,current-boost-limit = <805>; + qcom,switching-freq = <1600>; + qcom,ovp = <29>; + qcom,num-strings = <2>; +}; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index d09396393724..403707a3e503 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -282,12 +282,12 @@ config BACKLIGHT_TOSA If you have an Sharp SL-6000 Zaurus say Y to enable a driver for its backlight -config BACKLIGHT_PM8941_WLED - tristate "Qualcomm PM8941 WLED Driver" +config BACKLIGHT_QCOM_WLED + tristate "Qualcomm PMIC WLED Driver" select REGMAP help - If you have the Qualcomm PM8941, say Y to enable a driver for the - WLED block. + If you have the Qualcomm PMIC, say Y to enable a driver for the + WLED block. Currently it supports PM8941 and PMI8998. config BACKLIGHT_SAHARA tristate "Tabletkiosk Sahara Touch-iT Backlight Driver" diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 63c507c07437..6f8777037c37 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -48,8 +48,8 @@ obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o -obj-$(CONFIG_BACKLIGHT_PM8941_WLED) += pm8941-wled.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o +obj-$(CONFIG_BACKLIGHT_QCOM_WLED) += qcom-wled.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o diff --git a/drivers/video/backlight/pm8941-wled.c b/drivers/video/backlight/pm8941-wled.c deleted file mode 100644 index 82b85725a22a..000000000000 --- a/drivers/video/backlight/pm8941-wled.c +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015, Sony Mobile Communications, AB. - */ - -#include -#include -#include -#include -#include -#include - -/* From DT binding */ -#define PM8941_WLED_DEFAULT_BRIGHTNESS 2048 - -#define PM8941_WLED_REG_VAL_BASE 0x40 -#define PM8941_WLED_REG_VAL_MAX 0xFFF - -#define PM8941_WLED_REG_MOD_EN 0x46 -#define PM8941_WLED_REG_MOD_EN_BIT BIT(7) -#define PM8941_WLED_REG_MOD_EN_MASK BIT(7) - -#define PM8941_WLED_REG_SYNC 0x47 -#define PM8941_WLED_REG_SYNC_MASK 0x07 -#define PM8941_WLED_REG_SYNC_LED1 BIT(0) -#define PM8941_WLED_REG_SYNC_LED2 BIT(1) -#define PM8941_WLED_REG_SYNC_LED3 BIT(2) -#define PM8941_WLED_REG_SYNC_ALL 0x07 -#define PM8941_WLED_REG_SYNC_CLEAR 0x00 - -#define PM8941_WLED_REG_FREQ 0x4c -#define PM8941_WLED_REG_FREQ_MASK 0x0f - -#define PM8941_WLED_REG_OVP 0x4d -#define PM8941_WLED_REG_OVP_MASK 0x03 - -#define PM8941_WLED_REG_BOOST 0x4e -#define PM8941_WLED_REG_BOOST_MASK 0x07 - -#define PM8941_WLED_REG_SINK 0x4f -#define PM8941_WLED_REG_SINK_MASK 0xe0 -#define PM8941_WLED_REG_SINK_SHFT 0x05 - -/* Per-'string' registers below */ -#define PM8941_WLED_REG_STR_OFFSET 0x10 - -#define PM8941_WLED_REG_STR_MOD_EN_BASE 0x60 -#define PM8941_WLED_REG_STR_MOD_MASK BIT(7) -#define PM8941_WLED_REG_STR_MOD_EN BIT(7) - -#define PM8941_WLED_REG_STR_SCALE_BASE 0x62 -#define PM8941_WLED_REG_STR_SCALE_MASK 0x1f - -#define PM8941_WLED_REG_STR_MOD_SRC_BASE 0x63 -#define PM8941_WLED_REG_STR_MOD_SRC_MASK 0x01 -#define PM8941_WLED_REG_STR_MOD_SRC_INT 0x00 -#define PM8941_WLED_REG_STR_MOD_SRC_EXT 0x01 - -#define PM8941_WLED_REG_STR_CABC_BASE 0x66 -#define PM8941_WLED_REG_STR_CABC_MASK BIT(7) -#define PM8941_WLED_REG_STR_CABC_EN BIT(7) - -struct pm8941_wled_config { - u32 i_boost_limit; - u32 ovp; - u32 switch_freq; - u32 num_strings; - u32 i_limit; - bool cs_out_en; - bool ext_gen; - bool cabc_en; -}; - -struct pm8941_wled { - const char *name; - struct regmap *regmap; - u16 addr; - - struct pm8941_wled_config cfg; -}; - -static int pm8941_wled_update_status(struct backlight_device *bl) -{ - struct pm8941_wled *wled = bl_get_data(bl); - u16 val = bl->props.brightness; - u8 ctrl = 0; - int rc; - int i; - - if (bl->props.power != FB_BLANK_UNBLANK || - bl->props.fb_blank != FB_BLANK_UNBLANK || - bl->props.state & BL_CORE_FBBLANK) - val = 0; - - if (val != 0) - ctrl = PM8941_WLED_REG_MOD_EN_BIT; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_MOD_EN, - PM8941_WLED_REG_MOD_EN_MASK, ctrl); - if (rc) - return rc; - - for (i = 0; i < wled->cfg.num_strings; ++i) { - u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; - - rc = regmap_bulk_write(wled->regmap, - wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i, - v, 2); - if (rc) - return rc; - } - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR); - return rc; -} - -static int pm8941_wled_setup(struct pm8941_wled *wled) -{ - int rc; - int i; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_OVP, - PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_BOOST, - PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_FREQ, - PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq); - if (rc) - return rc; - - if (wled->cfg.cs_out_en) { - u8 all = (BIT(wled->cfg.num_strings) - 1) - << PM8941_WLED_REG_SINK_SHFT; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SINK, - PM8941_WLED_REG_SINK_MASK, all); - if (rc) - return rc; - } - - for (i = 0; i < wled->cfg.num_strings; ++i) { - u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i; - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_EN_BASE, - PM8941_WLED_REG_STR_MOD_MASK, - PM8941_WLED_REG_STR_MOD_EN); - if (rc) - return rc; - - if (wled->cfg.ext_gen) { - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_SRC_BASE, - PM8941_WLED_REG_STR_MOD_SRC_MASK, - PM8941_WLED_REG_STR_MOD_SRC_EXT); - if (rc) - return rc; - } - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_SCALE_BASE, - PM8941_WLED_REG_STR_SCALE_MASK, - wled->cfg.i_limit); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_CABC_BASE, - PM8941_WLED_REG_STR_CABC_MASK, - wled->cfg.cabc_en ? - PM8941_WLED_REG_STR_CABC_EN : 0); - if (rc) - return rc; - } - - return 0; -} - -static const struct pm8941_wled_config pm8941_wled_config_defaults = { - .i_boost_limit = 3, - .i_limit = 20, - .ovp = 2, - .switch_freq = 5, - .num_strings = 0, - .cs_out_en = false, - .ext_gen = false, - .cabc_en = false, -}; - -struct pm8941_wled_var_cfg { - const u32 *values; - u32 (*fn)(u32); - int size; -}; - -static const u32 pm8941_wled_i_boost_limit_values[] = { - 105, 385, 525, 805, 980, 1260, 1400, 1680, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = { - .values = pm8941_wled_i_boost_limit_values, - .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values), -}; - -static const u32 pm8941_wled_ovp_values[] = { - 35, 32, 29, 27, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = { - .values = pm8941_wled_ovp_values, - .size = ARRAY_SIZE(pm8941_wled_ovp_values), -}; - -static u32 pm8941_wled_num_strings_values_fn(u32 idx) -{ - return idx + 1; -} - -static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = { - .fn = pm8941_wled_num_strings_values_fn, - .size = 3, -}; - -static u32 pm8941_wled_switch_freq_values_fn(u32 idx) -{ - return 19200 / (2 * (1 + idx)); -} - -static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = { - .fn = pm8941_wled_switch_freq_values_fn, - .size = 16, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = { - .size = 26, -}; - -static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) -{ - if (idx >= cfg->size) - return UINT_MAX; - if (cfg->fn) - return cfg->fn(idx); - if (cfg->values) - return cfg->values[idx]; - return idx; -} - -static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) -{ - struct pm8941_wled_config *cfg = &wled->cfg; - u32 val; - int rc; - u32 c; - int i; - int j; - - const struct { - const char *name; - u32 *val_ptr; - const struct pm8941_wled_var_cfg *cfg; - } u32_opts[] = { - { - "qcom,current-boost-limit", - &cfg->i_boost_limit, - .cfg = &pm8941_wled_i_boost_limit_cfg, - }, - { - "qcom,current-limit", - &cfg->i_limit, - .cfg = &pm8941_wled_i_limit_cfg, - }, - { - "qcom,ovp", - &cfg->ovp, - .cfg = &pm8941_wled_ovp_cfg, - }, - { - "qcom,switching-freq", - &cfg->switch_freq, - .cfg = &pm8941_wled_switch_freq_cfg, - }, - { - "qcom,num-strings", - &cfg->num_strings, - .cfg = &pm8941_wled_num_strings_cfg, - }, - }; - const struct { - const char *name; - bool *val_ptr; - } bool_opts[] = { - { "qcom,cs-out", &cfg->cs_out_en, }, - { "qcom,ext-gen", &cfg->ext_gen, }, - { "qcom,cabc", &cfg->cabc_en, }, - }; - - rc = of_property_read_u32(dev->of_node, "reg", &val); - if (rc || val > 0xffff) { - dev_err(dev, "invalid IO resources\n"); - return rc ? rc : -EINVAL; - } - wled->addr = val; - - rc = of_property_read_string(dev->of_node, "label", &wled->name); - if (rc) - wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - - *cfg = pm8941_wled_config_defaults; - for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { - rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); - if (rc == -EINVAL) { - continue; - } else if (rc) { - dev_err(dev, "error reading '%s'\n", u32_opts[i].name); - return rc; - } - - c = UINT_MAX; - for (j = 0; c != val; j++) { - c = pm8941_wled_values(u32_opts[i].cfg, j); - if (c == UINT_MAX) { - dev_err(dev, "invalid value for '%s'\n", - u32_opts[i].name); - return -EINVAL; - } - } - - dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); - *u32_opts[i].val_ptr = j; - } - - for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { - if (of_property_read_bool(dev->of_node, bool_opts[i].name)) - *bool_opts[i].val_ptr = true; - } - - cfg->num_strings = cfg->num_strings + 1; - - return 0; -} - -static const struct backlight_ops pm8941_wled_ops = { - .update_status = pm8941_wled_update_status, -}; - -static int pm8941_wled_probe(struct platform_device *pdev) -{ - struct backlight_properties props; - struct backlight_device *bl; - struct pm8941_wled *wled; - struct regmap *regmap; - u32 val; - int rc; - - regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!regmap) { - dev_err(&pdev->dev, "Unable to get regmap\n"); - return -EINVAL; - } - - wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); - if (!wled) - return -ENOMEM; - - wled->regmap = regmap; - - rc = pm8941_wled_configure(wled, &pdev->dev); - if (rc) - return rc; - - rc = pm8941_wled_setup(wled); - if (rc) - return rc; - - val = PM8941_WLED_DEFAULT_BRIGHTNESS; - of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_RAW; - props.brightness = val; - props.max_brightness = PM8941_WLED_REG_VAL_MAX; - bl = devm_backlight_device_register(&pdev->dev, wled->name, - &pdev->dev, wled, - &pm8941_wled_ops, &props); - return PTR_ERR_OR_ZERO(bl); -}; - -static const struct of_device_id pm8941_wled_match_table[] = { - { .compatible = "qcom,pm8941-wled" }, - {} -}; -MODULE_DEVICE_TABLE(of, pm8941_wled_match_table); - -static struct platform_driver pm8941_wled_driver = { - .probe = pm8941_wled_probe, - .driver = { - .name = "pm8941-wled", - .of_match_table = pm8941_wled_match_table, - }, -}; - -module_platform_driver(pm8941_wled_driver); - -MODULE_DESCRIPTION("pm8941 wled driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c new file mode 100644 index 000000000000..82b85725a22a --- /dev/null +++ b/drivers/video/backlight/qcom-wled.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015, Sony Mobile Communications, AB. + */ + +#include +#include +#include +#include +#include +#include + +/* From DT binding */ +#define PM8941_WLED_DEFAULT_BRIGHTNESS 2048 + +#define PM8941_WLED_REG_VAL_BASE 0x40 +#define PM8941_WLED_REG_VAL_MAX 0xFFF + +#define PM8941_WLED_REG_MOD_EN 0x46 +#define PM8941_WLED_REG_MOD_EN_BIT BIT(7) +#define PM8941_WLED_REG_MOD_EN_MASK BIT(7) + +#define PM8941_WLED_REG_SYNC 0x47 +#define PM8941_WLED_REG_SYNC_MASK 0x07 +#define PM8941_WLED_REG_SYNC_LED1 BIT(0) +#define PM8941_WLED_REG_SYNC_LED2 BIT(1) +#define PM8941_WLED_REG_SYNC_LED3 BIT(2) +#define PM8941_WLED_REG_SYNC_ALL 0x07 +#define PM8941_WLED_REG_SYNC_CLEAR 0x00 + +#define PM8941_WLED_REG_FREQ 0x4c +#define PM8941_WLED_REG_FREQ_MASK 0x0f + +#define PM8941_WLED_REG_OVP 0x4d +#define PM8941_WLED_REG_OVP_MASK 0x03 + +#define PM8941_WLED_REG_BOOST 0x4e +#define PM8941_WLED_REG_BOOST_MASK 0x07 + +#define PM8941_WLED_REG_SINK 0x4f +#define PM8941_WLED_REG_SINK_MASK 0xe0 +#define PM8941_WLED_REG_SINK_SHFT 0x05 + +/* Per-'string' registers below */ +#define PM8941_WLED_REG_STR_OFFSET 0x10 + +#define PM8941_WLED_REG_STR_MOD_EN_BASE 0x60 +#define PM8941_WLED_REG_STR_MOD_MASK BIT(7) +#define PM8941_WLED_REG_STR_MOD_EN BIT(7) + +#define PM8941_WLED_REG_STR_SCALE_BASE 0x62 +#define PM8941_WLED_REG_STR_SCALE_MASK 0x1f + +#define PM8941_WLED_REG_STR_MOD_SRC_BASE 0x63 +#define PM8941_WLED_REG_STR_MOD_SRC_MASK 0x01 +#define PM8941_WLED_REG_STR_MOD_SRC_INT 0x00 +#define PM8941_WLED_REG_STR_MOD_SRC_EXT 0x01 + +#define PM8941_WLED_REG_STR_CABC_BASE 0x66 +#define PM8941_WLED_REG_STR_CABC_MASK BIT(7) +#define PM8941_WLED_REG_STR_CABC_EN BIT(7) + +struct pm8941_wled_config { + u32 i_boost_limit; + u32 ovp; + u32 switch_freq; + u32 num_strings; + u32 i_limit; + bool cs_out_en; + bool ext_gen; + bool cabc_en; +}; + +struct pm8941_wled { + const char *name; + struct regmap *regmap; + u16 addr; + + struct pm8941_wled_config cfg; +}; + +static int pm8941_wled_update_status(struct backlight_device *bl) +{ + struct pm8941_wled *wled = bl_get_data(bl); + u16 val = bl->props.brightness; + u8 ctrl = 0; + int rc; + int i; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & BL_CORE_FBBLANK) + val = 0; + + if (val != 0) + ctrl = PM8941_WLED_REG_MOD_EN_BIT; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_MOD_EN, + PM8941_WLED_REG_MOD_EN_MASK, ctrl); + if (rc) + return rc; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; + + rc = regmap_bulk_write(wled->regmap, + wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i, + v, 2); + if (rc) + return rc; + } + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SYNC, + PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SYNC, + PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR); + return rc; +} + +static int pm8941_wled_setup(struct pm8941_wled *wled) +{ + int rc; + int i; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_OVP, + PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_BOOST, + PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_FREQ, + PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq); + if (rc) + return rc; + + if (wled->cfg.cs_out_en) { + u8 all = (BIT(wled->cfg.num_strings) - 1) + << PM8941_WLED_REG_SINK_SHFT; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SINK, + PM8941_WLED_REG_SINK_MASK, all); + if (rc) + return rc; + } + + for (i = 0; i < wled->cfg.num_strings; ++i) { + u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i; + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_MOD_EN_BASE, + PM8941_WLED_REG_STR_MOD_MASK, + PM8941_WLED_REG_STR_MOD_EN); + if (rc) + return rc; + + if (wled->cfg.ext_gen) { + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_MOD_SRC_BASE, + PM8941_WLED_REG_STR_MOD_SRC_MASK, + PM8941_WLED_REG_STR_MOD_SRC_EXT); + if (rc) + return rc; + } + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_SCALE_BASE, + PM8941_WLED_REG_STR_SCALE_MASK, + wled->cfg.i_limit); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_CABC_BASE, + PM8941_WLED_REG_STR_CABC_MASK, + wled->cfg.cabc_en ? + PM8941_WLED_REG_STR_CABC_EN : 0); + if (rc) + return rc; + } + + return 0; +} + +static const struct pm8941_wled_config pm8941_wled_config_defaults = { + .i_boost_limit = 3, + .i_limit = 20, + .ovp = 2, + .switch_freq = 5, + .num_strings = 0, + .cs_out_en = false, + .ext_gen = false, + .cabc_en = false, +}; + +struct pm8941_wled_var_cfg { + const u32 *values; + u32 (*fn)(u32); + int size; +}; + +static const u32 pm8941_wled_i_boost_limit_values[] = { + 105, 385, 525, 805, 980, 1260, 1400, 1680, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = { + .values = pm8941_wled_i_boost_limit_values, + .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values), +}; + +static const u32 pm8941_wled_ovp_values[] = { + 35, 32, 29, 27, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = { + .values = pm8941_wled_ovp_values, + .size = ARRAY_SIZE(pm8941_wled_ovp_values), +}; + +static u32 pm8941_wled_num_strings_values_fn(u32 idx) +{ + return idx + 1; +} + +static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = { + .fn = pm8941_wled_num_strings_values_fn, + .size = 3, +}; + +static u32 pm8941_wled_switch_freq_values_fn(u32 idx) +{ + return 19200 / (2 * (1 + idx)); +} + +static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = { + .fn = pm8941_wled_switch_freq_values_fn, + .size = 16, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = { + .size = 26, +}; + +static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) +{ + if (idx >= cfg->size) + return UINT_MAX; + if (cfg->fn) + return cfg->fn(idx); + if (cfg->values) + return cfg->values[idx]; + return idx; +} + +static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) +{ + struct pm8941_wled_config *cfg = &wled->cfg; + u32 val; + int rc; + u32 c; + int i; + int j; + + const struct { + const char *name; + u32 *val_ptr; + const struct pm8941_wled_var_cfg *cfg; + } u32_opts[] = { + { + "qcom,current-boost-limit", + &cfg->i_boost_limit, + .cfg = &pm8941_wled_i_boost_limit_cfg, + }, + { + "qcom,current-limit", + &cfg->i_limit, + .cfg = &pm8941_wled_i_limit_cfg, + }, + { + "qcom,ovp", + &cfg->ovp, + .cfg = &pm8941_wled_ovp_cfg, + }, + { + "qcom,switching-freq", + &cfg->switch_freq, + .cfg = &pm8941_wled_switch_freq_cfg, + }, + { + "qcom,num-strings", + &cfg->num_strings, + .cfg = &pm8941_wled_num_strings_cfg, + }, + }; + const struct { + const char *name; + bool *val_ptr; + } bool_opts[] = { + { "qcom,cs-out", &cfg->cs_out_en, }, + { "qcom,ext-gen", &cfg->ext_gen, }, + { "qcom,cabc", &cfg->cabc_en, }, + }; + + rc = of_property_read_u32(dev->of_node, "reg", &val); + if (rc || val > 0xffff) { + dev_err(dev, "invalid IO resources\n"); + return rc ? rc : -EINVAL; + } + wled->addr = val; + + rc = of_property_read_string(dev->of_node, "label", &wled->name); + if (rc) + wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + + *cfg = pm8941_wled_config_defaults; + for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { + rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); + if (rc == -EINVAL) { + continue; + } else if (rc) { + dev_err(dev, "error reading '%s'\n", u32_opts[i].name); + return rc; + } + + c = UINT_MAX; + for (j = 0; c != val; j++) { + c = pm8941_wled_values(u32_opts[i].cfg, j); + if (c == UINT_MAX) { + dev_err(dev, "invalid value for '%s'\n", + u32_opts[i].name); + return -EINVAL; + } + } + + dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); + *u32_opts[i].val_ptr = j; + } + + for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { + if (of_property_read_bool(dev->of_node, bool_opts[i].name)) + *bool_opts[i].val_ptr = true; + } + + cfg->num_strings = cfg->num_strings + 1; + + return 0; +} + +static const struct backlight_ops pm8941_wled_ops = { + .update_status = pm8941_wled_update_status, +}; + +static int pm8941_wled_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct backlight_device *bl; + struct pm8941_wled *wled; + struct regmap *regmap; + u32 val; + int rc; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "Unable to get regmap\n"); + return -EINVAL; + } + + wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); + if (!wled) + return -ENOMEM; + + wled->regmap = regmap; + + rc = pm8941_wled_configure(wled, &pdev->dev); + if (rc) + return rc; + + rc = pm8941_wled_setup(wled); + if (rc) + return rc; + + val = PM8941_WLED_DEFAULT_BRIGHTNESS; + of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; + props.brightness = val; + props.max_brightness = PM8941_WLED_REG_VAL_MAX; + bl = devm_backlight_device_register(&pdev->dev, wled->name, + &pdev->dev, wled, + &pm8941_wled_ops, &props); + return PTR_ERR_OR_ZERO(bl); +}; + +static const struct of_device_id pm8941_wled_match_table[] = { + { .compatible = "qcom,pm8941-wled" }, + {} +}; +MODULE_DEVICE_TABLE(of, pm8941_wled_match_table); + +static struct platform_driver pm8941_wled_driver = { + .probe = pm8941_wled_probe, + .driver = { + .name = "pm8941-wled", + .of_match_table = pm8941_wled_match_table, + }, +}; + +module_platform_driver(pm8941_wled_driver); + +MODULE_DESCRIPTION("pm8941 wled driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From bb800a3715d4e9855db7a0586b57d68660ac623d Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:57:00 +0530 Subject: backlight: qcom-wled: Rename PM8941* to WLED3 Rename the PM8941* references as WLED3 to make the driver generic and have WLED support for other PMICs. Also rename "i_boost_limit" and "i_limit" variables to "boost_i_limit" and "string_i_limit" respectively to resemble the corresponding register names. Signed-off-by: Kiran Gunda Reviewed-by: Daniel Thompson Reviewed-by: Bjorn Andersson Acked-by: Pavel Machek Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 248 ++++++++++++++++++------------------ 1 file changed, 125 insertions(+), 123 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 82b85725a22a..f191242eca6a 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -10,77 +10,79 @@ #include /* From DT binding */ -#define PM8941_WLED_DEFAULT_BRIGHTNESS 2048 +#define WLED_DEFAULT_BRIGHTNESS 2048 -#define PM8941_WLED_REG_VAL_BASE 0x40 -#define PM8941_WLED_REG_VAL_MAX 0xFFF +#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF +#define WLED3_CTRL_REG_VAL_BASE 0x40 -#define PM8941_WLED_REG_MOD_EN 0x46 -#define PM8941_WLED_REG_MOD_EN_BIT BIT(7) -#define PM8941_WLED_REG_MOD_EN_MASK BIT(7) +/* WLED3 control registers */ +#define WLED3_CTRL_REG_MOD_EN 0x46 +#define WLED3_CTRL_REG_MOD_EN_BIT BIT(7) +#define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) -#define PM8941_WLED_REG_SYNC 0x47 -#define PM8941_WLED_REG_SYNC_MASK 0x07 -#define PM8941_WLED_REG_SYNC_LED1 BIT(0) -#define PM8941_WLED_REG_SYNC_LED2 BIT(1) -#define PM8941_WLED_REG_SYNC_LED3 BIT(2) -#define PM8941_WLED_REG_SYNC_ALL 0x07 -#define PM8941_WLED_REG_SYNC_CLEAR 0x00 +#define WLED3_CTRL_REG_FREQ 0x4c +#define WLED3_CTRL_REG_FREQ_MASK 0x0f -#define PM8941_WLED_REG_FREQ 0x4c -#define PM8941_WLED_REG_FREQ_MASK 0x0f +#define WLED3_CTRL_REG_OVP 0x4d +#define WLED3_CTRL_REG_OVP_MASK 0x03 -#define PM8941_WLED_REG_OVP 0x4d -#define PM8941_WLED_REG_OVP_MASK 0x03 +#define WLED3_CTRL_REG_ILIMIT 0x4e +#define WLED3_CTRL_REG_ILIMIT_MASK 0x07 -#define PM8941_WLED_REG_BOOST 0x4e -#define PM8941_WLED_REG_BOOST_MASK 0x07 +/* WLED3 sink registers */ +#define WLED3_SINK_REG_SYNC 0x47 +#define WLED3_SINK_REG_SYNC_MASK 0x07 +#define WLED3_SINK_REG_SYNC_LED1 BIT(0) +#define WLED3_SINK_REG_SYNC_LED2 BIT(1) +#define WLED3_SINK_REG_SYNC_LED3 BIT(2) +#define WLED3_SINK_REG_SYNC_ALL 0x07 +#define WLED3_SINK_REG_SYNC_CLEAR 0x00 -#define PM8941_WLED_REG_SINK 0x4f -#define PM8941_WLED_REG_SINK_MASK 0xe0 -#define PM8941_WLED_REG_SINK_SHFT 0x05 +#define WLED3_SINK_REG_CURR_SINK 0x4f +#define WLED3_SINK_REG_CURR_SINK_MASK 0xe0 +#define WLED3_SINK_REG_CURR_SINK_SHFT 0x05 -/* Per-'string' registers below */ -#define PM8941_WLED_REG_STR_OFFSET 0x10 +/* WLED3 per-'string' registers below */ +#define WLED3_SINK_REG_STR_OFFSET 0x10 -#define PM8941_WLED_REG_STR_MOD_EN_BASE 0x60 -#define PM8941_WLED_REG_STR_MOD_MASK BIT(7) -#define PM8941_WLED_REG_STR_MOD_EN BIT(7) +#define WLED3_SINK_REG_STR_MOD_EN_BASE 0x60 +#define WLED3_SINK_REG_STR_MOD_MASK BIT(7) +#define WLED3_SINK_REG_STR_MOD_EN BIT(7) -#define PM8941_WLED_REG_STR_SCALE_BASE 0x62 -#define PM8941_WLED_REG_STR_SCALE_MASK 0x1f +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR 0x62 +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK 0x1f -#define PM8941_WLED_REG_STR_MOD_SRC_BASE 0x63 -#define PM8941_WLED_REG_STR_MOD_SRC_MASK 0x01 -#define PM8941_WLED_REG_STR_MOD_SRC_INT 0x00 -#define PM8941_WLED_REG_STR_MOD_SRC_EXT 0x01 +#define WLED3_SINK_REG_STR_MOD_SRC_BASE 0x63 +#define WLED3_SINK_REG_STR_MOD_SRC_MASK 0x01 +#define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00 +#define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01 -#define PM8941_WLED_REG_STR_CABC_BASE 0x66 -#define PM8941_WLED_REG_STR_CABC_MASK BIT(7) -#define PM8941_WLED_REG_STR_CABC_EN BIT(7) +#define WLED3_SINK_REG_STR_CABC_BASE 0x66 +#define WLED3_SINK_REG_STR_CABC_MASK BIT(7) +#define WLED3_SINK_REG_STR_CABC_EN BIT(7) -struct pm8941_wled_config { - u32 i_boost_limit; +struct wled_config { + u32 boost_i_limit; u32 ovp; u32 switch_freq; u32 num_strings; - u32 i_limit; + u32 string_i_limit; bool cs_out_en; bool ext_gen; bool cabc_en; }; -struct pm8941_wled { +struct wled { const char *name; struct regmap *regmap; u16 addr; - struct pm8941_wled_config cfg; + struct wled_config cfg; }; -static int pm8941_wled_update_status(struct backlight_device *bl) +static int wled_update_status(struct backlight_device *bl) { - struct pm8941_wled *wled = bl_get_data(bl); + struct wled *wled = bl_get_data(bl); u16 val = bl->props.brightness; u8 ctrl = 0; int rc; @@ -92,11 +94,11 @@ static int pm8941_wled_update_status(struct backlight_device *bl) val = 0; if (val != 0) - ctrl = PM8941_WLED_REG_MOD_EN_BIT; + ctrl = WLED3_CTRL_REG_MOD_EN_BIT; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_MOD_EN, - PM8941_WLED_REG_MOD_EN_MASK, ctrl); + wled->addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, ctrl); if (rc) return rc; @@ -104,89 +106,89 @@ static int pm8941_wled_update_status(struct backlight_device *bl) u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; rc = regmap_bulk_write(wled->regmap, - wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i, + wled->addr + WLED3_CTRL_REG_VAL_BASE + 2 * i, v, 2); if (rc) return rc; } rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL); + wled->addr + WLED3_SINK_REG_SYNC, + WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_ALL); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR); + wled->addr + WLED3_SINK_REG_SYNC, + WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_CLEAR); return rc; } -static int pm8941_wled_setup(struct pm8941_wled *wled) +static int wled_setup(struct wled *wled) { int rc; int i; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_OVP, - PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp); + wled->addr + WLED3_CTRL_REG_OVP, + WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_BOOST, - PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit); + wled->addr + WLED3_CTRL_REG_ILIMIT, + WLED3_CTRL_REG_ILIMIT_MASK, wled->cfg.boost_i_limit); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_FREQ, - PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq); + wled->addr + WLED3_CTRL_REG_FREQ, + WLED3_CTRL_REG_FREQ_MASK, wled->cfg.switch_freq); if (rc) return rc; if (wled->cfg.cs_out_en) { u8 all = (BIT(wled->cfg.num_strings) - 1) - << PM8941_WLED_REG_SINK_SHFT; + << WLED3_SINK_REG_CURR_SINK_SHFT; rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SINK, - PM8941_WLED_REG_SINK_MASK, all); + wled->addr + WLED3_SINK_REG_CURR_SINK, + WLED3_SINK_REG_CURR_SINK_MASK, all); if (rc) return rc; } for (i = 0; i < wled->cfg.num_strings; ++i) { - u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i; + u16 addr = wled->addr + WLED3_SINK_REG_STR_OFFSET * i; rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_EN_BASE, - PM8941_WLED_REG_STR_MOD_MASK, - PM8941_WLED_REG_STR_MOD_EN); + addr + WLED3_SINK_REG_STR_MOD_EN_BASE, + WLED3_SINK_REG_STR_MOD_MASK, + WLED3_SINK_REG_STR_MOD_EN); if (rc) return rc; if (wled->cfg.ext_gen) { rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_SRC_BASE, - PM8941_WLED_REG_STR_MOD_SRC_MASK, - PM8941_WLED_REG_STR_MOD_SRC_EXT); + addr + WLED3_SINK_REG_STR_MOD_SRC_BASE, + WLED3_SINK_REG_STR_MOD_SRC_MASK, + WLED3_SINK_REG_STR_MOD_SRC_EXT); if (rc) return rc; } rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_SCALE_BASE, - PM8941_WLED_REG_STR_SCALE_MASK, - wled->cfg.i_limit); + addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR, + WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, + wled->cfg.string_i_limit); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_CABC_BASE, - PM8941_WLED_REG_STR_CABC_MASK, + addr + WLED3_SINK_REG_STR_CABC_BASE, + WLED3_SINK_REG_STR_CABC_MASK, wled->cfg.cabc_en ? - PM8941_WLED_REG_STR_CABC_EN : 0); + WLED3_SINK_REG_STR_CABC_EN : 0); if (rc) return rc; } @@ -194,9 +196,9 @@ static int pm8941_wled_setup(struct pm8941_wled *wled) return 0; } -static const struct pm8941_wled_config pm8941_wled_config_defaults = { - .i_boost_limit = 3, - .i_limit = 20, +static const struct wled_config wled3_config_defaults = { + .boost_i_limit = 3, + .string_i_limit = 20, .ovp = 2, .switch_freq = 5, .num_strings = 0, @@ -205,55 +207,55 @@ static const struct pm8941_wled_config pm8941_wled_config_defaults = { .cabc_en = false, }; -struct pm8941_wled_var_cfg { +struct wled_var_cfg { const u32 *values; u32 (*fn)(u32); int size; }; -static const u32 pm8941_wled_i_boost_limit_values[] = { +static const u32 wled3_boost_i_limit_values[] = { 105, 385, 525, 805, 980, 1260, 1400, 1680, }; -static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = { - .values = pm8941_wled_i_boost_limit_values, - .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values), +static const struct wled_var_cfg wled3_boost_i_limit_cfg = { + .values = wled3_boost_i_limit_values, + .size = ARRAY_SIZE(wled3_boost_i_limit_values), }; -static const u32 pm8941_wled_ovp_values[] = { +static const u32 wled3_ovp_values[] = { 35, 32, 29, 27, }; -static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = { - .values = pm8941_wled_ovp_values, - .size = ARRAY_SIZE(pm8941_wled_ovp_values), +static const struct wled_var_cfg wled3_ovp_cfg = { + .values = wled3_ovp_values, + .size = ARRAY_SIZE(wled3_ovp_values), }; -static u32 pm8941_wled_num_strings_values_fn(u32 idx) +static u32 wled3_num_strings_values_fn(u32 idx) { return idx + 1; } -static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = { - .fn = pm8941_wled_num_strings_values_fn, +static const struct wled_var_cfg wled3_num_strings_cfg = { + .fn = wled3_num_strings_values_fn, .size = 3, }; -static u32 pm8941_wled_switch_freq_values_fn(u32 idx) +static u32 wled3_switch_freq_values_fn(u32 idx) { return 19200 / (2 * (1 + idx)); } -static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = { - .fn = pm8941_wled_switch_freq_values_fn, +static const struct wled_var_cfg wled3_switch_freq_cfg = { + .fn = wled3_switch_freq_values_fn, .size = 16, }; -static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = { +static const struct wled_var_cfg wled3_string_i_limit_cfg = { .size = 26, }; -static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) +static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx) { if (idx >= cfg->size) return UINT_MAX; @@ -264,9 +266,9 @@ static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) return idx; } -static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) +static int wled_configure(struct wled *wled, struct device *dev) { - struct pm8941_wled_config *cfg = &wled->cfg; + struct wled_config *cfg = &wled->cfg; u32 val; int rc; u32 c; @@ -276,32 +278,32 @@ static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) const struct { const char *name; u32 *val_ptr; - const struct pm8941_wled_var_cfg *cfg; + const struct wled_var_cfg *cfg; } u32_opts[] = { { "qcom,current-boost-limit", - &cfg->i_boost_limit, - .cfg = &pm8941_wled_i_boost_limit_cfg, + &cfg->boost_i_limit, + .cfg = &wled3_boost_i_limit_cfg, }, { "qcom,current-limit", - &cfg->i_limit, - .cfg = &pm8941_wled_i_limit_cfg, + &cfg->string_i_limit, + .cfg = &wled3_string_i_limit_cfg, }, { "qcom,ovp", &cfg->ovp, - .cfg = &pm8941_wled_ovp_cfg, + .cfg = &wled3_ovp_cfg, }, { "qcom,switching-freq", &cfg->switch_freq, - .cfg = &pm8941_wled_switch_freq_cfg, + .cfg = &wled3_switch_freq_cfg, }, { "qcom,num-strings", &cfg->num_strings, - .cfg = &pm8941_wled_num_strings_cfg, + .cfg = &wled3_num_strings_cfg, }, }; const struct { @@ -324,7 +326,7 @@ static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) if (rc) wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - *cfg = pm8941_wled_config_defaults; + *cfg = wled3_config_defaults; for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); if (rc == -EINVAL) { @@ -336,7 +338,7 @@ static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) c = UINT_MAX; for (j = 0; c != val; j++) { - c = pm8941_wled_values(u32_opts[i].cfg, j); + c = wled3_values(u32_opts[i].cfg, j); if (c == UINT_MAX) { dev_err(dev, "invalid value for '%s'\n", u32_opts[i].name); @@ -358,15 +360,15 @@ static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) return 0; } -static const struct backlight_ops pm8941_wled_ops = { - .update_status = pm8941_wled_update_status, +static const struct backlight_ops wled_ops = { + .update_status = wled_update_status, }; -static int pm8941_wled_probe(struct platform_device *pdev) +static int wled_probe(struct platform_device *pdev) { struct backlight_properties props; struct backlight_device *bl; - struct pm8941_wled *wled; + struct wled *wled; struct regmap *regmap; u32 val; int rc; @@ -383,42 +385,42 @@ static int pm8941_wled_probe(struct platform_device *pdev) wled->regmap = regmap; - rc = pm8941_wled_configure(wled, &pdev->dev); + rc = wled_configure(wled, &pdev->dev); if (rc) return rc; - rc = pm8941_wled_setup(wled); + rc = wled_setup(wled); if (rc) return rc; - val = PM8941_WLED_DEFAULT_BRIGHTNESS; + val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.brightness = val; - props.max_brightness = PM8941_WLED_REG_VAL_MAX; + props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX; bl = devm_backlight_device_register(&pdev->dev, wled->name, &pdev->dev, wled, - &pm8941_wled_ops, &props); + &wled_ops, &props); return PTR_ERR_OR_ZERO(bl); }; -static const struct of_device_id pm8941_wled_match_table[] = { +static const struct of_device_id wled_match_table[] = { { .compatible = "qcom,pm8941-wled" }, {} }; -MODULE_DEVICE_TABLE(of, pm8941_wled_match_table); +MODULE_DEVICE_TABLE(of, wled_match_table); -static struct platform_driver pm8941_wled_driver = { - .probe = pm8941_wled_probe, +static struct platform_driver wled_driver = { + .probe = wled_probe, .driver = { - .name = "pm8941-wled", - .of_match_table = pm8941_wled_match_table, + .name = "qcom,wled", + .of_match_table = wled_match_table, }, }; -module_platform_driver(pm8941_wled_driver); +module_platform_driver(wled_driver); -MODULE_DESCRIPTION("pm8941 wled driver"); +MODULE_DESCRIPTION("Qualcomm WLED driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 775d2ffb4af658fbd619ca5ede91e9682916c53b Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:57:01 +0530 Subject: backlight: qcom-wled: Restructure the driver for WLED3 Restructure the driver to add the support for new WLED peripherals. Signed-off-by: Kiran Gunda Acked-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 373 ++++++++++++++++++++++-------------- 1 file changed, 234 insertions(+), 139 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index f191242eca6a..45eeda442c51 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -7,59 +7,71 @@ #include #include #include +#include #include /* From DT binding */ +#define WLED_MAX_STRINGS 4 + #define WLED_DEFAULT_BRIGHTNESS 2048 #define WLED3_SINK_REG_BRIGHT_MAX 0xFFF -#define WLED3_CTRL_REG_VAL_BASE 0x40 /* WLED3 control registers */ #define WLED3_CTRL_REG_MOD_EN 0x46 -#define WLED3_CTRL_REG_MOD_EN_BIT BIT(7) #define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) +#define WLED3_CTRL_REG_MOD_EN_SHIFT 7 #define WLED3_CTRL_REG_FREQ 0x4c -#define WLED3_CTRL_REG_FREQ_MASK 0x0f +#define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) #define WLED3_CTRL_REG_OVP 0x4d -#define WLED3_CTRL_REG_OVP_MASK 0x03 +#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) #define WLED3_CTRL_REG_ILIMIT 0x4e -#define WLED3_CTRL_REG_ILIMIT_MASK 0x07 +#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) /* WLED3 sink registers */ #define WLED3_SINK_REG_SYNC 0x47 -#define WLED3_SINK_REG_SYNC_MASK 0x07 -#define WLED3_SINK_REG_SYNC_LED1 BIT(0) -#define WLED3_SINK_REG_SYNC_LED2 BIT(1) -#define WLED3_SINK_REG_SYNC_LED3 BIT(2) -#define WLED3_SINK_REG_SYNC_ALL 0x07 #define WLED3_SINK_REG_SYNC_CLEAR 0x00 #define WLED3_SINK_REG_CURR_SINK 0x4f -#define WLED3_SINK_REG_CURR_SINK_MASK 0xe0 -#define WLED3_SINK_REG_CURR_SINK_SHFT 0x05 +#define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5) +#define WLED3_SINK_REG_CURR_SINK_SHFT 5 -/* WLED3 per-'string' registers below */ -#define WLED3_SINK_REG_STR_OFFSET 0x10 +/* WLED3 specific per-'string' registers below */ +#define WLED3_SINK_REG_BRIGHT(n) (0x40 + n) -#define WLED3_SINK_REG_STR_MOD_EN_BASE 0x60 +#define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10)) #define WLED3_SINK_REG_STR_MOD_MASK BIT(7) -#define WLED3_SINK_REG_STR_MOD_EN BIT(7) -#define WLED3_SINK_REG_STR_FULL_SCALE_CURR 0x62 -#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK 0x1f +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10)) +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0) -#define WLED3_SINK_REG_STR_MOD_SRC_BASE 0x63 -#define WLED3_SINK_REG_STR_MOD_SRC_MASK 0x01 +#define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10)) +#define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0) #define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00 #define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01 -#define WLED3_SINK_REG_STR_CABC_BASE 0x66 +#define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) #define WLED3_SINK_REG_STR_CABC_MASK BIT(7) -#define WLED3_SINK_REG_STR_CABC_EN BIT(7) + +struct wled_var_cfg { + const u32 *values; + u32 (*fn)(u32); + int size; +}; + +struct wled_u32_opts { + const char *name; + u32 *val_ptr; + const struct wled_var_cfg *cfg; +}; + +struct wled_bool_opts { + const char *name; + bool *val_ptr; +}; struct wled_config { u32 boost_i_limit; @@ -67,132 +79,179 @@ struct wled_config { u32 switch_freq; u32 num_strings; u32 string_i_limit; + u32 enabled_strings[WLED_MAX_STRINGS]; bool cs_out_en; bool ext_gen; - bool cabc_en; + bool cabc; }; struct wled { const char *name; + struct device *dev; struct regmap *regmap; - u16 addr; + u16 ctrl_addr; + u16 max_string_count; + u32 brightness; + u32 max_brightness; struct wled_config cfg; + int (*wled_set_brightness)(struct wled *wled, u16 brightness); }; +static int wled3_set_brightness(struct wled *wled, u16 brightness) +{ + int rc, i; + u8 v[2]; + + v[0] = brightness & 0xff; + v[1] = (brightness >> 8) & 0xf; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr + + WLED3_SINK_REG_BRIGHT(i), v, 2); + if (rc < 0) + return rc; + } + + return 0; +} + +static int wled_module_enable(struct wled *wled, int val) +{ + int rc; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + val << WLED3_CTRL_REG_MOD_EN_SHIFT); + return rc; +} + +static int wled_sync_toggle(struct wled *wled) +{ + int rc; + unsigned int mask = GENMASK(wled->max_string_count - 1, 0); + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_SYNC, + mask, mask); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_SYNC, + mask, WLED3_SINK_REG_SYNC_CLEAR); + + return rc; +} + static int wled_update_status(struct backlight_device *bl) { struct wled *wled = bl_get_data(bl); - u16 val = bl->props.brightness; - u8 ctrl = 0; - int rc; - int i; + u16 brightness = bl->props.brightness; + int rc = 0; if (bl->props.power != FB_BLANK_UNBLANK || bl->props.fb_blank != FB_BLANK_UNBLANK || bl->props.state & BL_CORE_FBBLANK) - val = 0; - - if (val != 0) - ctrl = WLED3_CTRL_REG_MOD_EN_BIT; + brightness = 0; - rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_CTRL_REG_MOD_EN, - WLED3_CTRL_REG_MOD_EN_MASK, ctrl); - if (rc) - return rc; + if (brightness) { + rc = wled->wled_set_brightness(wled, brightness); + if (rc < 0) { + dev_err(wled->dev, "wled failed to set brightness rc:%d\n", + rc); + return rc; + } - for (i = 0; i < wled->cfg.num_strings; ++i) { - u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; + rc = wled_sync_toggle(wled); + if (rc < 0) { + dev_err(wled->dev, "wled sync failed rc:%d\n", rc); + return rc; + } + } - rc = regmap_bulk_write(wled->regmap, - wled->addr + WLED3_CTRL_REG_VAL_BASE + 2 * i, - v, 2); - if (rc) + if (!!brightness != !!wled->brightness) { + rc = wled_module_enable(wled, !!brightness); + if (rc < 0) { + dev_err(wled->dev, "wled enable failed rc:%d\n", rc); return rc; + } } - rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_SINK_REG_SYNC, - WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_ALL); - if (rc) - return rc; + wled->brightness = brightness; - rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_SINK_REG_SYNC, - WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_CLEAR); return rc; } -static int wled_setup(struct wled *wled) +static int wled3_setup(struct wled *wled) { - int rc; - int i; + u16 addr; + u8 sink_en = 0; + int rc, i, j; rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_CTRL_REG_OVP, - WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); + wled->ctrl_addr + WLED3_CTRL_REG_OVP, + WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_CTRL_REG_ILIMIT, - WLED3_CTRL_REG_ILIMIT_MASK, wled->cfg.boost_i_limit); + wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, + WLED3_CTRL_REG_ILIMIT_MASK, + wled->cfg.boost_i_limit); if (rc) return rc; rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_CTRL_REG_FREQ, - WLED3_CTRL_REG_FREQ_MASK, wled->cfg.switch_freq); + wled->ctrl_addr + WLED3_CTRL_REG_FREQ, + WLED3_CTRL_REG_FREQ_MASK, + wled->cfg.switch_freq); if (rc) return rc; - if (wled->cfg.cs_out_en) { - u8 all = (BIT(wled->cfg.num_strings) - 1) - << WLED3_SINK_REG_CURR_SINK_SHFT; - - rc = regmap_update_bits(wled->regmap, - wled->addr + WLED3_SINK_REG_CURR_SINK, - WLED3_SINK_REG_CURR_SINK_MASK, all); - if (rc) - return rc; - } - for (i = 0; i < wled->cfg.num_strings; ++i) { - u16 addr = wled->addr + WLED3_SINK_REG_STR_OFFSET * i; - - rc = regmap_update_bits(wled->regmap, - addr + WLED3_SINK_REG_STR_MOD_EN_BASE, - WLED3_SINK_REG_STR_MOD_MASK, - WLED3_SINK_REG_STR_MOD_EN); + j = wled->cfg.enabled_strings[i]; + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_MOD_MASK, + WLED3_SINK_REG_STR_MOD_MASK); if (rc) return rc; if (wled->cfg.ext_gen) { - rc = regmap_update_bits(wled->regmap, - addr + WLED3_SINK_REG_STR_MOD_SRC_BASE, - WLED3_SINK_REG_STR_MOD_SRC_MASK, - WLED3_SINK_REG_STR_MOD_SRC_EXT); + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_MOD_SRC_MASK, + WLED3_SINK_REG_STR_MOD_SRC_EXT); if (rc) return rc; } - rc = regmap_update_bits(wled->regmap, - addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR, - WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, - wled->cfg.string_i_limit); + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, + wled->cfg.string_i_limit); if (rc) return rc; - rc = regmap_update_bits(wled->regmap, - addr + WLED3_SINK_REG_STR_CABC_BASE, - WLED3_SINK_REG_STR_CABC_MASK, - wled->cfg.cabc_en ? - WLED3_SINK_REG_STR_CABC_EN : 0); + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_CABC_MASK, + wled->cfg.cabc ? + WLED3_SINK_REG_STR_CABC_MASK : 0); if (rc) return rc; + + sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT); } + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK, + WLED3_SINK_REG_CURR_SINK_MASK, sink_en); + if (rc) + return rc; + return 0; } @@ -200,17 +259,12 @@ static const struct wled_config wled3_config_defaults = { .boost_i_limit = 3, .string_i_limit = 20, .ovp = 2, + .num_strings = 3, .switch_freq = 5, - .num_strings = 0, .cs_out_en = false, .ext_gen = false, - .cabc_en = false, -}; - -struct wled_var_cfg { - const u32 *values; - u32 (*fn)(u32); - int size; + .cabc = false, + .enabled_strings = {0, 1, 2, 3}, }; static const u32 wled3_boost_i_limit_values[] = { @@ -255,7 +309,11 @@ static const struct wled_var_cfg wled3_string_i_limit_cfg = { .size = 26, }; -static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx) +static const struct wled_var_cfg wled3_string_cfg = { + .size = 8, +}; + +static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx) { if (idx >= cfg->size) return UINT_MAX; @@ -266,68 +324,75 @@ static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx) return idx; } -static int wled_configure(struct wled *wled, struct device *dev) +static int wled_configure(struct wled *wled, int version) { struct wled_config *cfg = &wled->cfg; - u32 val; - int rc; - u32 c; - int i; - int j; - - const struct { - const char *name; - u32 *val_ptr; - const struct wled_var_cfg *cfg; - } u32_opts[] = { + struct device *dev = wled->dev; + const __be32 *prop_addr; + u32 size, val, c, string_len; + int rc, i, j; + + const struct wled_u32_opts *u32_opts = NULL; + const struct wled_u32_opts wled3_opts[] = { { - "qcom,current-boost-limit", - &cfg->boost_i_limit, + .name = "qcom,current-boost-limit", + .val_ptr = &cfg->boost_i_limit, .cfg = &wled3_boost_i_limit_cfg, }, { - "qcom,current-limit", - &cfg->string_i_limit, + .name = "qcom,current-limit", + .val_ptr = &cfg->string_i_limit, .cfg = &wled3_string_i_limit_cfg, }, { - "qcom,ovp", - &cfg->ovp, + .name = "qcom,ovp", + .val_ptr = &cfg->ovp, .cfg = &wled3_ovp_cfg, }, { - "qcom,switching-freq", - &cfg->switch_freq, + .name = "qcom,switching-freq", + .val_ptr = &cfg->switch_freq, .cfg = &wled3_switch_freq_cfg, }, { - "qcom,num-strings", - &cfg->num_strings, + .name = "qcom,num-strings", + .val_ptr = &cfg->num_strings, .cfg = &wled3_num_strings_cfg, }, }; - const struct { - const char *name; - bool *val_ptr; - } bool_opts[] = { + + const struct wled_bool_opts bool_opts[] = { { "qcom,cs-out", &cfg->cs_out_en, }, { "qcom,ext-gen", &cfg->ext_gen, }, - { "qcom,cabc", &cfg->cabc_en, }, + { "qcom,cabc", &cfg->cabc, }, }; - rc = of_property_read_u32(dev->of_node, "reg", &val); - if (rc || val > 0xffff) { - dev_err(dev, "invalid IO resources\n"); - return rc ? rc : -EINVAL; + prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); + if (!prop_addr) { + dev_err(wled->dev, "invalid IO resources\n"); + return -EINVAL; } - wled->addr = val; + wled->ctrl_addr = be32_to_cpu(*prop_addr); rc = of_property_read_string(dev->of_node, "label", &wled->name); if (rc) wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - *cfg = wled3_config_defaults; - for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { + switch (version) { + case 3: + u32_opts = wled3_opts; + size = ARRAY_SIZE(wled3_opts); + *cfg = wled3_config_defaults; + wled->wled_set_brightness = wled3_set_brightness; + wled->max_string_count = 3; + break; + + default: + dev_err(wled->dev, "Invalid WLED version\n"); + return -EINVAL; + } + + for (i = 0; i < size; ++i) { rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); if (rc == -EINVAL) { continue; @@ -338,12 +403,15 @@ static int wled_configure(struct wled *wled, struct device *dev) c = UINT_MAX; for (j = 0; c != val; j++) { - c = wled3_values(u32_opts[i].cfg, j); + c = wled_values(u32_opts[i].cfg, j); if (c == UINT_MAX) { dev_err(dev, "invalid value for '%s'\n", u32_opts[i].name); return -EINVAL; } + + if (c == val) + break; } dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); @@ -357,6 +425,15 @@ static int wled_configure(struct wled *wled, struct device *dev) cfg->num_strings = cfg->num_strings + 1; + string_len = of_property_count_elems_of_size(dev->of_node, + "qcom,enabled-strings", + sizeof(u32)); + if (string_len > 0) + of_property_read_u32_array(dev->of_node, + "qcom,enabled-strings", + wled->cfg.enabled_strings, + sizeof(u32)); + return 0; } @@ -370,6 +447,7 @@ static int wled_probe(struct platform_device *pdev) struct backlight_device *bl; struct wled *wled; struct regmap *regmap; + int version; u32 val; int rc; @@ -384,15 +462,32 @@ static int wled_probe(struct platform_device *pdev) return -ENOMEM; wled->regmap = regmap; + wled->dev = &pdev->dev; - rc = wled_configure(wled, &pdev->dev); - if (rc) - return rc; + version = (uintptr_t)of_device_get_match_data(&pdev->dev); + if (!version) { + dev_err(&pdev->dev, "Unknown device version\n"); + return -ENODEV; + } - rc = wled_setup(wled); + rc = wled_configure(wled, version); if (rc) return rc; + switch (version) { + case 3: + rc = wled3_setup(wled); + if (rc) { + dev_err(&pdev->dev, "wled3_setup failed\n"); + return rc; + } + break; + + default: + dev_err(wled->dev, "Invalid WLED version\n"); + break; + } + val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); @@ -407,7 +502,7 @@ static int wled_probe(struct platform_device *pdev) }; static const struct of_device_id wled_match_table[] = { - { .compatible = "qcom,pm8941-wled" }, + { .compatible = "qcom,pm8941-wled", .data = (void *)3 }, {} }; MODULE_DEVICE_TABLE(of, wled_match_table); -- cgit v1.2.3-59-g8ed1b From 03b2b5e8698623c0102ab2ec060f97bf606303a2 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:57:02 +0530 Subject: backlight: qcom-wled: Add support for WLED4 peripheral WLED4 peripheral is present on some PMICs like pmi8998 and pm660l. It has a different register map and configurations are also different. Add support for it. Signed-off-by: Kiran Gunda Reviewed-by: Bjorn Andersson Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 255 +++++++++++++++++++++++++++++++++++- 1 file changed, 253 insertions(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 45eeda442c51..5386ca90c7ee 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -17,7 +17,7 @@ #define WLED3_SINK_REG_BRIGHT_MAX 0xFFF -/* WLED3 control registers */ +/* WLED3/WLED4 control registers */ #define WLED3_CTRL_REG_MOD_EN 0x46 #define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) #define WLED3_CTRL_REG_MOD_EN_SHIFT 7 @@ -31,7 +31,7 @@ #define WLED3_CTRL_REG_ILIMIT 0x4e #define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) -/* WLED3 sink registers */ +/* WLED3/WLED4 sink registers */ #define WLED3_SINK_REG_SYNC 0x47 #define WLED3_SINK_REG_SYNC_CLEAR 0x00 @@ -56,6 +56,28 @@ #define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) #define WLED3_SINK_REG_STR_CABC_MASK BIT(7) +/* WLED4 specific sink registers */ +#define WLED4_SINK_REG_CURR_SINK 0x46 +#define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4) +#define WLED4_SINK_REG_CURR_SINK_SHFT 4 + +/* WLED4 specific per-'string' registers below */ +#define WLED4_SINK_REG_STR_MOD_EN(n) (0x50 + (n * 0x10)) +#define WLED4_SINK_REG_STR_MOD_MASK BIT(7) + +#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n) (0x52 + (n * 0x10)) +#define WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(3, 0) + +#define WLED4_SINK_REG_STR_MOD_SRC(n) (0x53 + (n * 0x10)) +#define WLED4_SINK_REG_STR_MOD_SRC_MASK BIT(0) +#define WLED4_SINK_REG_STR_MOD_SRC_INT 0x00 +#define WLED4_SINK_REG_STR_MOD_SRC_EXT 0x01 + +#define WLED4_SINK_REG_STR_CABC(n) (0x56 + (n * 0x10)) +#define WLED4_SINK_REG_STR_CABC_MASK BIT(7) + +#define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10)) + struct wled_var_cfg { const u32 *values; u32 (*fn)(u32); @@ -90,6 +112,7 @@ struct wled { struct device *dev; struct regmap *regmap; u16 ctrl_addr; + u16 sink_addr; u16 max_string_count; u32 brightness; u32 max_brightness; @@ -116,6 +139,29 @@ static int wled3_set_brightness(struct wled *wled, u16 brightness) return 0; } +static int wled4_set_brightness(struct wled *wled, u16 brightness) +{ + int rc, i; + u16 low_limit = wled->max_brightness * 4 / 1000; + u8 v[2]; + + /* WLED4's lower limit of operation is 0.4% */ + if (brightness > 0 && brightness < low_limit) + brightness = low_limit; + + v[0] = brightness & 0xff; + v[1] = (brightness >> 8) & 0xf; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + rc = regmap_bulk_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_BRIGHT(i), v, 2); + if (rc < 0) + return rc; + } + + return 0; +} + static int wled_module_enable(struct wled *wled, int val) { int rc; @@ -267,6 +313,120 @@ static const struct wled_config wled3_config_defaults = { .enabled_strings = {0, 1, 2, 3}, }; +static int wled4_setup(struct wled *wled) +{ + int rc, temp, i, j; + u16 addr; + u8 sink_en = 0; + u32 sink_cfg = 0; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_OVP, + WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, + WLED3_CTRL_REG_ILIMIT_MASK, + wled->cfg.boost_i_limit); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FREQ, + WLED3_CTRL_REG_FREQ_MASK, + wled->cfg.switch_freq); + if (rc < 0) + return rc; + + rc = regmap_read(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, &sink_cfg); + if (rc < 0) + return rc; + + for (i = 0; i < wled->cfg.num_strings; i++) { + j = wled->cfg.enabled_strings[i]; + temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; + sink_en |= 1 << temp; + } + + if (sink_cfg == sink_en) + return 0; + + rc = regmap_update_bits(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + WLED4_SINK_REG_CURR_SINK_MASK, 0); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) + return rc; + + /* Per sink/string configuration */ + for (i = 0; i < wled->cfg.num_strings; i++) { + j = wled->cfg.enabled_strings[i]; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_MOD_EN(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_MOD_MASK, + WLED4_SINK_REG_STR_MOD_MASK); + if (rc < 0) + return rc; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, + wled->cfg.string_i_limit); + if (rc < 0) + return rc; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_CABC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_CABC_MASK, + wled->cfg.cabc ? + WLED4_SINK_REG_STR_CABC_MASK : 0); + if (rc < 0) + return rc; + } + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + WLED4_SINK_REG_CURR_SINK_MASK, sink_en); + if (rc < 0) + return rc; + + rc = wled_sync_toggle(wled); + if (rc < 0) { + dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc); + return rc; + } + + return 0; +} + +static const struct wled_config wled4_config_defaults = { + .boost_i_limit = 4, + .string_i_limit = 10, + .ovp = 1, + .num_strings = 4, + .switch_freq = 11, + .cabc = false, +}; + static const u32 wled3_boost_i_limit_values[] = { 105, 385, 525, 805, 980, 1260, 1400, 1680, }; @@ -276,6 +436,15 @@ static const struct wled_var_cfg wled3_boost_i_limit_cfg = { .size = ARRAY_SIZE(wled3_boost_i_limit_values), }; +static const u32 wled4_boost_i_limit_values[] = { + 105, 280, 450, 620, 970, 1150, 1300, 1500, +}; + +static const struct wled_var_cfg wled4_boost_i_limit_cfg = { + .values = wled4_boost_i_limit_values, + .size = ARRAY_SIZE(wled4_boost_i_limit_values), +}; + static const u32 wled3_ovp_values[] = { 35, 32, 29, 27, }; @@ -285,6 +454,15 @@ static const struct wled_var_cfg wled3_ovp_cfg = { .size = ARRAY_SIZE(wled3_ovp_values), }; +static const u32 wled4_ovp_values[] = { + 31100, 29600, 19600, 18100, +}; + +static const struct wled_var_cfg wled4_ovp_cfg = { + .values = wled4_ovp_values, + .size = ARRAY_SIZE(wled4_ovp_values), +}; + static u32 wled3_num_strings_values_fn(u32 idx) { return idx + 1; @@ -295,6 +473,11 @@ static const struct wled_var_cfg wled3_num_strings_cfg = { .size = 3, }; +static const struct wled_var_cfg wled4_num_strings_cfg = { + .fn = wled3_num_strings_values_fn, + .size = 4, +}; + static u32 wled3_switch_freq_values_fn(u32 idx) { return 19200 / (2 * (1 + idx)); @@ -309,10 +492,24 @@ static const struct wled_var_cfg wled3_string_i_limit_cfg = { .size = 26, }; +static const u32 wled4_string_i_limit_values[] = { + 0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000, + 22500, 25000, 27500, 30000, +}; + +static const struct wled_var_cfg wled4_string_i_limit_cfg = { + .values = wled4_string_i_limit_values, + .size = ARRAY_SIZE(wled4_string_i_limit_values), +}; + static const struct wled_var_cfg wled3_string_cfg = { .size = 8, }; +static const struct wled_var_cfg wled4_string_cfg = { + .size = 16, +}; + static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx) { if (idx >= cfg->size) @@ -361,6 +558,34 @@ static int wled_configure(struct wled *wled, int version) }, }; + const struct wled_u32_opts wled4_opts[] = { + { + .name = "qcom,current-boost-limit", + .val_ptr = &cfg->boost_i_limit, + .cfg = &wled4_boost_i_limit_cfg, + }, + { + .name = "qcom,current-limit-microamp", + .val_ptr = &cfg->string_i_limit, + .cfg = &wled4_string_i_limit_cfg, + }, + { + .name = "qcom,ovp-millivolt", + .val_ptr = &cfg->ovp, + .cfg = &wled4_ovp_cfg, + }, + { + .name = "qcom,switching-freq", + .val_ptr = &cfg->switch_freq, + .cfg = &wled3_switch_freq_cfg, + }, + { + .name = "qcom,num-strings", + .val_ptr = &cfg->num_strings, + .cfg = &wled4_num_strings_cfg, + }, + }; + const struct wled_bool_opts bool_opts[] = { { "qcom,cs-out", &cfg->cs_out_en, }, { "qcom,ext-gen", &cfg->ext_gen, }, @@ -385,6 +610,22 @@ static int wled_configure(struct wled *wled, int version) *cfg = wled3_config_defaults; wled->wled_set_brightness = wled3_set_brightness; wled->max_string_count = 3; + wled->sink_addr = wled->ctrl_addr; + break; + + case 4: + u32_opts = wled4_opts; + size = ARRAY_SIZE(wled4_opts); + *cfg = wled4_config_defaults; + wled->wled_set_brightness = wled4_set_brightness; + wled->max_string_count = 4; + + prop_addr = of_get_address(dev->of_node, 1, NULL, NULL); + if (!prop_addr) { + dev_err(wled->dev, "invalid IO resources\n"); + return -EINVAL; + } + wled->sink_addr = be32_to_cpu(*prop_addr); break; default: @@ -483,6 +724,14 @@ static int wled_probe(struct platform_device *pdev) } break; + case 4: + rc = wled4_setup(wled); + if (rc) { + dev_err(&pdev->dev, "wled4_setup failed\n"); + return rc; + } + break; + default: dev_err(wled->dev, "Invalid WLED version\n"); break; @@ -503,6 +752,8 @@ static int wled_probe(struct platform_device *pdev) static const struct of_device_id wled_match_table[] = { { .compatible = "qcom,pm8941-wled", .data = (void *)3 }, + { .compatible = "qcom,pmi8998-wled", .data = (void *)4 }, + { .compatible = "qcom,pm660l-wled", .data = (void *)4 }, {} }; MODULE_DEVICE_TABLE(of, wled_match_table); -- cgit v1.2.3-59-g8ed1b From feeab87b30726ee3fc0522945c8efaa86a06d48b Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:57:03 +0530 Subject: backlight: qcom-wled: Add support for short circuit handling Handle the short circuit interrupt and check if the short circuit interrupt is valid. Re-enable the module to check if it goes away. Disable the module altogether if the short circuit event persists. Signed-off-by: Kiran Gunda Reviewed-by: Bjorn Andersson Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 144 +++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 5386ca90c7ee..658b1e0a72ec 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -2,6 +2,9 @@ /* Copyright (c) 2015, Sony Mobile Communications, AB. */ +#include +#include +#include #include #include #include @@ -56,6 +59,16 @@ #define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) #define WLED3_SINK_REG_STR_CABC_MASK BIT(7) +/* WLED4 specific control registers */ +#define WLED4_CTRL_REG_SHORT_PROTECT 0x5e +#define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7) + +#define WLED4_CTRL_REG_SEC_ACCESS 0xd0 +#define WLED4_CTRL_REG_SEC_UNLOCK 0xa5 + +#define WLED4_CTRL_REG_TEST1 0xe2 +#define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09 + /* WLED4 specific sink registers */ #define WLED4_SINK_REG_CURR_SINK 0x46 #define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4) @@ -105,17 +118,24 @@ struct wled_config { bool cs_out_en; bool ext_gen; bool cabc; + bool external_pfet; }; struct wled { const char *name; struct device *dev; struct regmap *regmap; + struct mutex lock; /* Lock to avoid race from thread irq handler */ + ktime_t last_short_event; u16 ctrl_addr; u16 sink_addr; u16 max_string_count; u32 brightness; u32 max_brightness; + u32 short_count; + bool disabled_by_short; + bool has_short_detect; + int short_irq; struct wled_config cfg; int (*wled_set_brightness)(struct wled *wled, u16 brightness); @@ -166,6 +186,9 @@ static int wled_module_enable(struct wled *wled, int val) { int rc; + if (wled->disabled_by_short) + return -ENXIO; + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, WLED3_CTRL_REG_MOD_EN_MASK, @@ -202,18 +225,19 @@ static int wled_update_status(struct backlight_device *bl) bl->props.state & BL_CORE_FBBLANK) brightness = 0; + mutex_lock(&wled->lock); if (brightness) { rc = wled->wled_set_brightness(wled, brightness); if (rc < 0) { dev_err(wled->dev, "wled failed to set brightness rc:%d\n", rc); - return rc; + goto unlock_mutex; } rc = wled_sync_toggle(wled); if (rc < 0) { dev_err(wled->dev, "wled sync failed rc:%d\n", rc); - return rc; + goto unlock_mutex; } } @@ -221,15 +245,61 @@ static int wled_update_status(struct backlight_device *bl) rc = wled_module_enable(wled, !!brightness); if (rc < 0) { dev_err(wled->dev, "wled enable failed rc:%d\n", rc); - return rc; + goto unlock_mutex; } } wled->brightness = brightness; +unlock_mutex: + mutex_unlock(&wled->lock); + return rc; } +#define WLED_SHORT_DLY_MS 20 +#define WLED_SHORT_CNT_MAX 5 +#define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC + +static irqreturn_t wled_short_irq_handler(int irq, void *_wled) +{ + struct wled *wled = _wled; + int rc; + s64 elapsed_time; + + wled->short_count++; + mutex_lock(&wled->lock); + rc = wled_module_enable(wled, false); + if (rc < 0) { + dev_err(wled->dev, "wled disable failed rc:%d\n", rc); + goto unlock_mutex; + } + + elapsed_time = ktime_us_delta(ktime_get(), + wled->last_short_event); + if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US) + wled->short_count = 1; + + if (wled->short_count > WLED_SHORT_CNT_MAX) { + dev_err(wled->dev, "Short trigged %d times, disabling WLED forever!\n", + wled->short_count); + wled->disabled_by_short = true; + goto unlock_mutex; + } + + wled->last_short_event = ktime_get(); + + msleep(WLED_SHORT_DLY_MS); + rc = wled_module_enable(wled, true); + if (rc < 0) + dev_err(wled->dev, "wled enable failed rc:%d\n", rc); + +unlock_mutex: + mutex_unlock(&wled->lock); + + return IRQ_HANDLED; +} + static int wled3_setup(struct wled *wled) { u16 addr; @@ -318,7 +388,7 @@ static int wled4_setup(struct wled *wled) int rc, temp, i, j; u16 addr; u8 sink_en = 0; - u32 sink_cfg = 0; + u32 sink_cfg; rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED3_CTRL_REG_OVP, @@ -340,6 +410,21 @@ static int wled4_setup(struct wled *wled) if (rc < 0) return rc; + if (wled->cfg.external_pfet) { + /* Unlock the secure register access */ + rc = regmap_write(wled->regmap, wled->ctrl_addr + + WLED4_CTRL_REG_SEC_ACCESS, + WLED4_CTRL_REG_SEC_UNLOCK); + if (rc < 0) + return rc; + + rc = regmap_write(wled->regmap, + wled->ctrl_addr + WLED4_CTRL_REG_TEST1, + WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2); + if (rc < 0) + return rc; + } + rc = regmap_read(wled->regmap, wled->sink_addr + WLED4_SINK_REG_CURR_SINK, &sink_cfg); if (rc < 0) @@ -425,6 +510,7 @@ static const struct wled_config wled4_config_defaults = { .num_strings = 4, .switch_freq = 11, .cabc = false, + .external_pfet = false, }; static const u32 wled3_boost_i_limit_values[] = { @@ -590,6 +676,7 @@ static int wled_configure(struct wled *wled, int version) { "qcom,cs-out", &cfg->cs_out_en, }, { "qcom,ext-gen", &cfg->ext_gen, }, { "qcom,cabc", &cfg->cabc, }, + { "qcom,external-pfet", &cfg->external_pfet, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); @@ -678,6 +765,38 @@ static int wled_configure(struct wled *wled, int version) return 0; } +static int wled_configure_short_irq(struct wled *wled, + struct platform_device *pdev) +{ + int rc; + + if (!wled->has_short_detect) + return 0; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED4_CTRL_REG_SHORT_PROTECT, + WLED4_CTRL_REG_SHORT_EN_MASK, + WLED4_CTRL_REG_SHORT_EN_MASK); + if (rc < 0) + return rc; + + wled->short_irq = platform_get_irq_byname(pdev, "short"); + if (wled->short_irq < 0) { + dev_dbg(&pdev->dev, "short irq is not used\n"); + return 0; + } + + rc = devm_request_threaded_irq(wled->dev, wled->short_irq, + NULL, wled_short_irq_handler, + IRQF_ONESHOT, + "wled_short_irq", wled); + if (rc < 0) + dev_err(wled->dev, "Unable to request short_irq (err:%d)\n", + rc); + + return rc; +} + static const struct backlight_ops wled_ops = { .update_status = wled_update_status, }; @@ -711,6 +830,7 @@ static int wled_probe(struct platform_device *pdev) return -ENODEV; } + mutex_init(&wled->lock); rc = wled_configure(wled, version); if (rc) return rc; @@ -725,6 +845,7 @@ static int wled_probe(struct platform_device *pdev) break; case 4: + wled->has_short_detect = true; rc = wled4_setup(wled); if (rc) { dev_err(&pdev->dev, "wled4_setup failed\n"); @@ -737,6 +858,10 @@ static int wled_probe(struct platform_device *pdev) break; } + rc = wled_configure_short_irq(wled, pdev); + if (rc < 0) + return rc; + val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); @@ -750,6 +875,16 @@ static int wled_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(bl); }; +static int wled_remove(struct platform_device *pdev) +{ + struct wled *wled = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&wled->lock); + disable_irq(wled->short_irq); + + return 0; +} + static const struct of_device_id wled_match_table[] = { { .compatible = "qcom,pm8941-wled", .data = (void *)3 }, { .compatible = "qcom,pmi8998-wled", .data = (void *)4 }, @@ -760,6 +895,7 @@ MODULE_DEVICE_TABLE(of, wled_match_table); static struct platform_driver wled_driver = { .probe = wled_probe, + .remove = wled_remove, .driver = { .name = "qcom,wled", .of_match_table = wled_match_table, -- cgit v1.2.3-59-g8ed1b From 8663c188beeacf35d4865185a6713d6e8ded4fea Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 1 Nov 2019 11:57:04 +0530 Subject: backlight: qcom-wled: Add auto string detection logic The auto string detection algorithm checks if the current WLED sink configuration is valid. It tries enabling every sink and checks if the OVP fault is observed. Based on this information it detects and enables the valid sink configuration. Auto calibration will be triggered when the OVP fault interrupts are seen frequently thereby it tries to fix the sink configuration. The auto-detection also kicks in when the connected LED string of the display-backlight malfunctions (because of damage) and requires the damaged string to be turned off to prevent the complete panel and/or board from being damaged. Signed-off-by: Kiran Gunda Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 400 +++++++++++++++++++++++++++++++++++- 1 file changed, 394 insertions(+), 6 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 658b1e0a72ec..33b6007c5e55 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -17,19 +17,29 @@ #define WLED_MAX_STRINGS 4 #define WLED_DEFAULT_BRIGHTNESS 2048 - +#define WLED_SOFT_START_DLY_US 10000 #define WLED3_SINK_REG_BRIGHT_MAX 0xFFF /* WLED3/WLED4 control registers */ +#define WLED3_CTRL_REG_FAULT_STATUS 0x08 +#define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0) +#define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1) +#define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2) + +#define WLED3_CTRL_REG_INT_RT_STS 0x10 +#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1) + #define WLED3_CTRL_REG_MOD_EN 0x46 #define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) #define WLED3_CTRL_REG_MOD_EN_SHIFT 7 +#define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48 + #define WLED3_CTRL_REG_FREQ 0x4c #define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) #define WLED3_CTRL_REG_OVP 0x4d -#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) +#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) #define WLED3_CTRL_REG_ILIMIT 0x4e #define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) @@ -119,6 +129,7 @@ struct wled_config { bool ext_gen; bool cabc; bool external_pfet; + bool auto_detection_enabled; }; struct wled { @@ -127,17 +138,22 @@ struct wled { struct regmap *regmap; struct mutex lock; /* Lock to avoid race from thread irq handler */ ktime_t last_short_event; + ktime_t start_ovp_fault_time; u16 ctrl_addr; u16 sink_addr; u16 max_string_count; + u16 auto_detection_ovp_count; u32 brightness; u32 max_brightness; u32 short_count; + u32 auto_detect_count; bool disabled_by_short; bool has_short_detect; int short_irq; + int ovp_irq; struct wled_config cfg; + struct delayed_work ovp_work; int (*wled_set_brightness)(struct wled *wled, u16 brightness); }; @@ -182,6 +198,13 @@ static int wled4_set_brightness(struct wled *wled, u16 brightness) return 0; } +static void wled_ovp_work(struct work_struct *work) +{ + struct wled *wled = container_of(work, + struct wled, ovp_work.work); + enable_irq(wled->ovp_irq); +} + static int wled_module_enable(struct wled *wled, int val) { int rc; @@ -193,7 +216,25 @@ static int wled_module_enable(struct wled *wled, int val) WLED3_CTRL_REG_MOD_EN, WLED3_CTRL_REG_MOD_EN_MASK, val << WLED3_CTRL_REG_MOD_EN_SHIFT); - return rc; + if (rc < 0) + return rc; + + if (wled->ovp_irq > 0) { + if (val) { + /* + * The hardware generates a storm of spurious OVP + * interrupts during soft start operations. So defer + * enabling the IRQ for 10ms to ensure that the + * soft start is complete. + */ + schedule_delayed_work(&wled->ovp_work, HZ / 100); + } else { + if (!cancel_delayed_work_sync(&wled->ovp_work)) + disable_irq(wled->ovp_irq); + } + } + + return 0; } static int wled_sync_toggle(struct wled *wled) @@ -300,6 +341,304 @@ unlock_mutex: return IRQ_HANDLED; } +#define AUTO_DETECT_BRIGHTNESS 200 + +static void wled_auto_string_detection(struct wled *wled) +{ + int rc = 0, i; + u32 sink_config = 0, int_sts; + u8 sink_test = 0, sink_valid = 0, val; + + /* Read configured sink configuration */ + rc = regmap_read(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, &sink_config); + if (rc < 0) { + dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n", + rc); + goto failed_detect; + } + + /* Disable the module before starting detection */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc); + goto failed_detect; + } + + /* Set low brightness across all sinks */ + rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS); + if (rc < 0) { + dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n", + rc); + goto failed_detect; + } + + if (wled->cfg.cabc) { + for (i = 0; i < wled->cfg.num_strings; i++) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + 0); + if (rc < 0) + goto failed_detect; + } + } + + /* Disable all sinks */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc); + goto failed_detect; + } + + /* Iterate through the strings one by one */ + for (i = 0; i < wled->cfg.num_strings; i++) { + sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + i)); + + /* Enable feedback control */ + rc = regmap_write(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_FEEDBACK_CONTROL, i + 1); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n", + i + 1, rc); + goto failed_detect; + } + + /* Enable the sink */ + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, sink_test); + if (rc < 0) { + dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n", + i + 1, rc); + goto failed_detect; + } + + /* Enable the module */ + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", + rc); + goto failed_detect; + } + + usleep_range(WLED_SOFT_START_DLY_US, + WLED_SOFT_START_DLY_US + 1000); + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_INT_RT_STS, &int_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED3_CTRL_INT_RT_STS rc=%d\n", + rc); + goto failed_detect; + } + + if (int_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS) + dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n", + i + 1); + else + sink_valid |= sink_test; + + /* Disable the module */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", + rc); + goto failed_detect; + } + } + + if (!sink_valid) { + dev_err(wled->dev, "No valid WLED sinks found\n"); + wled->disabled_by_short = true; + goto failed_detect; + } + + if (sink_valid != sink_config) { + dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n", + sink_config, sink_valid); + sink_config = sink_valid; + } + + /* Write the new sink configuration */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + sink_config); + if (rc < 0) { + dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n", + rc); + goto failed_detect; + } + + /* Enable valid sinks */ + for (i = 0; i < wled->cfg.num_strings; i++) { + if (wled->cfg.cabc) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + WLED4_SINK_REG_STR_CABC_MASK); + if (rc < 0) + goto failed_detect; + } + + if (sink_config & BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i)) + val = WLED4_SINK_REG_STR_MOD_MASK; + else + val = 0x0; /* Disable modulator_en for unused sink */ + + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_MOD_EN(i), val); + if (rc < 0) { + dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n", + rc); + goto failed_detect; + } + } + + /* Restore the feedback setting */ + rc = regmap_write(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n", + rc); + goto failed_detect; + } + + /* Restore brightness */ + rc = wled4_set_brightness(wled, wled->brightness); + if (rc < 0) { + dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n", + rc); + goto failed_detect; + } + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc); + goto failed_detect; + } + +failed_detect: + return; +} + +#define WLED_AUTO_DETECT_OVP_COUNT 5 +#define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC +static bool wled_auto_detection_required(struct wled *wled) +{ + s64 elapsed_time_us; + + if (!wled->cfg.auto_detection_enabled) + return false; + + /* + * Check if the OVP fault was an occasional one + * or if it's firing continuously, the latter qualifies + * for an auto-detection check. + */ + if (!wled->auto_detection_ovp_count) { + wled->start_ovp_fault_time = ktime_get(); + wled->auto_detection_ovp_count++; + } else { + elapsed_time_us = ktime_us_delta(ktime_get(), + wled->start_ovp_fault_time); + if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) + wled->auto_detection_ovp_count = 0; + else + wled->auto_detection_ovp_count++; + + if (wled->auto_detection_ovp_count >= + WLED_AUTO_DETECT_OVP_COUNT) { + wled->auto_detection_ovp_count = 0; + return true; + } + } + + return false; +} + +static int wled_auto_detection_at_init(struct wled *wled) +{ + int rc; + u32 fault_status, rt_status; + + if (!wled->cfg.auto_detection_enabled) + return 0; + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, + &rt_status); + if (rc < 0) { + dev_err(wled->dev, "Failed to read RT status rc=%d\n", rc); + return rc; + } + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, + &fault_status); + if (rc < 0) { + dev_err(wled->dev, "Failed to read fault status rc=%d\n", rc); + return rc; + } + + if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) || + (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) { + mutex_lock(&wled->lock); + wled_auto_string_detection(wled); + mutex_unlock(&wled->lock); + } + + return rc; +} + +static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) +{ + struct wled *wled = _wled; + int rc; + u32 int_sts, fault_sts; + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n", + rc); + return IRQ_HANDLED; + } + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_FAULT_STATUS, &fault_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n", + rc); + return IRQ_HANDLED; + } + + if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | + WLED3_CTRL_REG_ILIM_FAULT_BIT)) + dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n", + int_sts, fault_sts); + + if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) { + if (wled_auto_detection_required(wled)) { + mutex_lock(&wled->lock); + wled_auto_string_detection(wled); + mutex_unlock(&wled->lock); + } + } + + return IRQ_HANDLED; +} + static int wled3_setup(struct wled *wled) { u16 addr; @@ -436,8 +775,10 @@ static int wled4_setup(struct wled *wled) sink_en |= 1 << temp; } - if (sink_cfg == sink_en) - return 0; + if (sink_cfg == sink_en) { + rc = wled_auto_detection_at_init(wled); + return rc; + } rc = regmap_update_bits(wled->regmap, wled->sink_addr + WLED4_SINK_REG_CURR_SINK, @@ -500,7 +841,9 @@ static int wled4_setup(struct wled *wled) return rc; } - return 0; + rc = wled_auto_detection_at_init(wled); + + return rc; } static const struct wled_config wled4_config_defaults = { @@ -511,6 +854,7 @@ static const struct wled_config wled4_config_defaults = { .switch_freq = 11, .cabc = false, .external_pfet = false, + .auto_detection_enabled = false, }; static const u32 wled3_boost_i_limit_values[] = { @@ -677,6 +1021,7 @@ static int wled_configure(struct wled *wled, int version) { "qcom,ext-gen", &cfg->ext_gen, }, { "qcom,cabc", &cfg->cabc, }, { "qcom,external-pfet", &cfg->external_pfet, }, + { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); @@ -797,6 +1142,40 @@ static int wled_configure_short_irq(struct wled *wled, return rc; } +static int wled_configure_ovp_irq(struct wled *wled, + struct platform_device *pdev) +{ + int rc; + u32 val; + + wled->ovp_irq = platform_get_irq_byname(pdev, "ovp"); + if (wled->ovp_irq < 0) { + dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n"); + return 0; + } + + rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL, + wled_ovp_irq_handler, IRQF_ONESHOT, + "wled_ovp_irq", wled); + if (rc < 0) { + dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n", + rc); + wled->ovp_irq = 0; + return 0; + } + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, &val); + if (rc < 0) + return rc; + + /* Keep OVP irq disabled until module is enabled */ + if (!(val & WLED3_CTRL_REG_MOD_EN_MASK)) + disable_irq(wled->ovp_irq); + + return 0; +} + static const struct backlight_ops wled_ops = { .update_status = wled_update_status, }; @@ -837,6 +1216,7 @@ static int wled_probe(struct platform_device *pdev) switch (version) { case 3: + wled->cfg.auto_detection_enabled = false; rc = wled3_setup(wled); if (rc) { dev_err(&pdev->dev, "wled3_setup failed\n"); @@ -858,10 +1238,16 @@ static int wled_probe(struct platform_device *pdev) break; } + INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work); + rc = wled_configure_short_irq(wled, pdev); if (rc < 0) return rc; + rc = wled_configure_ovp_irq(wled, pdev); + if (rc < 0) + return rc; + val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); @@ -880,7 +1266,9 @@ static int wled_remove(struct platform_device *pdev) struct wled *wled = dev_get_drvdata(&pdev->dev); mutex_destroy(&wled->lock); + cancel_delayed_work_sync(&wled->ovp_work); disable_irq(wled->short_irq); + disable_irq(wled->ovp_irq); return 0; } -- cgit v1.2.3-59-g8ed1b From 0b0cb52bd80eda76c4b9921f5cf9c1b709d44e83 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Oct 2019 17:41:53 +0200 Subject: video: backlight: tosa: Use GPIO lookup table The driver should not require a machine specific header. Change it to pass the GPIO line through a lookup table, and move the timing generator definitions into the drivers itself. Signed-off-by: Arnd Bergmann Acked-by: Robert Jarzmik Reviewed-by: Linus Walleij Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- arch/arm/mach-pxa/include/mach/tosa.h | 15 --------------- arch/arm/mach-pxa/tosa.c | 22 ++++++++++++++++++++++ drivers/video/backlight/tosa_bl.c | 10 +++++----- drivers/video/backlight/tosa_bl.h | 8 ++++++++ drivers/video/backlight/tosa_lcd.c | 28 +++++++++++++++++++++------- 5 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 drivers/video/backlight/tosa_bl.h (limited to 'drivers/video') diff --git a/arch/arm/mach-pxa/include/mach/tosa.h b/arch/arm/mach-pxa/include/mach/tosa.h index a499ed17931e..8bfaca3a8b64 100644 --- a/arch/arm/mach-pxa/include/mach/tosa.h +++ b/arch/arm/mach-pxa/include/mach/tosa.h @@ -72,18 +72,6 @@ #define TOSA_GPIO_BAT0_TH_ON (TOSA_TC6393XB_GPIO_BASE + 14) #define TOSA_GPIO_BAT1_TH_ON (TOSA_TC6393XB_GPIO_BASE + 15) -/* - * Timing Generator - */ -#define TG_PNLCTL 0x00 -#define TG_TPOSCTL 0x01 -#define TG_DUTYCTL 0x02 -#define TG_GPOSR 0x03 -#define TG_GPODR1 0x04 -#define TG_GPODR2 0x05 -#define TG_PINICTL 0x06 -#define TG_HPOSCTL 0x07 - /* * PXA GPIOs */ @@ -192,7 +180,4 @@ #define TOSA_KEY_MAIL KEY_MAIL #endif -struct spi_device; -extern int tosa_bl_enable(struct spi_device *spi, int enable); - #endif /* _ASM_ARCH_TOSA_H_ */ diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index f537ff1c3ba7..4e13893edeb9 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -813,6 +813,26 @@ static struct pxa2xx_spi_controller pxa_ssp_master_info = { .num_chipselect = 1, }; +static struct gpiod_lookup_table tosa_lcd_gpio_table = { + .dev_id = "spi2.0", + .table = { + GPIO_LOOKUP("tc6393xb", + TOSA_GPIO_TG_ON - TOSA_TC6393XB_GPIO_BASE, + "tg #pwr", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct gpiod_lookup_table tosa_lcd_bl_gpio_table = { + .dev_id = "i2c-tosa-bl", + .table = { + GPIO_LOOKUP("tc6393xb", + TOSA_GPIO_BL_C20MA - TOSA_TC6393XB_GPIO_BASE, + "backlight", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static struct spi_board_info spi_board_info[] __initdata = { { .modalias = "tosa-lcd", @@ -923,6 +943,8 @@ static void __init tosa_init(void) platform_scoop_config = &tosa_pcmcia_config; pxa2xx_set_spi_info(2, &pxa_ssp_master_info); + gpiod_add_lookup_table(&tosa_lcd_gpio_table); + gpiod_add_lookup_table(&tosa_lcd_bl_gpio_table); spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); clk_add_alias("CLK_CK3P6MI", tc6393xb_device.name, "GPIO11_CLK", NULL); diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 1275e815bd86..cff5e96fd988 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -18,7 +18,7 @@ #include -#include +#include "tosa_bl.h" #define COMADJ_DEFAULT 97 @@ -28,6 +28,7 @@ struct tosa_bl_data { struct i2c_client *i2c; struct backlight_device *bl; + struct gpio_desc *gpio; int comadj; }; @@ -42,7 +43,7 @@ static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness) i2c_smbus_write_byte_data(data->i2c, DAC_CH2, (u8)(brightness & 0xff)); /* SetBacklightVR */ - gpio_set_value(TOSA_GPIO_BL_C20MA, brightness & 0x100); + gpiod_set_value(data->gpio, brightness & 0x100); tosa_bl_enable(spi, brightness); } @@ -87,9 +88,8 @@ static int tosa_bl_probe(struct i2c_client *client, return -ENOMEM; data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; - - ret = devm_gpio_request_one(&client->dev, TOSA_GPIO_BL_C20MA, - GPIOF_OUT_INIT_LOW, "backlight"); + data->gpio = devm_gpiod_get(&client->dev, "backlight", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(data->gpio); if (ret) { dev_dbg(&data->bl->dev, "Unable to request gpio!\n"); return ret; diff --git a/drivers/video/backlight/tosa_bl.h b/drivers/video/backlight/tosa_bl.h new file mode 100644 index 000000000000..589e17e6fdb2 --- /dev/null +++ b/drivers/video/backlight/tosa_bl.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _TOSA_BL_H +#define _TOSA_BL_H + +struct spi_device; +extern int tosa_bl_enable(struct spi_device *spi, int enable); + +#endif diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 29af8e27b6e5..e8ab583e5098 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -19,7 +19,7 @@ #include -#include +#include "tosa_bl.h" #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) @@ -28,12 +28,26 @@ #define TG_REG0_UD 0x0004 #define TG_REG0_LR 0x0008 +/* + * Timing Generator + */ +#define TG_PNLCTL 0x00 +#define TG_TPOSCTL 0x01 +#define TG_DUTYCTL 0x02 +#define TG_GPOSR 0x03 +#define TG_GPODR1 0x04 +#define TG_GPODR2 0x05 +#define TG_PINICTL 0x06 +#define TG_HPOSCTL 0x07 + + #define DAC_BASE 0x4e struct tosa_lcd_data { struct spi_device *spi; struct lcd_device *lcd; struct i2c_client *i2c; + struct gpio_desc *gpiod_tg; int lcd_power; bool is_vga; @@ -66,7 +80,7 @@ EXPORT_SYMBOL(tosa_bl_enable); static void tosa_lcd_tg_init(struct tosa_lcd_data *data) { /* TG on */ - gpio_set_value(TOSA_GPIO_TG_ON, 0); + gpiod_set_value(data->gpiod_tg, 0); mdelay(60); @@ -100,6 +114,7 @@ static void tosa_lcd_tg_on(struct tosa_lcd_data *data) */ struct i2c_adapter *adap = i2c_get_adapter(0); struct i2c_board_info info = { + .dev_name = "tosa-bl", .type = "tosa-bl", .addr = DAC_BASE, .platform_data = data->spi, @@ -121,7 +136,7 @@ static void tosa_lcd_tg_off(struct tosa_lcd_data *data) mdelay(50); /* TG Off */ - gpio_set_value(TOSA_GPIO_TG_ON, 1); + gpiod_set_value(data->gpiod_tg, 1); mdelay(100); } @@ -191,10 +206,9 @@ static int tosa_lcd_probe(struct spi_device *spi) data->spi = spi; spi_set_drvdata(spi, data); - ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON, - GPIOF_OUT_INIT_LOW, "tg #pwr"); - if (ret < 0) - return ret; + data->gpiod_tg = devm_gpiod_get(&spi->dev, "tg #pwr", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_tg)) + return PTR_ERR(data->gpiod_tg); mdelay(60); -- cgit v1.2.3-59-g8ed1b From 8a05548375713c0320d6746dfaff6329fdafd0fd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:22 +0200 Subject: backlight: gpio: Remove unneeded include We no longer use any symbols from of_gpio.h. Remove this include. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Daniel Thompson Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 18e053e4716c..7e1990199fae 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 3cfc291bfe3f071a285fa6b1c0c6ca22a83c2f06 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:23 +0200 Subject: backlight: gpio: Remove stray newline Remove a double newline from the driver. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 7e1990199fae..3955b513f2f8 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -91,7 +91,6 @@ static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) return FB_BLANK_UNBLANK; } - static int gpio_backlight_probe(struct platform_device *pdev) { struct gpio_backlight_platform_data *pdata = -- cgit v1.2.3-59-g8ed1b From 706dc68102bc7421a9e6573d149ab6d769d71cc7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:24 +0200 Subject: backlight: gpio: Explicitly set the direction of the GPIO The GPIO backlight driver currently requests the line 'as is', without acively setting its direction. This can lead to problems: if the line is in input mode by default, we won't be able to drive it later when updating the status and also reading its initial value doesn't make sense for backlight setting. Request the line 'as is' initially, so that we can read its value without affecting it but then change the direction to output explicitly when setting the initial brightness. Also: check the current direction and only read the value if it's output. Signed-off-by: Bartosz Golaszewski Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 3955b513f2f8..52f17c9ca1c3 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -25,9 +25,8 @@ struct gpio_backlight { int def_value; }; -static int gpio_backlight_update_status(struct backlight_device *bl) +static int gpio_backlight_get_next_brightness(struct backlight_device *bl) { - struct gpio_backlight *gbl = bl_get_data(bl); int brightness = bl->props.brightness; if (bl->props.power != FB_BLANK_UNBLANK || @@ -35,6 +34,14 @@ static int gpio_backlight_update_status(struct backlight_device *bl) bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; + return brightness; +} + +static int gpio_backlight_update_status(struct backlight_device *bl) +{ + struct gpio_backlight *gbl = bl_get_data(bl); + int brightness = gpio_backlight_get_next_brightness(bl); + gpiod_set_value_cansleep(gbl->gpiod, brightness); return 0; @@ -85,7 +92,8 @@ static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) return gbl->def_value ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; /* if the enable GPIO is disabled, do not enable the backlight */ - if (gpiod_get_value_cansleep(gbl->gpiod) == 0) + if (gpiod_get_direction(gbl->gpiod) == 0 && + gpiod_get_value_cansleep(gbl->gpiod) == 0) return FB_BLANK_POWERDOWN; return FB_BLANK_UNBLANK; @@ -98,7 +106,7 @@ static int gpio_backlight_probe(struct platform_device *pdev) struct backlight_properties props; struct backlight_device *bl; struct gpio_backlight *gbl; - int ret; + int ret, init_brightness; gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); if (gbl == NULL) @@ -151,7 +159,12 @@ static int gpio_backlight_probe(struct platform_device *pdev) bl->props.power = gpio_backlight_initial_power_state(gbl); bl->props.brightness = 1; - backlight_update_status(bl); + init_brightness = gpio_backlight_get_next_brightness(bl); + ret = gpiod_direction_output(gbl->gpiod, init_brightness); + if (ret) { + dev_err(&pdev->dev, "failed to set initial brightness\n"); + return ret; + } platform_set_drvdata(pdev, bl); return 0; -- cgit v1.2.3-59-g8ed1b From f35f06b784a1603aae3542918b09da8c8627e9fb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:26 +0200 Subject: backlight: gpio: Simplify the platform data handling Now that the last user of platform data (sh ecovec24) defines a proper GPIO lookup and sets the 'default-on' device property, we can drop the platform_data-specific GPIO handling and unify a big chunk of code. The only field used from the platform data is now the fbdev pointer. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Daniel Thompson Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 62 ++++++-------------------------- 1 file changed, 11 insertions(+), 51 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 52f17c9ca1c3..ddc7d3b288b7 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -6,7 +6,6 @@ #include #include #include -#include /* Only for legacy support */ #include #include #include @@ -61,28 +60,6 @@ static const struct backlight_ops gpio_backlight_ops = { .check_fb = gpio_backlight_check_fb, }; -static int gpio_backlight_probe_dt(struct platform_device *pdev, - struct gpio_backlight *gbl) -{ - struct device *dev = &pdev->dev; - int ret; - - gbl->def_value = device_property_read_bool(dev, "default-on"); - - gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); - if (IS_ERR(gbl->gpiod)) { - ret = PTR_ERR(gbl->gpiod); - - if (ret != -EPROBE_DEFER) { - dev_err(dev, - "Error: The gpios parameter is missing or invalid.\n"); - } - return ret; - } - - return 0; -} - static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) { struct device_node *node = gbl->dev->of_node; @@ -114,35 +91,18 @@ static int gpio_backlight_probe(struct platform_device *pdev) gbl->dev = &pdev->dev; - if (pdev->dev.fwnode) { - ret = gpio_backlight_probe_dt(pdev, gbl); - if (ret) - return ret; - } else if (pdata) { - /* - * Legacy platform data GPIO retrieveal. Do not expand - * the use of this code path, currently only used by one - * SH board. - */ - unsigned long flags = GPIOF_DIR_OUT; - + if (pdata) gbl->fbdev = pdata->fbdev; - gbl->def_value = pdata->def_value; - flags |= gbl->def_value ? GPIOF_INIT_HIGH : GPIOF_INIT_LOW; - - ret = devm_gpio_request_one(gbl->dev, pdata->gpio, flags, - pdata ? pdata->name : "backlight"); - if (ret < 0) { - dev_err(&pdev->dev, "unable to request GPIO\n"); - return ret; - } - gbl->gpiod = gpio_to_desc(pdata->gpio); - if (!gbl->gpiod) - return -EINVAL; - } else { - dev_err(&pdev->dev, - "failed to find platform data or device tree node.\n"); - return -ENODEV; + + gbl->def_value = device_property_read_bool(&pdev->dev, "default-on"); + + gbl->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); + if (IS_ERR(gbl->gpiod)) { + ret = PTR_ERR(gbl->gpiod); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Error: The gpios parameter is missing or invalid.\n"); + return ret; } memset(&props, 0, sizeof(props)); -- cgit v1.2.3-59-g8ed1b From d17465a0af3f669f56289649be0fe6ab0fb63e39 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:29 +0200 Subject: backlight: gpio: Use a helper variable for &pdev->dev Instead of dereferencing pdev each time, use a helper variable for the associated device pointer. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Daniel Thompson Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index ddc7d3b288b7..d6969fae25cd 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -78,29 +78,29 @@ static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) static int gpio_backlight_probe(struct platform_device *pdev) { - struct gpio_backlight_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct gpio_backlight_platform_data *pdata = dev_get_platdata(dev); struct backlight_properties props; struct backlight_device *bl; struct gpio_backlight *gbl; int ret, init_brightness; - gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); + gbl = devm_kzalloc(dev, sizeof(*gbl), GFP_KERNEL); if (gbl == NULL) return -ENOMEM; - gbl->dev = &pdev->dev; + gbl->dev = dev; if (pdata) gbl->fbdev = pdata->fbdev; - gbl->def_value = device_property_read_bool(&pdev->dev, "default-on"); + gbl->def_value = device_property_read_bool(dev, "default-on"); - gbl->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); + gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); if (IS_ERR(gbl->gpiod)) { ret = PTR_ERR(gbl->gpiod); if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, + dev_err(dev, "Error: The gpios parameter is missing or invalid.\n"); return ret; } @@ -108,11 +108,10 @@ static int gpio_backlight_probe(struct platform_device *pdev) memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.max_brightness = 1; - bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), - &pdev->dev, gbl, &gpio_backlight_ops, - &props); + bl = devm_backlight_device_register(dev, dev_name(dev), dev, gbl, + &gpio_backlight_ops, &props); if (IS_ERR(bl)) { - dev_err(&pdev->dev, "failed to register backlight\n"); + dev_err(dev, "failed to register backlight\n"); return PTR_ERR(bl); } @@ -122,7 +121,7 @@ static int gpio_backlight_probe(struct platform_device *pdev) init_brightness = gpio_backlight_get_next_brightness(bl); ret = gpiod_direction_output(gbl->gpiod, init_brightness); if (ret) { - dev_err(&pdev->dev, "failed to set initial brightness\n"); + dev_err(dev, "failed to set initial brightness\n"); return ret; } -- cgit v1.2.3-59-g8ed1b From 9afa302473f3ed925acd2499b624fdc0c527e0d0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:36:30 +0200 Subject: backlight: gpio: Pull gpio_backlight_initial_power_state() into probe The probe function in the gpio-backlight driver is quite short. If we pull gpio_backlight_initial_power_state() into probe we can drop two more fields from struct gpio_backlight and shrink the driver code. Signed-off-by: Bartosz Golaszewski Acked-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/gpio_backlight.c | 38 ++++++++++++-------------------- 1 file changed, 14 insertions(+), 24 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index d6969fae25cd..75409ddfba3e 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -17,11 +17,8 @@ #include struct gpio_backlight { - struct device *dev; struct device *fbdev; - struct gpio_desc *gpiod; - int def_value; }; static int gpio_backlight_get_next_brightness(struct backlight_device *bl) @@ -60,41 +57,24 @@ static const struct backlight_ops gpio_backlight_ops = { .check_fb = gpio_backlight_check_fb, }; -static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) -{ - struct device_node *node = gbl->dev->of_node; - - /* Not booted with device tree or no phandle link to the node */ - if (!node || !node->phandle) - return gbl->def_value ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; - - /* if the enable GPIO is disabled, do not enable the backlight */ - if (gpiod_get_direction(gbl->gpiod) == 0 && - gpiod_get_value_cansleep(gbl->gpiod) == 0) - return FB_BLANK_POWERDOWN; - - return FB_BLANK_UNBLANK; -} - static int gpio_backlight_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_backlight_platform_data *pdata = dev_get_platdata(dev); + struct device_node *of_node = dev->of_node; struct backlight_properties props; struct backlight_device *bl; struct gpio_backlight *gbl; - int ret, init_brightness; + int ret, init_brightness, def_value; gbl = devm_kzalloc(dev, sizeof(*gbl), GFP_KERNEL); if (gbl == NULL) return -ENOMEM; - gbl->dev = dev; - if (pdata) gbl->fbdev = pdata->fbdev; - gbl->def_value = device_property_read_bool(dev, "default-on"); + def_value = device_property_read_bool(dev, "default-on"); gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); if (IS_ERR(gbl->gpiod)) { @@ -115,7 +95,17 @@ static int gpio_backlight_probe(struct platform_device *pdev) return PTR_ERR(bl); } - bl->props.power = gpio_backlight_initial_power_state(gbl); + /* Set the initial power state */ + if (!of_node || !of_node->phandle) + /* Not booted with device tree or no phandle link to the node */ + bl->props.power = def_value ? FB_BLANK_UNBLANK + : FB_BLANK_POWERDOWN; + else if (gpiod_get_direction(gbl->gpiod) == 0 && + gpiod_get_value_cansleep(gbl->gpiod) == 0) + bl->props.power = FB_BLANK_POWERDOWN; + else + bl->props.power = FB_BLANK_UNBLANK; + bl->props.brightness = 1; init_brightness = gpio_backlight_get_next_brightness(bl); -- cgit v1.2.3-59-g8ed1b From 102a1b382177d89f75bc49b931c329a317cf531f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 12 Nov 2019 09:30:25 +0000 Subject: backlight: qcom-wled: Fix spelling mistake "trigged" -> "triggered" There is a spelling mistake in a dev_err error message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/qcom-wled.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 33b6007c5e55..d46052d8ff41 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -322,7 +322,7 @@ static irqreturn_t wled_short_irq_handler(int irq, void *_wled) wled->short_count = 1; if (wled->short_count > WLED_SHORT_CNT_MAX) { - dev_err(wled->dev, "Short trigged %d times, disabling WLED forever!\n", + dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n", wled->short_count); wled->disabled_by_short = true; goto unlock_mutex; -- cgit v1.2.3-59-g8ed1b