video: backlight: add Skyworks/Analogictech AAT2870 led controller driver
Add support for Skyworks AAT2870 LED Backlight Driver and Multiple LDO
Lighting Management Unit. Only backlight is supported as for now. Supported
backlight level range is from 2 to 255 with step of 1.
Tested-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2014090..dfe4b3b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -783,6 +783,16 @@
help
HLCDC supports video output to an attached LCD panel.
+config BACKLIGHT_AAT2870
+ bool "Backlight Driver for AAT2870"
+ depends on BACKLIGHT
+ select DM_I2C
+ help
+ Say Y to enable the backlight driver for Skyworks AAT2870 LED
+ Backlight Driver and Multiple LDO Lighting Management Unit.
+ Only backlight is supported as for now. Supported backlight
+ level range is from 2 to 255 with step of 1.
+
config BACKLIGHT_LM3532
bool "Backlight Driver for LM3532"
depends on BACKLIGHT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 277ed09..ebe4a39 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,6 +29,7 @@
endif
+obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o
obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o
diff --git a/drivers/video/aat2870_backlight.c b/drivers/video/aat2870_backlight.c
new file mode 100644
index 0000000..209d15b
--- /dev/null
+++ b/drivers/video/aat2870_backlight.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
+
+#include <backlight.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <linux/err.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+#define AAT2870_BL_MIN_BRIGHTNESS 0x01
+#define AAT2870_BL_DEF_BRIGHTNESS 0x64
+#define AAT2870_BL_MAX_BRIGHTNESS 0xff
+
+#define AAT2870_BL_CH_EN 0x00
+#define AAT2870_BLM 0x01
+
+#define AAT2870_BL_CH_ALL 0xff
+
+#define AAT2870_CURRENT_MAX 27900000
+#define AAT2870_CURRENT_STEP 900000
+
+struct aat2870_backlight_priv {
+ struct gpio_desc enable_gpio;
+
+ int channels;
+ int max_current;
+};
+
+static int aat2870_backlight_enable(struct udevice *dev)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+
+ /* Enable backlight for defined set of channels */
+ ret = dm_i2c_reg_write(dev, AAT2870_BL_CH_EN, priv->channels);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int aat2870_backlight_set_brightness(struct udevice *dev, int percent)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int brightness, ret;
+
+ if (percent == BACKLIGHT_DEFAULT)
+ percent = AAT2870_BL_DEF_BRIGHTNESS;
+
+ if (percent < AAT2870_BL_MIN_BRIGHTNESS)
+ percent = AAT2870_BL_MIN_BRIGHTNESS;
+
+ if (percent > AAT2870_BL_MAX_BRIGHTNESS)
+ percent = AAT2870_BL_MAX_BRIGHTNESS;
+
+ brightness = percent * priv->max_current;
+ brightness /= AAT2870_BL_MAX_BRIGHTNESS;
+
+ /* Set brightness level */
+ ret = dm_i2c_reg_write(dev, AAT2870_BLM, brightness);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int aat2870_backlight_of_to_plat(struct udevice *dev)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = gpio_request_by_name(dev, "enable-gpios", 0,
+ &priv->enable_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_err("%s: cannot get enable-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Backlight is one of children but has no dedicated driver */
+ ofnode backlight = ofnode_find_subnode(dev_ofnode(dev), "backlight");
+ if (ofnode_valid(backlight) && ofnode_is_enabled(backlight)) {
+ /* Number of channel is equal to bit number */
+ priv->channels = dev_read_u32_default(dev, "channels", AAT2870_BL_CH_ALL);
+ if (priv->channels != AAT2870_BL_CH_ALL)
+ priv->channels = BIT(priv->channels);
+
+ /* 450mA - 27900mA range with a 900mA step */
+ priv->max_current = dev_read_u32_default(dev, "current-max-microamp",
+ AAT2870_CURRENT_MAX);
+ priv->max_current /= AAT2870_CURRENT_STEP;
+ }
+
+ return 0;
+}
+
+static int aat2870_backlight_probe(struct udevice *dev)
+{
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ return 0;
+}
+
+static const struct backlight_ops aat2870_backlight_ops = {
+ .enable = aat2870_backlight_enable,
+ .set_brightness = aat2870_backlight_set_brightness,
+};
+
+static const struct udevice_id aat2870_backlight_ids[] = {
+ { .compatible = "analogictech,aat2870" },
+ { .compatible = "skyworks,aat2870" },
+ { }
+};
+
+U_BOOT_DRIVER(aat2870_backlight) = {
+ .name = "aat2870_backlight",
+ .id = UCLASS_PANEL_BACKLIGHT,
+ .of_match = aat2870_backlight_ids,
+ .of_to_plat = aat2870_backlight_of_to_plat,
+ .probe = aat2870_backlight_probe,
+ .ops = &aat2870_backlight_ops,
+ .priv_auto = sizeof(struct aat2870_backlight_priv),
+};