- Core Frameworks

- Add backlight_device_get_by_name() to the API
 
 - New Device Support
    - Add support for WLED5 to Qualcomm WLED
 
  - Fix-ups
    - Convert to GPIO descriptors; l4f00242t03
    - Device Tree fix-ups; qcom-wled
 
  - Bug Fixes
    - Properly disable regulators on .probe() failure
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAl7XZbwACgkQUa+KL4f8
 d2FvuA//bkTfuewuIz8KEAjpDRftXqvhroq5++kzvziYCcleQzk1SytpaCUXxw20
 Cn/sEXzdmzDbfwvZI9X+Ptsg3rf+MzP3dXc6azkmcYRP8gGdAJ7Tz4lH9Y9pH3lk
 AckZO4FWnmQ5a9KEPxhW+FZwwCeMH0DZd2eXUpgD+GwZePDi7TmdNWcaD7nhsaRd
 14DTce1I0ZJrV39KcOXZbx80v6l6b8jtiIjYLPVS9wQx4KiGoHlPERztEANDakIp
 FDexk4smP+wfFQuhcYunNB31ZzTE50uT1SsilMOoIs0OIgPt/dfDr2DiRN8wp2Rv
 5kSXO1qou3KNRFTvn1WqfWXBQmmJRQfZUvQypppMHbzx48xlAbStQF1h9eYod6EU
 J8EoO/EbyOPNXc7do0NKwnfNvdz1tmI9Qpj4Hz3T9C9spzQ9FPU3G+5VsdB/Rv5G
 B2AJAOG/GJqILDG8hLJ0ZaFYArmLyQCFbJhaRtvPqOSMmlWKgeFdSC8AzSNhf6EC
 3Xkk4f5toKao7lgcEOWRbVYKVsdjSSxr4pwyW5VNi5XfR1DsKQj8gyCf5qrmasQE
 11v8juUP/R/6XrHvCco0zgMlVk7k/O0IQ9k0ozRKqGBtz+UKN28EzsuV/TJ0qIVN
 UTwNoKuh9QixnTRb9Jnfl3EioFIv7JjXQVpLPhj+sGInjKv3y4Y=
 =sHzj
 -----END PGP SIGNATURE-----

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

Pull backlight updates from Lee Jones:
 "Core Framework:
   - Add backlight_device_get_by_name() to the API

  New Device Support:
   - Add support for WLED5 to Qualcomm WLED

  Fix-ups:
   - Convert to GPIO descriptors in l4f00242t03
   - Device Tree fix-ups for qcom-wled

  Bug Fixes:
   - Properly disable regulators on .probe() failure"

* tag 'backlight-next-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
  backlight: Add backlight_device_get_by_name()
  backlight: qcom-wled: Add support for WLED5 peripheral that is present on PM8150L PMICs
  dt-bindings: backlight: qcom-wled: Add WLED5 bindings
  backlight: qcom-wled: Add callback functions
  dt-bindings: backlight: qcom-wled: Convert the wled bindings to .yaml format
  backlight: l4f00242t03: Convert to GPIO descriptors
  backlight: lp855x: Ensure regulators are disabled on probe failure
This commit is contained in:
Linus Torvalds 2020-06-04 10:43:07 -07:00
commit 302d5b3321
10 changed files with 867 additions and 286 deletions

View File

@ -1,154 +0,0 @@
Binding for Qualcomm Technologies, Inc. WLED driver
WLED (White Light Emitting Diode) driver is used for controlling display
backlight that is part of PMIC on Qualcomm Technologies, Inc. reference
platforms. The PMIC is connected to the host processor via SPMI bus.
- compatible
Usage: required
Value type: <string>
Definition: should be one of:
"qcom,pm8941-wled"
"qcom,pmi8998-wled"
"qcom,pm660l-wled"
- reg
Usage: required
Value type: <prop encoded array>
Definition: Base address of the WLED modules.
- default-brightness
Usage: optional
Value type: <u32>
Definition: brightness value on boot, value from: 0-4095.
Default: 2048
- label
Usage: required
Value type: <string>
Definition: The name of the backlight device
- qcom,cs-out
Usage: optional
Value type: <bool>
Definition: enable current sink output.
This property is supported only for PM8941.
- qcom,cabc
Usage: optional
Value type: <bool>
Definition: enable content adaptive backlight control.
- qcom,ext-gen
Usage: optional
Value type: <bool>
Definition: use externally generated modulator signal to dim.
This property is supported only for PM8941.
- qcom,current-limit
Usage: optional
Value type: <u32>
Definition: mA; per-string current limit; value from 0 to 25 with
1 mA step. Default 20 mA.
This property is supported only for pm8941.
- qcom,current-limit-microamp
Usage: optional
Value type: <u32>
Definition: uA; per-string current limit; value from 0 to 30000 with
2500 uA step. Default 25 mA.
- qcom,current-boost-limit
Usage: optional
Value type: <u32>
Definition: mA; boost current limit.
For pm8941: one of: 105, 385, 525, 805, 980, 1260, 1400,
1680. Default: 805 mA.
For pmi8998: one of: 105, 280, 450, 620, 970, 1150, 1300,
1500. Default: 970 mA.
- qcom,switching-freq
Usage: optional
Value type: <u32>
Definition: kHz; switching frequency; one of: 600, 640, 685, 738,
800, 872, 960, 1066, 1200, 1371, 1600, 1920, 2400, 3200,
4800, 9600.
Default: for pm8941: 1600 kHz
for pmi8998: 800 kHz
- qcom,ovp
Usage: optional
Value type: <u32>
Definition: V; Over-voltage protection limit; one of:
27, 29, 32, 35. Default: 29V
This property is supported only for PM8941.
- qcom,ovp-millivolt
Usage: optional
Value type: <u32>
Definition: mV; Over-voltage protection limit;
For pmi8998: one of 18100, 19600, 29600, 31100.
Default 29600 mV.
If this property is not specified for PM8941, it
falls back to "qcom,ovp" property.
- qcom,num-strings
Usage: optional
Value type: <u32>
Definition: #; number of led strings attached;
value: For PM8941 from 1 to 3. Default: 2
For PMI8998 from 1 to 4.
- interrupts
Usage: optional
Value type: <prop encoded array>
Definition: Interrupts associated with WLED. This should be
"short" and "ovp" interrupts. Interrupts can be
specified as per the encoding listed under
Documentation/devicetree/bindings/spmi/
qcom,spmi-pmic-arb.txt.
- interrupt-names
Usage: optional
Value type: <string>
Definition: Interrupt names associated with the interrupts.
Must be "short" and "ovp". The short circuit detection
is not supported for PM8941.
- qcom,enabled-strings
Usage: optional
Value tyoe: <u32 array>
Definition: Array of the WLED strings numbered from 0 to 3. Each
string of leds are operated individually. Specify the
list of strings used by the device. Any combination of
led strings can be used.
- qcom,external-pfet
Usage: optional
Value type: <bool>
Definition: Specify if external PFET control for short circuit
protection is used. This property is supported only
for PMI8998.
- qcom,auto-string-detection
Usage: optional
Value type: <bool>
Definition: Enables auto-detection of the WLED string configuration.
This feature is not supported for PM8941.
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>;
qcom,enabled-strings = <0 1>;
};

View File

@ -0,0 +1,261 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/backlight/qcom-wled.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Binding for Qualcomm Technologies, Inc. WLED driver
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
- Kiran Gunda <kgunda@codeaurora.org>
description: |
WLED (White Light Emitting Diode) driver is used for controlling display
backlight that is part of PMIC on Qualcomm Technologies, Inc. reference
platforms. The PMIC is connected to the host processor via SPMI bus.
properties:
compatible:
enum:
- qcom,pm8941-wled
- qcom,pmi8998-wled
- qcom,pm660l-wled
- qcom,pm8150l-wled
reg:
maxItems: 1
default-brightness:
description: |
brightness value on boot.
label: true
max-brightness:
description: |
Maximum brightness level.
qcom,cs-out:
description: |
enable current sink output.
This property is supported only for WLED3.
type: boolean
qcom,cabc:
description: |
enable content adaptive backlight control.
type: boolean
qcom,ext-gen:
description: |
use externally generated modulator signal to dim.
This property is supported only for WLED3.
type: boolean
qcom,current-limit:
description: |
mA; per-string current limit.
This property is supported only for WLED3.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
default: 20
minimum: 0
maximum: 25
qcom,current-limit-microamp:
description: |
uA; per-string current limit.
default: 25
minimum: 0
maximum: 30000
multipleOf: 25
qcom,current-boost-limit:
description: |
mA; boost current limit.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
qcom,switching-freq:
description: |
kHz; switching frequency.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371, 1600, 1920, 2400, 3200, 4800, 9600 ]
qcom,ovp:
description: |
V; Over-voltage protection limit.
This property is supported only for WLED3.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 27, 29, 32, 35 ]
- default: 29
qcom,ovp-millivolt:
description: |
Over-voltage protection limit. This property is for WLED4 only.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 18100, 19600, 29600, 31100 ]
- default: 29600
qcom,num-strings:
description: |
number of led strings attached.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
qcom,enabled-strings:
description: |
Array of the WLED strings numbered from 0 to 3. Each
string of leds are operated individually. Specify the
list of strings used by the device. Any combination of
led strings can be used.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 4
qcom,external-pfet:
description: |
Specify if external PFET control for short circuit
protection is used. This property is supported only
for WLED4.
type: boolean
qcom,auto-string-detection:
description: |
Enables auto-detection of the WLED string configuration.
This feature is not supported for WLED3.
type: boolean
interrupts:
minItems: 1
items:
- description: over voltage protection interrupt.
- description: short circuit interrupt.
interrupt-names:
minItems: 1
items:
- const: ovp
- const: short
qcom,modulator-sel:
description: |
Selects the modulator used for brightness modulation.
Allowed values are,
0 - Modulator A
1 - Modulator B
This property is applicable only to WLED5 peripheral.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 0, 1 ]
- default: 0
qcom,cabc-sel:
description: |
Selects the CABC pin signal used for brightness modulation.
Allowed values are,
0 - CABC disabled
1 - CABC 1
2 - CABC 2
3 - External signal (e.g. LPG) is used for dimming
This property is applicable only to WLED5 peripheral.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 0, 1, 2, 3 ]
allOf:
- if:
properties:
compatible:
contains:
const: qcom,pm8941-wled
then:
properties:
qcom,current-boost-limit:
enum: [ 105, 385, 525, 805, 980, 1260, 1400, 1680 ]
default: 805
qcom,switching-freq:
default: 1600
qcom,num-strings:
enum: [ 1, 2, 3 ]
interrupts:
maxItems: 1
interrupt-names:
maxItems: 1
else:
properties:
qcom,current-boost-limit:
enum: [ 105, 280, 450, 620, 970, 1150, 1300, 1500 ]
default: 970
qcom,switching-freq:
default: 800
qcom,num-strings:
enum: [ 1, 2, 3, 4 ]
interrupts:
minItems: 2
interrupt-names:
minItems: 2
- if:
properties:
compatible:
contains:
enum:
- qcom,pm8150l-wled
then:
properties:
default-brightness:
minimum: 0
maximum: 32767
max-brightness:
minimum: 0
maximum: 32767
else:
properties:
default-brightness:
minimum: 0
maximum: 4095
max-brightness:
minimum: 0
maximum: 4095
required:
- compatible
- reg
- label
additionalProperties: false
examples:
- |
backlight@d800 {
compatible = "qcom,pm8941-wled";
reg = <0xd800 0x100>;
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>;
qcom,enabled-strings = <0 1>;
};

View File

@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/irq.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
@ -20,8 +21,6 @@
#include <linux/mfd/mc13783.h>
#include <linux/spi/spi.h>
#include <linux/regulator/machine.h>
#include <linux/spi/l4f00242t03.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@ -351,9 +350,19 @@ static const struct imx_fb_platform_data mx27_3ds_fb_data __initconst = {
};
/* LCD */
static struct l4f00242t03_pdata mx27_3ds_lcd_pdata = {
.reset_gpio = LCD_RESET,
.data_enable_gpio = LCD_ENABLE,
static struct gpiod_lookup_table mx27_3ds_lcd_gpiod_table = {
.dev_id = "spi0.0", /* Bus 0 chipselect 0 */
.table = {
/*
* The i.MX27 has the i.MX21 GPIO controller, the GPIOs
* numbered IMX_GPIO_NR(1, 3) and IMX_GPIO_NR(1, 31)
* are in "bank 1" which is subtracted by one in the macro
* so these are actually bank 0 on "imx21-gpio.0".
*/
GPIO_LOOKUP("imx21-gpio.0", 3, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("imx21-gpio.0", 31, "enable", GPIO_ACTIVE_HIGH),
{ },
},
};
static struct spi_board_info mx27_3ds_spi_devs[] __initdata = {
@ -370,7 +379,6 @@ static struct spi_board_info mx27_3ds_spi_devs[] __initdata = {
.max_speed_hz = 5000000,
.bus_num = 0,
.chip_select = 0, /* SS0 */
.platform_data = &mx27_3ds_lcd_pdata,
},
};
@ -416,6 +424,7 @@ static void __init mx27pdk_late_init(void)
if (!otg_mode_host)
imx27_add_fsl_usb2_udc(&otg_device_pdata);
gpiod_add_lookup_table(&mx27_3ds_lcd_gpiod_table);
mx27_3ds_spi_devs[0].irq = gpio_to_irq(PMIC_INT);
spi_register_board_info(mx27_3ds_spi_devs,
ARRAY_SIZE(mx27_3ds_spi_devs));

View File

@ -10,10 +10,10 @@
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/platform_device.h>
#include <linux/mfd/mc13783.h>
#include <linux/spi/spi.h>
#include <linux/spi/l4f00242t03.h>
#include <linux/regulator/machine.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
@ -160,9 +160,23 @@ static struct mx3fb_platform_data mx3fb_pdata __initdata = {
};
/* LCD */
static struct l4f00242t03_pdata mx31_3ds_l4f00242t03_pdata = {
.reset_gpio = IOMUX_TO_GPIO(MX31_PIN_LCS1),
.data_enable_gpio = IOMUX_TO_GPIO(MX31_PIN_SER_RS),
static struct gpiod_lookup_table mx31_3ds_lcd_gpiod_table = {
.dev_id = "spi0.2", /* Bus 0 chipselect 2 */
.table = {
/*
* "reset" has IOMUX_TO_GPIO(IOMUX_PIN(88, 28)).
* The macro only shifts 88 to bits 9..16 and then
* mask it and shift it back. The GPIO number is 88.
* 88 is 2*32+24
*/
GPIO_LOOKUP("imx31-gpio.2", 24, "reset", GPIO_ACTIVE_HIGH),
/*
* Same reasoning as above for
* IOMUX_TO_GPIO(IOMUX_PIN(89, 27), pin 89 is 2*32+25.
*/
GPIO_LOOKUP("imx31-gpio.2", 25, "enable", GPIO_ACTIVE_HIGH),
{ },
},
};
/*
@ -387,7 +401,6 @@ static struct spi_board_info mx31_3ds_spi_devs[] __initdata = {
.max_speed_hz = 5000000,
.bus_num = 0,
.chip_select = 2, /* SS2 */
.platform_data = &mx31_3ds_l4f00242t03_pdata,
},
};
@ -566,6 +579,7 @@ static void __init mx31_3ds_init(void)
static void __init mx31_3ds_late(void)
{
gpiod_add_lookup_table(&mx31_3ds_lcd_gpiod_table);
mx31_3ds_spi_devs[0].irq = gpio_to_irq(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3));
spi_register_board_info(mx31_3ds_spi_devs,
ARRAY_SIZE(mx31_3ds_spi_devs));

View File

@ -432,6 +432,27 @@ struct backlight_device *backlight_device_get_by_type(enum backlight_type type)
}
EXPORT_SYMBOL(backlight_device_get_by_type);
/**
* backlight_device_get_by_name - Get backlight device by name
* @name: Device name
*
* This function looks up a backlight device by its name. It obtains a reference
* on the backlight device and it is the caller's responsibility to drop the
* reference by calling backlight_put().
*
* Returns:
* A pointer to the backlight device if found, otherwise NULL.
*/
struct backlight_device *backlight_device_get_by_name(const char *name)
{
struct device *dev;
dev = class_find_device_by_name(backlight_class, name);
return dev ? to_backlight_device(dev) : NULL;
}
EXPORT_SYMBOL(backlight_device_get_by_name);
/**
* backlight_device_unregister - unregisters a backlight device object.
* @bd: the backlight device object to be unregistered and freed.

View File

@ -14,13 +14,11 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/lcd.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/spi/l4f00242t03.h>
struct l4f00242t03_priv {
struct spi_device *spi;
@ -28,16 +26,18 @@ struct l4f00242t03_priv {
int lcd_state;
struct regulator *io_reg;
struct regulator *core_reg;
struct gpio_desc *reset;
struct gpio_desc *enable;
};
static void l4f00242t03_reset(unsigned int gpio)
static void l4f00242t03_reset(struct gpio_desc *gpiod)
{
pr_debug("l4f00242t03_reset.\n");
gpio_set_value(gpio, 1);
gpiod_set_value(gpiod, 1);
mdelay(100);
gpio_set_value(gpio, 0);
gpiod_set_value(gpiod, 0);
mdelay(10); /* tRES >= 100us */
gpio_set_value(gpio, 1);
gpiod_set_value(gpiod, 1);
mdelay(20);
}
@ -45,7 +45,6 @@ static void l4f00242t03_reset(unsigned int gpio)
static void l4f00242t03_lcd_init(struct spi_device *spi)
{
struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) };
int ret;
@ -76,21 +75,20 @@ static void l4f00242t03_lcd_init(struct spi_device *spi)
return;
}
l4f00242t03_reset(pdata->reset_gpio);
l4f00242t03_reset(priv->reset);
gpio_set_value(pdata->data_enable_gpio, 1);
gpiod_set_value(priv->enable, 1);
msleep(60);
spi_write(spi, (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16));
}
static void l4f00242t03_lcd_powerdown(struct spi_device *spi)
{
struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
dev_dbg(&spi->dev, "Powering down LCD\n");
gpio_set_value(pdata->data_enable_gpio, 0);
gpiod_set_value(priv->enable, 0);
regulator_disable(priv->io_reg);
regulator_disable(priv->core_reg);
@ -168,13 +166,6 @@ static struct lcd_ops l4f_ops = {
static int l4f00242t03_probe(struct spi_device *spi)
{
struct l4f00242t03_priv *priv;
struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
int ret;
if (pdata == NULL) {
dev_err(&spi->dev, "Uninitialized platform data.\n");
return -EINVAL;
}
priv = devm_kzalloc(&spi->dev, sizeof(struct l4f00242t03_priv),
GFP_KERNEL);
@ -187,21 +178,21 @@ static int l4f00242t03_probe(struct spi_device *spi)
priv->spi = spi;
ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio,
GPIOF_OUT_INIT_HIGH, "lcd l4f00242t03 reset");
if (ret) {
priv->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset)) {
dev_err(&spi->dev,
"Unable to get the lcd l4f00242t03 reset gpio.\n");
return ret;
return PTR_ERR(priv->reset);
}
gpiod_set_consumer_name(priv->reset, "lcd l4f00242t03 reset");
ret = devm_gpio_request_one(&spi->dev, pdata->data_enable_gpio,
GPIOF_OUT_INIT_LOW, "lcd l4f00242t03 data enable");
if (ret) {
priv->enable = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(priv->enable)) {
dev_err(&spi->dev,
"Unable to get the lcd l4f00242t03 data en gpio.\n");
return ret;
return PTR_ERR(priv->enable);
}
gpiod_set_consumer_name(priv->enable, "lcd l4f00242t03 data enable");
priv->io_reg = devm_regulator_get(&spi->dev, "vdd");
if (IS_ERR(priv->io_reg)) {

View File

@ -456,7 +456,7 @@ static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
ret = regulator_enable(lp->enable);
if (ret < 0) {
dev_err(lp->dev, "failed to enable vddio: %d\n", ret);
return ret;
goto disable_supply;
}
/*
@ -471,24 +471,34 @@ static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
ret = lp855x_configure(lp);
if (ret) {
dev_err(lp->dev, "device config err: %d", ret);
return ret;
goto disable_vddio;
}
ret = lp855x_backlight_register(lp);
if (ret) {
dev_err(lp->dev,
"failed to register backlight. err: %d\n", ret);
return ret;
goto disable_vddio;
}
ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
if (ret) {
dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
return ret;
goto disable_vddio;
}
backlight_update_status(lp->bl);
return 0;
disable_vddio:
if (lp->enable)
regulator_disable(lp->enable);
disable_supply:
if (lp->supply)
regulator_disable(lp->supply);
return ret;
}
static int lp855x_remove(struct i2c_client *cl)
@ -497,6 +507,8 @@ static int lp855x_remove(struct i2c_client *cl)
lp->bl->props.brightness = 0;
backlight_update_status(lp->bl);
if (lp->enable)
regulator_disable(lp->enable);
if (lp->supply)
regulator_disable(lp->supply);
sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);

View File

@ -15,16 +15,21 @@
/* From DT binding */
#define WLED_MAX_STRINGS 4
#define MOD_A 0
#define MOD_B 1
#define WLED_DEFAULT_BRIGHTNESS 2048
#define WLED_SOFT_START_DLY_US 10000
#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF
#define WLED5_SINK_REG_BRIGHT_MAX_12B 0xFFF
#define WLED5_SINK_REG_BRIGHT_MAX_15B 0x7FFF
/* 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 WLED5_CTRL_REG_OVP_PRE_ALARM_BIT BIT(4)
#define WLED3_CTRL_REG_INT_RT_STS 0x10
#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1)
@ -40,6 +45,7 @@
#define WLED3_CTRL_REG_OVP 0x4d
#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0)
#define WLED5_CTRL_REG_OVP_MASK GENMASK(3, 0)
#define WLED3_CTRL_REG_ILIMIT 0x4e
#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0)
@ -101,6 +107,44 @@
#define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10))
/* WLED5 specific control registers */
#define WLED5_CTRL_REG_OVP_INT_CTL 0x5f
#define WLED5_CTRL_REG_OVP_INT_TIMER_MASK GENMASK(2, 0)
/* WLED5 specific sink registers */
#define WLED5_SINK_REG_MOD_A_EN 0x50
#define WLED5_SINK_REG_MOD_B_EN 0x60
#define WLED5_SINK_REG_MOD_EN_MASK BIT(7)
#define WLED5_SINK_REG_MOD_A_SRC_SEL 0x51
#define WLED5_SINK_REG_MOD_B_SRC_SEL 0x61
#define WLED5_SINK_REG_MOD_SRC_SEL_HIGH 0
#define WLED5_SINK_REG_MOD_SRC_SEL_EXT 0x03
#define WLED5_SINK_REG_MOD_SRC_SEL_MASK GENMASK(1, 0)
#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL 0x52
#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL 0x62
#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B 0
#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B 1
#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB 0x53
#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB 0x54
#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB 0x63
#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB 0x64
#define WLED5_SINK_REG_MOD_SYNC_BIT 0x65
#define WLED5_SINK_REG_SYNC_MOD_A_BIT BIT(0)
#define WLED5_SINK_REG_SYNC_MOD_B_BIT BIT(1)
#define WLED5_SINK_REG_SYNC_MASK GENMASK(1, 0)
/* WLED5 specific per-'string' registers below */
#define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n) (0x72 + (n * 0x10))
#define WLED5_SINK_REG_STR_SRC_SEL(n) (0x73 + (n * 0x10))
#define WLED5_SINK_REG_SRC_SEL_MOD_A 0
#define WLED5_SINK_REG_SRC_SEL_MOD_B 1
#define WLED5_SINK_REG_SRC_SEL_MASK GENMASK(1, 0)
struct wled_var_cfg {
const u32 *values;
u32 (*fn)(u32);
@ -125,6 +169,8 @@ struct wled_config {
u32 num_strings;
u32 string_i_limit;
u32 enabled_strings[WLED_MAX_STRINGS];
u32 mod_sel;
u32 cabc_sel;
bool cs_out_en;
bool ext_gen;
bool cabc;
@ -147,14 +193,39 @@ struct wled {
u32 max_brightness;
u32 short_count;
u32 auto_detect_count;
u32 version;
bool disabled_by_short;
bool has_short_detect;
bool cabc_disabled;
int short_irq;
int ovp_irq;
struct wled_config cfg;
struct delayed_work ovp_work;
/* Configures the brightness. Applicable for wled3, wled4 and wled5 */
int (*wled_set_brightness)(struct wled *wled, u16 brightness);
/* Configures the cabc register. Applicable for wled4 and wled5 */
int (*wled_cabc_config)(struct wled *wled, bool enable);
/*
* Toggles the sync bit for the brightness update to take place.
* Applicable for WLED3, WLED4 and WLED5.
*/
int (*wled_sync_toggle)(struct wled *wled);
/*
* Time to wait before checking the OVP status after wled module enable.
* Applicable for WLED4 and WLED5.
*/
int (*wled_ovp_delay)(struct wled *wled);
/*
* Determines if the auto string detection is required.
* Applicable for WLED4 and WLED5
*/
bool (*wled_auto_detection_required)(struct wled *wled);
};
static int wled3_set_brightness(struct wled *wled, u16 brightness)
@ -198,6 +269,28 @@ static int wled4_set_brightness(struct wled *wled, u16 brightness)
return 0;
}
static int wled5_set_brightness(struct wled *wled, u16 brightness)
{
int rc, offset;
u16 low_limit = wled->max_brightness * 1 / 1000;
u8 v[2];
/* WLED5's lower limit is 0.1% */
if (brightness < low_limit)
brightness = low_limit;
v[0] = brightness & 0xff;
v[1] = (brightness >> 8) & 0x7f;
offset = (wled->cfg.mod_sel == MOD_A) ?
WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB;
rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
v, 2);
return rc;
}
static void wled_ovp_work(struct work_struct *work)
{
struct wled *wled = container_of(work,
@ -237,7 +330,7 @@ static int wled_module_enable(struct wled *wled, int val)
return 0;
}
static int wled_sync_toggle(struct wled *wled)
static int wled3_sync_toggle(struct wled *wled)
{
int rc;
unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
@ -255,6 +348,88 @@ static int wled_sync_toggle(struct wled *wled)
return rc;
}
static int wled5_sync_toggle(struct wled *wled)
{
int rc;
u8 val;
val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
WLED5_SINK_REG_SYNC_MOD_B_BIT;
rc = regmap_update_bits(wled->regmap,
wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
WLED5_SINK_REG_SYNC_MASK, val);
if (rc < 0)
return rc;
return regmap_update_bits(wled->regmap,
wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
WLED5_SINK_REG_SYNC_MASK, 0);
}
static int wled_ovp_fault_status(struct wled *wled, bool *fault_set)
{
int rc;
u32 int_rt_sts, fault_sts;
*fault_set = false;
rc = regmap_read(wled->regmap,
wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
&int_rt_sts);
if (rc < 0) {
dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
return rc;
}
rc = regmap_read(wled->regmap,
wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
&fault_sts);
if (rc < 0) {
dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
return rc;
}
if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
*fault_set = true;
if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT))
*fault_set = true;
if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
WLED5_CTRL_REG_OVP_PRE_ALARM_BIT)))
*fault_set = true;
if (*fault_set)
dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
int_rt_sts, fault_sts);
return rc;
}
static int wled4_ovp_delay(struct wled *wled)
{
return WLED_SOFT_START_DLY_US;
}
static int wled5_ovp_delay(struct wled *wled)
{
int rc, delay_us;
u32 val;
u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24};
/* For WLED5, get the delay based on OVP timer */
rc = regmap_read(wled->regmap, wled->ctrl_addr +
WLED5_CTRL_REG_OVP_INT_CTL, &val);
if (rc < 0)
delay_us =
ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000;
else
delay_us = 2 * WLED_SOFT_START_DLY_US;
dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us);
return delay_us;
}
static int wled_update_status(struct backlight_device *bl)
{
struct wled *wled = bl_get_data(bl);
@ -275,7 +450,7 @@ static int wled_update_status(struct backlight_device *bl)
goto unlock_mutex;
}
rc = wled_sync_toggle(wled);
rc = wled->wled_sync_toggle(wled);
if (rc < 0) {
dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
goto unlock_mutex;
@ -298,6 +473,50 @@ static int wled_update_status(struct backlight_device *bl)
return rc;
}
static int wled4_cabc_config(struct wled *wled, bool enable)
{
int i, j, rc;
u8 val;
for (i = 0; i < wled->cfg.num_strings; i++) {
j = wled->cfg.enabled_strings[i];
val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0;
rc = regmap_update_bits(wled->regmap, wled->sink_addr +
WLED4_SINK_REG_STR_CABC(j),
WLED4_SINK_REG_STR_CABC_MASK, val);
if (rc < 0)
return rc;
}
return 0;
}
static int wled5_cabc_config(struct wled *wled, bool enable)
{
int rc, offset;
u8 reg;
if (wled->cabc_disabled)
return 0;
reg = enable ? wled->cfg.cabc_sel : 0;
offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL :
WLED5_SINK_REG_MOD_B_SRC_SEL;
rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset,
WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg);
if (rc < 0) {
pr_err("Error in configuring CABC rc=%d\n", rc);
return rc;
}
if (!wled->cfg.cabc_sel)
wled->cabc_disabled = true;
return 0;
}
#define WLED_SHORT_DLY_MS 20
#define WLED_SHORT_CNT_MAX 5
#define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC
@ -345,9 +564,10 @@ static irqreturn_t wled_short_irq_handler(int irq, void *_wled)
static void wled_auto_string_detection(struct wled *wled)
{
int rc = 0, i;
u32 sink_config = 0, int_sts;
int rc = 0, i, delay_time_us;
u32 sink_config = 0;
u8 sink_test = 0, sink_valid = 0, val;
bool fault_set;
/* Read configured sink configuration */
rc = regmap_read(wled->regmap, wled->sink_addr +
@ -376,14 +596,9 @@ static void wled_auto_string_detection(struct wled *wled)
}
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;
}
rc = wled->wled_cabc_config(wled, false);
if (rc < 0)
goto failed_detect;
}
/* Disable all sinks */
@ -427,18 +642,17 @@ static void wled_auto_string_detection(struct wled *wled)
goto failed_detect;
}
usleep_range(WLED_SOFT_START_DLY_US,
WLED_SOFT_START_DLY_US + 1000);
delay_time_us = wled->wled_ovp_delay(wled);
usleep_range(delay_time_us, delay_time_us + 1000);
rc = regmap_read(wled->regmap, wled->ctrl_addr +
WLED3_CTRL_REG_INT_RT_STS, &int_sts);
rc = wled_ovp_fault_status(wled, &fault_set);
if (rc < 0) {
dev_err(wled->dev, "Error in reading WLED3_CTRL_INT_RT_STS rc=%d\n",
dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
rc);
goto failed_detect;
}
if (int_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
if (fault_set)
dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n",
i + 1);
else
@ -478,30 +692,30 @@ static void wled_auto_string_detection(struct wled *wled)
}
/* 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)
if (wled->version == 4) {
for (i = 0; i < wled->cfg.num_strings; i++) {
if (sink_config &
BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i))
val = WLED4_SINK_REG_STR_MOD_MASK;
else
/* Disable modulator_en for unused sink */
val = 0;
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;
}
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;
}
}
}
/* Enable CABC */
rc = wled->wled_cabc_config(wled, true);
if (rc < 0)
goto failed_detect;
/* Restore the feedback setting */
rc = regmap_write(wled->regmap,
wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0);
@ -534,7 +748,8 @@ static void wled_auto_string_detection(struct wled *wled)
#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)
static bool wled4_auto_detection_required(struct wled *wled)
{
s64 elapsed_time_us;
@ -567,32 +782,39 @@ static bool wled_auto_detection_required(struct wled *wled)
return false;
}
static bool wled5_auto_detection_required(struct wled *wled)
{
if (!wled->cfg.auto_detection_enabled)
return false;
/*
* Unlike WLED4, WLED5 has OVP fault density interrupt configuration
* i.e. to count the number of OVP alarms for a certain duration before
* triggering OVP fault interrupt. By default, number of OVP fault
* events counted before an interrupt is fired is 32 and the time
* interval is 12 ms. If we see one OVP fault interrupt, then that
* should qualify for a real OVP fault condition to run auto detection
* algorithm.
*/
return true;
}
static int wled_auto_detection_at_init(struct wled *wled)
{
int rc;
u32 fault_status, rt_status;
bool fault_set;
if (!wled->cfg.auto_detection_enabled)
return 0;
rc = regmap_read(wled->regmap,
wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
&rt_status);
rc = wled_ovp_fault_status(wled, &fault_set);
if (rc < 0) {
dev_err(wled->dev, "Failed to read RT status rc=%d\n", rc);
dev_err(wled->dev, "Error in getting OVP fault_sts, 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)) {
if (fault_set) {
mutex_lock(&wled->lock);
wled_auto_string_detection(wled);
mutex_unlock(&wled->lock);
@ -629,7 +851,7 @@ static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
int_sts, fault_sts);
if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) {
if (wled_auto_detection_required(wled)) {
if (wled->wled_auto_detection_required(wled)) {
mutex_lock(&wled->lock);
wled_auto_string_detection(wled);
mutex_unlock(&wled->lock);
@ -811,17 +1033,12 @@ static int wled4_setup(struct wled *wled)
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 = wled4_cabc_config(wled, wled->cfg.cabc);
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,
@ -835,7 +1052,7 @@ static int wled4_setup(struct wled *wled)
if (rc < 0)
return rc;
rc = wled_sync_toggle(wled);
rc = wled->wled_sync_toggle(wled);
if (rc < 0) {
dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc);
return rc;
@ -857,6 +1074,119 @@ static const struct wled_config wled4_config_defaults = {
.auto_detection_enabled = false,
};
static int wled5_setup(struct wled *wled)
{
int rc, temp, i, j, offset;
u8 sink_en = 0;
u16 addr;
u32 val;
rc = regmap_update_bits(wled->regmap,
wled->ctrl_addr + WLED3_CTRL_REG_OVP,
WLED5_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;
/* 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_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 + WLED5_SINK_REG_STR_SRC_SEL(j);
rc = regmap_update_bits(wled->regmap, addr,
WLED5_SINK_REG_SRC_SEL_MASK,
wled->cfg.mod_sel == MOD_A ?
WLED5_SINK_REG_SRC_SEL_MOD_A :
WLED5_SINK_REG_SRC_SEL_MOD_B);
temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
sink_en |= 1 << temp;
}
rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false);
if (rc < 0)
return rc;
/* Enable one of the modulators A or B based on mod_sel */
addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN;
val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
rc = regmap_update_bits(wled->regmap, addr,
WLED5_SINK_REG_MOD_EN_MASK, val);
if (rc < 0)
return rc;
addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN;
val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
rc = regmap_update_bits(wled->regmap, addr,
WLED5_SINK_REG_MOD_EN_MASK, val);
if (rc < 0)
return rc;
offset = (wled->cfg.mod_sel == MOD_A) ?
WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL :
WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL;
addr = wled->sink_addr + offset;
val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ?
WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B;
rc = regmap_write(wled->regmap, addr, val);
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;
/* This updates only FSC configuration in WLED5 */
rc = wled->wled_sync_toggle(wled);
if (rc < 0) {
pr_err("Failed to toggle sync reg rc:%d\n", rc);
return rc;
}
rc = wled_auto_detection_at_init(wled);
if (rc < 0)
return rc;
return 0;
}
static const struct wled_config wled5_config_defaults = {
.boost_i_limit = 5,
.string_i_limit = 10,
.ovp = 4,
.num_strings = 4,
.switch_freq = 11,
.mod_sel = 0,
.cabc_sel = 0,
.cabc = false,
.external_pfet = false,
.auto_detection_enabled = false,
};
static const u32 wled3_boost_i_limit_values[] = {
105, 385, 525, 805, 980, 1260, 1400, 1680,
};
@ -875,6 +1205,16 @@ static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
.size = ARRAY_SIZE(wled4_boost_i_limit_values),
};
static inline u32 wled5_boost_i_limit_values_fn(u32 idx)
{
return 525 + (idx * 175);
}
static const struct wled_var_cfg wled5_boost_i_limit_cfg = {
.fn = wled5_boost_i_limit_values_fn,
.size = 8,
};
static const u32 wled3_ovp_values[] = {
35, 32, 29, 27,
};
@ -893,6 +1233,21 @@ static const struct wled_var_cfg wled4_ovp_cfg = {
.size = ARRAY_SIZE(wled4_ovp_values),
};
static inline u32 wled5_ovp_values_fn(u32 idx)
{
/*
* 0000 - 38.5 V
* 0001 - 37 V ..
* 1111 - 16 V
*/
return 38500 - (idx * 1500);
}
static const struct wled_var_cfg wled5_ovp_cfg = {
.fn = wled5_ovp_values_fn,
.size = 16,
};
static u32 wled3_num_strings_values_fn(u32 idx)
{
return idx + 1;
@ -940,6 +1295,14 @@ static const struct wled_var_cfg wled4_string_cfg = {
.size = 16,
};
static const struct wled_var_cfg wled5_mod_sel_cfg = {
.size = 2,
};
static const struct wled_var_cfg wled5_cabc_sel_cfg = {
.size = 4,
};
static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
{
if (idx >= cfg->size)
@ -951,7 +1314,7 @@ static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
return idx;
}
static int wled_configure(struct wled *wled, int version)
static int wled_configure(struct wled *wled)
{
struct wled_config *cfg = &wled->cfg;
struct device *dev = wled->dev;
@ -1016,6 +1379,44 @@ static int wled_configure(struct wled *wled, int version)
},
};
const struct wled_u32_opts wled5_opts[] = {
{
.name = "qcom,current-boost-limit",
.val_ptr = &cfg->boost_i_limit,
.cfg = &wled5_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 = &wled5_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,
},
{
.name = "qcom,modulator-sel",
.val_ptr = &cfg->mod_sel,
.cfg = &wled5_mod_sel_cfg,
},
{
.name = "qcom,cabc-sel",
.val_ptr = &cfg->cabc_sel,
.cfg = &wled5_cabc_sel_cfg,
},
};
const struct wled_bool_opts bool_opts[] = {
{ "qcom,cs-out", &cfg->cs_out_en, },
{ "qcom,ext-gen", &cfg->ext_gen, },
@ -1035,12 +1436,13 @@ static int wled_configure(struct wled *wled, int version)
if (rc)
wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
switch (version) {
switch (wled->version) {
case 3:
u32_opts = wled3_opts;
size = ARRAY_SIZE(wled3_opts);
*cfg = wled3_config_defaults;
wled->wled_set_brightness = wled3_set_brightness;
wled->wled_sync_toggle = wled3_sync_toggle;
wled->max_string_count = 3;
wled->sink_addr = wled->ctrl_addr;
break;
@ -1050,6 +1452,31 @@ static int wled_configure(struct wled *wled, int version)
size = ARRAY_SIZE(wled4_opts);
*cfg = wled4_config_defaults;
wled->wled_set_brightness = wled4_set_brightness;
wled->wled_sync_toggle = wled3_sync_toggle;
wled->wled_cabc_config = wled4_cabc_config;
wled->wled_ovp_delay = wled4_ovp_delay;
wled->wled_auto_detection_required =
wled4_auto_detection_required;
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;
case 5:
u32_opts = wled5_opts;
size = ARRAY_SIZE(wled5_opts);
*cfg = wled5_config_defaults;
wled->wled_set_brightness = wled5_set_brightness;
wled->wled_sync_toggle = wled5_sync_toggle;
wled->wled_cabc_config = wled5_cabc_config;
wled->wled_ovp_delay = wled5_ovp_delay;
wled->wled_auto_detection_required =
wled5_auto_detection_required;
wled->max_string_count = 4;
prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
@ -1186,7 +1613,6 @@ static int wled_probe(struct platform_device *pdev)
struct backlight_device *bl;
struct wled *wled;
struct regmap *regmap;
int version;
u32 val;
int rc;
@ -1203,18 +1629,22 @@ static int wled_probe(struct platform_device *pdev)
wled->regmap = regmap;
wled->dev = &pdev->dev;
version = (uintptr_t)of_device_get_match_data(&pdev->dev);
if (!version) {
wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev);
if (!wled->version) {
dev_err(&pdev->dev, "Unknown device version\n");
return -ENODEV;
}
mutex_init(&wled->lock);
rc = wled_configure(wled, version);
rc = wled_configure(wled);
if (rc)
return rc;
switch (version) {
val = WLED3_SINK_REG_BRIGHT_MAX;
of_property_read_u32(pdev->dev.of_node, "max-brightness", &val);
wled->max_brightness = val;
switch (wled->version) {
case 3:
wled->cfg.auto_detection_enabled = false;
rc = wled3_setup(wled);
@ -1233,6 +1663,18 @@ static int wled_probe(struct platform_device *pdev)
}
break;
case 5:
wled->has_short_detect = true;
if (wled->cfg.cabc_sel)
wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B;
rc = wled5_setup(wled);
if (rc) {
dev_err(&pdev->dev, "wled5_setup failed\n");
return rc;
}
break;
default:
dev_err(wled->dev, "Invalid WLED version\n");
break;
@ -1254,7 +1696,7 @@ static int wled_probe(struct platform_device *pdev)
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.brightness = val;
props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
props.max_brightness = wled->max_brightness;
bl = devm_backlight_device_register(&pdev->dev, wled->name,
&pdev->dev, wled,
&wled_ops, &props);
@ -1277,6 +1719,7 @@ 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 },
{ .compatible = "qcom,pm8150l-wled", .data = (void *)5 },
{}
};
MODULE_DEVICE_TABLE(of, wled_match_table);

View File

@ -190,6 +190,7 @@ extern void backlight_force_update(struct backlight_device *bd,
extern int backlight_register_notifier(struct notifier_block *nb);
extern int backlight_unregister_notifier(struct notifier_block *nb);
extern struct backlight_device *backlight_device_get_by_type(enum backlight_type type);
struct backlight_device *backlight_device_get_by_name(const char *name);
extern int backlight_device_set_brightness(struct backlight_device *bd, unsigned long brightness);
#define to_backlight_device(obj) container_of(obj, struct backlight_device, dev)

View File

@ -1,17 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* l4f00242t03.h -- Platform glue for Epson L4F00242T03 LCD
*
* Copyright (c) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
* Based on Marek Vasut work in lms283gf05.h
*/
#ifndef _INCLUDE_LINUX_SPI_L4F00242T03_H_
#define _INCLUDE_LINUX_SPI_L4F00242T03_H_
struct l4f00242t03_pdata {
unsigned int reset_gpio;
unsigned int data_enable_gpio;
};
#endif /* _INCLUDE_LINUX_SPI_L4F00242T03_H_ */