* [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
* [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 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 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 0/4] backlight: add new max25014 backlight driver
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
The Maxim MAX25014 is an automotive grade backlight driver IC. Its
datasheet can be found at [1].
With its integrated boost controller, it can power 4 channels (led
strings) and has a number of different modes using pwm and or i2c.
Currently implemented is only i2c control.
link: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX25014.pdf [1]
Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
---
Changes in v5:
- moved comment about current functions of the driver to the actual
comment section of the commit
- fixed the led@0 property, regex patternProperty is not needed as of
now
- added extra clarification about the ISET field/register
- moved #address-cells and #size-cells to the correct location
- remove leftover default-brightness in backlight nodes
- Link to v4: https://lore.kernel.org/r/20251009-max25014-v4-0-6adb2a0aa35f@gocontroll.com
Changes in v4:
- use a led node to describe the backlight
- use led-sources to enable specific channels
- also wait 2ms when there is a supply but no enable
- change dev_warn() to dev_err() in error path in max25014_check_errors()
- set backlight_properties.scale to BACKLIGHT_SCALE_LINEAR
- rebase latest next
- add address-cells and size-cells to i2c4 in av101hdt-a10.dtso
- Link to v3: https://lore.kernel.org/r/20250911-max25014-v3-0-d03f4eba375e@gocontroll.com
Changes in v3:
- fixed commit message type intgrated -> integrated
- added maximum and description to maxim,iset-property
- dropped unused labels and pinctrl in bindings example
- put the compatible first in the bindings example and dts
- removed brackets around defines
- removed the leftover pdata struct field
- removed the initial_brightness struct field
- Link to v2: https://lore.kernel.org/r/20250819-max25014-v2-0-5fd7aeb141ea@gocontroll.com
Changes in v2:
- Remove leftover unused property from the bindings example
- Complete the bindings example with all properties
- Remove some double info from the maxim,iset property
- Remove platform_data header, fold its data into the max25014 struct
- Don't force defines to be unsigned
- Remove stray struct max25014 declaration
- Remove chipname and device from the max25014 struct
- Inline the max25014_backlight_register() and strings_mask() functions
- Remove CONFIG_OF ifdef
- Link to v1: https://lore.kernel.org/r/20250725-max25014-v1-0-0e8cce92078e@gocontroll.com
---
Maud Spierings (4):
dt-bindings: backlight: Add max25014 support
backlight: add max25014atg backlight
arm64: dts: freescale: moduline-display-av101hdt-a10: add backlight
arm64: dts: freescale: moduline-display-av123z7m-n17: add backlight
.../bindings/leds/backlight/maxim,max25014.yaml | 107 ++++++
MAINTAINERS | 6 +
...x8p-ml81-moduline-display-106-av101hdt-a10.dtso | 30 ++
...x8p-ml81-moduline-display-106-av123z7m-n17.dtso | 25 +-
drivers/video/backlight/Kconfig | 7 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/max25014.c | 409 +++++++++++++++++++++
7 files changed, 584 insertions(+), 1 deletion(-)
---
base-commit: 9c0826a5d9aa4d52206dd89976858457a2a8a7ed
change-id: 20250626-max25014-4207591e1af5
Best regards,
--
Maud Spierings <maudspierings@gocontroll.com>
^ permalink raw reply
* Re: [PATCH] backlight: pwm_bl: apply the initial backlight state with sane defaults
From: Uwe Kleine-König @ 2025-11-07 8:00 UTC (permalink / raw)
To: Daniel Thompson, Lee Jones
Cc: Michael Grzeschik, Jingoo Han, Helge Deller, Pengutronix,
linux-pwm, dri-devel, linux-fbdev, linux-kernel
In-Reply-To: <aQNRK5ksNDMMve0x@aspen.lan>
[-- Attachment #1: Type: text/plain, Size: 947 bytes --]
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.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: (subset) [PATCH v6] backlight: led-backlight: add devlink to supplier LEDs
From: Lee Jones @ 2025-11-06 17:00 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Helge Deller,
Tony Lindgren, Pavel Machek, Jean-Jacques Hiblot, Tomi Valkeinen,
Luca Ceresoli
Cc: Saravana Kannan, Hervé Codina, Thomas Petazzoni,
Daniel Thompson, dri-devel, linux-fbdev, linux-kernel,
Alexander Sverdlin
In-Reply-To: <20250519-led-backlight-add-devlink-to-supplier-class-device-v6-1-845224aeb2ce@bootlin.com>
On Mon, 19 May 2025 22:19:11 +0200, Luca Ceresoli wrote:
> led-backlight is a consumer of one or multiple LED class devices, but
> devlink is currently unable to create correct supplier-producer links when
> the supplier is a class device. It creates instead a link where the
> supplier is the parent of the expected device.
>
> One consequence is that removal order is not correctly enforced.
>
> [...]
Applied, thanks!
[1/1] backlight: led-backlight: add devlink to supplier LEDs
commit: 67d8eed26eadb1edd4873d24889be26aa9b73fe5
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: (subset) [PATCH] backlight: pwm_bl: apply the initial backlight state with sane defaults
From: Lee Jones @ 2025-11-06 16:59 UTC (permalink / raw)
To: Uwe Kleine-König, Lee Jones, Daniel Thompson, Jingoo Han,
Helge Deller, Michael Grzeschik
Cc: Pengutronix, linux-pwm, dri-devel, linux-fbdev, linux-kernel
In-Reply-To: <20250731-blpwm-v1-1-0171fd31bff9@pengutronix.de>
On Thu, 31 Jul 2025 10:47:18 +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.
>
> [...]
Applied, thanks!
[1/1] backlight: pwm_bl: apply the initial backlight state with sane defaults
commit: c596a53cb0c607ccff34aac30ada774aa28b7dc0
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: (subset) [PATCH 1/2] leds: Drop duplicate LEDS_EXPRESSWIRE config
From: Lee Jones @ 2025-11-06 16:58 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Daniel Thompson, Jingoo Han,
Helge Deller, Duje Mihanović
Cc: Randy Dunlap, linux-leds, linux-kernel, dri-devel, linux-fbdev
In-Reply-To: <20250729-expresswire-dep-fix-v1-1-635cd4cc746b@dujemihanovic.xyz>
On Tue, 29 Jul 2025 19:18:29 +0200, Duje Mihanović wrote:
> While moving said config symbol out of the "if NEW_LEDS" block, I
> accidentally left a copy inside that block. Remove it.
>
>
Applied, thanks!
[1/2] leds: Drop duplicate LEDS_EXPRESSWIRE config
commit: ea1c4c7e648d1ca91577071fc42fdc219521098c
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: (subset) [PATCH 2/2] backlight: ktd2801: Depend on GPIOLIB
From: Lee Jones @ 2025-11-06 16:56 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Daniel Thompson, Jingoo Han,
Helge Deller, Duje Mihanović
Cc: Randy Dunlap, linux-leds, linux-kernel, dri-devel, linux-fbdev
In-Reply-To: <20250729-expresswire-dep-fix-v1-2-635cd4cc746b@dujemihanovic.xyz>
On Tue, 29 Jul 2025 19:18:30 +0200, Duje Mihanović wrote:
> The LEDS_EXPRESSWIRE library used by the driver requires GPIOLIB. Make
> sure this dependency is not left unsatisfied.
>
>
Applied, thanks!
[2/2] backlight: ktd2801: Depend on GPIOLIB
commit: d95963e309bc1211e28051314e72638d98225614
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH 0/2] ExpressWire dependency fixes
From: Lee Jones @ 2025-11-06 16:56 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Daniel Thompson, Jingoo Han,
Helge Deller, Duje Mihanović
Cc: Randy Dunlap, linux-leds, linux-kernel, dri-devel, linux-fbdev
In-Reply-To: <20250729-expresswire-dep-fix-v1-0-635cd4cc746b@dujemihanovic.xyz>
On Tue, 29 Jul 2025 19:18:28 +0200, Duje Mihanović wrote:
> This tiny series fixes two dependency issues with the ExpressWire
> library and the KTD2801 backlight driver. Thanks to Randy for reporting
> these.
>
>
Applied, thanks!
[1/2] leds: Drop duplicate LEDS_EXPRESSWIRE config
(no commit info)
[2/2] backlight: ktd2801: Depend on GPIOLIB
commit: d95963e309bc1211e28051314e72638d98225614
--
Lee Jones [李琼斯]
^ permalink raw reply
* [PATCH] drm, fbcon, vga_switcheroo: Avoid race condition in fbcon setup
From: Thomas Zimmermann @ 2025-11-05 16:14 UTC (permalink / raw)
To: maarten.lankhorst, mripard, airlied, simona, deller, lukas,
ville.syrjala, sam, javierm
Cc: dri-devel, linux-fbdev, Thomas Zimmermann
Protect vga_switcheroo_client_fb_set() with console lock. Avoids OOB
access in fbcon_remap_all(). Without holding the console lock the call
races with switching outputs.
VGA switcheroo calls fbcon_remap_all() when switching clients. The fbcon
function uses struct fb_info.node, which is set by register_framebuffer().
As the fb-helper code currently sets up VGA switcheroo before registering
the framebuffer, the value of node is -1 and therefore not a legal value.
For example, fbcon uses the value within set_con2fb_map() [1] as an index
into an array.
Moving vga_switcheroo_client_fb_set() after register_framebuffer() can
result in VGA switching that does not switch fbcon correctly.
Therefore move vga_switcheroo_client_fb_set() under fbcon_fb_registered(),
which already holds the console lock. Fbdev calls fbcon_fb_registered()
from within register_framebuffer(). Serializes the helper with VGA
switcheroo's call to fbcon_remap_all().
Although vga_switcheroo_client_fb_set() takes an instance of struct fb_info
as parameter, it really only needs the contained fbcon state. Moving the
call to fbcon initialization is therefore cleaner than before. Only amdgpu,
i915, nouveau and radeon support vga_switcheroo. For all other drivers,
this change does nothing.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://elixir.bootlin.com/linux/v6.17/source/drivers/video/fbdev/core/fbcon.c#L2942 # [1]
---
drivers/gpu/drm/drm_fb_helper.c | 14 --------------
drivers/video/fbdev/core/fbcon.c | 9 +++++++++
2 files changed, 9 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 53e9dc0543de..c0343ec16a57 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -31,9 +31,7 @@
#include <linux/console.h>
#include <linux/export.h>
-#include <linux/pci.h>
#include <linux/sysrq.h>
-#include <linux/vga_switcheroo.h>
#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
@@ -570,11 +568,6 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
*/
void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)
{
- struct fb_info *info = fb_helper->info;
- struct device *dev = info->device;
-
- if (dev_is_pci(dev))
- vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL);
unregister_framebuffer(fb_helper->info);
}
EXPORT_SYMBOL(drm_fb_helper_unregister_info);
@@ -1614,7 +1607,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
struct drm_fb_helper_surface_size sizes;
- struct fb_info *info;
int ret;
if (drm_WARN_ON(dev, !dev->driver->fbdev_probe))
@@ -1635,12 +1627,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
strcpy(fb_helper->fb->comm, "[fbcon]");
- info = fb_helper->info;
-
- /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
- if (dev_is_pci(info->device))
- vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
-
return 0;
}
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 7f35ad66b462..863944530c8e 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -66,6 +66,7 @@
#include <linux/string.h>
#include <linux/kd.h>
#include <linux/panic.h>
+#include <linux/pci.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/fb.h>
@@ -78,6 +79,7 @@
#include <linux/interrupt.h>
#include <linux/crc32.h> /* For counting font checksums */
#include <linux/uaccess.h>
+#include <linux/vga_switcheroo.h>
#include <asm/irq.h>
#include "fbcon.h"
@@ -2894,6 +2896,9 @@ void fbcon_fb_unregistered(struct fb_info *info)
console_lock();
+ if (info->device && dev_is_pci(info->device))
+ vga_switcheroo_client_fb_set(to_pci_dev(info->device), NULL);
+
fbcon_registered_fb[info->node] = NULL;
fbcon_num_registered_fb--;
@@ -3027,6 +3032,10 @@ static int do_fb_registered(struct fb_info *info)
}
}
+ /* Set the fb info for vga_switcheroo clients. Does nothing otherwise. */
+ if (info->device && dev_is_pci(info->device))
+ vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
+
return ret;
}
--
2.51.1
^ permalink raw reply related
* Re: [PATCH v2 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-04 12:22 UTC (permalink / raw)
To: Krzysztof Kozlowski
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, Pengyu Luo
In-Reply-To: <20251104-attractive-dragonfly-of-perspective-0d52d6@kuoka>
On Tue, Nov 4, 2025 at 3:36 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Mon, Nov 03, 2025 at 07:06:48PM +0800, Junjie Cao wrote:
> > +struct reg_init_data;
> > +
> > +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;
> > + int init_tbl_size;
> > + bool bl_enable;
> > +};
> > +
> > +enum reg_access {
> > + REG_NONE_ACCESS = 0,
> > + REG_RD_ACCESS = 1,
> > + REG_WR_ACCESS = 2,
> > +};
> > +
> > +const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
>
> Why isn't this static?
>
That was an oversight on my part. It will be static.
> > + [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,
> > +};
> > +
>
> ...
>
> > + 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, ret,
> > + "Failed to validate chip id\n")
>
> Why are you exiting the probe with a positive value like 4 or 8? This
> makes no sense. Registers do not coontain Linux return codes.
>
Thanks for pointing this out. Using -ENODEV makes more sense. I will do
it in the next version.
Regards,
Junjie
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-04 12:09 UTC (permalink / raw)
To: Krzysztof Kozlowski
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, Pengyu Luo
In-Reply-To: <20251104-dancing-panda-of-patience-49bcc7@kuoka>
On Tue, Nov 4, 2025 at 3:30 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Mon, Nov 03, 2025 at 07:06:47PM +0800, Junjie Cao wrote:
> > From: Pengyu Luo <mitltlatltl@gmail.com>
> >
> > Add Awinic AW99706 backlight binding documentation.
> >
> > Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
>
> Messed DCO chain. This wasn't here, so you must have altered v1 to add
> some weird change.
>
> This is a blocker, please read carefully submitting patches and DCO.
>
Apologies for the DCO mess.
The tablet device is currently with Pengyu. He helped with testing and
tweaked the .c driver file (patch 2/2) after my change. The entire
series was then re-formatted on his machine, which caused his git
configuration to be incorrectly applied to the From: line of this
dt-binding patch (patch 1/2).
I am the actual author of this dt-binding file. I will correct the
authorship and DCO chain in v2.
> > ---
> > 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
>
> ...
>
> > + 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 ]
>
> Please wrap code according to the preferred limit expressed in Kernel
> coding style (checkpatch is not a coding style description, but only a
> tool).
>
ACK.
> > + 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>;
> > +
> > + aw99706@76 {
>
> Node names should be generic. See also an explanation and list of
> examples (not exhaustive) in DT specification:
> https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation
> If you cannot find a name matching your device, please check in kernel
> sources for similar cases or you can grow the spec (via pull request to
> DT spec repo).
>
I see. backlight@76, thanks for your detailed explanation.
Regards,
Junjie
^ permalink raw reply
* Re: [PATCH v2 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Krzysztof Kozlowski @ 2025-11-04 7:36 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, Pengyu Luo
In-Reply-To: <20251103110648.878325-3-caojunjie650@gmail.com>
On Mon, Nov 03, 2025 at 07:06:48PM +0800, Junjie Cao wrote:
> +struct reg_init_data;
> +
> +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;
> + int init_tbl_size;
> + bool bl_enable;
> +};
> +
> +enum reg_access {
> + REG_NONE_ACCESS = 0,
> + REG_RD_ACCESS = 1,
> + REG_WR_ACCESS = 2,
> +};
> +
> +const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
Why isn't this static?
> + [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,
> +};
> +
...
> + 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, ret,
> + "Failed to validate chip id\n")
Why are you exiting the probe with a positive value like 4 or 8? This
makes no sense. Registers do not coontain Linux return codes.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Krzysztof Kozlowski @ 2025-11-04 7:30 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, Pengyu Luo
In-Reply-To: <20251103110648.878325-2-caojunjie650@gmail.com>
On Mon, Nov 03, 2025 at 07:06:47PM +0800, Junjie Cao wrote:
> From: Pengyu Luo <mitltlatltl@gmail.com>
>
> Add Awinic AW99706 backlight binding documentation.
>
> Signed-off-by: Junjie Cao <caojunjie650@gmail.com>
Messed DCO chain. This wasn't here, so you must have altered v1 to add
some weird change.
This is a blocker, please read carefully submitting patches and DCO.
> ---
> 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
...
> + 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 ]
Please wrap code according to the preferred limit expressed in Kernel
coding style (checkpatch is not a coding style description, but only a
tool).
> + 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>;
> +
> + aw99706@76 {
Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation
If you cannot find a name matching your device, please check in kernel
sources for similar cases or you can grow the spec (via pull request to
DT spec repo).
> + 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
* Re: [PATCH v2 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-04 5:23 UTC (permalink / raw)
To: Daniel Thompson
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, Pengyu Luo
In-Reply-To: <aQjY5_uEaTv4_L2s@aspen.lan>
On Tue, Nov 4, 2025 at 12:30 AM Daniel Thompson <daniel@riscstar.com> wrote:
>
> On Mon, Nov 03, 2025 at 07:06:48PM +0800, Junjie Cao wrote:
> > From: Pengyu Luo <mitltlatltl@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: Pengyu Luo <mitltlatltl@gmail.com>
> > Signed-off-by: Junjie Cao <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
>
> Thanks for the changes.
>
> I'm afraid I don't like encoding the `shift` in the DT properties table.
> Caching something that is so easy to recalculate makes no sense to me.
> See below:
>
>
> > +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;
> > + u8 shift;
>
> There should bee no need to record `shift` here. It's just a
> duplicating information already held in `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, __builtin_ctz(AW99706_DIM_MODE_MASK),
>
> These __builtin_ctz() calls shouldn't be in the lookup table (if they
> are not in the lookup table then can never be inconsistant with the
> mask).
>
>
> > + 1,
> > + },
> <snip>
> > + {
> > + "awinic,ramp-ctl", aw99706_dt_property_lookup,
> > + NULL, 0,
> > + AW99706_CFG6_REG,
> > + AW99706_RAMP_CTL_MASK, __builtin_ctz(AW99706_RAMP_CTL_MASK),
> > + 2,
> > + },
> > +};
> > +
> > +struct reg_init_data {
> > + u8 reg;
> > + u8 mask;
> > + u8 val;
> > +};
> > +
> > +static struct reg_init_data reg_init_tbl[ARRAY_SIZE(aw99706_dt_props)];
> > +
> > +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);
> > + }
> > +
> > + reg_init_tbl[i].reg = prop->reg;
> > + reg_init_tbl[i].mask = prop->mask;
> > + reg_init_tbl[i].val = val << prop->shift;
>
> Can't you just use FIELD_PREP() to set val (either here or at the point
> the init table is consumed)? That why there's no ffs() or clz() at all.
>
Thanks for it, I tried to find something like FIELD_PREP() to avoid
keeping shift, but I failed to notice it in the bitfield.h. I will drop
the shift field and use it to handle bit operations gracefully.
>
> > + }
> > +
> > + aw->init_tbl = reg_init_tbl;
> > + aw->init_tbl_size = ARRAY_SIZE(reg_init_tbl);
>
> Copying a pointer to a single instance static data buffer into a
> dynamically allocated data structure isn't right.
>
> You should include the init table as part of `struct aw99706_device`.
>
I see. Multiple instances should be taken into account.
>
> > +
> > + 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 < aw->init_tbl_size; 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;
> > +
> > + FIELD_MODIFY(AW99706_BACKLIGHT_EN_MASK, &val, en);
>
> This should use FIELD_PREP() not FIELD_MODIFY();
>
ACK. FIELD_PREP() makes more sense here.
Regards,
Junjie
^ permalink raw reply
* Re: [PATCH v2 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Daniel Thompson @ 2025-11-03 16:31 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, Pengyu Luo
In-Reply-To: <20251103110648.878325-3-caojunjie650@gmail.com>
On Mon, Nov 03, 2025 at 07:06:48PM +0800, Junjie Cao wrote:
> From: Pengyu Luo <mitltlatltl@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: Pengyu Luo <mitltlatltl@gmail.com>
> Signed-off-by: Junjie Cao <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
Thanks for the changes.
I'm afraid I don't like encoding the `shift` in the DT properties table.
Caching something that is so easy to recalculate makes no sense to me.
See below:
> +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;
> + u8 shift;
There should bee no need to record `shift` here. It's just a
duplicating information already held in `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, __builtin_ctz(AW99706_DIM_MODE_MASK),
These __builtin_ctz() calls shouldn't be in the lookup table (if they
are not in the lookup table then can never be inconsistant with the
mask).
> + 1,
> + },
<snip>
> + {
> + "awinic,ramp-ctl", aw99706_dt_property_lookup,
> + NULL, 0,
> + AW99706_CFG6_REG,
> + AW99706_RAMP_CTL_MASK, __builtin_ctz(AW99706_RAMP_CTL_MASK),
> + 2,
> + },
> +};
> +
> +struct reg_init_data {
> + u8 reg;
> + u8 mask;
> + u8 val;
> +};
> +
> +static struct reg_init_data reg_init_tbl[ARRAY_SIZE(aw99706_dt_props)];
> +
> +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);
> + }
> +
> + reg_init_tbl[i].reg = prop->reg;
> + reg_init_tbl[i].mask = prop->mask;
> + reg_init_tbl[i].val = val << prop->shift;
Can't you just use FIELD_PREP() to set val (either here or at the point
the init table is consumed)? That why there's no ffs() or clz() at all.
> + }
> +
> + aw->init_tbl = reg_init_tbl;
> + aw->init_tbl_size = ARRAY_SIZE(reg_init_tbl);
Copying a pointer to a single instance static data buffer into a
dynamically allocated data structure isn't right.
You should include the init table as part of `struct aw99706_device`.
> +
> + 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 < aw->init_tbl_size; 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;
> +
> + FIELD_MODIFY(AW99706_BACKLIGHT_EN_MASK, &val, en);
This should use FIELD_PREP() not FIELD_MODIFY();
> + 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;
> +}
Daniel.
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Rob Herring (Arm) @ 2025-11-03 12:23 UTC (permalink / raw)
To: Junjie Cao
Cc: Conor Dooley, linux-kernel, devicetree, Pengyu Luo,
Daniel Thompson, linux-leds, Jingoo Han, dri-devel,
Krzysztof Kozlowski, Helge Deller, Lee Jones, Pavel Machek,
linux-fbdev
In-Reply-To: <20251103110648.878325-2-caojunjie650@gmail.com>
On Mon, 03 Nov 2025 19:06:47 +0800, Junjie Cao wrote:
> From: Pengyu Luo <mitltlatltl@gmail.com>
>
> Add Awinic AW99706 backlight binding documentation.
>
> Signed-off-by: Junjie Cao <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 | 100 ++++++++++++++++++
> 1 file changed, 100 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml:39:111: [warning] line too long (113 > 110 characters) (line-length)
dtschema/dtc warnings/errors:
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20251103110648.878325-2-caojunjie650@gmail.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* [PATCH v2 2/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-03 11:06 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,
Pengyu Luo, Junjie Cao
In-Reply-To: <20251103110648.878325-1-caojunjie650@gmail.com>
From: Pengyu Luo <mitltlatltl@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: Pengyu Luo <mitltlatltl@gmail.com>
Signed-off-by: Junjie Cao <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 | 492 ++++++++++++++++++++++++++++++
4 files changed, 507 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..f65e35905
--- /dev/null
+++ b/drivers/video/backlight/aw99706.c
@@ -0,0 +1,492 @@
+// 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 reg_init_data;
+
+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;
+ int init_tbl_size;
+ bool bl_enable;
+};
+
+enum reg_access {
+ REG_NONE_ACCESS = 0,
+ REG_RD_ACCESS = 1,
+ REG_WR_ACCESS = 2,
+};
+
+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);
+}
+
+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;
+ u8 shift;
+ 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, __builtin_ctz(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, __builtin_ctz(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, __builtin_ctz(AW99706_SW_ILMT_MASK),
+ 3000000,
+ },
+ {
+ "awinic,iled-max", aw99706_dt_property_iled_max_convert,
+ NULL, 0,
+ AW99706_CFG2_REG,
+ AW99706_ILED_MAX_MASK, __builtin_ctz(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, __builtin_ctz(AW99706_UVLOSEL_MASK),
+ 2200000,
+ },
+ {
+ "awinic,ramp-ctl", aw99706_dt_property_lookup,
+ NULL, 0,
+ AW99706_CFG6_REG,
+ AW99706_RAMP_CTL_MASK, __builtin_ctz(AW99706_RAMP_CTL_MASK),
+ 2,
+ },
+};
+
+struct reg_init_data {
+ u8 reg;
+ u8 mask;
+ u8 val;
+};
+
+static struct reg_init_data reg_init_tbl[ARRAY_SIZE(aw99706_dt_props)];
+
+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);
+ }
+
+ reg_init_tbl[i].reg = prop->reg;
+ reg_init_tbl[i].mask = prop->mask;
+ reg_init_tbl[i].val = val << prop->shift;
+ }
+
+ aw->init_tbl = reg_init_tbl;
+ aw->init_tbl_size = ARRAY_SIZE(reg_init_tbl);
+
+ 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 < aw->init_tbl_size; 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;
+
+ FIELD_MODIFY(AW99706_BACKLIGHT_EN_MASK, &val, 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, ret,
+ "Failed to validate chip id\n");
+
+ 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
* [PATCH v2 1/2] dt-bindings: leds: backlight: Add Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-03 11:06 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,
Pengyu Luo, Junjie Cao
In-Reply-To: <20251103110648.878325-1-caojunjie650@gmail.com>
From: Pengyu Luo <mitltlatltl@gmail.com>
Add Awinic AW99706 backlight binding documentation.
Signed-off-by: Junjie Cao <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 | 100 ++++++++++++++++++
1 file changed, 100 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..9b7266e61
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
@@ -0,0 +1,100 @@
+# 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>;
+
+ aw99706@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 v2 0/2] backlight: aw99706: Add support for Awinic AW99706 backlight
From: Junjie Cao @ 2025-11-03 11:06 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,
Pengyu Luo, Junjie Cao
From: Pengyu Luo <mitltlatltl@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>
---
base-commit: 72fb0170ef1f45addf726319c52a0562b6913707
---
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 | 100 ++++
MAINTAINERS | 6 +
drivers/video/backlight/Kconfig | 8 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/aw99706.c | 492 ++++++++++++++++++
5 files changed, 607 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 2/2] staging: sm750fb: avoid chained assignment in setcolreg()
From: Cristian Del Gobbo @ 2025-11-02 23:01 UTC (permalink / raw)
To: sudip.mukherjee
Cc: teddy.wang, gregkh, linux-fbdev, linux-staging, linux-kernel,
Cristian Del Gobbo
In-Reply-To: <20251102230139.1720-1-cristiandelgobbo87@gmail.com>
Replace the chained assignment of red/green/blue with a temporary
variable and braces. This keeps behavior identical while improving
readability and satisfying checkpatch.
No functional change intended.
Signed-off-by: Cristian Del Gobbo <cristiandelgobbo87@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 9740f2705679..fecd7457e615 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -537,8 +537,13 @@ static int lynxfb_ops_setcolreg(unsigned int regno,
return -EINVAL;
}
- if (info->var.grayscale)
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ if (info->var.grayscale) {
+ int lum = (red * 77 + green * 151 + blue * 28) >> 8;
+
+ red = lum;
+ green = lum;
+ blue = lum;
+ }
if (var->bits_per_pixel == 8 &&
info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
--
2.34.1
^ permalink raw reply related
* [PATCH 1/2] staging: sm750fb: align sm750_hw_cursor_set_size() arguments
From: Cristian Del Gobbo @ 2025-11-02 23:01 UTC (permalink / raw)
To: sudip.mukherjee
Cc: teddy.wang, gregkh, linux-fbdev, linux-staging, linux-kernel,
Cristian Del Gobbo
Align the continued arguments with the opening parenthesis to satisfy
CodingStyle and checkpatch.
No functional change intended.
Signed-off-by: Cristian Del Gobbo <cristiandelgobbo87@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 3659af7e519d..9740f2705679 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -121,8 +121,8 @@ static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
sm750_hw_cursor_disable(cursor);
if (fbcursor->set & FB_CUR_SETSIZE)
sm750_hw_cursor_set_size(cursor,
- fbcursor->image.width,
- fbcursor->image.height);
+ fbcursor->image.width,
+ fbcursor->image.height);
if (fbcursor->set & FB_CUR_SETPOS)
sm750_hw_cursor_set_pos(cursor,
--
2.34.1
^ permalink raw reply related
* [syzbot] [fbdev?] KASAN: vmalloc-out-of-bounds Write in imageblit (6)
From: syzbot @ 2025-11-02 21:10 UTC (permalink / raw)
To: deller, dri-devel, linux-fbdev, linux-kernel, simona, soci,
syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: 691d401c7e0e Merge tag 'spi-fix-v6.18-rc3' of git://git.ke..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=14607932580000
kernel config: https://syzkaller.appspot.com/x/.config?x=19d831c6d0386a9c
dashboard link: https://syzkaller.appspot.com/bug?extid=5a40432dfe8f86ee657a
compiler: gcc (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
Unfortunately, I don't have any reproducer for this issue yet.
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/5eaccda03e4f/disk-691d401c.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/0df5294a69bc/vmlinux-691d401c.xz
kernel image: https://storage.googleapis.com/syzbot-assets/f4f242731772/bzImage-691d401c.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+5a40432dfe8f86ee657a@syzkaller.appspotmail.com
==================================================================
BUG: KASAN: vmalloc-out-of-bounds in fb_write_offset drivers/video/fbdev/core/sysmem.h:30 [inline]
BUG: KASAN: vmalloc-out-of-bounds in fb_bitmap_2ppw drivers/video/fbdev/core/fb_imageblit.h:364 [inline]
BUG: KASAN: vmalloc-out-of-bounds in fb_bitmap_imageblit drivers/video/fbdev/core/fb_imageblit.h:462 [inline]
BUG: KASAN: vmalloc-out-of-bounds in fb_imageblit drivers/video/fbdev/core/fb_imageblit.h:492 [inline]
BUG: KASAN: vmalloc-out-of-bounds in sys_imageblit+0x1a6f/0x1e60 drivers/video/fbdev/core/sysimgblt.c:24
Write of size 8 at addr ffffc90003e99280 by task kworker/0:2/781
CPU: 0 UID: 0 PID: 781 Comm: kworker/0:2 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/02/2025
Workqueue: events_power_efficient fb_flashcursor
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xcd/0x630 mm/kasan/report.c:482
kasan_report+0xe0/0x110 mm/kasan/report.c:595
fb_write_offset drivers/video/fbdev/core/sysmem.h:30 [inline]
fb_bitmap_2ppw drivers/video/fbdev/core/fb_imageblit.h:364 [inline]
fb_bitmap_imageblit drivers/video/fbdev/core/fb_imageblit.h:462 [inline]
fb_imageblit drivers/video/fbdev/core/fb_imageblit.h:492 [inline]
sys_imageblit+0x1a6f/0x1e60 drivers/video/fbdev/core/sysimgblt.c:24
drm_fbdev_shmem_defio_imageblit+0x20/0x130 drivers/gpu/drm/drm_fbdev_shmem.c:38
soft_cursor+0x524/0xa10 drivers/video/fbdev/core/softcursor.c:74
bit_cursor+0xe8c/0x17e0 drivers/video/fbdev/core/bitblit.c:395
fb_flashcursor drivers/video/fbdev/core/fbcon.c:401 [inline]
fb_flashcursor+0x310/0x400 drivers/video/fbdev/core/fbcon.c:370
process_one_work+0x9cf/0x1b70 kernel/workqueue.c:3263
process_scheduled_works kernel/workqueue.c:3346 [inline]
worker_thread+0x6c8/0xf10 kernel/workqueue.c:3427
kthread+0x3c5/0x780 kernel/kthread.c:463
ret_from_fork+0x675/0x7d0 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
The buggy address belongs to a vmalloc virtual mapping
Memory state around the buggy address:
ffffc90003e99180: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
ffffc90003e99200: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
>ffffc90003e99280: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
^
ffffc90003e99300: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
ffffc90003e99380: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
==================================================================
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ 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