diff mbox series

[1/2] leds: aw21024: Add support for Awinic's AW21024

Message ID 20220513190409.3682501-1-kyle.swenson@est.tech
State New
Headers show
Series [1/2] leds: aw21024: Add support for Awinic's AW21024 | expand

Commit Message

Kyle Swenson May 13, 2022, 7:04 p.m. UTC
The Awinic AW21024 LED controller is a 24-channel RGB LED controller.
Each LED on the controller can be controlled individually or grouped
with other LEDs on the controller to form a multi-color LED.  Arbitrary
combinations of individual and grouped LED control should be possible.

Signed-off-by: Kyle Swenson <kyle.swenson@est.tech>
---
 drivers/leds/Kconfig        |  11 ++
 drivers/leds/Makefile       |   1 +
 drivers/leds/leds-aw21024.c | 314 ++++++++++++++++++++++++++++++++++++
 3 files changed, 326 insertions(+)
 create mode 100644 drivers/leds/leds-aw21024.c

Comments

kernel test robot May 16, 2022, 10:21 a.m. UTC | #1
Hi Kyle,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on pavel-leds/for-next]
[also build test ERROR on robh/for-next v5.18-rc7 next-20220513]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Kyle-Swenson/leds-aw21024-Add-support-for-Awinic-s-AW21024/20220514-030705
base:   git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: mips-randconfig-r006-20220516 (https://download.01.org/0day-ci/archive/20220516/202205161802.8WYsbizM-lkp@intel.com/config)
compiler: mips-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/38eeda60299918b5599f4a58714dc91f9741677c
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kyle-Swenson/leds-aw21024-Add-support-for-Awinic-s-AW21024/20220514-030705
        git checkout 38eeda60299918b5599f4a58714dc91f9741677c
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash drivers/leds/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   drivers/leds/leds-aw21024.c: In function 'aw21024_led_brightness_set':
>> drivers/leds/leds-aw21024.c:64:31: error: implicit declaration of function 'i2c_smbus_write_byte_data' [-Werror=implicit-function-declaration]
      64 |                         ret = i2c_smbus_write_byte_data(parent->client,
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/leds/leds-aw21024.c: In function 'aw21024_configure':
>> drivers/leds/leds-aw21024.c:213:15: error: implicit declaration of function 'i2c_smbus_read_byte_data' [-Werror=implicit-function-declaration]
     213 |         ret = i2c_smbus_read_byte_data(client, AW21024_REG_SW_RESET);
         |               ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/leds/leds-aw21024.c: At top level:
>> drivers/leds/leds-aw21024.c:310:1: warning: data definition has no type or storage class
     310 | module_i2c_driver(aw21024_driver);
         | ^~~~~~~~~~~~~~~~~
>> drivers/leds/leds-aw21024.c:310:1: error: type defaults to 'int' in declaration of 'module_i2c_driver' [-Werror=implicit-int]
>> drivers/leds/leds-aw21024.c:310:1: warning: parameter names (without types) in function declaration
   drivers/leds/leds-aw21024.c:301:26: warning: 'aw21024_driver' defined but not used [-Wunused-variable]
     301 | static struct i2c_driver aw21024_driver = {
         |                          ^~~~~~~~~~~~~~
   drivers/leds/leds-aw21024.c:295:34: warning: 'of_aw21024_leds_match' defined but not used [-Wunused-const-variable=]
     295 | static const struct of_device_id of_aw21024_leds_match[] = {
         |                                  ^~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/i2c_smbus_write_byte_data +64 drivers/leds/leds-aw21024.c

    51	
    52	static int aw21024_led_brightness_set(struct led_classdev *led_cdev,
    53						enum led_brightness brightness)
    54	{
    55		struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
    56		struct aw21024_led_data *led = container_of(mc_cdev, struct aw21024_led_data, mc_cdev);
    57		struct aw21024 *parent = led->parent;
    58		int i;
    59		int ret = 0;
    60	
    61		mutex_lock(&parent->lock);
    62		if (mc_cdev->num_colors && mc_cdev->subled_info) {
    63			for (i = 0; i < led->nregs; i++) {
  > 64				ret = i2c_smbus_write_byte_data(parent->client,
    65						AW21024_REG_DC_CURRENT(led->regs[i]),
    66						mc_cdev->subled_info[i].intensity);
    67				if (ret < 0)
    68					goto unlock_ret;
    69	
    70				ret = i2c_smbus_write_byte_data(parent->client,
    71						AW21024_REG_BRIGHTNESS(led->regs[i]),
    72						brightness);
    73				if (ret < 0)
    74					goto unlock_ret;
    75			}
    76		} else {
    77			ret = i2c_smbus_write_byte_data(parent->client,
    78						AW21024_REG_DC_CURRENT(led->regs[0]), 0xFF);
    79			if (ret < 0)
    80				goto unlock_ret;
    81	
    82			ret = i2c_smbus_write_byte_data(parent->client,
    83						AW21024_REG_BRIGHTNESS(led->regs[0]),
    84						brightness);
    85			if (ret < 0)
    86				goto unlock_ret;
    87		}
    88		ret = i2c_smbus_write_byte_data(parent->client, AW21024_REG_UPDATE, 0x0);
    89	unlock_ret:
    90		mutex_unlock(&parent->lock);
    91		return ret;
    92	}
    93	
    94	static int aw21024_probe_dt(struct aw21024 *data)
    95	{
    96		struct device *dev = &data->client->dev;
    97		struct fwnode_handle *child = NULL;
    98		struct fwnode_handle *led_node = NULL;
    99		struct led_init_data init_data = {};
   100		u32 color_id;
   101		int  ret, num_colors;
   102		unsigned int nleds = 0;
   103		struct aw21024_led_data *led;
   104		struct led_classdev *led_cdev;
   105		struct mc_subled *mc_led_info;
   106	
   107		nleds = device_get_child_node_count(dev);
   108	
   109		data->leds = devm_kcalloc(dev, nleds, sizeof(*(data->leds)), GFP_KERNEL);
   110		if (!data->leds)
   111			return -ENOMEM;
   112	
   113		device_for_each_child_node(dev, child) {
   114			led = devm_kzalloc(dev, sizeof(struct aw21024_led_data), GFP_KERNEL);
   115			if (!led) {
   116				ret = -ENOMEM;
   117				goto ret_put_child;
   118			}
   119			led->parent = data;
   120			led_cdev = &led->mc_cdev.led_cdev;
   121			init_data.fwnode = child;
   122	
   123			led_cdev->brightness_set_blocking = aw21024_led_brightness_set;
   124			data->leds[data->nleds] = led;
   125	
   126			ret = fwnode_property_count_u32(child, "reg");
   127			if (ret < 0) {
   128				dev_err(dev, "reg property is invalid in node %s\n",
   129						fwnode_get_name(child));
   130				goto ret_put_child;
   131			}
   132	
   133			led->regs = devm_kcalloc(dev, ret, sizeof(*(led->regs)), GFP_KERNEL);
   134			led->nregs = ret;
   135			if (!led->regs) {
   136				ret = -ENOMEM;
   137				goto ret_put_child;
   138			}
   139	
   140			ret = fwnode_property_read_u32_array(child, "reg", led->regs, led->nregs);
   141			if (ret) {
   142				dev_err(dev, "Failed to read reg array, error=%d\n", ret);
   143				goto ret_put_child;
   144			}
   145	
   146			if (led->nregs > 1) {
   147				mc_led_info = devm_kcalloc(dev, led->nregs,
   148							    sizeof(*mc_led_info), GFP_KERNEL);
   149				if (!mc_led_info) {
   150					ret = -ENOMEM;
   151					goto ret_put_child;
   152				}
   153	
   154				num_colors = 0;
   155				fwnode_for_each_child_node(child, led_node) {
   156					if (num_colors > led->nregs) {
   157						ret = -EINVAL;
   158						fwnode_handle_put(led_node);
   159						goto ret_put_child;
   160					}
   161	
   162					ret = fwnode_property_read_u32(led_node, "color",
   163								       &color_id);
   164					if (ret) {
   165						fwnode_handle_put(led_node);
   166						goto ret_put_child;
   167					}
   168					mc_led_info[num_colors].color_index = color_id;
   169					num_colors++;
   170				}
   171	
   172				led->mc_cdev.num_colors = num_colors;
   173				led->mc_cdev.subled_info = mc_led_info;
   174				ret = devm_led_classdev_multicolor_register_ext(dev,
   175									&led->mc_cdev,
   176									&init_data);
   177				if (ret < 0) {
   178					dev_warn(dev, "Failed to register multicolor LED %s, err=%d\n",
   179							    fwnode_get_name(child), ret);
   180					goto ret_put_child;
   181				}
   182			} else {
   183				ret = devm_led_classdev_register_ext(dev,
   184								    &led->mc_cdev.led_cdev, &init_data);
   185				if (ret < 0) {
   186					dev_warn(dev, "Failed to register LED %s, err=%d\n",
   187							fwnode_get_name(child), ret);
   188					goto ret_put_child;
   189				}
   190			}
   191			data->nleds++;
   192		}
   193	
   194		return 0;
   195	
   196	ret_put_child:
   197		fwnode_handle_put(child);
   198		return ret;
   199	}
   200	
   201	/* Expected to be called prior to registering with the LEDs class */
   202	static int aw21024_configure(struct aw21024 *priv)
   203	{
   204		int ret = 0;
   205		struct i2c_client *client = priv->client;
   206	
   207		ret = i2c_smbus_write_byte_data(client, AW21024_REG_GCR0, AW21024_GCR0_CHIPEN);
   208		if (ret < 0) {
   209			dev_err(&client->dev, "Failed to write chip enable\n");
   210			return -ENODEV;
   211		}
   212	
 > 213		ret = i2c_smbus_read_byte_data(client, AW21024_REG_SW_RESET);
   214		if (ret < 0) {
   215			dev_err(&client->dev, "Failed to read chip id\n");
   216			return -ENODEV;
   217		}
   218	
   219		if (ret != AW21024_CHIP_ID) {
   220			dev_err(&client->dev, "Chip ID 0x%02X doesn't match expected (0x%02X)\n",
   221									ret, AW21024_CHIP_ID);
   222			return -ENODEV;
   223		}
   224	
   225		ret = i2c_smbus_read_byte_data(client, AW21024_REG_VERSION);
   226		if (ret < 0) {
   227			dev_err(&client->dev, "Failed to read chip version\n");
   228			return -ENODEV;
   229		}
   230		if (ret != AW21024_CHIP_VERSION)
   231			dev_warn(&client->dev, "Chip version 0x%02X doesn't match expected 0x%02X\n",
   232									ret, AW21024_CHIP_VERSION);
   233	
   234		i2c_smbus_write_byte_data(client, AW21024_REG_SW_RESET, 0x00);
   235		mdelay(2);
   236		i2c_smbus_write_byte_data(client, AW21024_REG_GCR0, AW21024_GCR0_CHIPEN);
   237		i2c_smbus_write_byte_data(client, AW21024_REG_GCC, 0xFF);
   238	
   239		return 0;
   240	}
   241
diff mbox series

Patch

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a49979f41eee..c813d7c16ff8 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -102,10 +102,21 @@  config LEDS_AW2013
 	  LED driver.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-aw2013.
 
+config LEDS_AW21024
+	tristate "LED Support for Awinic AW21024"
+	depends on LEDS_CLASS
+	depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
+	help
+	  If you say yes here you get support for Awinic's AW21024, a 24-channel
+	  RGB LED Driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called leds-aw21024.
+
 config LEDS_BCM6328
 	tristate "LED Support for Broadcom BCM6328"
 	depends on LEDS_CLASS
 	depends on HAS_IOMEM
 	depends on OF
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd198..09a0e3cb21cb 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -14,10 +14,11 @@  obj-$(CONFIG_LEDS_ADP5520)		+= leds-adp5520.o
 obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
 obj-$(CONFIG_LEDS_APU)			+= leds-apu.o
 obj-$(CONFIG_LEDS_ARIEL)		+= leds-ariel.o
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
 obj-$(CONFIG_LEDS_AW2013)		+= leds-aw2013.o
+obj-$(CONFIG_LEDS_AW21024)		+= leds-aw21024.o
 obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
 obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
 obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
 obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)		+= leds-clevo-mail.o
diff --git a/drivers/leds/leds-aw21024.c b/drivers/leds/leds-aw21024.c
new file mode 100644
index 000000000000..4eebc3de1a8a
--- /dev/null
+++ b/drivers/leds/leds-aw21024.c
@@ -0,0 +1,314 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Awinic AW21024 LED chip driver
+// Copyright (C) 2022 Nordix Foundation https://www.nordix.org
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include <linux/led-class-multicolor.h>
+
+/* Called COL0, COL1,..., COL23 in datasheet */
+#define AW21024_REG_DC_CURRENT(_led)	(0x4a + (_led))
+
+/* Called BR0, BR1,..., BR23 in datasheet */
+#define AW21024_REG_BRIGHTNESS(_led)	(0x01 + (_led))
+
+#define AW21024_REG_UPDATE				0x49 /* Write 0x00 to update BR */
+
+#define AW21024_REG_GCR0				0x00 /* Global configuration register */
+#define AW21024_REG_GCC					0x6e /* Global current control */
+#define AW21024_REG_SW_RESET			0x7f
+#define AW21024_REG_VERSION				0x7e
+
+#define AW21024_GCR0_CHIPEN				BIT(0)
+#define AW21024_CHIP_ID					0x18
+#define AW21024_CHIP_VERSION			0xA8
+
+struct aw21024_led_data {
+	struct led_classdev_mc mc_cdev;
+	struct work_struct work;
+	unsigned int *regs;
+	unsigned int nregs;
+	struct aw21024 *parent;
+};
+
+struct aw21024 {
+	struct i2c_client *client;
+	struct device *dev;
+	struct gpio_desc *enable_gpio;
+	struct mutex lock;
+	struct aw21024_led_data **leds;
+	unsigned int nleds;
+};
+
+static int aw21024_led_brightness_set(struct led_classdev *led_cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
+	struct aw21024_led_data *led = container_of(mc_cdev, struct aw21024_led_data, mc_cdev);
+	struct aw21024 *parent = led->parent;
+	int i;
+	int ret = 0;
+
+	mutex_lock(&parent->lock);
+	if (mc_cdev->num_colors && mc_cdev->subled_info) {
+		for (i = 0; i < led->nregs; i++) {
+			ret = i2c_smbus_write_byte_data(parent->client,
+					AW21024_REG_DC_CURRENT(led->regs[i]),
+					mc_cdev->subled_info[i].intensity);
+			if (ret < 0)
+				goto unlock_ret;
+
+			ret = i2c_smbus_write_byte_data(parent->client,
+					AW21024_REG_BRIGHTNESS(led->regs[i]),
+					brightness);
+			if (ret < 0)
+				goto unlock_ret;
+		}
+	} else {
+		ret = i2c_smbus_write_byte_data(parent->client,
+					AW21024_REG_DC_CURRENT(led->regs[0]), 0xFF);
+		if (ret < 0)
+			goto unlock_ret;
+
+		ret = i2c_smbus_write_byte_data(parent->client,
+					AW21024_REG_BRIGHTNESS(led->regs[0]),
+					brightness);
+		if (ret < 0)
+			goto unlock_ret;
+	}
+	ret = i2c_smbus_write_byte_data(parent->client, AW21024_REG_UPDATE, 0x0);
+unlock_ret:
+	mutex_unlock(&parent->lock);
+	return ret;
+}
+
+static int aw21024_probe_dt(struct aw21024 *data)
+{
+	struct device *dev = &data->client->dev;
+	struct fwnode_handle *child = NULL;
+	struct fwnode_handle *led_node = NULL;
+	struct led_init_data init_data = {};
+	u32 color_id;
+	int  ret, num_colors;
+	unsigned int nleds = 0;
+	struct aw21024_led_data *led;
+	struct led_classdev *led_cdev;
+	struct mc_subled *mc_led_info;
+
+	nleds = device_get_child_node_count(dev);
+
+	data->leds = devm_kcalloc(dev, nleds, sizeof(*(data->leds)), GFP_KERNEL);
+	if (!data->leds)
+		return -ENOMEM;
+
+	device_for_each_child_node(dev, child) {
+		led = devm_kzalloc(dev, sizeof(struct aw21024_led_data), GFP_KERNEL);
+		if (!led) {
+			ret = -ENOMEM;
+			goto ret_put_child;
+		}
+		led->parent = data;
+		led_cdev = &led->mc_cdev.led_cdev;
+		init_data.fwnode = child;
+
+		led_cdev->brightness_set_blocking = aw21024_led_brightness_set;
+		data->leds[data->nleds] = led;
+
+		ret = fwnode_property_count_u32(child, "reg");
+		if (ret < 0) {
+			dev_err(dev, "reg property is invalid in node %s\n",
+					fwnode_get_name(child));
+			goto ret_put_child;
+		}
+
+		led->regs = devm_kcalloc(dev, ret, sizeof(*(led->regs)), GFP_KERNEL);
+		led->nregs = ret;
+		if (!led->regs) {
+			ret = -ENOMEM;
+			goto ret_put_child;
+		}
+
+		ret = fwnode_property_read_u32_array(child, "reg", led->regs, led->nregs);
+		if (ret) {
+			dev_err(dev, "Failed to read reg array, error=%d\n", ret);
+			goto ret_put_child;
+		}
+
+		if (led->nregs > 1) {
+			mc_led_info = devm_kcalloc(dev, led->nregs,
+						    sizeof(*mc_led_info), GFP_KERNEL);
+			if (!mc_led_info) {
+				ret = -ENOMEM;
+				goto ret_put_child;
+			}
+
+			num_colors = 0;
+			fwnode_for_each_child_node(child, led_node) {
+				if (num_colors > led->nregs) {
+					ret = -EINVAL;
+					fwnode_handle_put(led_node);
+					goto ret_put_child;
+				}
+
+				ret = fwnode_property_read_u32(led_node, "color",
+							       &color_id);
+				if (ret) {
+					fwnode_handle_put(led_node);
+					goto ret_put_child;
+				}
+				mc_led_info[num_colors].color_index = color_id;
+				num_colors++;
+			}
+
+			led->mc_cdev.num_colors = num_colors;
+			led->mc_cdev.subled_info = mc_led_info;
+			ret = devm_led_classdev_multicolor_register_ext(dev,
+								&led->mc_cdev,
+								&init_data);
+			if (ret < 0) {
+				dev_warn(dev, "Failed to register multicolor LED %s, err=%d\n",
+						    fwnode_get_name(child), ret);
+				goto ret_put_child;
+			}
+		} else {
+			ret = devm_led_classdev_register_ext(dev,
+							    &led->mc_cdev.led_cdev, &init_data);
+			if (ret < 0) {
+				dev_warn(dev, "Failed to register LED %s, err=%d\n",
+						fwnode_get_name(child), ret);
+				goto ret_put_child;
+			}
+		}
+		data->nleds++;
+	}
+
+	return 0;
+
+ret_put_child:
+	fwnode_handle_put(child);
+	return ret;
+}
+
+/* Expected to be called prior to registering with the LEDs class */
+static int aw21024_configure(struct aw21024 *priv)
+{
+	int ret = 0;
+	struct i2c_client *client = priv->client;
+
+	ret = i2c_smbus_write_byte_data(client, AW21024_REG_GCR0, AW21024_GCR0_CHIPEN);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to write chip enable\n");
+		return -ENODEV;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, AW21024_REG_SW_RESET);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to read chip id\n");
+		return -ENODEV;
+	}
+
+	if (ret != AW21024_CHIP_ID) {
+		dev_err(&client->dev, "Chip ID 0x%02X doesn't match expected (0x%02X)\n",
+								ret, AW21024_CHIP_ID);
+		return -ENODEV;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, AW21024_REG_VERSION);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to read chip version\n");
+		return -ENODEV;
+	}
+	if (ret != AW21024_CHIP_VERSION)
+		dev_warn(&client->dev, "Chip version 0x%02X doesn't match expected 0x%02X\n",
+								ret, AW21024_CHIP_VERSION);
+
+	i2c_smbus_write_byte_data(client, AW21024_REG_SW_RESET, 0x00);
+	mdelay(2);
+	i2c_smbus_write_byte_data(client, AW21024_REG_GCR0, AW21024_GCR0_CHIPEN);
+	i2c_smbus_write_byte_data(client, AW21024_REG_GCC, 0xFF);
+
+	return 0;
+}
+
+static int aw21024_probe(struct i2c_client *client)
+{
+	struct aw21024 *priv;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client = client;
+	priv->dev = &client->dev;
+
+	mutex_init(&priv->lock);
+
+	priv->enable_gpio = devm_gpiod_get_optional(priv->dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio))
+		return dev_err_probe(priv->dev, PTR_ERR(priv->enable_gpio),
+				       "Failed to get enable GPIO\n");
+
+	if (priv->enable_gpio) {
+		mdelay(1);
+		gpiod_direction_output(priv->enable_gpio, 1);
+		mdelay(1);
+	}
+
+	i2c_set_clientdata(client, priv);
+
+	ret = aw21024_configure(priv);
+	if (ret < 0)
+		return ret;
+
+	return aw21024_probe_dt(priv);
+}
+
+static int aw21024_remove(struct i2c_client *client)
+{
+	struct aw21024 *priv = i2c_get_clientdata(client);
+	int ret;
+
+	ret = gpiod_direction_output(priv->enable_gpio, 0);
+	if (ret)
+		dev_err(priv->dev, "Failed to disable chip, err=%d\n", ret);
+
+	mutex_destroy(&priv->lock);
+	return 0;
+}
+
+static const struct i2c_device_id aw21024_id[] = {
+	{ "aw21024", 0 }, /* 24 Channel */
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aw21024_id);
+
+static const struct of_device_id of_aw21024_leds_match[] = {
+	{ .compatible = "awinic,aw21024", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_aw21024_leds_match);
+
+static struct i2c_driver aw21024_driver = {
+	.driver		= {
+		.name	= "aw21024",
+		.of_match_table = of_match_ptr(of_aw21024_leds_match),
+	},
+	.probe_new		= aw21024_probe,
+	.remove		= aw21024_remove,
+	.id_table = aw21024_id,
+};
+module_i2c_driver(aw21024_driver);
+
+MODULE_AUTHOR("Kyle Swenson <kyle.swenson@est.tech>");
+MODULE_DESCRIPTION("Awinic AW21024 LED driver");
+MODULE_LICENSE("GPL");