* [PATCH v5 1/4] dt-bindings: backlight: Add max25014 support
From: Maud Spierings via B4 Relay @ 2025-11-07 12:49 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev, imx,
linux-arm-kernel, Maud Spierings
In-Reply-To: <20251107-max25014-v5-0-9a6aa57306bf@gocontroll.com>
From: Maud Spierings <maudspierings@gocontroll.com>
The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
with integrated boost controller.
Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
---
In the current implementation the control registers for channel 1,
control all channels. So only one led subnode with led-sources is
supported right now. If at some point the driver functionality is
expanded the bindings can be easily extended with it.
---
.../bindings/leds/backlight/maxim,max25014.yaml | 107 +++++++++++++++++++++
MAINTAINERS | 5 +
2 files changed, 112 insertions(+)
diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
new file mode 100644
index 000000000000..e83723224b07
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim max25014 backlight controller
+
+maintainers:
+ - Maud Spierings <maudspierings@gocontroll.com>
+
+properties:
+ compatible:
+ enum:
+ - maxim,max25014
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ enable-gpios:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ power-supply:
+ description: Regulator which controls the boost converter input rail.
+
+ pwms:
+ maxItems: 1
+
+ maxim,iset:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ maximum: 15
+ default: 11
+ description:
+ Value of the ISET field in the ISET register. This controls the current
+ scale of the outputs, a higher number means more current.
+
+ led@0:
+ type: object
+ description: Properties for a string of connected LEDs.
+ $ref: common.yaml#
+
+ properties:
+ reg:
+ const: 0
+
+ led-sources:
+ allOf:
+ - minItems: 1
+ maxItems: 4
+ items:
+ minimum: 0
+ maximum: 3
+ default: [0, 1, 2, 3]
+
+ default-brightness:
+ minimum: 0
+ maximum: 100
+ default: 50
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ backlight@6f {
+ compatible = "maxim,max25014";
+ reg = <0x6f>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ power-supply = <®_backlight>;
+ pwms = <&pwm1>;
+ maxim,iset = <7>;
+
+ led@0 {
+ reg = <0>;
+ led-sources = <0 1 2 3>;
+ default-brightness = <50>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 58c7e3f678d8..606ce086f758 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15261,6 +15261,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
F: drivers/media/i2c/max2175*
F: include/uapi/linux/max2175.h
+MAX25014 BACKLIGHT DRIVER
+M: Maud Spierings <maudspierings@gocontroll.com>
+S: Maintained
+F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
+
MAX31335 RTC DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-rtc@vger.kernel.org
--
2.51.2
^ permalink raw reply related
* [PATCH v5 2/4] backlight: add max25014atg backlight
From: Maud Spierings via B4 Relay @ 2025-11-07 12:49 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev, imx,
linux-arm-kernel, Maud Spierings
In-Reply-To: <20251107-max25014-v5-0-9a6aa57306bf@gocontroll.com>
From: Maud Spierings <maudspierings@gocontroll.com>
The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
with integrated boost controller.
Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
---
MAINTAINERS | 1 +
drivers/video/backlight/Kconfig | 7 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/max25014.c | 409 +++++++++++++++++++++++++++++++++++++
4 files changed, 418 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 606ce086f758..d082d3f8cfae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15265,6 +15265,7 @@ MAX25014 BACKLIGHT DRIVER
M: Maud Spierings <maudspierings@gocontroll.com>
S: Maintained
F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
+F: drivers/video/backlight/max25014.c
MAX31335 RTC DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index d9374d208cee..d3bb6ccd4185 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -262,6 +262,13 @@ config BACKLIGHT_DA9052
help
Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs.
+config BACKLIGHT_MAX25014
+ tristate "Backlight driver for the Maxim MAX25014 chip"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you are using a MAX25014 chip as a backlight driver say Y to enable it.
+
config BACKLIGHT_MAX8925
tristate "Backlight driver for MAX8925"
depends on MFD_MAX8925
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index dfbb169bf6ea..1170d9ec40b8 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
+obj-$(CONFIG_BACKLIGHT_MAX25014) += max25014.o
obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o
obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o
diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c
new file mode 100644
index 000000000000..36bae508697e
--- /dev/null
+++ b/drivers/video/backlight/max25014.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Backlight driver for Maxim MAX25014
+ *
+ * Copyright (C) 2025 GOcontroll B.V.
+ * Author: Maud Spierings <maudspierings@gocontroll.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define MAX25014_ISET_DEFAULT_100 11
+#define MAX_BRIGHTNESS 100
+#define MIN_BRIGHTNESS 0
+#define TON_MAX 130720 /* @153Hz */
+#define TON_STEP 1307 /* @153Hz */
+#define TON_MIN 0
+
+#define MAX25014_DEV_ID 0x00
+#define MAX25014_REV_ID 0x01
+#define MAX25014_ISET 0x02
+#define MAX25014_IMODE 0x03
+#define MAX25014_TON1H 0x04
+#define MAX25014_TON1L 0x05
+#define MAX25014_TON2H 0x06
+#define MAX25014_TON2L 0x07
+#define MAX25014_TON3H 0x08
+#define MAX25014_TON3L 0x09
+#define MAX25014_TON4H 0x0A
+#define MAX25014_TON4L 0x0B
+#define MAX25014_TON_1_4_LSB 0x0C
+#define MAX25014_SETTING 0x12
+#define MAX25014_DISABLE 0x13
+#define MAX25014_BSTMON 0x14
+#define MAX25014_IOUT1 0x15
+#define MAX25014_IOUT2 0x16
+#define MAX25014_IOUT3 0x17
+#define MAX25014_IOUT4 0x18
+#define MAX25014_OPEN 0x1B
+#define MAX25014_SHORT_GND 0x1C
+#define MAX25014_SHORT_LED 0x1D
+#define MAX25014_MASK 0x1E
+#define MAX25014_DIAG 0x1F
+
+#define MAX25014_IMODE_HDIM BIT(2)
+#define MAX25014_ISET_ENABLE BIT(5)
+#define MAX25014_ISET_PSEN BIT(4)
+#define MAX25014_DIAG_HW_RST BIT(2)
+#define MAX25014_SETTING_FPWM GENMASK(6, 4)
+
+struct max25014 {
+ struct i2c_client *client;
+ struct backlight_device *bl;
+ struct regmap *regmap;
+ struct gpio_desc *enable;
+ struct regulator *vin; /* regulator for boost converter Vin rail */
+ uint32_t iset;
+ uint8_t strings_mask;
+};
+
+static const struct regmap_config max25014_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX25014_DIAG,
+};
+
+/**
+ * @brief control the brightness with i2c registers
+ *
+ * @param regmap trivial
+ * @param brt brightness
+ * @return int
+ */
+static int max25014_register_control(struct regmap *regmap, uint32_t brt)
+{
+ uint32_t reg = TON_STEP * brt;
+ int ret;
+ /*
+ * 18 bit number lowest, 2 bits in first register,
+ * next lowest 8 in the L register, next 8 in the H register
+ * Seemingly setting the strength of only one string controls all of
+ * them, individual settings don't affect the outcome.
+ */
+
+ ret = regmap_write(regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011);
+ if (ret != 0)
+ return ret;
+ ret = regmap_write(regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111);
+ if (ret != 0)
+ return ret;
+ return regmap_write(regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111);
+}
+
+static int max25014_check_errors(struct max25014 *maxim)
+{
+ uint8_t i;
+ int ret;
+ uint32_t val;
+
+ ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val);
+ if (ret != 0)
+ return ret;
+ if (val > 0) {
+ dev_err(&maxim->client->dev, "Open led strings detected on:\n");
+ for (i = 0; i < 4; i++) {
+ if (val & 1 << i)
+ dev_err(&maxim->client->dev, "string %d\n", i + 1);
+ }
+ return -EIO;
+ }
+
+ ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
+ if (ret != 0)
+ return ret;
+ if (val > 0) {
+ dev_err(&maxim->client->dev, "Short to ground detected on:\n");
+ for (i = 0; i < 4; i++) {
+ if (val & 1 << i)
+ dev_err(&maxim->client->dev, "string %d\n", i + 1);
+ }
+ return -EIO;
+ }
+
+ ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
+ if (ret != 0)
+ return ret;
+ if (val > 0) {
+ dev_err(&maxim->client->dev, "Shorted led detected on:\n");
+ for (i = 0; i < 4; i++) {
+ if (val & 1 << i)
+ dev_err(&maxim->client->dev, "string %d\n", i + 1);
+ }
+ return -EIO;
+ }
+
+ ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val);
+ if (ret != 0)
+ return ret;
+ /*
+ * The HW_RST bit always starts at 1 after power up.
+ * It is reset on first read, does not indicate an error.
+ */
+ if (val > 0 && val != MAX25014_DIAG_HW_RST) {
+ if (val & 0b1)
+ dev_err(&maxim->client->dev,
+ "Overtemperature shutdown\n");
+ if (val & 0b10)
+ dev_err(&maxim->client->dev,
+ "Chip is getting too hot (>125C)\n");
+ if (val & 0b1000)
+ dev_err(&maxim->client->dev,
+ "Boost converter overvoltage\n");
+ if (val & 0b10000)
+ dev_err(&maxim->client->dev,
+ "Boost converter undervoltage\n");
+ if (val & 0b100000)
+ dev_err(&maxim->client->dev, "IREF out of range\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * 1. disable unused strings
+ * 2. set dim mode
+ * 3. set initial brightness
+ * 4. set setting register
+ * 5. enable the backlight
+ */
+static int max25014_configure(struct max25014 *maxim)
+{
+ int ret;
+ uint32_t val;
+
+ ret = regmap_write(maxim->regmap, MAX25014_DISABLE,
+ maxim->strings_mask);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_write(maxim->regmap, MAX25014_IMODE, MAX25014_IMODE_HDIM);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_read(maxim->regmap, MAX25014_SETTING, &val);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_write(maxim->regmap, MAX25014_SETTING,
+ val & ~MAX25014_SETTING_FPWM);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_write(maxim->regmap, MAX25014_ISET,
+ maxim->iset | MAX25014_ISET_ENABLE |
+ MAX25014_ISET_PSEN);
+ return ret;
+}
+
+static int max25014_update_status(struct backlight_device *bl_dev)
+{
+ struct max25014 *maxim = bl_get_data(bl_dev);
+
+ if (backlight_is_blank(maxim->bl))
+ bl_dev->props.brightness = 0;
+
+ return max25014_register_control(maxim->regmap,
+ bl_dev->props.brightness);
+}
+
+static const struct backlight_ops max25014_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = max25014_update_status,
+};
+
+static int max25014_parse_dt(struct max25014 *maxim,
+ uint32_t *initial_brightness)
+{
+ struct device *dev = &maxim->client->dev;
+ struct device_node *node = dev->of_node;
+ struct fwnode_handle *child;
+ uint32_t strings[4];
+ int res, i;
+
+ if (!node) {
+ dev_err(dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ child = device_get_next_child_node(dev, NULL);
+ if (child) {
+ res = fwnode_property_count_u32(child, "led-sources");
+ if (res > 0) {
+ fwnode_property_read_u32_array(child, "led-sources",
+ strings, res);
+
+ /* set all strings as disabled, then enable those in led-sources*/
+ maxim->strings_mask = 0xf;
+ for (i = 0; i < res; i++) {
+ if (strings[i] <= 4)
+ maxim->strings_mask &= ~BIT(strings[i]);
+ }
+ }
+
+ fwnode_property_read_u32(child, "default-brightness",
+ initial_brightness);
+
+ fwnode_handle_put(child);
+ }
+
+ maxim->iset = MAX25014_ISET_DEFAULT_100;
+ of_property_read_u32(node, "maxim,iset", &maxim->iset);
+
+ if (maxim->iset < 0 || maxim->iset > 15) {
+ dev_err(dev,
+ "Invalid iset, should be a value from 0-15, entered was %d\n",
+ maxim->iset);
+ return -EINVAL;
+ }
+
+ if (*initial_brightness < 0 || *initial_brightness > 100) {
+ dev_err(dev,
+ "Invalid initial brightness, should be a value from 0-100, entered was %d\n",
+ *initial_brightness);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max25014_probe(struct i2c_client *cl)
+{
+ struct backlight_device *bl;
+ const struct i2c_device_id *id = i2c_client_get_device_id(cl);
+ struct max25014 *maxim;
+ struct backlight_properties props;
+ int ret;
+ uint32_t initial_brightness = 50;
+
+ maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL);
+ if (!maxim)
+ return -ENOMEM;
+
+ maxim->client = cl;
+
+ ret = max25014_parse_dt(maxim, &initial_brightness);
+ if (ret < 0)
+ return ret;
+
+ maxim->vin = devm_regulator_get_optional(&maxim->client->dev, "power");
+ if (IS_ERR(maxim->vin)) {
+ if (PTR_ERR(maxim->vin) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ maxim->vin = NULL;
+ }
+
+ if (maxim->vin) {
+ ret = regulator_enable(maxim->vin);
+ if (ret < 0) {
+ dev_err(&maxim->client->dev,
+ "failed to enable Vin: %d\n", ret);
+ return ret;
+ }
+ }
+
+ maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable",
+ GPIOD_ASIS);
+ if (IS_ERR(maxim->enable)) {
+ ret = PTR_ERR(maxim->enable);
+ dev_err(&maxim->client->dev, "failed to get enable gpio: %d\n",
+ ret);
+ goto disable_vin;
+ }
+
+ if (maxim->enable)
+ gpiod_set_value_cansleep(maxim->enable, 1);
+
+ /* Enable can be tied to vin rail wait if either is available */
+ if (maxim->enable || maxim->vin) {
+ /* Datasheet Electrical Characteristics tSTARTUP 2ms */
+ usleep_range(2000, 2500);
+ }
+
+ maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config);
+ if (IS_ERR(maxim->regmap)) {
+ ret = PTR_ERR(maxim->regmap);
+ dev_err(&maxim->client->dev,
+ "failed to initialize the i2c regmap: %d\n", ret);
+ goto disable_full;
+ }
+
+ i2c_set_clientdata(cl, maxim);
+
+ ret = max25014_check_errors(maxim);
+ if (ret) { /* error is already reported in the above function */
+ goto disable_full;
+ }
+
+ ret = max25014_configure(maxim);
+ if (ret) {
+ dev_err(&maxim->client->dev, "device config err: %d", ret);
+ goto disable_full;
+ }
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = MAX_BRIGHTNESS;
+ props.brightness = initial_brightness;
+ props.scale = BACKLIGHT_SCALE_LINEAR;
+
+ bl = devm_backlight_device_register(&maxim->client->dev, id->name,
+ &maxim->client->dev, maxim,
+ &max25014_bl_ops, &props);
+ if (IS_ERR(bl))
+ return PTR_ERR(bl);
+
+ maxim->bl = bl;
+
+ return 0;
+
+disable_full:
+ if (maxim->enable)
+ gpiod_set_value_cansleep(maxim->enable, 0);
+disable_vin:
+ if (maxim->vin)
+ regulator_disable(maxim->vin);
+ return ret;
+}
+
+static void max25014_remove(struct i2c_client *cl)
+{
+ struct max25014 *maxim = i2c_get_clientdata(cl);
+
+ maxim->bl->props.brightness = 0;
+ max25014_update_status(maxim->bl);
+ if (maxim->enable)
+ gpiod_set_value_cansleep(maxim->enable, 0);
+ if (maxim->vin)
+ regulator_disable(maxim->vin);
+}
+
+static const struct of_device_id max25014_dt_ids[] = {
+ { .compatible = "maxim,max25014", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max25014_dt_ids);
+
+static const struct i2c_device_id max25014_ids[] = {
+ { "max25014" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max25014_ids);
+
+static struct i2c_driver max25014_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(max25014_dt_ids),
+ },
+ .probe = max25014_probe,
+ .remove = max25014_remove,
+ .id_table = max25014_ids,
+};
+module_i2c_driver(max25014_driver);
+
+MODULE_DESCRIPTION("Maxim MAX25014 backlight driver");
+MODULE_AUTHOR("Maud Spierings <maudspierings@gocontroll.com>");
+MODULE_LICENSE("GPL");
--
2.51.2
^ permalink raw reply related
* [PATCH v5 4/4] arm64: dts: freescale: moduline-display-av123z7m-n17: add backlight
From: Maud Spierings via B4 Relay @ 2025-11-07 12:50 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev, imx,
linux-arm-kernel, Maud Spierings
In-Reply-To: <20251107-max25014-v5-0-9a6aa57306bf@gocontroll.com>
From: Maud Spierings <maudspierings@gocontroll.com>
Add the missing backlight.
Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
---
...x8p-ml81-moduline-display-106-av123z7m-n17.dtso | 25 +++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso
index 3eb665ce9d5d..786a04ef40c8 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso
+++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso
@@ -16,6 +16,7 @@
panel {
compatible = "boe,av123z7m-n17";
+ backlight = <&backlight>;
enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
pinctrl-0 = <&pinctrl_panel>;
pinctrl-names = "default";
@@ -91,10 +92,32 @@ lvds1_out: endpoint {
};
};
- /* max25014 @ 0x6f */
+ backlight: backlight@6f {
+ compatible = "maxim,max25014";
+ reg = <0x6f>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_backlight>;
+ maxim,iset = <7>;
+
+ led@0 {
+ reg = <0>;
+ led-sources = <0 1 2 3>;
+ default-brightness = <50>;
+ };
+ };
};
&iomuxc {
+ pinctrl_backlight: backlightgrp {
+ fsl,pins = <
+ MX8MP_IOMUXC_GPIO1_IO04__GPIO1_IO04
+ (MX8MP_PULL_UP | MX8MP_PULL_ENABLE)
+ >;
+ };
+
pinctrl_lvds_bridge: lvdsbridgegrp {
fsl,pins = <
MX8MP_IOMUXC_SAI1_TXD2__GPIO4_IO14
--
2.51.2
^ permalink raw reply related
* [PATCH v5 3/4] arm64: dts: freescale: moduline-display-av101hdt-a10: add backlight
From: Maud Spierings via B4 Relay @ 2025-11-07 12:50 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev, imx,
linux-arm-kernel, Maud Spierings
In-Reply-To: <20251107-max25014-v5-0-9a6aa57306bf@gocontroll.com>
From: Maud Spierings <maudspierings@gocontroll.com>
Add the missing backlight driver.
Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
---
...x8p-ml81-moduline-display-106-av101hdt-a10.dtso | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso
index e3965caca6be..0e2914861154 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso
+++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso
@@ -17,6 +17,7 @@
panel {
compatible = "boe,av101hdt-a10";
+ backlight = <&backlight>;
enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
pinctrl-0 = <&pinctrl_panel>;
pinctrl-names = "default";
@@ -40,7 +41,36 @@ reg_vbus: regulator-vbus {
};
};
+&i2c4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ backlight: backlight@6f {
+ compatible = "maxim,max25014";
+ reg = <0x6f>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_backlight>;
+ maxim,iset = <7>;
+
+ led@0 {
+ reg = <0>;
+ led-sources = <0 1 2>;
+ default-brightness = <50>;
+ };
+ };
+};
+
&iomuxc {
+ pinctrl_backlight: backlightgrp {
+ fsl,pins = <
+ MX8MP_IOMUXC_GPIO1_IO04__GPIO1_IO04
+ (MX8MP_PULL_UP | MX8MP_PULL_ENABLE)
+ >;
+ };
+
pinctrl_panel: panelgrp {
fsl,pins = <
MX8MP_IOMUXC_GPIO1_IO07__GPIO1_IO07
--
2.51.2
^ permalink raw reply related
* Re: [PATCH] backlight: pwm_bl: apply the initial backlight state with sane defaults
From: Daniel Thompson @ 2025-11-07 14:51 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Daniel Thompson, Lee Jones, Michael Grzeschik, Jingoo Han,
Helge Deller, Pengutronix, linux-pwm, dri-devel, linux-fbdev,
linux-kernel
In-Reply-To: <t6wtfnmnclnzwdpbmdcalvyf3mulmpexnryolxkygqkpx7vdwz@dqwbwvlzawrr>
On Fri, Nov 07, 2025 at 09:00:33AM +0100, Uwe Kleine-König wrote:
> On Thu, Oct 30, 2025 at 11:51:07AM +0000, Daniel Thompson wrote:
> > On Thu, Jul 31, 2025 at 10:47:18AM +0200, Michael Grzeschik wrote:
> > > Currently when calling pwm_apply_might_sleep in the probe routine
> > > the pwm will be configured with an not fully defined state.
> > >
> > > The duty_cycle is not yet set in that moment. There is a final
> > > backlight_update_status call that will have a properly setup state.
> > > However this change in the backlight can create a short flicker if the
> > > backlight was already preinitialised.
> > >
> > > We fix the flicker by moving the pwm_apply after the default duty_cycle
> > > can be calculated.
> > >
> > > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> >
> > Reviewed-by: Daniel Thompson (RISCstar) <danielt@kernel.org>
>
> I guess this tag resulted in Lee picking up the change. I wonder if you
> share my concern and who's responsibility it is now to address it.
You mean the ordering the backlight registration versus setting the
properties in the probe method?
I definitely share the concern that there's a short window where
something could request a brightness via sysfs and then have it
overwritten by the remains of the probe method. Likewise I can't see
why there would be any problem moving the call to
pwm_backlight_initial_power_state() before the backlight is registered.
Thus I'd be happy to see the backlight registered later in the probe
method.
On the other hand I don't see any problem calling
backlight_update_status() from the probe method. This is a relatively
common approach in backlight drivers to impose the initial brightness
on the hardware without needing extra code paths.
backlight_update_status() is guarded with a mutex and should be
idempotent for most drivers. Therefore it is OK even if something gets
in via sysfs and provokes an update before the probe method has started
it's own update.
In terms of who should follow up I've got no strong opinions on that.
It's worth noting that I don't own any hardware that uses a PWM
backlight so I'm not in a position to test it!
Daniel.
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: backlight: Add max25014 supporty
From: Frank Li @ 2025-11-07 15:35 UTC (permalink / raw)
To: maudspierings
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <20251107-max25014-v5-1-9a6aa57306bf@gocontroll.com>
On Fri, Nov 07, 2025 at 01:49:58PM +0100, Maud Spierings via B4 Relay wrote:
> From: Maud Spierings <maudspierings@gocontroll.com>
>
> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> with integrated boost controller.
>
> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>
> ---
>
> In the current implementation the control registers for channel 1,
> control all channels. So only one led subnode with led-sources is
> supported right now. If at some point the driver functionality is
> expanded the bindings can be easily extended with it.
> ---
> .../bindings/leds/backlight/maxim,max25014.yaml | 107 +++++++++++++++++++++
> MAINTAINERS | 5 +
> 2 files changed, 112 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> new file mode 100644
> index 000000000000..e83723224b07
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim max25014 backlight controller
> +
> +maintainers:
> + - Maud Spierings <maudspierings@gocontroll.com>
> +
> +properties:
> + compatible:
> + enum:
> + - maxim,max25014
> +
> + reg:
> + maxItems: 1
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> + enable-gpios:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + power-supply:
> + description: Regulator which controls the boost converter input rail.
> +
> + pwms:
> + maxItems: 1
> +
> + maxim,iset:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + maximum: 15
> + default: 11
> + description:
> + Value of the ISET field in the ISET register. This controls the current
> + scale of the outputs, a higher number means more current.
> +
> + led@0:
> + type: object
> + description: Properties for a string of connected LEDs.
> + $ref: common.yaml#
> +
> + properties:
> + reg:
> + const: 0
If reg is const 0, why need use led@0?
Frank
> +
> + led-sources:
> + allOf:
> + - minItems: 1
> + maxItems: 4
> + items:
> + minimum: 0
> + maximum: 3
> + default: [0, 1, 2, 3]
> +
> + default-brightness:
> + minimum: 0
> + maximum: 100
> + default: 50
> +
> + required:
> + - reg
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + backlight@6f {
> + compatible = "maxim,max25014";
> + reg = <0x6f>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
> + interrupt-parent = <&gpio1>;
> + interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
> + power-supply = <®_backlight>;
> + pwms = <&pwm1>;
> + maxim,iset = <7>;
> +
> + led@0 {
> + reg = <0>;
> + led-sources = <0 1 2 3>;
> + default-brightness = <50>;
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 58c7e3f678d8..606ce086f758 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15261,6 +15261,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
> F: drivers/media/i2c/max2175*
> F: include/uapi/linux/max2175.h
>
> +MAX25014 BACKLIGHT DRIVER
> +M: Maud Spierings <maudspierings@gocontroll.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> +
> MAX31335 RTC DRIVER
> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
> L: linux-rtc@vger.kernel.org
>
> --
> 2.51.2
>
>
^ permalink raw reply
* Re: [PATCH] backlight: pwm_bl: apply the initial backlight state with sane defaults
From: Michael Grzeschik @ 2025-11-07 15:48 UTC (permalink / raw)
To: Daniel Thompson
Cc: Uwe Kleine-König, Daniel Thompson, Lee Jones, Jingoo Han,
Helge Deller, Pengutronix, linux-pwm, dri-devel, linux-fbdev,
linux-kernel
In-Reply-To: <aQ4HY5Hncv1fvxVk@aspen.lan>
[-- Attachment #1: Type: text/plain, Size: 2866 bytes --]
On Fri, Nov 07, 2025 at 02:51:15PM +0000, Daniel Thompson wrote:
>On Fri, Nov 07, 2025 at 09:00:33AM +0100, Uwe Kleine-König wrote:
>> On Thu, Oct 30, 2025 at 11:51:07AM +0000, Daniel Thompson wrote:
>> > On Thu, Jul 31, 2025 at 10:47:18AM +0200, Michael Grzeschik wrote:
>> > > Currently when calling pwm_apply_might_sleep in the probe routine
>> > > the pwm will be configured with an not fully defined state.
>> > >
>> > > The duty_cycle is not yet set in that moment. There is a final
>> > > backlight_update_status call that will have a properly setup state.
>> > > However this change in the backlight can create a short flicker if the
>> > > backlight was already preinitialised.
>> > >
>> > > We fix the flicker by moving the pwm_apply after the default duty_cycle
>> > > can be calculated.
>> > >
>> > > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
>> >
>> > Reviewed-by: Daniel Thompson (RISCstar) <danielt@kernel.org>
>>
>> I guess this tag resulted in Lee picking up the change. I wonder if you
>> share my concern and who's responsibility it is now to address it.
>
>You mean the ordering the backlight registration versus setting the
>properties in the probe method?
>
>I definitely share the concern that there's a short window where
>something could request a brightness via sysfs and then have it
>overwritten by the remains of the probe method. Likewise I can't see
>why there would be any problem moving the call to
>pwm_backlight_initial_power_state() before the backlight is registered.
>Thus I'd be happy to see the backlight registered later in the probe
>method.
>
>On the other hand I don't see any problem calling
>backlight_update_status() from the probe method. This is a relatively
>common approach in backlight drivers to impose the initial brightness
>on the hardware without needing extra code paths.
>backlight_update_status() is guarded with a mutex and should be
>idempotent for most drivers. Therefore it is OK even if something gets
>in via sysfs and provokes an update before the probe method has started
>it's own update.
>
>In terms of who should follow up I've got no strong opinions on that.
>It's worth noting that I don't own any hardware that uses a PWM
>backlight so I'm not in a position to test it!
Depending on the setup of the hardware, calling pwm_apply_might_sleep
inbetween before having a fixed definition of how the pwm should be
setup, makes the backlight flicker. Therefor it is better to touch
it as late as possible.
Michael
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlighty
From: Frank Li @ 2025-11-07 15:51 UTC (permalink / raw)
To: maudspierings
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <20251107-max25014-v5-2-9a6aa57306bf@gocontroll.com>
On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
> From: Maud Spierings <maudspierings@gocontroll.com>
>
> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> with integrated boost controller.
>
> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
> ---
> MAINTAINERS | 1 +
> drivers/video/backlight/Kconfig | 7 +
> drivers/video/backlight/Makefile | 1 +
> drivers/video/backlight/max25014.c | 409 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 418 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 606ce086f758..d082d3f8cfae 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15265,6 +15265,7 @@ MAX25014 BACKLIGHT DRIVER
> M: Maud Spierings <maudspierings@gocontroll.com>
> S: Maintained
> F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> +F: drivers/video/backlight/max25014.c
>
> MAX31335 RTC DRIVER
> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index d9374d208cee..d3bb6ccd4185 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -262,6 +262,13 @@ config BACKLIGHT_DA9052
> help
> Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs.
>
> +config BACKLIGHT_MAX25014
> + tristate "Backlight driver for the Maxim MAX25014 chip"
> + depends on I2C
> + select REGMAP_I2C
> + help
> + If you are using a MAX25014 chip as a backlight driver say Y to enable it.
> +
> config BACKLIGHT_MAX8925
> tristate "Backlight driver for MAX8925"
> depends on MFD_MAX8925
> diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
> index dfbb169bf6ea..1170d9ec40b8 100644
> --- a/drivers/video/backlight/Makefile
> +++ b/drivers/video/backlight/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
> obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
> obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
> obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
> +obj-$(CONFIG_BACKLIGHT_MAX25014) += max25014.o
> obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
> obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o
> obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o
> diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c
> new file mode 100644
> index 000000000000..36bae508697e
> --- /dev/null
...
> +
> +struct max25014 {
> + struct i2c_client *client;
> + struct backlight_device *bl;
> + struct regmap *regmap;
> + struct gpio_desc *enable;
> + struct regulator *vin; /* regulator for boost converter Vin rail */
> + uint32_t iset;
> + uint8_t strings_mask;
> +};
> +
> +static const struct regmap_config max25014_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = MAX25014_DIAG,
> +};
> +
> +/**
> + * @brief control the brightness with i2c registers
> + *
> + * @param regmap trivial
> + * @param brt brightness
> + * @return int
> + */
> +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
> +{
> + uint32_t reg = TON_STEP * brt;
> + int ret;
> + /*
> + * 18 bit number lowest, 2 bits in first register,
> + * next lowest 8 in the L register, next 8 in the H register
> + * Seemingly setting the strength of only one string controls all of
> + * them, individual settings don't affect the outcome.
> + */
> +
> + ret = regmap_write(regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011);
> + if (ret != 0)
if (ret), check others regmap_*()
> + return ret;
> + ret = regmap_write(regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111);
> + if (ret != 0)
> + return ret;
> + return regmap_write(regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111);
> +}
> +
> +static int max25014_check_errors(struct max25014 *maxim)
> +{
> + uint8_t i;
> + int ret;
> + uint32_t val;
> +
> + ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
uint32 always >= 0
So
if (val)
> + dev_err(&maxim->client->dev, "Open led strings detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
> + dev_err(&maxim->client->dev, "Short to ground detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
> + dev_err(&maxim->client->dev, "Shorted led detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val);
> + if (ret != 0)
> + return ret;
> + /*
> + * The HW_RST bit always starts at 1 after power up.
> + * It is reset on first read, does not indicate an error.
> + */
> + if (val > 0 && val != MAX25014_DIAG_HW_RST) {
> + if (val & 0b1)
BIT(0)
> + dev_err(&maxim->client->dev,
> + "Overtemperature shutdown\n");
> + if (val & 0b10)
> + dev_err(&maxim->client->dev,
> + "Chip is getting too hot (>125C)\n");
> + if (val & 0b1000)
> + dev_err(&maxim->client->dev,
> + "Boost converter overvoltage\n");
> + if (val & 0b10000)
> + dev_err(&maxim->client->dev,
> + "Boost converter undervoltage\n");
> + if (val & 0b100000)
> + dev_err(&maxim->client->dev, "IREF out of range\n");
> + return -EIO;
> + }
> + return 0;
> +}
> +
...
> +static int max25014_parse_dt(struct max25014 *maxim,
> + uint32_t *initial_brightness)
> +{
> + struct device *dev = &maxim->client->dev;
> + struct device_node *node = dev->of_node;
> + struct fwnode_handle *child;
> + uint32_t strings[4];
> + int res, i;
> +
> + if (!node) {
> + dev_err(dev, "no platform data\n");
> + return -EINVAL;
> + }
call from probe, check other place
return dev_err_probe()
> +
> + child = device_get_next_child_node(dev, NULL);
> + if (child) {
> + res = fwnode_property_count_u32(child, "led-sources");
> + if (res > 0) {
> + fwnode_property_read_u32_array(child, "led-sources",
> + strings, res);
> +
> + /* set all strings as disabled, then enable those in led-sources*/
> + maxim->strings_mask = 0xf;
> + for (i = 0; i < res; i++) {
> + if (strings[i] <= 4)
> + maxim->strings_mask &= ~BIT(strings[i]);
> + }
> + }
> +
> + fwnode_property_read_u32(child, "default-brightness",
> + initial_brightness);
> +
> + fwnode_handle_put(child);
> + }
> +
> + maxim->iset = MAX25014_ISET_DEFAULT_100;
> + of_property_read_u32(node, "maxim,iset", &maxim->iset);
> +
> + if (maxim->iset < 0 || maxim->iset > 15) {
> + dev_err(dev,
> + "Invalid iset, should be a value from 0-15, entered was %d\n",
> + maxim->iset);
> + return -EINVAL;
> + }
> +
> + if (*initial_brightness < 0 || *initial_brightness > 100) {
> + dev_err(dev,
> + "Invalid initial brightness, should be a value from 0-100, entered was %d\n",
> + *initial_brightness);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int max25014_probe(struct i2c_client *cl)
> +{
> + struct backlight_device *bl;
> + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
> + struct max25014 *maxim;
> + struct backlight_properties props;
> + int ret;
> + uint32_t initial_brightness = 50;
try keep reverise christmas order
> +
> + maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL);
> + if (!maxim)
> + return -ENOMEM;
> +
> + maxim->client = cl;
> +
> + ret = max25014_parse_dt(maxim, &initial_brightness);
> + if (ret < 0)
> + return ret;
> +
> + maxim->vin = devm_regulator_get_optional(&maxim->client->dev, "power");
> + if (IS_ERR(maxim->vin)) {
> + if (PTR_ERR(maxim->vin) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> + maxim->vin = NULL;
> + }
> +
> + if (maxim->vin) {
> + ret = regulator_enable(maxim->vin);
> + if (ret < 0) {
> + dev_err(&maxim->client->dev,
> + "failed to enable Vin: %d\n", ret);
> + return ret;
> + }
> + }
use devm_regulator_get_enable_optional() to combine devm_regulator_get_optional()
and regulator_enable() to one call.
> +
> + maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable",
> + GPIOD_ASIS);
> + if (IS_ERR(maxim->enable)) {
> + ret = PTR_ERR(maxim->enable);
> + dev_err(&maxim->client->dev, "failed to get enable gpio: %d\n",
> + ret);
> + goto disable_vin;
> + }
> +
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 1);
gpiod_set_value_cansleep() tolerate NULL, so needn't if check here
and if you pass GPIOD_OUT_HIGH at devm_gpiod_get_optional(), needn't call
this function.
> +
> + /* Enable can be tied to vin rail wait if either is available */
> + if (maxim->enable || maxim->vin) {
> + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
> + usleep_range(2000, 2500);
now perfer use fsleep()
Frank
> + }
> +
> + maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config);
> + if (IS_ERR(maxim->regmap)) {
> + ret = PTR_ERR(maxim->regmap);
> + dev_err(&maxim->client->dev,
> + "failed to initialize the i2c regmap: %d\n", ret);
> + goto disable_full;
> + }
> +
> + i2c_set_clientdata(cl, maxim);
> +
> + ret = max25014_check_errors(maxim);
> + if (ret) { /* error is already reported in the above function */
> + goto disable_full;
> + }
> +
> + ret = max25014_configure(maxim);
> + if (ret) {
> + dev_err(&maxim->client->dev, "device config err: %d", ret);
> + goto disable_full;
> + }
> +
> + memset(&props, 0, sizeof(props));
> + props.type = BACKLIGHT_PLATFORM;
> + props.max_brightness = MAX_BRIGHTNESS;
> + props.brightness = initial_brightness;
> + props.scale = BACKLIGHT_SCALE_LINEAR;
> +
> + bl = devm_backlight_device_register(&maxim->client->dev, id->name,
> + &maxim->client->dev, maxim,
> + &max25014_bl_ops, &props);
> + if (IS_ERR(bl))
> + return PTR_ERR(bl);
> +
> + maxim->bl = bl;
> +
> + return 0;
> +
> +disable_full:
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 0);
> +disable_vin:
> + if (maxim->vin)
> + regulator_disable(maxim->vin);
> + return ret;
> +}
> +
> +static void max25014_remove(struct i2c_client *cl)
> +{
> + struct max25014 *maxim = i2c_get_clientdata(cl);
> +
> + maxim->bl->props.brightness = 0;
> + max25014_update_status(maxim->bl);
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 0);
> + if (maxim->vin)
> + regulator_disable(maxim->vin);
> +}
> +
> +static const struct of_device_id max25014_dt_ids[] = {
> + { .compatible = "maxim,max25014", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, max25014_dt_ids);
> +
> +static const struct i2c_device_id max25014_ids[] = {
> + { "max25014" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, max25014_ids);
> +
> +static struct i2c_driver max25014_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = of_match_ptr(max25014_dt_ids),
> + },
> + .probe = max25014_probe,
> + .remove = max25014_remove,
> + .id_table = max25014_ids,
> +};
> +module_i2c_driver(max25014_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX25014 backlight driver");
> +MODULE_AUTHOR("Maud Spierings <maudspierings@gocontroll.com>");
> +MODULE_LICENSE("GPL");
>
> --
> 2.51.2
>
>
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlight
From: Daniel Thompson @ 2025-11-07 16:14 UTC (permalink / raw)
To: maudspierings
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <20251107-max25014-v5-2-9a6aa57306bf@gocontroll.com>
On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
> From: Maud Spierings <maudspierings@gocontroll.com>
>
> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> with integrated boost controller.
>
> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
> diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c
> new file mode 100644
> index 000000000000..36bae508697e
> --- /dev/null
> +++ b/drivers/video/backlight/max25014.c
> @@ -0,0 +1,409 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Backlight driver for Maxim MAX25014
> + *
> + * Copyright (C) 2025 GOcontroll B.V.
> + * Author: Maud Spierings <maudspierings@gocontroll.com>
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#define MAX25014_ISET_DEFAULT_100 11
> +#define MAX_BRIGHTNESS 100
> +#define MIN_BRIGHTNESS 0
> +#define TON_MAX 130720 /* @153Hz */
> +#define TON_STEP 1307 /* @153Hz */
> +#define TON_MIN 0
> +
> +#define MAX25014_DEV_ID 0x00
> +#define MAX25014_REV_ID 0x01
> +#define MAX25014_ISET 0x02
> +#define MAX25014_IMODE 0x03
> +#define MAX25014_TON1H 0x04
> +#define MAX25014_TON1L 0x05
> +#define MAX25014_TON2H 0x06
> +#define MAX25014_TON2L 0x07
> +#define MAX25014_TON3H 0x08
> +#define MAX25014_TON3L 0x09
> +#define MAX25014_TON4H 0x0A
> +#define MAX25014_TON4L 0x0B
> +#define MAX25014_TON_1_4_LSB 0x0C
> +#define MAX25014_SETTING 0x12
> +#define MAX25014_DISABLE 0x13
> +#define MAX25014_BSTMON 0x14
> +#define MAX25014_IOUT1 0x15
> +#define MAX25014_IOUT2 0x16
> +#define MAX25014_IOUT3 0x17
> +#define MAX25014_IOUT4 0x18
> +#define MAX25014_OPEN 0x1B
> +#define MAX25014_SHORT_GND 0x1C
> +#define MAX25014_SHORT_LED 0x1D
> +#define MAX25014_MASK 0x1E
> +#define MAX25014_DIAG 0x1F
> +
> +#define MAX25014_IMODE_HDIM BIT(2)
> +#define MAX25014_ISET_ENABLE BIT(5)
> +#define MAX25014_ISET_PSEN BIT(4)
> +#define MAX25014_DIAG_HW_RST BIT(2)
> +#define MAX25014_SETTING_FPWM GENMASK(6, 4)
> +
> +struct max25014 {
> + struct i2c_client *client;
> + struct backlight_device *bl;
> + struct regmap *regmap;
> + struct gpio_desc *enable;
> + struct regulator *vin; /* regulator for boost converter Vin rail */
> + uint32_t iset;
> + uint8_t strings_mask;
> +};
> +
> +static const struct regmap_config max25014_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = MAX25014_DIAG,
> +};
> +
> +/**
> + * @brief control the brightness with i2c registers
> + *
> + * @param regmap trivial
> + * @param brt brightness
> + * @return int
> + */
> +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
This isn't a good name for a function. It doesn't really say what it
does. Please find a more descriptive name.
> +{
> + uint32_t reg = TON_STEP * brt;
> + int ret;
> + /*
> + * 18 bit number lowest, 2 bits in first register,
> + * next lowest 8 in the L register, next 8 in the H register
> + * Seemingly setting the strength of only one string controls all of
> + * them, individual settings don't affect the outcome.
> + */
> +
> + ret = regmap_write(regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011);
> + if (ret != 0)
> + return ret;
> + ret = regmap_write(regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111);
> + if (ret != 0)
> + return ret;
> + return regmap_write(regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111);
> +}
> +
> +static int max25014_check_errors(struct max25014 *maxim)
> +{
> + uint8_t i;
> + int ret;
> + uint32_t val;
> +
> + ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
> + dev_err(&maxim->client->dev, "Open led strings detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
> + dev_err(&maxim->client->dev, "Short to ground detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
> + if (ret != 0)
> + return ret;
> + if (val > 0) {
> + dev_err(&maxim->client->dev, "Shorted led detected on:\n");
> + for (i = 0; i < 4; i++) {
> + if (val & 1 << i)
> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
> + }
> + return -EIO;
> + }
> +
> + ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val);
> + if (ret != 0)
> + return ret;
> + /*
> + * The HW_RST bit always starts at 1 after power up.
> + * It is reset on first read, does not indicate an error.
> + */
> + if (val > 0 && val != MAX25014_DIAG_HW_RST) {
> + if (val & 0b1)
> + dev_err(&maxim->client->dev,
> + "Overtemperature shutdown\n");
> + if (val & 0b10)
> + dev_err(&maxim->client->dev,
> + "Chip is getting too hot (>125C)\n");
> + if (val & 0b1000)
> + dev_err(&maxim->client->dev,
> + "Boost converter overvoltage\n");
> + if (val & 0b10000)
> + dev_err(&maxim->client->dev,
> + "Boost converter undervoltage\n");
> + if (val & 0b100000)
> + dev_err(&maxim->client->dev, "IREF out of range\n");
> + return -EIO;
> + }
> + return 0;
> +}
> +
> +/*
> + * 1. disable unused strings
> + * 2. set dim mode
> + * 3. set initial brightness
How does this code set the initial brightness? It doens't set the
MAX25014_TON* registers.
> + * 4. set setting register
> + * 5. enable the backlight
> + */
> +static int max25014_configure(struct max25014 *maxim)
> +{
> + int ret;
> + uint32_t val;
> +
> + ret = regmap_write(maxim->regmap, MAX25014_DISABLE,
> + maxim->strings_mask);
> + if (ret != 0)
> + return ret;
> +
> + ret = regmap_write(maxim->regmap, MAX25014_IMODE, MAX25014_IMODE_HDIM);
> + if (ret != 0)
> + return ret;
> +
> + ret = regmap_read(maxim->regmap, MAX25014_SETTING, &val);
> + if (ret != 0)
> + return ret;
> +
> + ret = regmap_write(maxim->regmap, MAX25014_SETTING,
> + val & ~MAX25014_SETTING_FPWM);
> + if (ret != 0)
> + return ret;
> +
> + ret = regmap_write(maxim->regmap, MAX25014_ISET,
> + maxim->iset | MAX25014_ISET_ENABLE |
> + MAX25014_ISET_PSEN);
> + return ret;
> +}
> +
> +static int max25014_update_status(struct backlight_device *bl_dev)
> +{
> + struct max25014 *maxim = bl_get_data(bl_dev);
> +
> + if (backlight_is_blank(maxim->bl))
> + bl_dev->props.brightness = 0;
> +
> + return max25014_register_control(maxim->regmap,
> + bl_dev->props.brightness);
> +}
> +
> +static const struct backlight_ops max25014_bl_ops = {
> + .options = BL_CORE_SUSPENDRESUME,
> + .update_status = max25014_update_status,
> +};
> +
> +static int max25014_parse_dt(struct max25014 *maxim,
> + uint32_t *initial_brightness)
> +{
> + struct device *dev = &maxim->client->dev;
> + struct device_node *node = dev->of_node;
> + struct fwnode_handle *child;
> + uint32_t strings[4];
> + int res, i;
> +
> + if (!node) {
> + dev_err(dev, "no platform data\n");
> + return -EINVAL;
> + }
> +
> + child = device_get_next_child_node(dev, NULL);
> + if (child) {
> + res = fwnode_property_count_u32(child, "led-sources");
> + if (res > 0) {
> + fwnode_property_read_u32_array(child, "led-sources",
> + strings, res);
> +
> + /* set all strings as disabled, then enable those in led-sources*/
> + maxim->strings_mask = 0xf;
> + for (i = 0; i < res; i++) {
> + if (strings[i] <= 4)
> + maxim->strings_mask &= ~BIT(strings[i]);
> + }
> + }
> +
> + fwnode_property_read_u32(child, "default-brightness",
> + initial_brightness);
> +
> + fwnode_handle_put(child);
> + }
> +
> + maxim->iset = MAX25014_ISET_DEFAULT_100;
> + of_property_read_u32(node, "maxim,iset", &maxim->iset);
> +
> + if (maxim->iset < 0 || maxim->iset > 15) {
> + dev_err(dev,
> + "Invalid iset, should be a value from 0-15, entered was %d\n",
> + maxim->iset);
> + return -EINVAL;
> + }
> +
> + if (*initial_brightness < 0 || *initial_brightness > 100) {
> + dev_err(dev,
> + "Invalid initial brightness, should be a value from 0-100, entered was %d\n",
> + *initial_brightness);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int max25014_probe(struct i2c_client *cl)
> +{
> + struct backlight_device *bl;
> + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
> + struct max25014 *maxim;
> + struct backlight_properties props;
> + int ret;
> + uint32_t initial_brightness = 50;
> +
> + maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL);
> + if (!maxim)
> + return -ENOMEM;
> +
> + maxim->client = cl;
> +
> + ret = max25014_parse_dt(maxim, &initial_brightness);
> + if (ret < 0)
> + return ret;
> +
> + maxim->vin = devm_regulator_get_optional(&maxim->client->dev, "power");
I would have expected to see devm_regulator_get() here. Why do you care
whether you get a real regulator or a dummy if you just NULL check
maxim->vin everywhere?
> + if (IS_ERR(maxim->vin)) {
> + if (PTR_ERR(maxim->vin) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> + maxim->vin = NULL;
> + }
> +
> + if (maxim->vin) {
If you had called devm_regulator_get() there would be no need for a NULL
check here.
> + ret = regulator_enable(maxim->vin);
> + if (ret < 0) {
> + dev_err(&maxim->client->dev,
> + "failed to enable Vin: %d\n", ret);
> + return ret;
> + }
> + }
> +
> + maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable",
> + GPIOD_ASIS);
> + if (IS_ERR(maxim->enable)) {
> + ret = PTR_ERR(maxim->enable);
> + dev_err(&maxim->client->dev, "failed to get enable gpio: %d\n",
> + ret);
> + goto disable_vin;
> + }
> +
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 1);
No need for NULL pointer check here (see
https://elixir.bootlin.com/linux/v6.18-rc4/source/drivers/gpio/gpiolib.c#L358-L363 ).
> +
> + /* Enable can be tied to vin rail wait if either is available */
> + if (maxim->enable || maxim->vin) {
> + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
> + usleep_range(2000, 2500);
> + }
If you really want to keep the devm_regulator_get_optional() I guess
maybe you could persuade me it's need to avoid this sleep... although
I'd be fairly happy to remove the NULL checks here too!
> +
> + maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config);
> + if (IS_ERR(maxim->regmap)) {
> + ret = PTR_ERR(maxim->regmap);
> + dev_err(&maxim->client->dev,
> + "failed to initialize the i2c regmap: %d\n", ret);
> + goto disable_full;
> + }
> +
> + i2c_set_clientdata(cl, maxim);
> +
> + ret = max25014_check_errors(maxim);
> + if (ret) { /* error is already reported in the above function */
> + goto disable_full;
> + }
> +
> + ret = max25014_configure(maxim);
> + if (ret) {
> + dev_err(&maxim->client->dev, "device config err: %d", ret);
> + goto disable_full;
> + }
> +
> + memset(&props, 0, sizeof(props));
> + props.type = BACKLIGHT_PLATFORM;
> + props.max_brightness = MAX_BRIGHTNESS;
> + props.brightness = initial_brightness;
> + props.scale = BACKLIGHT_SCALE_LINEAR;
> +
> + bl = devm_backlight_device_register(&maxim->client->dev, id->name,
> + &maxim->client->dev, maxim,
> + &max25014_bl_ops, &props);
> + if (IS_ERR(bl))
> + return PTR_ERR(bl);
> +
> + maxim->bl = bl;
> +
> + return 0;
> +
> +disable_full:
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 0);
Again, NULL check isn't needed.
> +disable_vin:
> + if (maxim->vin)
> + regulator_disable(maxim->vin);
> + return ret;
> +}
> +
> +static void max25014_remove(struct i2c_client *cl)
> +{
> + struct max25014 *maxim = i2c_get_clientdata(cl);
> +
> + maxim->bl->props.brightness = 0;
> + max25014_update_status(maxim->bl);
> + if (maxim->enable)
> + gpiod_set_value_cansleep(maxim->enable, 0);
Lose the NULL check.
> + if (maxim->vin)
> + regulator_disable(maxim->vin);
> +}
> +
> +static const struct of_device_id max25014_dt_ids[] = {
> + { .compatible = "maxim,max25014", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, max25014_dt_ids);
> +
> +static const struct i2c_device_id max25014_ids[] = {
> + { "max25014" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, max25014_ids);
> +
> +static struct i2c_driver max25014_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = of_match_ptr(max25014_dt_ids),
> + },
> + .probe = max25014_probe,
> + .remove = max25014_remove,
> + .id_table = max25014_ids,
> +};
> +module_i2c_driver(max25014_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX25014 backlight driver");
> +MODULE_AUTHOR("Maud Spierings <maudspierings@gocontroll.com>");
> +MODULE_LICENSE("GPL");
Daniel.
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlighty
From: Daniel Thompson @ 2025-11-07 16:22 UTC (permalink / raw)
To: Frank Li
Cc: maudspierings, Lee Jones, Daniel Thompson, Jingoo Han,
Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Helge Deller, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Liam Girdwood, Mark Brown, dri-devel, linux-leds,
devicetree, linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4Vb4eUmSX0Nj6+@lizhi-Precision-Tower-5810>
On Fri, Nov 07, 2025 at 10:51:11AM -0500, Frank Li wrote:
> On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
> > From: Maud Spierings <maudspierings@gocontroll.com>
> >
> > The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> > with integrated boost controller.
> >
> > Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
<snip>
> > +static int max25014_probe(struct i2c_client *cl)
> > +{
> > + struct backlight_device *bl;
> > + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
> > + struct max25014 *maxim;
> > + struct backlight_properties props;
> > + int ret;
> > + uint32_t initial_brightness = 50;
>
> try keep reverise christmas order
I thought reverse christmas tree order only applied to code where the
maintainers called it out in Development/process (e.g. netdev and tip).
Daniel.
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: backlight: Add max25014 supporty
From: Conor Dooley @ 2025-11-07 17:14 UTC (permalink / raw)
To: Frank Li
Cc: maudspierings, Lee Jones, Daniel Thompson, Jingoo Han,
Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Helge Deller, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Liam Girdwood, Mark Brown, dri-devel, linux-leds,
devicetree, linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4RqNiGsngOWrV5@lizhi-Precision-Tower-5810>
[-- Attachment #1: Type: text/plain, Size: 914 bytes --]
On Fri, Nov 07, 2025 at 10:35:04AM -0500, Frank Li wrote:
> On Fri, Nov 07, 2025 at 01:49:58PM +0100, Maud Spierings via B4 Relay wrote:
> > From: Maud Spierings <maudspierings@gocontroll.com>
> >
> > The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> > with integrated boost controller.
> >
> > Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
> > + led@0:
> > + type: object
> > + description: Properties for a string of connected LEDs.
> > + $ref: common.yaml#
> > +
> > + properties:
> > + reg:
> > + const: 0
>
> If reg is const 0, why need use led@0?
> > In the current implementation the control registers for channel 1,
> > control all channels. So only one led subnode with led-sources is
> > supported right now. If at some point the driver functionality is
> > expanded the bindings can be easily extended with it.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: backlight: Add max25014 support
From: Conor Dooley @ 2025-11-07 18:16 UTC (permalink / raw)
To: maudspierings
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <20251107-max25014-v5-1-9a6aa57306bf@gocontroll.com>
[-- Attachment #1: Type: text/plain, Size: 4739 bytes --]
On Fri, Nov 07, 2025 at 01:49:58PM +0100, Maud Spierings via B4 Relay wrote:
> From: Maud Spierings <maudspierings@gocontroll.com>
>
> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> with integrated boost controller.
>
> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>
> ---
>
> In the current implementation the control registers for channel 1,
> control all channels. So only one led subnode with led-sources is
> supported right now. If at some point the driver functionality is
> expanded the bindings can be easily extended with it.
I'm sorry if I asked this before and forgot or w/e, but how backwards
compatible is this? If they control all channels and it gets changed to
only control channel one, how will a changed kernel understand the
difference between a new devicetree that only wants to control channel 1
and an old devicetree that is trying to use channel 1 to control all
channels?
Cheers,
Conor.
> ---
> .../bindings/leds/backlight/maxim,max25014.yaml | 107 +++++++++++++++++++++
> MAINTAINERS | 5 +
> 2 files changed, 112 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> new file mode 100644
> index 000000000000..e83723224b07
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim max25014 backlight controller
> +
> +maintainers:
> + - Maud Spierings <maudspierings@gocontroll.com>
> +
> +properties:
> + compatible:
> + enum:
> + - maxim,max25014
> +
> + reg:
> + maxItems: 1
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> + enable-gpios:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + power-supply:
> + description: Regulator which controls the boost converter input rail.
> +
> + pwms:
> + maxItems: 1
> +
> + maxim,iset:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + maximum: 15
> + default: 11
> + description:
> + Value of the ISET field in the ISET register. This controls the current
> + scale of the outputs, a higher number means more current.
> +
> + led@0:
> + type: object
> + description: Properties for a string of connected LEDs.
> + $ref: common.yaml#
> +
> + properties:
> + reg:
> + const: 0
> +
> + led-sources:
> + allOf:
> + - minItems: 1
> + maxItems: 4
> + items:
> + minimum: 0
> + maximum: 3
> + default: [0, 1, 2, 3]
> +
> + default-brightness:
> + minimum: 0
> + maximum: 100
> + default: 50
> +
> + required:
> + - reg
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + backlight@6f {
> + compatible = "maxim,max25014";
> + reg = <0x6f>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
> + interrupt-parent = <&gpio1>;
> + interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
> + power-supply = <®_backlight>;
> + pwms = <&pwm1>;
> + maxim,iset = <7>;
> +
> + led@0 {
> + reg = <0>;
> + led-sources = <0 1 2 3>;
> + default-brightness = <50>;
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 58c7e3f678d8..606ce086f758 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15261,6 +15261,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
> F: drivers/media/i2c/max2175*
> F: include/uapi/linux/max2175.h
>
> +MAX25014 BACKLIGHT DRIVER
> +M: Maud Spierings <maudspierings@gocontroll.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
> +
> MAX31335 RTC DRIVER
> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
> L: linux-rtc@vger.kernel.org
>
> --
> 2.51.2
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlighty
From: Frank Li @ 2025-11-07 20:42 UTC (permalink / raw)
To: Daniel Thompson
Cc: maudspierings, Lee Jones, Daniel Thompson, Jingoo Han,
Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Helge Deller, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Liam Girdwood, Mark Brown, dri-devel, linux-leds,
devicetree, linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4cyVKzgBUfpsj9@aspen.lan>
On Fri, Nov 07, 2025 at 04:22:33PM +0000, Daniel Thompson wrote:
> On Fri, Nov 07, 2025 at 10:51:11AM -0500, Frank Li wrote:
> > On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
> > > From: Maud Spierings <maudspierings@gocontroll.com>
> > >
> > > The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
> > > with integrated boost controller.
> > >
> > > Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>
> <snip>
>
> > > +static int max25014_probe(struct i2c_client *cl)
> > > +{
> > > + struct backlight_device *bl;
> > > + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
> > > + struct max25014 *maxim;
> > > + struct backlight_properties props;
> > > + int ret;
> > > + uint32_t initial_brightness = 50;
> >
> > try keep reverise christmas order
>
> I thought reverse christmas tree order only applied to code where the
> maintainers called it out in Development/process (e.g. netdev and tip).
But, but it is small change when you update next version.
Frank
>
>
> Daniel.
^ permalink raw reply
* [PATCH v3] fbdev: vga16fb: Request memory region.
From: Javier Garcia @ 2025-11-08 9:33 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, shuah, Javier Garcia
In-Reply-To: <f9998d60-e532-48ae-8e03-d867cc0ec847@gmx.de>
This patch reserve and release VGA memory region.
This align with Documentation/drm/todo.rst
"Request memory regions in all fbdev drivers"
I've tested with 32bits kernel and qemu.
Signed-off-by: Javier Garcia <rampxxxx@gmail.com>
---
v1 -> v3:
* Add space after comma.
* v2 https://lore.kernel.org/lkml/20251028191615.2765711-1-rampxxxx@gmail.com/
v1 -> v2:
* Add release in vga16fb_remove , thanks Helge Deller.
* v1 https://lore.kernel.org/lkml/20251016171845.1397153-1-rampxxxx@gmail.com/
drivers/video/fbdev/vga16fb.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c
index eedab14c7d51..3b4c50d98ba6 100644
--- a/drivers/video/fbdev/vga16fb.c
+++ b/drivers/video/fbdev/vga16fb.c
@@ -1319,6 +1319,11 @@ static int vga16fb_probe(struct platform_device *dev)
if (ret)
return ret;
+ if (!request_mem_region(vga16fb_fix.smem_start, vga16fb_fix.smem_len,
+ "vga16b")) {
+ dev_err(&dev->dev, "vga16b: cannot reserve video memory at 0x%lx\n",
+ vga16fb_fix.smem_start);
+ }
printk(KERN_DEBUG "vga16fb: initializing\n");
info = framebuffer_alloc(sizeof(struct vga16fb_par), &dev->dev);
@@ -1398,6 +1403,8 @@ static int vga16fb_probe(struct platform_device *dev)
err_ioremap:
framebuffer_release(info);
err_fb_alloc:
+ release_mem_region(vga16fb_fix.smem_start,
+ vga16fb_fix.smem_len);
return ret;
}
@@ -1407,6 +1414,8 @@ static void vga16fb_remove(struct platform_device *dev)
if (info)
unregister_framebuffer(info);
+ release_mem_region(vga16fb_fix.smem_start,
+ vga16fb_fix.smem_len);
}
static const struct platform_device_id vga16fb_driver_id_table[] = {
--
2.50.1
^ permalink raw reply related
* [PATCH v3 0/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-09 3:22 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev,
Junjie Cao
Add support for Awinic AW99706 backlight, which can be found in
tablet and notebook backlight, one case is the Lenovo Legion Y700
Gen4. This driver refers to the official datasheets and android
driver, they can be found in [1].
[1] https://www.awinic.com/en/productDetail/AW99706QNR
Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
---
base-commit: 72fb0170ef1f45addf726319c52a0562b6913707
---
Changes in v3:
- remove the shift field (Daniel)
- use FIELD_PREP() to replace FIELD_MODIFY() (Daniel)
- include init table into aw99706_device (Daniel)
- breaking a long line (Krzysztof)
- rename backlight node (Krzysztof)
- make aw99706_regs static (Krzysztof)
- return -ENODEV if it is a unknown chip id (Krzysztof)
- use __ffs() to handle shift, since mask is not compiletime in for loop
- reorder functions and structs
- Link to v2: https://lore.kernel.org/linux-leds/20251103110648.878325-1-caojunjie650@gmail.com
Changes in v2:
- add handler for max-brightness and default-brightness
- add properties(max-brightness, default-brightness) (Krzysztof)
- use proper units for properties (Krzysztof)
- drop non-fixed properties (Krzysztof)
- include default values in the aw99706_dt_props table (Daniel)
- warn when a property value from DT is invalid (Daniel)
- drop warning when optional properties are missing (Daniel)
- add a function pointer into the aw99706_dt_props table to handle lookup (Daniel)
- use a lookup function instead of hardcoding the formula for the iLED max (Daniel)
- move BL enalbe handler into aw99706_update_brightness (Daniel)
- Link to v1: https://lore.kernel.org/linux-leds/20251026123923.1531727-3-caojunjie650@gmail.com
Junjie Cao (2):
dt-bindings: leds: backlight: Add Awinic AW99706 backlight
backlight: aw99706: Add support for Awinic AW99706 backlight
.../leds/backlight/awinic,aw99706.yaml | 101 ++++
MAINTAINERS | 6 +
drivers/video/backlight/Kconfig | 8 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/aw99706.c | 471 ++++++++++++++++++
5 files changed, 587 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
create mode 100644 drivers/video/backlight/aw99706.c
--
2.51.1.dirty
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-09 3:22 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev,
Junjie Cao
In-Reply-To: <20251109032240.3422503-1-caojunjie650@gmail.com>
Add Awinic AW99706 backlight binding documentation.
Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
---
Changes in v3:
- breaking a long line (Krzysztof)
- rename backlight node (Krzysztof)
- Link to v2: https://lore.kernel.org/linux-leds/20251103110648.878325-2-caojunjie650@gmail.com
Changes in v2:
- use proper units for properties (Krzysztof)
- drop non-fixed properties (Krzysztof)
- add properties(max-brightness, default-brightness) (Krzysztof)
- Link to v1: https://lore.kernel.org/linux-leds/20251026123923.1531727-2-caojunjie650@gmail.com
.../leds/backlight/awinic,aw99706.yaml | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
diff --git a/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml b/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
new file mode 100644
index 000000000..f48ce7a34
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/backlight/awinic,aw99706.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW99706 6-channel WLED Backlight Driver
+
+maintainers:
+ - Junjie Cao <caojunjie650@gmail.com>
+
+allOf:
+ - $ref: common.yaml#
+
+properties:
+ compatible:
+ const: awinic,aw99706
+
+ reg:
+ maxItems: 1
+
+ enable-gpios:
+ description: GPIO to use to enable/disable the backlight (HWEN pin).
+ maxItems: 1
+
+ awinic,dim-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Select dimming mode of the device.
+ 0 = Bypass mode.
+ 1 = DC mode.
+ 2 = MIX mode(PWM at low brightness and DC at high brightness).
+ 3 = MIX-26k mode(MIX mode with different PWM frequency).
+ enum: [ 0, 1, 2, 3 ]
+ default: 1
+
+ awinic,sw-freq-hz:
+ description: Boost switching frequency in Hz.
+ enum: [ 300000, 400000, 500000, 600000, 660000, 750000, 850000, 1000000,
+ 1200000, 1330000, 1500000, 1700000 ]
+ default: 750000
+
+ awinic,sw-ilmt-microamp:
+ description: Switching current limitation in uA.
+ enum: [ 1500000, 2000000, 2500000, 3000000 ]
+ default: 3000000
+
+ awinic,iled-max-microamp:
+ description: Maximum LED current setting in uA.
+ minimum: 5000
+ maximum: 50000
+ multipleOf: 500
+ default: 20000
+
+ awinic,uvlo-thres-microvolt:
+ description: UVLO(Under Voltage Lock Out) in uV.
+ enum: [ 2200000, 5000000 ]
+ default: 2200000
+
+ awinic,ramp-ctl:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Select ramp control and filter of the device.
+ 0 = Fade in/fade out.
+ 1 = Light filter.
+ 2 = Medium filter.
+ 3 = Heavy filter.
+ enum: [ 0, 1, 2, 3 ]
+ default: 2
+
+required:
+ - compatible
+ - reg
+ - enable-gpios
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ backlight@76 {
+ compatible = "awinic,aw99706";
+ reg = <0x76>;
+ enable-gpios = <&tlmm 88 GPIO_ACTIVE_HIGH>;
+ default-brightness = <2047>;
+ max-brightness = <4095>;
+ awinic,dim-mode = <1>;
+ awinic,sw-freq-hz = <750000>;
+ awinic,sw-ilmt-microamp = <3000000>;
+ awinic,uvlo-thres-microvolt = <2200000>;
+ awinic,iled-max-microamp = <20000>;
+ awinic,ramp-ctl = <2>;
+ };
+ };
+
+...
--
2.51.1.dirty
^ permalink raw reply related
* [PATCH v3 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-09 3:22 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller
Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev,
Junjie Cao, Pengyu Luo
In-Reply-To: <20251109032240.3422503-1-caojunjie650@gmail.com>
Add support for Awinic AW99706 backlight, which can be found in
tablet and notebook backlight, one case is the Lenovo Legion Y700
Gen4. This driver refers to the official datasheets and android
driver, they can be found in [1].
[1] https://www.awinic.com/en/productDetail/AW99706QNR
Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
Tested-by: Pengyu Luo <mitltlatltl@gmail.com>
Signed-off-by: Pengyu Luo <mitltlatltl@gmail.com>
---
Changes in v3:
- remove the shift field (Daniel)
- use FIELD_PREP() to replace FIELD_MODIFY() (Daniel)
- include init table into aw99706_device (Daniel)
- make aw99706_regs static (Krzysztof)
- return -ENODEV if it is a unknown chip id (Krzysztof)
- use __ffs() to handle shift, since mask is not compiletime in for loop
- reorder functions and structs
- Link to v2: https://lore.kernel.org/linux-leds/20251103110648.878325-3-caojunjie650@gmail.com
Changes in v2:
- add handler for max-brightness and default-brightness
- use proper units for properties (Krzysztof)
- drop non-fixed properties (Krzysztof)
- include default values in the aw99706_dt_props table (Daniel)
- warn when a property value from DT is invalid (Daniel)
- drop warning when optional properties are missing (Daniel)
- add a function pointer into the aw99706_dt_props table to handle lookup (Daniel)
- use a lookup function instead of hardcoding the formula for the iLED max (Daniel)
- move BL enalbe handler into aw99706_update_brightness (Daniel)
- Link to v1: https://lore.kernel.org/linux-leds/20251026123923.1531727-3-caojunjie650@gmail.com
MAINTAINERS | 6 +
drivers/video/backlight/Kconfig | 8 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/aw99706.c | 471 ++++++++++++++++++++++++++++++
4 files changed, 486 insertions(+)
create mode 100644 drivers/video/backlight/aw99706.c
diff --git a/MAINTAINERS b/MAINTAINERS
index be21f1fa8..551d8328e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4140,6 +4140,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml
F: drivers/iio/adc/hx711.c
+AWINIC AW99706 WLED BACKLIGHT DRIVER
+M: Junjie Cao <caojunjie650@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
+F: drivers/video/backlight/aw99706.c
+
AX.25 NETWORK LAYER
L: linux-hams@vger.kernel.org
S: Orphan
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index d9374d208..35c7bfad0 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -156,6 +156,14 @@ config BACKLIGHT_ATMEL_LCDC
If in doubt, it's safe to enable this option; it doesn't kick
in unless the board's description says it's wired that way.
+config BACKLIGHT_AW99706
+ tristate "Backlight Driver for Awinic AW99706"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you have a LCD backlight connected to the WLED output of AW99706
+ WLED output, say Y here to enable this driver.
+
config BACKLIGHT_EP93XX
tristate "Cirrus EP93xx Backlight Driver"
depends on FB_EP93XX
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index dfbb169bf..a5d62b018 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE_DWI) += apple_dwi_bl.o
obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
+obj-$(CONFIG_BACKLIGHT_AW99706) += aw99706.o
obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
diff --git a/drivers/video/backlight/aw99706.c b/drivers/video/backlight/aw99706.c
new file mode 100644
index 000000000..b7c1d24b1
--- /dev/null
+++ b/drivers/video/backlight/aw99706.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * aw99706 - Backlight driver for the AWINIC AW99706
+ *
+ * Copyright (C) 2025 Junjie Cao <caojunjie650@gmail.com>
+ * Copyright (C) 2025 Pengyu Luo <mitltlatltl@gmail.com>
+ *
+ * Based on vendor driver:
+ * Copyright (c) 2023 AWINIC Technology CO., LTD
+ */
+
+#include <linux/backlight.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define AW99706_MAX_BRT_LVL 4095
+#define AW99706_REG_MAX 0x1F
+#define AW99706_ID 0x07
+
+/* registers list */
+#define AW99706_CFG0_REG 0x00
+#define AW99706_DIM_MODE_MASK GENMASK(1, 0)
+
+#define AW99706_CFG1_REG 0x01
+#define AW99706_SW_FREQ_MASK GENMASK(3, 0)
+#define AW99706_SW_ILMT_MASK GENMASK(5, 4)
+
+#define AW99706_CFG2_REG 0x02
+#define AW99706_ILED_MAX_MASK GENMASK(6, 0)
+#define AW99706_UVLOSEL_MASK BIT(7)
+
+#define AW99706_CFG3_REG 0x03
+#define AW99706_CFG4_REG 0x04
+#define AW99706_BRT_MSB_MASK GENMASK(3, 0)
+
+#define AW99706_CFG5_REG 0x05
+#define AW99706_BRT_LSB_MASK GENMASK(7, 0)
+
+#define AW99706_CFG6_REG 0x06
+#define AW99706_RAMP_CTL_MASK GENMASK(7, 6)
+
+#define AW99706_CFG7_REG 0x07
+#define AW99706_CFG8_REG 0x08
+#define AW99706_CFG9_REG 0x09
+#define AW99706_CFGA_REG 0x0A
+#define AW99706_CFGB_REG 0x0B
+#define AW99706_CFGC_REG 0x0C
+#define AW99706_CFGD_REG 0x0D
+#define AW99706_FLAG_REG 0x10
+#define AW99706_BACKLIGHT_EN_MASK BIT(7)
+
+#define AW99706_CHIPID_REG 0x11
+#define AW99706_LED_OPEN_FLAG_REG 0x12
+#define AW99706_LED_SHORT_FLAG_REG 0x13
+#define AW99706_MTPLDOSEL_REG 0x1E
+#define AW99706_MTPRUN_REG 0x1F
+
+#define RESV 0
+
+/* Boost switching frequency table, in Hz */
+static const u32 aw99706_sw_freq_tbl[] = {
+ RESV, RESV, RESV, RESV, 300000, 400000, 500000, 600000,
+ 660000, 750000, 850000, 1000000, 1200000, 1330000, 1500000, 1700000
+};
+
+/* Switching current limitation table, in uA */
+static const u32 aw99706_sw_ilmt_tbl[] = {
+ 1500000, 2000000, 2500000, 3000000
+};
+
+/* ULVO threshold table, in uV */
+static const u32 aw99706_ulvo_thres_tbl[] = {
+ 2200000, 5000000
+};
+
+struct aw99706_dt_prop {
+ const char * const name;
+ int (*lookup)(const struct aw99706_dt_prop *prop, u32 dt_val, u8 *val);
+ const u32 * const lookup_tbl;
+ u8 tbl_size;
+ u8 reg;
+ u8 mask;
+ u32 def_val;
+};
+
+static int aw99706_dt_property_lookup(const struct aw99706_dt_prop *prop,
+ u32 dt_val, u8 *val)
+{
+ int i;
+
+ if (!prop->lookup_tbl) {
+ *val = dt_val;
+ return 0;
+ }
+
+ for (i = 0; i < prop->tbl_size; i++)
+ if (prop->lookup_tbl[i] == dt_val)
+ break;
+
+ *val = i;
+
+ return i == prop->tbl_size ? -1 : 0;
+}
+
+#define MIN_ILED_MAX 5000
+#define MAX_ILED_MAX 50000
+#define STEP_ILED_MAX 500
+
+static int
+aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop *prop,
+ u32 dt_val, u8 *val)
+{
+ if (dt_val > MAX_ILED_MAX || dt_val < MIN_ILED_MAX)
+ return -1;
+
+ *val = (dt_val - MIN_ILED_MAX) / STEP_ILED_MAX;
+
+ return (dt_val - MIN_ILED_MAX) % STEP_ILED_MAX;
+}
+
+static const struct aw99706_dt_prop aw99706_dt_props[] = {
+ {
+ "awinic,dim-mode", aw99706_dt_property_lookup,
+ NULL, 0,
+ AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
+ },
+ {
+ "awinic,sw-freq", aw99706_dt_property_lookup,
+ aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
+ AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
+ },
+ {
+ "awinic,sw-ilmt", aw99706_dt_property_lookup,
+ aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
+ AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
+ },
+ {
+ "awinic,iled-max", aw99706_dt_property_iled_max_convert,
+ NULL, 0,
+ AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
+
+ },
+ {
+ "awinic,uvlo-thres", aw99706_dt_property_lookup,
+ aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
+ AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
+ },
+ {
+ "awinic,ramp-ctl", aw99706_dt_property_lookup,
+ NULL, 0,
+ AW99706_CFG6_REG, AW99706_RAMP_CTL_MASK, 2,
+ }
+};
+
+struct reg_init_data {
+ u8 reg;
+ u8 mask;
+ u8 val;
+};
+
+struct aw99706_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
+ struct backlight_device *bl_dev;
+ struct gpio_desc *hwen_gpio;
+ struct reg_init_data init_tbl[ARRAY_SIZE(aw99706_dt_props)];
+ bool bl_enable;
+};
+
+enum reg_access {
+ REG_NONE_ACCESS = 0,
+ REG_RD_ACCESS = 1,
+ REG_WR_ACCESS = 2,
+};
+
+static const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
+ [AW99706_CFG0_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG1_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG2_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG3_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG4_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG5_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG6_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG7_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG8_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFG9_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFGA_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFGB_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFGC_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_CFGD_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
+ [AW99706_FLAG_REG] = REG_RD_ACCESS,
+ [AW99706_CHIPID_REG] = REG_RD_ACCESS,
+ [AW99706_LED_OPEN_FLAG_REG] = REG_RD_ACCESS,
+ [AW99706_LED_SHORT_FLAG_REG] = REG_RD_ACCESS,
+
+ /*
+ * Write bit is dropped here, writing BIT(0) to MTPLDOSEL will unlock
+ * Multi-time Programmable (MTP).
+ */
+ [AW99706_MTPLDOSEL_REG] = REG_RD_ACCESS,
+ [AW99706_MTPRUN_REG] = REG_NONE_ACCESS,
+};
+
+static bool aw99706_readable_reg(struct device *dev, unsigned int reg)
+{
+ return aw99706_regs[reg] & REG_RD_ACCESS;
+}
+
+static bool aw99706_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return aw99706_regs[reg] & REG_WR_ACCESS;
+}
+
+static inline int aw99706_i2c_read(struct aw99706_device *aw, u8 reg,
+ unsigned int *val)
+{
+ return regmap_read(aw->regmap, reg, val);
+}
+
+static inline int aw99706_i2c_write(struct aw99706_device *aw, u8 reg, u8 val)
+{
+ return regmap_write(aw->regmap, reg, val);
+}
+
+static inline int aw99706_i2c_update_bits(struct aw99706_device *aw, u8 reg,
+ u8 mask, u8 val)
+{
+ return regmap_update_bits(aw->regmap, reg, mask, val);
+}
+
+static void aw99706_dt_parse(struct aw99706_device *aw,
+ struct backlight_properties *bl_props)
+{
+ const struct aw99706_dt_prop *prop;
+ u32 dt_val;
+ int ret, i;
+ u8 val;
+
+ for (i = 0; i < ARRAY_SIZE(aw99706_dt_props); i++) {
+ prop = &aw99706_dt_props[i];
+ ret = device_property_read_u32(aw->dev, prop->name, &dt_val);
+ if (ret < 0)
+ dt_val = prop->def_val;
+
+ if (prop->lookup(prop, dt_val, &val)) {
+ dev_warn(aw->dev, "invalid value %d for property %s, using default value %d\n",
+ dt_val, prop->name, prop->def_val);
+
+ prop->lookup(prop, prop->def_val, &val);
+ }
+
+ aw->init_tbl[i].reg = prop->reg;
+ aw->init_tbl[i].mask = prop->mask;
+ aw->init_tbl[i].val = val << __ffs(prop->mask);
+ }
+
+ bl_props->brightness = AW99706_MAX_BRT_LVL >> 1;
+ bl_props->max_brightness = AW99706_MAX_BRT_LVL;
+ device_property_read_u32(aw->dev, "default-brightness",
+ &bl_props->brightness);
+ device_property_read_u32(aw->dev, "max-brightness",
+ &bl_props->max_brightness);
+
+ if (bl_props->max_brightness > AW99706_MAX_BRT_LVL)
+ bl_props->max_brightness = AW99706_MAX_BRT_LVL;
+
+ if (bl_props->brightness > bl_props->max_brightness)
+ bl_props->brightness = bl_props->max_brightness;
+}
+
+static int aw99706_hw_init(struct aw99706_device *aw)
+{
+ int ret, i;
+
+ gpiod_set_value_cansleep(aw->hwen_gpio, 1);
+
+ for (i = 0; i < ARRAY_SIZE(aw->init_tbl); i++) {
+ ret = aw99706_i2c_update_bits(aw, aw->init_tbl[i].reg,
+ aw->init_tbl[i].mask,
+ aw->init_tbl[i].val);
+ if (ret < 0) {
+ dev_err(aw->dev, "Failed to write init data %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw99706_bl_enable(struct aw99706_device *aw, bool en)
+{
+ int ret;
+ u8 val;
+
+ val = FIELD_PREP(AW99706_BACKLIGHT_EN_MASK, en);
+ ret = aw99706_i2c_update_bits(aw, AW99706_CFGD_REG,
+ AW99706_BACKLIGHT_EN_MASK, val);
+ if (ret)
+ dev_err(aw->dev, "Failed to enable backlight!\n");
+
+ return ret;
+}
+
+static int aw99706_update_brightness(struct aw99706_device *aw, u32 brt_lvl)
+{
+ bool bl_enable_now = !!brt_lvl;
+ int ret;
+
+ ret = aw99706_i2c_write(aw, AW99706_CFG4_REG,
+ (brt_lvl >> 8) & AW99706_BRT_MSB_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = aw99706_i2c_write(aw, AW99706_CFG5_REG,
+ brt_lvl & AW99706_BRT_LSB_MASK);
+ if (ret < 0)
+ return ret;
+
+ if (aw->bl_enable != bl_enable_now) {
+ ret = aw99706_bl_enable(aw, bl_enable_now);
+ if (!ret)
+ aw->bl_enable = bl_enable_now;
+ }
+
+ return ret;
+}
+
+static int aw99706_bl_update_status(struct backlight_device *bl)
+{
+ struct aw99706_device *aw = bl_get_data(bl);
+
+ return aw99706_update_brightness(aw, bl->props.brightness);
+}
+
+static const struct backlight_ops aw99706_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = aw99706_bl_update_status,
+};
+
+static const struct regmap_config aw99706_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AW99706_REG_MAX,
+ .writeable_reg = aw99706_writeable_reg,
+ .readable_reg = aw99706_readable_reg,
+};
+
+static int aw99706_chip_id_read(struct aw99706_device *aw)
+{
+ int ret;
+ unsigned int val;
+
+ ret = aw99706_i2c_read(aw, AW99706_CHIPID_REG, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int aw99706_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct aw99706_device *aw;
+ struct backlight_device *bl_dev;
+ struct backlight_properties props = {};
+ int ret = 0;
+
+ aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
+ if (!aw)
+ return -ENOMEM;
+
+ aw->client = client;
+ aw->dev = dev;
+ i2c_set_clientdata(client, aw);
+
+ aw->regmap = devm_regmap_init_i2c(client, &aw99706_regmap_config);
+ if (IS_ERR(aw->regmap))
+ return dev_err_probe(dev, PTR_ERR(aw->regmap),
+ "Failed to init regmap\n");
+
+ ret = aw99706_chip_id_read(aw);
+ if (ret != AW99706_ID)
+ return dev_err_probe(dev, -ENODEV,
+ "Unknown chip id 0x%02x\n", ret);
+
+ aw99706_dt_parse(aw, &props);
+
+ aw->hwen_gpio = devm_gpiod_get(aw->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(aw->hwen_gpio))
+ return dev_err_probe(dev, PTR_ERR(aw->hwen_gpio),
+ "Failed to get enable gpio\n");
+
+ ret = aw99706_hw_init(aw);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to initialize the chip\n");
+
+ props.type = BACKLIGHT_RAW;
+ props.scale = BACKLIGHT_SCALE_LINEAR;
+
+ bl_dev = devm_backlight_device_register(dev, "aw99706-backlight", dev,
+ aw, &aw99706_bl_ops, &props);
+ if (IS_ERR(bl_dev))
+ return dev_err_probe(dev, PTR_ERR(bl_dev),
+ "Failed to register backlight!\n");
+
+ aw->bl_dev = bl_dev;
+
+ return 0;
+}
+
+static void aw99706_remove(struct i2c_client *client)
+{
+ struct aw99706_device *aw = i2c_get_clientdata(client);
+
+ aw99706_update_brightness(aw, 0);
+
+ msleep(50);
+
+ gpiod_set_value_cansleep(aw->hwen_gpio, 0);
+}
+
+static int aw99706_suspend(struct device *dev)
+{
+ struct aw99706_device *aw = dev_get_drvdata(dev);
+
+ return aw99706_update_brightness(aw, 0);
+}
+
+static int aw99706_resume(struct device *dev)
+{
+ struct aw99706_device *aw = dev_get_drvdata(dev);
+
+ return aw99706_hw_init(aw);
+}
+
+static SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume);
+
+static const struct i2c_device_id aw99706_ids[] = {
+ { "aw99706" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw99706_ids);
+
+static const struct of_device_id aw99706_match_table[] = {
+ { .compatible = "awinic,aw99706", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw99706_match_table);
+
+static struct i2c_driver aw99706_i2c_driver = {
+ .probe = aw99706_probe,
+ .remove = aw99706_remove,
+ .id_table = aw99706_ids,
+ .driver = {
+ .name = "aw99706",
+ .of_match_table = aw99706_match_table,
+ .pm = &aw99706_pm_ops,
+ },
+};
+
+module_i2c_driver(aw99706_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BackLight driver for aw99706");
--
2.51.1.dirty
^ permalink raw reply related
* Re: [PATCH v3 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Krzysztof Kozlowski @ 2025-11-09 17:08 UTC (permalink / raw)
To: Junjie Cao
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, dri-devel,
linux-leds, devicetree, linux-kernel, linux-fbdev
In-Reply-To: <20251109032240.3422503-2-caojunjie650@gmail.com>
On Sun, Nov 09, 2025 at 11:22:39AM +0800, Junjie Cao wrote:
> Add Awinic AW99706 backlight binding documentation.
>
> Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
> ---
> Changes in v3:
> - breaking a long line (Krzysztof)
> - rename backlight node (Krzysztof)
> - Link to v2: https://lore.kernel.org/linux-leds/20251103110648.878325-2-caojunjie650@gmail.com
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: backlight: Add max25014 support
From: Maud Spierings @ 2025-11-10 7:55 UTC (permalink / raw)
To: Conor Dooley
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <20251107-qualified-varsity-78953d06d7c6@spud>
On 11/7/25 19:16, Conor Dooley wrote:
> On Fri, Nov 07, 2025 at 01:49:58PM +0100, Maud Spierings via B4 Relay wrote:
>> From: Maud Spierings <maudspierings@gocontroll.com>
>>
>> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
>> with integrated boost controller.
>>
>> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>>
>> ---
>>
>> In the current implementation the control registers for channel 1,
>> control all channels. So only one led subnode with led-sources is
>> supported right now. If at some point the driver functionality is
>> expanded the bindings can be easily extended with it.
>
> I'm sorry if I asked this before and forgot or w/e, but how backwards
> compatible is this? If they control all channels and it gets changed to
> only control channel one, how will a changed kernel understand the
> difference between a new devicetree that only wants to control channel 1
> and an old devicetree that is trying to use channel 1 to control all
> channels?
>
The idea is that an implementation like that will add multiple led@
subnodes without the led-sources property.
So devicetrees controlled by one channel will still have only one led
with multiple sources in the devicetree.
Kind regards,
Maud
>
>> ---
>> .../bindings/leds/backlight/maxim,max25014.yaml | 107 +++++++++++++++++++++
>> MAINTAINERS | 5 +
>> 2 files changed, 112 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> new file mode 100644
>> index 000000000000..e83723224b07
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> @@ -0,0 +1,107 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Maxim max25014 backlight controller
>> +
>> +maintainers:
>> + - Maud Spierings <maudspierings@gocontroll.com>
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - maxim,max25014
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + "#address-cells":
>> + const: 1
>> +
>> + "#size-cells":
>> + const: 0
>> +
>> + enable-gpios:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + power-supply:
>> + description: Regulator which controls the boost converter input rail.
>> +
>> + pwms:
>> + maxItems: 1
>> +
>> + maxim,iset:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + maximum: 15
>> + default: 11
>> + description:
>> + Value of the ISET field in the ISET register. This controls the current
>> + scale of the outputs, a higher number means more current.
>> +
>> + led@0:
>> + type: object
>> + description: Properties for a string of connected LEDs.
>> + $ref: common.yaml#
>> +
>> + properties:
>> + reg:
>> + const: 0
>> +
>> + led-sources:
>> + allOf:
>> + - minItems: 1
>> + maxItems: 4
>> + items:
>> + minimum: 0
>> + maximum: 3
>> + default: [0, 1, 2, 3]
>> +
>> + default-brightness:
>> + minimum: 0
>> + maximum: 100
>> + default: 50
>> +
>> + required:
>> + - reg
>> +
>> + additionalProperties: false
>> +
>> +required:
>> + - compatible
>> + - reg
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> +
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + backlight@6f {
>> + compatible = "maxim,max25014";
>> + reg = <0x6f>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
>> + interrupt-parent = <&gpio1>;
>> + interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
>> + power-supply = <®_backlight>;
>> + pwms = <&pwm1>;
>> + maxim,iset = <7>;
>> +
>> + led@0 {
>> + reg = <0>;
>> + led-sources = <0 1 2 3>;
>> + default-brightness = <50>;
>> + };
>> + };
>> + };
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 58c7e3f678d8..606ce086f758 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -15261,6 +15261,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
>> F: drivers/media/i2c/max2175*
>> F: include/uapi/linux/max2175.h
>>
>> +MAX25014 BACKLIGHT DRIVER
>> +M: Maud Spierings <maudspierings@gocontroll.com>
>> +S: Maintained
>> +F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> +
>> MAX31335 RTC DRIVER
>> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
>> L: linux-rtc@vger.kernel.org
>>
>> --
>> 2.51.2
>>
>>
^ permalink raw reply
* Re: [PATCH v5 1/4] dt-bindings: backlight: Add max25014 supporty
From: Maud Spierings @ 2025-11-10 7:59 UTC (permalink / raw)
To: Frank Li
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4RqNiGsngOWrV5@lizhi-Precision-Tower-5810>
On 11/7/25 16:35, Frank Li wrote:
> On Fri, Nov 07, 2025 at 01:49:58PM +0100, Maud Spierings via B4 Relay wrote:
>> From: Maud Spierings <maudspierings@gocontroll.com>
>>
>> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
>> with integrated boost controller.
>>
>> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>>
>> ---
>>
>> In the current implementation the control registers for channel 1,
>> control all channels. So only one led subnode with led-sources is
>> supported right now. If at some point the driver functionality is
>> expanded the bindings can be easily extended with it.
>> ---
>> .../bindings/leds/backlight/maxim,max25014.yaml | 107 +++++++++++++++++++++
>> MAINTAINERS | 5 +
>> 2 files changed, 112 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> new file mode 100644
>> index 000000000000..e83723224b07
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> @@ -0,0 +1,107 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Maxim max25014 backlight controller
>> +
>> +maintainers:
>> + - Maud Spierings <maudspierings@gocontroll.com>
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - maxim,max25014
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + "#address-cells":
>> + const: 1
>> +
>> + "#size-cells":
>> + const: 0
>> +
>> + enable-gpios:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + power-supply:
>> + description: Regulator which controls the boost converter input rail.
>> +
>> + pwms:
>> + maxItems: 1
>> +
>> + maxim,iset:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + maximum: 15
>> + default: 11
>> + description:
>> + Value of the ISET field in the ISET register. This controls the current
>> + scale of the outputs, a higher number means more current.
>> +
>> + led@0:
>> + type: object
>> + description: Properties for a string of connected LEDs.
>> + $ref: common.yaml#
>> +
>> + properties:
>> + reg:
>> + const: 0
>
> If reg is const 0, why need use led@0?
>
I made it this way so that at a later point the driver can be expanded
with 4 led subnodes when that functionality gets supported. If I were to
lock it to just led: right now I feel that may cause incompatibility
later on.
If there is a better way to do this, I'm open to suggestions.
Kind regards,
Maud
>
>> +
>> + led-sources:
>> + allOf:
>> + - minItems: 1
>> + maxItems: 4
>> + items:
>> + minimum: 0
>> + maximum: 3
>> + default: [0, 1, 2, 3]
>> +
>> + default-brightness:
>> + minimum: 0
>> + maximum: 100
>> + default: 50
>> +
>> + required:
>> + - reg
>> +
>> + additionalProperties: false
>> +
>> +required:
>> + - compatible
>> + - reg
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> +
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + backlight@6f {
>> + compatible = "maxim,max25014";
>> + reg = <0x6f>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
>> + interrupt-parent = <&gpio1>;
>> + interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
>> + power-supply = <®_backlight>;
>> + pwms = <&pwm1>;
>> + maxim,iset = <7>;
>> +
>> + led@0 {
>> + reg = <0>;
>> + led-sources = <0 1 2 3>;
>> + default-brightness = <50>;
>> + };
>> + };
>> + };
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 58c7e3f678d8..606ce086f758 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -15261,6 +15261,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
>> F: drivers/media/i2c/max2175*
>> F: include/uapi/linux/max2175.h
>>
>> +MAX25014 BACKLIGHT DRIVER
>> +M: Maud Spierings <maudspierings@gocontroll.com>
>> +S: Maintained
>> +F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> +
>> MAX31335 RTC DRIVER
>> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
>> L: linux-rtc@vger.kernel.org
>>
>> --
>> 2.51.2
>>
>>
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlighty
From: Maud Spierings @ 2025-11-10 8:25 UTC (permalink / raw)
To: Frank Li
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4Vb4eUmSX0Nj6+@lizhi-Precision-Tower-5810>
On 11/7/25 16:51, Frank Li wrote:
> On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
>> From: Maud Spierings <maudspierings@gocontroll.com>
>>
>> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
>> with integrated boost controller.
>>
>> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>> ---
>> MAINTAINERS | 1 +
>> drivers/video/backlight/Kconfig | 7 +
>> drivers/video/backlight/Makefile | 1 +
>> drivers/video/backlight/max25014.c | 409 +++++++++++++++++++++++++++++++++++++
>> 4 files changed, 418 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 606ce086f758..d082d3f8cfae 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -15265,6 +15265,7 @@ MAX25014 BACKLIGHT DRIVER
>> M: Maud Spierings <maudspierings@gocontroll.com>
>> S: Maintained
>> F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml
>> +F: drivers/video/backlight/max25014.c
>>
>> MAX31335 RTC DRIVER
>> M: Antoniu Miclaus <antoniu.miclaus@analog.com>
>> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
>> index d9374d208cee..d3bb6ccd4185 100644
>> --- a/drivers/video/backlight/Kconfig
>> +++ b/drivers/video/backlight/Kconfig
>> @@ -262,6 +262,13 @@ config BACKLIGHT_DA9052
>> help
>> Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs.
>>
>> +config BACKLIGHT_MAX25014
>> + tristate "Backlight driver for the Maxim MAX25014 chip"
>> + depends on I2C
>> + select REGMAP_I2C
>> + help
>> + If you are using a MAX25014 chip as a backlight driver say Y to enable it.
>> +
>> config BACKLIGHT_MAX8925
>> tristate "Backlight driver for MAX8925"
>> depends on MFD_MAX8925
>> diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
>> index dfbb169bf6ea..1170d9ec40b8 100644
>> --- a/drivers/video/backlight/Makefile
>> +++ b/drivers/video/backlight/Makefile
>> @@ -45,6 +45,7 @@ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
>> obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
>> obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
>> obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
>> +obj-$(CONFIG_BACKLIGHT_MAX25014) += max25014.o
>> obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
>> obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o
>> obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o
>> diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c
>> new file mode 100644
>> index 000000000000..36bae508697e
>> --- /dev/null
> ...
>> +
>> +struct max25014 {
>> + struct i2c_client *client;
>> + struct backlight_device *bl;
>> + struct regmap *regmap;
>> + struct gpio_desc *enable;
>> + struct regulator *vin; /* regulator for boost converter Vin rail */
>> + uint32_t iset;
>> + uint8_t strings_mask;
>> +};
>> +
>> +static const struct regmap_config max25014_regmap_config = {
>> + .reg_bits = 8,
>> + .val_bits = 8,
>> + .max_register = MAX25014_DIAG,
>> +};
>> +
>> +/**
>> + * @brief control the brightness with i2c registers
>> + *
>> + * @param regmap trivial
>> + * @param brt brightness
>> + * @return int
>> + */
>> +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
>> +{
>> + uint32_t reg = TON_STEP * brt;
>> + int ret;
>> + /*
>> + * 18 bit number lowest, 2 bits in first register,
>> + * next lowest 8 in the L register, next 8 in the H register
>> + * Seemingly setting the strength of only one string controls all of
>> + * them, individual settings don't affect the outcome.
>> + */
>> +
>> + ret = regmap_write(regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011);
>> + if (ret != 0)
>
> if (ret), check others regmap_*()
>
>> + return ret;
>> + ret = regmap_write(regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111);
>> + if (ret != 0)
>> + return ret;
>> + return regmap_write(regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111);
>> +}
>> +
>> +static int max25014_check_errors(struct max25014 *maxim)
>> +{
>> + uint8_t i;
>> + int ret;
>> + uint32_t val;
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>
> uint32 always >= 0
>
> So
> if (val)
>
>> + dev_err(&maxim->client->dev, "Open led strings detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>> + dev_err(&maxim->client->dev, "Short to ground detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>> + dev_err(&maxim->client->dev, "Shorted led detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val);
>> + if (ret != 0)
>> + return ret;
>> + /*
>> + * The HW_RST bit always starts at 1 after power up.
>> + * It is reset on first read, does not indicate an error.
>> + */
>> + if (val > 0 && val != MAX25014_DIAG_HW_RST) {
>> + if (val & 0b1)
>
> BIT(0)
>
>> + dev_err(&maxim->client->dev,
>> + "Overtemperature shutdown\n");
>> + if (val & 0b10)
>> + dev_err(&maxim->client->dev,
>> + "Chip is getting too hot (>125C)\n");
>> + if (val & 0b1000)
>> + dev_err(&maxim->client->dev,
>> + "Boost converter overvoltage\n");
>> + if (val & 0b10000)
>> + dev_err(&maxim->client->dev,
>> + "Boost converter undervoltage\n");
>> + if (val & 0b100000)
>> + dev_err(&maxim->client->dev, "IREF out of range\n");
>> + return -EIO;
>> + }
>> + return 0;
>> +}
>> +
> ...
>> +static int max25014_parse_dt(struct max25014 *maxim,
>> + uint32_t *initial_brightness)
>> +{
>> + struct device *dev = &maxim->client->dev;
>> + struct device_node *node = dev->of_node;
>> + struct fwnode_handle *child;
>> + uint32_t strings[4];
>> + int res, i;
>> +
>> + if (!node) {
>> + dev_err(dev, "no platform data\n");
>> + return -EINVAL;
>> + }
>
> call from probe, check other place
>
> return dev_err_probe()
>
>> +
>> + child = device_get_next_child_node(dev, NULL);
>> + if (child) {
>> + res = fwnode_property_count_u32(child, "led-sources");
>> + if (res > 0) {
>> + fwnode_property_read_u32_array(child, "led-sources",
>> + strings, res);
>> +
>> + /* set all strings as disabled, then enable those in led-sources*/
>> + maxim->strings_mask = 0xf;
>> + for (i = 0; i < res; i++) {
>> + if (strings[i] <= 4)
>> + maxim->strings_mask &= ~BIT(strings[i]);
>> + }
>> + }
>> +
>> + fwnode_property_read_u32(child, "default-brightness",
>> + initial_brightness);
>> +
>> + fwnode_handle_put(child);
>> + }
>> +
>> + maxim->iset = MAX25014_ISET_DEFAULT_100;
>> + of_property_read_u32(node, "maxim,iset", &maxim->iset);
>> +
>> + if (maxim->iset < 0 || maxim->iset > 15) {
>> + dev_err(dev,
>> + "Invalid iset, should be a value from 0-15, entered was %d\n",
>> + maxim->iset);
>> + return -EINVAL;
>> + }
>> +
>> + if (*initial_brightness < 0 || *initial_brightness > 100) {
>> + dev_err(dev,
>> + "Invalid initial brightness, should be a value from 0-100, entered was %d\n",
>> + *initial_brightness);
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int max25014_probe(struct i2c_client *cl)
>> +{
>> + struct backlight_device *bl;
>> + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
>> + struct max25014 *maxim;
>> + struct backlight_properties props;
>> + int ret;
>> + uint32_t initial_brightness = 50;
>
> try keep reverise christmas order
>
>> +
>> + maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL);
>> + if (!maxim)
>> + return -ENOMEM;
>> +
>> + maxim->client = cl;
>> +
>> + ret = max25014_parse_dt(maxim, &initial_brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + maxim->vin = devm_regulator_get_optional(&maxim->client->dev, "power");
>> + if (IS_ERR(maxim->vin)) {
>> + if (PTR_ERR(maxim->vin) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + maxim->vin = NULL;
>> + }
>> +
>> + if (maxim->vin) {
>> + ret = regulator_enable(maxim->vin);
>> + if (ret < 0) {
>> + dev_err(&maxim->client->dev,
>> + "failed to enable Vin: %d\n", ret);
>> + return ret;
>> + }
>> + }
>
> use devm_regulator_get_enable_optional() to combine devm_regulator_get_optional()
> and regulator_enable() to one call.
This however does not return the regulator and will not allow further
potential power management, I did look into using that one but decided
against it. There is no runtime PM implemented right now so it wouldn't
really matter at this point. If it is desirable I will switch it, and it
will have to be switched back when PM gets implemented.
>> +
>> + maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable",
>> + GPIOD_ASIS);
>> + if (IS_ERR(maxim->enable)) {
>> + ret = PTR_ERR(maxim->enable);
>> + dev_err(&maxim->client->dev, "failed to get enable gpio: %d\n",
>> + ret);
>> + goto disable_vin;
>> + }
>> +
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 1);
>
> gpiod_set_value_cansleep() tolerate NULL, so needn't if check here
>
> and if you pass GPIOD_OUT_HIGH at devm_gpiod_get_optional(), needn't call
> this function.
got it, changed.
>> +
>> + /* Enable can be tied to vin rail wait if either is available */
>> + if (maxim->enable || maxim->vin) {
>> + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
>> + usleep_range(2000, 2500);
>
> now perfer use fsleep()
Ah didn't know of that, thanks!
Other small remarks have also been resolved.
Kind regards,
Maud
>> + }
>> +
>> + maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config);
>> + if (IS_ERR(maxim->regmap)) {
>> + ret = PTR_ERR(maxim->regmap);
>> + dev_err(&maxim->client->dev,
>> + "failed to initialize the i2c regmap: %d\n", ret);
>> + goto disable_full;
>> + }
>> +
>> + i2c_set_clientdata(cl, maxim);
>> +
>> + ret = max25014_check_errors(maxim);
>> + if (ret) { /* error is already reported in the above function */
>> + goto disable_full;
>> + }
>> +
>> + ret = max25014_configure(maxim);
>> + if (ret) {
>> + dev_err(&maxim->client->dev, "device config err: %d", ret);
>> + goto disable_full;
>> + }
>> +
>> + memset(&props, 0, sizeof(props));
>> + props.type = BACKLIGHT_PLATFORM;
>> + props.max_brightness = MAX_BRIGHTNESS;
>> + props.brightness = initial_brightness;
>> + props.scale = BACKLIGHT_SCALE_LINEAR;
>> +
>> + bl = devm_backlight_device_register(&maxim->client->dev, id->name,
>> + &maxim->client->dev, maxim,
>> + &max25014_bl_ops, &props);
>> + if (IS_ERR(bl))
>> + return PTR_ERR(bl);
>> +
>> + maxim->bl = bl;
>> +
>> + return 0;
>> +
>> +disable_full:
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 0);
>> +disable_vin:
>> + if (maxim->vin)
>> + regulator_disable(maxim->vin);
>> + return ret;
>> +}
>> +
>> +static void max25014_remove(struct i2c_client *cl)
>> +{
>> + struct max25014 *maxim = i2c_get_clientdata(cl);
>> +
>> + maxim->bl->props.brightness = 0;
>> + max25014_update_status(maxim->bl);
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 0);
>> + if (maxim->vin)
>> + regulator_disable(maxim->vin);
>> +}
>> +
>> +static const struct of_device_id max25014_dt_ids[] = {
>> + { .compatible = "maxim,max25014", },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, max25014_dt_ids);
>> +
>> +static const struct i2c_device_id max25014_ids[] = {
>> + { "max25014" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, max25014_ids);
>> +
>> +static struct i2c_driver max25014_driver = {
>> + .driver = {
>> + .name = KBUILD_MODNAME,
>> + .of_match_table = of_match_ptr(max25014_dt_ids),
>> + },
>> + .probe = max25014_probe,
>> + .remove = max25014_remove,
>> + .id_table = max25014_ids,
>> +};
>> +module_i2c_driver(max25014_driver);
>> +
>> +MODULE_DESCRIPTION("Maxim MAX25014 backlight driver");
>> +MODULE_AUTHOR("Maud Spierings <maudspierings@gocontroll.com>");
>> +MODULE_LICENSE("GPL");
>>
>> --
>> 2.51.2
>>
>>
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlight
From: Maud Spierings @ 2025-11-10 8:40 UTC (permalink / raw)
To: Daniel Thompson
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aQ4a2SBDldYgQb56@aspen.lan>
On 11/7/25 17:14, Daniel Thompson wrote:
> On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
>> From: Maud Spierings <maudspierings@gocontroll.com>
>>
>> The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC
>> with integrated boost controller.
>>
>> Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
>> diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c
>> new file mode 100644
>> index 000000000000..36bae508697e
>> --- /dev/null
>> +++ b/drivers/video/backlight/max25014.c
>> @@ -0,0 +1,409 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Backlight driver for Maxim MAX25014
>> + *
>> + * Copyright (C) 2025 GOcontroll B.V.
>> + * Author: Maud Spierings <maudspierings@gocontroll.com>
>> + */
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#define MAX25014_ISET_DEFAULT_100 11
>> +#define MAX_BRIGHTNESS 100
>> +#define MIN_BRIGHTNESS 0
>> +#define TON_MAX 130720 /* @153Hz */
>> +#define TON_STEP 1307 /* @153Hz */
>> +#define TON_MIN 0
>> +
>> +#define MAX25014_DEV_ID 0x00
>> +#define MAX25014_REV_ID 0x01
>> +#define MAX25014_ISET 0x02
>> +#define MAX25014_IMODE 0x03
>> +#define MAX25014_TON1H 0x04
>> +#define MAX25014_TON1L 0x05
>> +#define MAX25014_TON2H 0x06
>> +#define MAX25014_TON2L 0x07
>> +#define MAX25014_TON3H 0x08
>> +#define MAX25014_TON3L 0x09
>> +#define MAX25014_TON4H 0x0A
>> +#define MAX25014_TON4L 0x0B
>> +#define MAX25014_TON_1_4_LSB 0x0C
>> +#define MAX25014_SETTING 0x12
>> +#define MAX25014_DISABLE 0x13
>> +#define MAX25014_BSTMON 0x14
>> +#define MAX25014_IOUT1 0x15
>> +#define MAX25014_IOUT2 0x16
>> +#define MAX25014_IOUT3 0x17
>> +#define MAX25014_IOUT4 0x18
>> +#define MAX25014_OPEN 0x1B
>> +#define MAX25014_SHORT_GND 0x1C
>> +#define MAX25014_SHORT_LED 0x1D
>> +#define MAX25014_MASK 0x1E
>> +#define MAX25014_DIAG 0x1F
>> +
>> +#define MAX25014_IMODE_HDIM BIT(2)
>> +#define MAX25014_ISET_ENABLE BIT(5)
>> +#define MAX25014_ISET_PSEN BIT(4)
>> +#define MAX25014_DIAG_HW_RST BIT(2)
>> +#define MAX25014_SETTING_FPWM GENMASK(6, 4)
>> +
>> +struct max25014 {
>> + struct i2c_client *client;
>> + struct backlight_device *bl;
>> + struct regmap *regmap;
>> + struct gpio_desc *enable;
>> + struct regulator *vin; /* regulator for boost converter Vin rail */
>> + uint32_t iset;
>> + uint8_t strings_mask;
>> +};
>> +
>> +static const struct regmap_config max25014_regmap_config = {
>> + .reg_bits = 8,
>> + .val_bits = 8,
>> + .max_register = MAX25014_DIAG,
>> +};
>> +
>> +/**
>> + * @brief control the brightness with i2c registers
>> + *
>> + * @param regmap trivial
>> + * @param brt brightness
>> + * @return int
>> + */
>> +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
>
> This isn't a good name for a function. It doesn't really say what it
> does. Please find a more descriptive name.
Having a lot of difficulties find a succinct name that fits better,
max25014_register_brightness_control()?
max25014_i2c_brightness_control()?
>
>> +{
>> + uint32_t reg = TON_STEP * brt;
>> + int ret;
>> + /*
>> + * 18 bit number lowest, 2 bits in first register,
>> + * next lowest 8 in the L register, next 8 in the H register
>> + * Seemingly setting the strength of only one string controls all of
>> + * them, individual settings don't affect the outcome.
>> + */
>> +
>> + ret = regmap_write(regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011);
>> + if (ret != 0)
>> + return ret;
>> + ret = regmap_write(regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111);
>> + if (ret != 0)
>> + return ret;
>> + return regmap_write(regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111);
>> +}
>> +
>> +static int max25014_check_errors(struct max25014 *maxim)
>> +{
>> + uint8_t i;
>> + int ret;
>> + uint32_t val;
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>> + dev_err(&maxim->client->dev, "Open led strings detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>> + dev_err(&maxim->client->dev, "Short to ground detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_SHORT_GND, &val);
>> + if (ret != 0)
>> + return ret;
>> + if (val > 0) {
>> + dev_err(&maxim->client->dev, "Shorted led detected on:\n");
>> + for (i = 0; i < 4; i++) {
>> + if (val & 1 << i)
>> + dev_err(&maxim->client->dev, "string %d\n", i + 1);
>> + }
>> + return -EIO;
>> + }
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val);
>> + if (ret != 0)
>> + return ret;
>> + /*
>> + * The HW_RST bit always starts at 1 after power up.
>> + * It is reset on first read, does not indicate an error.
>> + */
>> + if (val > 0 && val != MAX25014_DIAG_HW_RST) {
>> + if (val & 0b1)
>> + dev_err(&maxim->client->dev,
>> + "Overtemperature shutdown\n");
>> + if (val & 0b10)
>> + dev_err(&maxim->client->dev,
>> + "Chip is getting too hot (>125C)\n");
>> + if (val & 0b1000)
>> + dev_err(&maxim->client->dev,
>> + "Boost converter overvoltage\n");
>> + if (val & 0b10000)
>> + dev_err(&maxim->client->dev,
>> + "Boost converter undervoltage\n");
>> + if (val & 0b100000)
>> + dev_err(&maxim->client->dev, "IREF out of range\n");
>> + return -EIO;
>> + }
>> + return 0;
>> +}
>> +
>> +/*
>> + * 1. disable unused strings
>> + * 2. set dim mode
>> + * 3. set initial brightness
>
> How does this code set the initial brightness? It doens't set the
> MAX25014_TON* registers.
Yep forgot to remove that, I discovered the backlight core takes care of
the default brightness, so I removed it from here.
>
>> + * 4. set setting register
>> + * 5. enable the backlight
>> + */
>> +static int max25014_configure(struct max25014 *maxim)
>> +{
>> + int ret;
>> + uint32_t val;
>> +
>> + ret = regmap_write(maxim->regmap, MAX25014_DISABLE,
>> + maxim->strings_mask);
>> + if (ret != 0)
>> + return ret;
>> +
>> + ret = regmap_write(maxim->regmap, MAX25014_IMODE, MAX25014_IMODE_HDIM);
>> + if (ret != 0)
>> + return ret;
>> +
>> + ret = regmap_read(maxim->regmap, MAX25014_SETTING, &val);
>> + if (ret != 0)
>> + return ret;
>> +
>> + ret = regmap_write(maxim->regmap, MAX25014_SETTING,
>> + val & ~MAX25014_SETTING_FPWM);
>> + if (ret != 0)
>> + return ret;
>> +
>> + ret = regmap_write(maxim->regmap, MAX25014_ISET,
>> + maxim->iset | MAX25014_ISET_ENABLE |
>> + MAX25014_ISET_PSEN);
>> + return ret;
>> +}
>> +
>> +static int max25014_update_status(struct backlight_device *bl_dev)
>> +{
>> + struct max25014 *maxim = bl_get_data(bl_dev);
>> +
>> + if (backlight_is_blank(maxim->bl))
>> + bl_dev->props.brightness = 0;
>> +
>> + return max25014_register_control(maxim->regmap,
>> + bl_dev->props.brightness);
>> +}
>> +
>> +static const struct backlight_ops max25014_bl_ops = {
>> + .options = BL_CORE_SUSPENDRESUME,
>> + .update_status = max25014_update_status,
>> +};
>> +
>> +static int max25014_parse_dt(struct max25014 *maxim,
>> + uint32_t *initial_brightness)
>> +{
>> + struct device *dev = &maxim->client->dev;
>> + struct device_node *node = dev->of_node;
>> + struct fwnode_handle *child;
>> + uint32_t strings[4];
>> + int res, i;
>> +
>> + if (!node) {
>> + dev_err(dev, "no platform data\n");
>> + return -EINVAL;
>> + }
>> +
>> + child = device_get_next_child_node(dev, NULL);
>> + if (child) {
>> + res = fwnode_property_count_u32(child, "led-sources");
>> + if (res > 0) {
>> + fwnode_property_read_u32_array(child, "led-sources",
>> + strings, res);
>> +
>> + /* set all strings as disabled, then enable those in led-sources*/
>> + maxim->strings_mask = 0xf;
>> + for (i = 0; i < res; i++) {
>> + if (strings[i] <= 4)
>> + maxim->strings_mask &= ~BIT(strings[i]);
>> + }
>> + }
>> +
>> + fwnode_property_read_u32(child, "default-brightness",
>> + initial_brightness);
>> +
>> + fwnode_handle_put(child);
>> + }
>> +
>> + maxim->iset = MAX25014_ISET_DEFAULT_100;
>> + of_property_read_u32(node, "maxim,iset", &maxim->iset);
>> +
>> + if (maxim->iset < 0 || maxim->iset > 15) {
>> + dev_err(dev,
>> + "Invalid iset, should be a value from 0-15, entered was %d\n",
>> + maxim->iset);
>> + return -EINVAL;
>> + }
>> +
>> + if (*initial_brightness < 0 || *initial_brightness > 100) {
>> + dev_err(dev,
>> + "Invalid initial brightness, should be a value from 0-100, entered was %d\n",
>> + *initial_brightness);
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int max25014_probe(struct i2c_client *cl)
>> +{
>> + struct backlight_device *bl;
>> + const struct i2c_device_id *id = i2c_client_get_device_id(cl);
>> + struct max25014 *maxim;
>> + struct backlight_properties props;
>> + int ret;
>> + uint32_t initial_brightness = 50;
>> +
>> + maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL);
>> + if (!maxim)
>> + return -ENOMEM;
>> +
>> + maxim->client = cl;
>> +
>> + ret = max25014_parse_dt(maxim, &initial_brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + maxim->vin = devm_regulator_get_optional(&maxim->client->dev, "power");
>
> I would have expected to see devm_regulator_get() here. Why do you care
> whether you get a real regulator or a dummy if you just NULL check
> maxim->vin everywhere?
>
>
>> + if (IS_ERR(maxim->vin)) {
>> + if (PTR_ERR(maxim->vin) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + maxim->vin = NULL;
>> + }
>> +
>> + if (maxim->vin) {
>
> If you had called devm_regulator_get() there would be no need for a NULL
> check here.
>
>
>> + ret = regulator_enable(maxim->vin);
>> + if (ret < 0) {
>> + dev_err(&maxim->client->dev,
>> + "failed to enable Vin: %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable",
>> + GPIOD_ASIS);
>> + if (IS_ERR(maxim->enable)) {
>> + ret = PTR_ERR(maxim->enable);
>> + dev_err(&maxim->client->dev, "failed to get enable gpio: %d\n",
>> + ret);
>> + goto disable_vin;
>> + }
>> +
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 1);
>
> No need for NULL pointer check here (see
> https://elixir.bootlin.com/linux/v6.18-rc4/source/drivers/gpio/gpiolib.c#L358-L363 ).
>
>
>> +
>> + /* Enable can be tied to vin rail wait if either is available */
>> + if (maxim->enable || maxim->vin) {
>> + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
>> + usleep_range(2000, 2500);
>> + }
>
> If you really want to keep the devm_regulator_get_optional() I guess
> maybe you could persuade me it's need to avoid this sleep... although
> I'd be fairly happy to remove the NULL checks here too!
Just wait unconditionally?
kind regards,
Maud
>
>> +
>> + maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config);
>> + if (IS_ERR(maxim->regmap)) {
>> + ret = PTR_ERR(maxim->regmap);
>> + dev_err(&maxim->client->dev,
>> + "failed to initialize the i2c regmap: %d\n", ret);
>> + goto disable_full;
>> + }
>> +
>> + i2c_set_clientdata(cl, maxim);
>> +
>> + ret = max25014_check_errors(maxim);
>> + if (ret) { /* error is already reported in the above function */
>> + goto disable_full;
>> + }
>> +
>> + ret = max25014_configure(maxim);
>> + if (ret) {
>> + dev_err(&maxim->client->dev, "device config err: %d", ret);
>> + goto disable_full;
>> + }
>> +
>> + memset(&props, 0, sizeof(props));
>> + props.type = BACKLIGHT_PLATFORM;
>> + props.max_brightness = MAX_BRIGHTNESS;
>> + props.brightness = initial_brightness;
>> + props.scale = BACKLIGHT_SCALE_LINEAR;
>> +
>> + bl = devm_backlight_device_register(&maxim->client->dev, id->name,
>> + &maxim->client->dev, maxim,
>> + &max25014_bl_ops, &props);
>> + if (IS_ERR(bl))
>> + return PTR_ERR(bl);
>> +
>> + maxim->bl = bl;
>> +
>> + return 0;
>> +
>> +disable_full:
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 0);
>
> Again, NULL check isn't needed.
>
>> +disable_vin:
>> + if (maxim->vin)
>> + regulator_disable(maxim->vin);
>> + return ret;
>> +}
>> +
>> +static void max25014_remove(struct i2c_client *cl)
>> +{
>> + struct max25014 *maxim = i2c_get_clientdata(cl);
>> +
>> + maxim->bl->props.brightness = 0;
>> + max25014_update_status(maxim->bl);
>> + if (maxim->enable)
>> + gpiod_set_value_cansleep(maxim->enable, 0);
>
> Lose the NULL check.
>
>> + if (maxim->vin)
>> + regulator_disable(maxim->vin);
>> +}
>> +
>> +static const struct of_device_id max25014_dt_ids[] = {
>> + { .compatible = "maxim,max25014", },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, max25014_dt_ids);
>> +
>> +static const struct i2c_device_id max25014_ids[] = {
>> + { "max25014" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, max25014_ids);
>> +
>> +static struct i2c_driver max25014_driver = {
>> + .driver = {
>> + .name = KBUILD_MODNAME,
>> + .of_match_table = of_match_ptr(max25014_dt_ids),
>> + },
>> + .probe = max25014_probe,
>> + .remove = max25014_remove,
>> + .id_table = max25014_ids,
>> +};
>> +module_i2c_driver(max25014_driver);
>> +
>> +MODULE_DESCRIPTION("Maxim MAX25014 backlight driver");
>> +MODULE_AUTHOR("Maud Spierings <maudspierings@gocontroll.com>");
>> +MODULE_LICENSE("GPL");
>
>
> Daniel.
^ permalink raw reply
* [PATCH] fbdev/vga16fb: Use dev_* fn's instead printk.
From: Javier Garcia @ 2025-11-10 9:04 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, shuah, Javier Garcia
- Family dev_* fn's will show device name, giving extra info to logs.
- Delete the prefix `vga16fb:` from msg strings, not needed now.
[ 1.037947] vga16fb vga-framebuffer.0: initializing
Signed-off-by: Javier Garcia <rampxxxx@gmail.com>
---
drivers/video/fbdev/vga16fb.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c
index 3b4c50d98ba6..432ba7c8164b 100644
--- a/drivers/video/fbdev/vga16fb.c
+++ b/drivers/video/fbdev/vga16fb.c
@@ -1324,7 +1324,7 @@ static int vga16fb_probe(struct platform_device *dev)
dev_err(&dev->dev, "vga16b: cannot reserve video memory at 0x%lx\n",
vga16fb_fix.smem_start);
}
- printk(KERN_DEBUG "vga16fb: initializing\n");
+ dev_dbg(&dev->dev, "initializing\n");
info = framebuffer_alloc(sizeof(struct vga16fb_par), &dev->dev);
if (!info) {
@@ -1336,12 +1336,12 @@ static int vga16fb_probe(struct platform_device *dev)
info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS_BASE, 0);
if (!info->screen_base) {
- printk(KERN_ERR "vga16fb: unable to map device\n");
+ dev_err(&dev->dev, "unable to map device\n");
ret = -ENOMEM;
goto err_ioremap;
}
- printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base);
+ dev_info(&dev->dev, "mapped to 0x%p\n", info->screen_base);
par = info->par;
par->isVGA = screen_info_video_type(si) == VIDEO_TYPE_VGAC;
@@ -1369,13 +1369,13 @@ static int vga16fb_probe(struct platform_device *dev)
i = (info->var.bits_per_pixel == 8) ? 256 : 16;
ret = fb_alloc_cmap(&info->cmap, i, 0);
if (ret) {
- printk(KERN_ERR "vga16fb: unable to allocate colormap\n");
+ dev_err(&dev->dev, "unable to allocate colormap\n");
ret = -ENOMEM;
goto err_alloc_cmap;
}
if (vga16fb_check_var(&info->var, info)) {
- printk(KERN_ERR "vga16fb: unable to validate variable\n");
+ dev_err(&dev->dev, "unable to validate variable\n");
ret = -EINVAL;
goto err_check_var;
}
@@ -1386,7 +1386,7 @@ static int vga16fb_probe(struct platform_device *dev)
if (ret)
goto err_check_var;
if (register_framebuffer(info) < 0) {
- printk(KERN_ERR "vga16fb: unable to register framebuffer\n");
+ dev_err(&dev->dev, "unable to register framebuffer\n");
ret = -EINVAL;
goto err_check_var;
}
--
2.50.1
^ permalink raw reply related
* Re: [PATCH v5 2/4] backlight: add max25014atg backlight
From: Daniel Thompson @ 2025-11-10 10:01 UTC (permalink / raw)
To: Maud Spierings
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <f4e52cc1-9c5f-4069-9079-83be794ab2b3@gocontroll.com>
On Mon, Nov 10, 2025 at 09:40:07AM +0100, Maud Spierings wrote:
> On 11/7/25 17:14, Daniel Thompson wrote:
> > On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
> > > +/**
> > > + * @brief control the brightness with i2c registers
> > > + *
> > > + * @param regmap trivial
> > > + * @param brt brightness
> > > + * @return int
> > > + */
> > > +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
> >
> > This isn't a good name for a function. It doesn't really say what it
> > does. Please find a more descriptive name.
>
> Having a lot of difficulties find a succinct name that fits better,
> max25014_register_brightness_control()?
> max25014_i2c_brightness_control()?
I'd focus on what it does rather than how it does it meaning something
like max25014_update_brightness() would work.
However, at present, this code is only called from
max25014_update_status() so the simplest thing to do is to move the
code into max25014_update_status() and remove this function entirely
(then it doesn't matter what it is called ;-) ).
> > > +/*
> > > + * 1. disable unused strings
> > > + * 2. set dim mode
> > > + * 3. set initial brightness
> >
> > How does this code set the initial brightness? It doens't set the
> > MAX25014_TON* registers.
>
> Yep forgot to remove that, I discovered the backlight core takes care of the
> default brightness, so I removed it from here.
What do you mean by this? Are you sure you aren't relying on another
driver to enable the backlight rather than the backlight core?
> > > + * 4. set setting register
> > > + * 5. enable the backlight
> > > + */
> > > +static int max25014_configure(struct max25014 *maxim)
> > > +static int max25014_probe(struct i2c_client *cl)
> > > <snip>
> > > +
> > > + /* Enable can be tied to vin rail wait if either is available */
> > > + if (maxim->enable || maxim->vin) {
> > > + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
> > > + usleep_range(2000, 2500);
> > > + }
> >
> > If you really want to keep the devm_regulator_get_optional() I guess
> > maybe you could persuade me it's need to avoid this sleep... although
> > I'd be fairly happy to remove the NULL checks here too!
>
> Just wait unconditionally?
If you think it will be unusual for the driver to be used without enable
or regulator then it's ok to wait unconditionally (all examples you
have added so far have an enable pin).
Daniel.
^ permalink raw reply
* Re: [PATCH v5 2/4] backlight: add max25014atg backlight
From: Maud Spierings @ 2025-11-10 10:03 UTC (permalink / raw)
To: Daniel Thompson
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Helge Deller, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Liam Girdwood, Mark Brown, dri-devel, linux-leds, devicetree,
linux-kernel, linux-fbdev, imx, linux-arm-kernel
In-Reply-To: <aRG359gIeP48V2ZZ@aspen.lan>
On 11/10/25 11:01, Daniel Thompson wrote:
> On Mon, Nov 10, 2025 at 09:40:07AM +0100, Maud Spierings wrote:
>> On 11/7/25 17:14, Daniel Thompson wrote:
>>> On Fri, Nov 07, 2025 at 01:49:59PM +0100, Maud Spierings via B4 Relay wrote:
>>>> +/**
>>>> + * @brief control the brightness with i2c registers
>>>> + *
>>>> + * @param regmap trivial
>>>> + * @param brt brightness
>>>> + * @return int
>>>> + */
>>>> +static int max25014_register_control(struct regmap *regmap, uint32_t brt)
>>>
>>> This isn't a good name for a function. It doesn't really say what it
>>> does. Please find a more descriptive name.
>>
>> Having a lot of difficulties find a succinct name that fits better,
>> max25014_register_brightness_control()?
>> max25014_i2c_brightness_control()?
>
> I'd focus on what it does rather than how it does it meaning something
> like max25014_update_brightness() would work.
>
> However, at present, this code is only called from
> max25014_update_status() so the simplest thing to do is to move the
> code into max25014_update_status() and remove this function entirely
> (then it doesn't matter what it is called ;-) ).
>
Perhaps this could be seperated out if/when pwm functionality is
implemented. I believe the brightness may also be controlled that way in
hybrid mode, but I am not entirely sure.
>
>>>> +/*
>>>> + * 1. disable unused strings
>>>> + * 2. set dim mode
>>>> + * 3. set initial brightness
>>>
>>> How does this code set the initial brightness? It doens't set the
>>> MAX25014_TON* registers.
>>
>> Yep forgot to remove that, I discovered the backlight core takes care of the
>> default brightness, so I removed it from here.
>
> What do you mean by this? Are you sure you aren't relying on another
> driver to enable the backlight rather than the backlight core?
Not that I know of, there is the systemd backlight service, but I am
pretty sure I can see it first turn on, then get switched to the old
value by the systemd service. Unless the simple-panel driver controls
it? The backlight is linked to that.
>>>> + * 4. set setting register
>>>> + * 5. enable the backlight
>>>> + */
>>>> +static int max25014_configure(struct max25014 *maxim)
>
>
>>>> +static int max25014_probe(struct i2c_client *cl)
>>>> <snip>
>>>> +
>>>> + /* Enable can be tied to vin rail wait if either is available */
>>>> + if (maxim->enable || maxim->vin) {
>>>> + /* Datasheet Electrical Characteristics tSTARTUP 2ms */
>>>> + usleep_range(2000, 2500);
>>>> + }
>>>
>>> If you really want to keep the devm_regulator_get_optional() I guess
>>> maybe you could persuade me it's need to avoid this sleep... although
>>> I'd be fairly happy to remove the NULL checks here too!
>>
>> Just wait unconditionally?
>
> If you think it will be unusual for the driver to be used without enable
> or regulator then it's ok to wait unconditionally (all examples you
> have added so far have an enable pin).
I think it may actually be a very common implementation to have the
enable pin attached to Vin, we don't have it set up that way. But it is
displayed that way in an example schematic in the datasheet.
Kind regards,
Maud
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox