From nobody Wed May  7 19:32:17 2025
Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com
 [209.85.214.182])
	(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))
	(No client certificate requested)
	by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F0491A5B8C;
	Sat,  5 Apr 2025 18:33:29 +0000 (UTC)
Authentication-Results: smtp.subspace.kernel.org;
 arc=none smtp.client-ip=209.85.214.182
ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;
	t=1743878013; cv=none;
 b=iV56dOsTBHl6jr/eHr8v3UnkFTkDIWHqzAfgLn+e5NrU6eF7A++Qj748fEllDtRPGeZ3E6iJjrE/PqIk9cPWLYXkdDPDIQItoyhpePpoln6JTFgK+7Kig7QilRwf+SYWmvuOSYP941dTx59KXCDzUJ88J1Ds5Y3RFf2moaR+pWA=
ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org;
	s=arc-20240116; t=1743878013; c=relaxed/simple;
	bh=+2hYXupkEjy0Gjg8xUmHA8JahtEcmvYnkxPcsfNcrvE=;
	h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:
	 MIME-Version:Content-Type;
 b=IlMIGkpsuPnGakgFuFTx8mt0I+JloA5RT/a0P4KxJav8CIhs1rrWWzmATHpPm/hn4rhfj0U8ZWzLOSswFffgNNwALVKAlu3vBtfyOSWnUE9C3lWvclJu0wpZ7lcoaRbbZNpCSAxIHs/8w/opw2jqbyzEptIr1OvriRsEuFewZg4=
ARC-Authentication-Results: i=1; smtp.subspace.kernel.org;
 dmarc=pass (p=none dis=none) header.from=gmail.com;
 spf=pass smtp.mailfrom=gmail.com;
 dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com
 header.b=KYaZEB98; arc=none smtp.client-ip=209.85.214.182
Authentication-Results: smtp.subspace.kernel.org;
 dmarc=pass (p=none dis=none) header.from=gmail.com
Authentication-Results: smtp.subspace.kernel.org;
 spf=pass smtp.mailfrom=gmail.com
Authentication-Results: smtp.subspace.kernel.org;
	dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com
 header.b="KYaZEB98"
Received: by mail-pl1-f182.google.com with SMTP id
 d9443c01a7336-2264aefc45dso40832765ad.0;
        Sat, 05 Apr 2025 11:33:29 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20230601; t=1743878008; x=1744482808;
 darn=vger.kernel.org;
        h=content-transfer-encoding:mime-version:references:in-reply-to
         :message-id:date:subject:cc:to:from:from:to:cc:subject:date
         :message-id:reply-to;
        bh=b49Qib5wPH9sBYHmQ5Oygz2nJAhksJmt1HDiGwQ5O+M=;
        b=KYaZEB98ZW8yrVcvZTEYQU/4OY94nMCL92oWAiTmSxw+SufFFUC/L3EQu/kg00LTSS
         sClJU17XW9bMK+jtR6O+4rDYFRy6GV97p/56ZFLqDFOQwttTcdl3nQTwLTyn5R6QMRAw
         JW7U6o91yVIXg/q3J5YzKgVzh9cputxX24BwLyNRWPtQIb56hT1O0AlSUmSEg03txbxR
         9+wkzkFtiSVw0xc1U2FhjN/OMW0GleGaqZ618MXvqEKaWtjmTFf/VUjymI1J0GlVU8Jo
         4SIIHbsDcZoQTDrHINmnlLsH6YQq6TVvDnBMRXOoxwcaDXUz/9CagrqmPq6ewUyDOznb
         /DrA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20230601; t=1743878008; x=1744482808;
        h=content-transfer-encoding:mime-version:references:in-reply-to
         :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
         :subject:date:message-id:reply-to;
        bh=b49Qib5wPH9sBYHmQ5Oygz2nJAhksJmt1HDiGwQ5O+M=;
        b=ryr6TceQFpXXhnfePQP+b41zcw26znTZAh7qYTlSyeu9Ou4rjJnKD+KOnLtLwv2WcS
         IDiWNp8H5So28Ycmp+aMneOdaahN2HIERJYB18uJm1AbjgziBMV05KNV4JtRWsog/EBr
         gO9mEzUDo+VFgqHcTR8Pwzp40FZxI1BwHyGI4T2Gy0luBsh4Zz+7z9sx4sF4Oiyc/scP
         pp6q97NZYsPDzvvwJ9r1AARxs9Iwl+JTnR9NIjL4DyNK3VIYYb0yxmCyESvTFyn1CIub
         dR9/n7OlsxXNnnh2DtsrgDHn9tdvVulbl9z8/coUiAnP3E7oxjbq/mGkb/74LVpwUXtf
         njhA==
X-Forwarded-Encrypted: i=1;
 AJvYcCVWOHq9xclkJ2RXEbd95dO855CsOTokiHOUhE0bPfmSUJboPMJOgJz/2GqNHJfGpGV+gX+j6yjx3vqChaM=@vger.kernel.org,
 AJvYcCVnFao8Q7QeZVfZp9YuTBG6MKMn2y/9llV+pA7foHbzrw8GLCjxEp7n2/7cMu/kYh02cBRLI8iW73AIlQ==@vger.kernel.org
X-Gm-Message-State: AOJu0YwiMJVYw1U/y4Oe5n7F3+wJqxQXlkcxY04jEFtM472PUCxW+6Ge
	z5Ad/nVNiYWL/lmN3aa52fOjMc4vOhVWEs+n9jZ71BoCM/2h9yERSznFIXiD
X-Gm-Gg: ASbGnctiY4RrLm4YVZ1ki3n0s3QO7mRZdbkOqktYL9hYvV/VWXW3uTYognMoFpNpaTK
	ug9GUMJP6v9r3wExgKf3iF4Ccv2QXRNBhIPse3MWgrktCXLKPoPXEaJ4f1ozjCVmCdcwpjl56gB
	PwL6gfzzNf5x3xr71oCnpSu1lC4G9d65I/lFIsQXfvphzcZin9bAtjUdlUxU61WD6wvNv5NbOTV
	jRFZ1gVVzWK2PHFJm9ap0v8WUjf0XhkvZikxUO/l+sf4h3wWjNfIJTJbqpYgLA3myHJToukREw7
	fIsN1WweROpKNpDmpwJj4NcFEZ2jHPXDX2Zr/+oKCdvQBmxAZ0ud3590
X-Google-Smtp-Source: 
 AGHT+IEd1leGFUQMpacZd4MtegN4ATe5sQPZIx3ZKqmgzLgGXZ0tjVOfh2XDqVxlVLcdN+680BVUPA==
X-Received: by 2002:a17:903:3baf:b0:21f:164d:93fe with SMTP id
 d9443c01a7336-22a8a8e437dmr117306895ad.53.1743878008078;
        Sat, 05 Apr 2025 11:33:28 -0700 (PDT)
Received: from localhost.localdomain ([123.17.0.117])
        by smtp.gmail.com with ESMTPSA id
 d9443c01a7336-2297866e61dsm52980965ad.190.2025.04.05.11.33.24
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Sat, 05 Apr 2025 11:33:27 -0700 (PDT)
From: Nam Tran <trannamatk@gmail.com>
To: pavel@kernel.org,
	lee@kernel.org,
	krzk+dt@kernel.org,
	robh@kernel.org,
	conor+dt@kernel.org,
	corbet@lwn.net
Cc: devicetree@vger.kernel.org,
	linux-leds@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Nam Tran <trannamatk@gmail.com>
Subject: [PATCH v4 2/5] leds: add TI/National Semiconductor LP5812 LED Driver
Date: Sun,  6 Apr 2025 01:32:43 +0700
Message-Id: <20250405183246.198568-3-trannamatk@gmail.com>
X-Mailer: git-send-email 2.25.1
In-Reply-To: <20250405183246.198568-1-trannamatk@gmail.com>
References: <20250405183246.198568-1-trannamatk@gmail.com>
Precedence: bulk
X-Mailing-List: linux-kernel@vger.kernel.org
List-Id: <linux-kernel.vger.kernel.org>
List-Subscribe: <mailto:linux-kernel+subscribe@vger.kernel.org>
List-Unsubscribe: <mailto:linux-kernel+unsubscribe@vger.kernel.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

The LP5812 is a 4=C3=973 matrix RGB LED driver
with an autonomous animation engine
and time-cross-multiplexing (TCM) support for up to 12 LEDs.
Each LED can be configured through the related registers
to realize vivid and fancy lighting effects.

Signed-off-by: Nam Tran <trannamatk@gmail.com>
---
 MAINTAINERS                |    4 +
 drivers/leds/Kconfig       |   16 +
 drivers/leds/Makefile      |    1 +
 drivers/leds/leds-lp5812.c | 2821 ++++++++++++++++++++++++++++++++++++
 drivers/leds/leds-lp5812.h |  350 +++++
 5 files changed, 3192 insertions(+)
 create mode 100644 drivers/leds/leds-lp5812.c
 create mode 100644 drivers/leds/leds-lp5812.h

diff --git a/MAINTAINERS b/MAINTAINERS
index afbc481d0311..3fcc7192e01a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23536,6 +23536,10 @@ M:	Nam Tran <trannamatk@gmail.com>
 L:	linux-leds@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/leds/ti,lp5812.yaml
+F:	drivers/leds/Kconfig
+F:	drivers/leds/Makefile
+F:	drivers/leds/leds-lp5812.c
+F:	drivers/leds/leds-lp5812.h
=20
 TEXAS INSTRUMENTS' LB8864 LED BACKLIGHT DRIVER
 M:	Alexander Sverdlin <alexander.sverdlin@siemens.com>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 2b27d043921c..fa3acc9fa4da 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -483,6 +483,22 @@ config LEDS_LP5569
 	  Driver provides direct control via LED class and interface for
 	  programming the engines.
=20
+config LEDS_LP5812
+	tristate "Enable LP5812 support LED matrix 4x3"
+	depends on LEDS_CLASS && I2C
+	depends on LEDS_CLASS_MULTICOLOR
+	help
+	  If you say Y here you get support for TI LP5812 LED driver.
+
+	  The LP5812 is an I2C-based RGB LED driver that supports a 4x3 LED matri=
x.
+	  It provides independent brightness control and color mixing for each LE=
D.
+	  This driver integrates with the Linux LED framework and LED multicolor =
class.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called leds-lp5812.
+
+	  If unsure, say N.
+
 config LEDS_LP8501
 	tristate "LED Support for TI LP8501 LED driver chip"
 	depends on LEDS_CLASS && I2C
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6ad52e219ec6..944278f1673e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_LP5523)		+=3D leds-lp5523.o
 obj-$(CONFIG_LEDS_LP5562)		+=3D leds-lp5562.o
 obj-$(CONFIG_LEDS_LP5569)		+=3D leds-lp5569.o
 obj-$(CONFIG_LEDS_LP55XX_COMMON)	+=3D leds-lp55xx-common.o
+obj-$(CONFIG_LEDS_LP5812) 		+=3D leds-lp5812.o
 obj-$(CONFIG_LEDS_LP8501)		+=3D leds-lp8501.o
 obj-$(CONFIG_LEDS_LP8788)		+=3D leds-lp8788.o
 obj-$(CONFIG_LEDS_LP8860)		+=3D leds-lp8860.o
diff --git a/drivers/leds/leds-lp5812.c b/drivers/leds/leds-lp5812.c
new file mode 100644
index 000000000000..033753eb0245
--- /dev/null
+++ b/drivers/leds/leds-lp5812.c
@@ -0,0 +1,2821 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LP5812 LED driver
+ *
+ * Copyright (C) 2025 Texas Instruments
+ *
+ * Author: Jared Zhou <jared-zhou@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mod_devicetable.h>
+#include <linux/string.h>
+
+#include "leds-lp5812.h"
+
+#define to_lp5812_led(x) container_of(x, struct lp5812_led, kobj)
+#define to_anim_engine_unit(x) container_of(x, struct anim_engine_unit, ko=
bj)
+
+static int lp5812_init_dev_config(struct lp5812_chip *chip,
+		const char *drive_mode, int rm_led_sysfs);
+
+static struct drive_mode_led_map chip_leds_map[] =3D {
+	{
+		"direct_mode",
+		(const char *[]){LED0, LED1, LED2, LED3, NULL}
+	},
+	{
+		"tcmscan:1:0", /* tcm 1 scan; scan order 0 out0 */
+		(const char *[]){LED_A0, LED_A1, LED_A2, NULL}
+	},
+	{
+		"tcmscan:1:1", /* tcm 1 scan; scan order 0 out1 */
+		(const char *[]){LED_B0, LED_B1, LED_B2, NULL}
+	},
+	{
+		"tcmscan:1:2", /* tcm 1 scan; scan order 0 out2 */
+		(const char *[]){LED_C0, LED_C1, LED_C2, NULL}
+	},
+	{
+		"tcmscan:1:3", /* tcm 1 scan; scan order 0 out3 */
+		(const char *[]){LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out0; scan order 1 out1 */
+		"tcmscan:2:0:1",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_B0, LED_B1, LED_B2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out0; scan order 1 out2 */
+		"tcmscan:2:0:2",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_C0, LED_C1, LED_C2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out0; scan order 1 out3 */
+		"tcmscan:2:0:3",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out1; scan order 1 out2 */
+		"tcmscan:2:1:2",
+		(const char *[]){LED_B0, LED_B1, LED_B2, LED_C0, LED_C1, LED_C2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out1; scan order 1 out3 */
+		"tcmscan:2:1:3",
+		(const char *[]){LED_B0, LED_B1, LED_B2, LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 2 scan, scan order 0 out2; scan order 1 out3 */
+		"tcmscan:2:2:3",
+		(const char *[]){LED_C0, LED_C1, LED_C2, LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 3 scan, scan order 0 out0; scan order 1 out1;
+	   * scan order 2 out2
+	   */
+		"tcmscan:3:0:1:2",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_B0, LED_B1, LED_B2,
+		LED_C0, LED_C1, LED_C2, NULL}
+	},
+	{ /* tcm 3 scan, scan order 0 out0; scan order 1 out1;
+	   * scan order 2 out3
+	   */
+		"tcmscan:3:0:1:3",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_B0, LED_B1, LED_B2,
+		LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 3 scan, scan order 0 out0; scan order 1 out2;
+	   * scan order 2 out3
+	   */
+		"tcmscan:3:0:2:3",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_C0, LED_C1, LED_C2,
+		LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* tcm 4 scan, scan order 0 out0; scan order 1 out1;
+	   * scan order 2 out2; scan order 3 out3
+	   */
+		"tcmscan:4:0:1:2:3",
+		(const char *[]){LED_A0, LED_A1, LED_A2, LED_B0, LED_B1, LED_B2,
+		LED_C0, LED_C1, LED_C2, LED_D0, LED_D1, LED_D2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out0; scan order 0 out1 */
+		"mixscan:1:0:1",
+		(const char *[]){LED0, LED_B0, LED_B1, NULL}
+	},
+	{ /* mix 1 scan, direct connect out0; scan order 0 out2 */
+		"mixscan:1:0:2",
+		(const char *[]){LED0, LED_C0, LED_C2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out0; scan order 0 out3 */
+		"mixscan:1:0:3",
+		(const char *[]){LED0, LED_D1, LED_D2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out1; scan order 0 out0 */
+		"mixscan:1:1:0",
+		(const char *[]){LED1, LED_A1, LED_A2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out1; scan order 0 out2 */
+		"mixscan:1:1:2",
+		(const char *[]){LED1, LED_C0, LED_C1, NULL}
+	},
+	{ /* mix 1 scan, direct connect out1; scan order 0 out3 */
+		"mixscan:1:1:3",
+		(const char *[]){LED1, LED_D0, LED_D2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out2; scan order 0 out0 */
+		"mixscan:1:2:0",
+		(const char *[]){LED2, LED_A0, LED_A2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out2; scan order 0 out1 */
+		"mixscan:1:2:1",
+		(const char *[]){LED2, LED_B1, LED_B2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out2; scan order 0 out3 */
+		"mixscan:1:2:3",
+		(const char *[]){LED2, LED_D0, LED_D1, NULL}
+	},
+	{ /* mix 1 scan, direct connect out3; scan order 0 out0 */
+		"mixscan:1:3:0",
+		(const char *[]){LED3, LED_A0, LED_A1, NULL}
+	},
+	{ /* mix 1 scan, direct connect out3; scan order 0 out1 */
+		"mixscan:1:3:1",
+		(const char *[]){LED3, LED_B0, LED_B2, NULL}
+	},
+	{ /* mix 1 scan, direct connect out3; scan order 0 out2 */
+		"mixscan:1:3:2",
+		(const char *[]){LED3, LED_C1, LED_C2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out0; scan order 0 out1;
+	   * scan order 1 out2
+	   */
+		"mixscan:2:0:1:2",
+		(const char *[]){LED0, LED_B0, LED_B1, LED_C0, LED_C2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out0; scan order 0 out1;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:0:1:3",
+		(const char *[]){LED0, LED_B0, LED_B1, LED_D1, LED_D2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out0; scan order 0 out2;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:0:2:3",
+		(const char *[]){LED0, LED_C0, LED_C2, LED_D1, LED_D2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out1; scan order 0 out0;
+	   * scan order 1 out2
+	   */
+		"mixscan:2:1:0:2",
+		(const char *[]){LED1, LED_A1, LED_A2, LED_C0, LED_C1, NULL}
+	},
+	{ /* mix 2 scan, direct connect out1; scan order 0 out0;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:1:0:3",
+		(const char *[]){LED1, LED_A1, LED_A2, LED_D0, LED_D2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out1; scan order 0 out2;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:1:2:3",
+		(const char *[]){LED1, LED_C0, LED_C1, LED_D0, LED_D2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out2; scan order 0 out0;
+	   * scan order 1 out1
+	   */
+		"mixscan:2:2:0:1",
+		(const char *[]){LED2, LED_A0, LED_A2, LED_B1, LED_B2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out2; scan order 0 out0;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:2:0:3",
+		(const char *[]){LED2, LED_A0, LED_A2, LED_D0, LED_D1, NULL}
+	},
+	{ /* mix 2 scan, direct connect out2; scan order 0 out1;
+	   * scan order 1 out3
+	   */
+		"mixscan:2:2:1:3",
+		(const char *[]){LED2, LED_B1, LED_B2, LED_D0, LED_D1, NULL}
+	},
+	{ /* mix 2 scan, direct connect out3; scan order 0 out0;
+	   * scan order 1 out1
+	   */
+		"mixscan:2:3:0:1",
+		(const char *[]){LED3, LED_A0, LED_A1, LED_B0, LED_B2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out3; scan order 0 out0;
+	   * scan order 1 out2
+	   */
+		"mixscan:2:3:0:2",
+		(const char *[]){LED3, LED_A0, LED_A1, LED_C1, LED_C2, NULL}
+	},
+	{ /* mix 2 scan, direct connect out3; scan order 0 out1;
+	   * scan order 1 out2
+	   */
+		"mixscan:2:3:1:2",
+		(const char *[]){LED3, LED_B0, LED_B2, LED_C1, LED_C2, NULL}
+	},
+	{ /* mix 3 scan, direct connect out0; scan order 0 out1;
+	   * scan order 1 out2; scan order 2 out3
+	   */
+		"mixscan:3:0:1:2:3",
+		(const char *[]){LED0, LED_B0, LED_B1, LED_C0, LED_C2, LED_D1,
+		LED_D2, NULL}
+	},
+	{ /* mix 3 scan, direct connect out1; scan order 0 out0;
+	   * scan order 1 out2; scan order 2 out3
+	   */
+		"mixscan:3:1:0:2:3",
+		(const char *[]){LED1, LED_A1, LED_A2, LED_C0, LED_C1, LED_D0,
+		LED_D2, NULL}
+	},
+	{ /* mix 3 scan, direct connect out2; scan order 0 out0;
+	   * scan order 1 out1; scan order 2 out3
+	   */
+		"mixscan:3:2:0:1:3",
+		(const char *[]){LED2, LED_A0, LED_A2, LED_B1, LED_B2, LED_D0,
+		LED_D1, NULL}
+	},
+	{ /* mix 3 scan, direct connect out3; scan order 0 out0;
+	   * scan order 1 out1; scan order 2 out2
+	   */
+		"mixscan:3:3:0:1:2",
+		(const char *[]){LED3, LED_A0, LED_A1, LED_B0, LED_B2, LED_C1,
+		LED_C2, NULL}
+	}
+};
+
+static const char *led_name_array[MAX_LEDS] =3D {
+	LED0, LED1, LED2, LED3, LED_A0, LED_A1, LED_A2, LED_B0, LED_B1,
+	LED_B2, LED_C0, LED_C1, LED_C2, LED_D0, LED_D1, LED_D2
+};
+
+static u16 anim_base_addr_array[MAX_LEDS] =3D {
+	LED0_AUTO_BASE_ADRR, LED1_AUTO_BASE_ADRR, LED2_AUTO_BASE_ADRR,
+	LED3_AUTO_BASE_ADRR, LED_A0_AUTO_BASE_ADRR, LED_A1_AUTO_BASE_ADRR,
+	LED_A2_AUTO_BASE_ADRR, LED_B0_AUTO_BASE_ADRR, LED_B1_AUTO_BASE_ADRR,
+	LED_B2_AUTO_BASE_ADRR, LED_C0_AUTO_BASE_ADRR, LED_C1_AUTO_BASE_ADRR,
+	LED_C2_AUTO_BASE_ADRR, LED_D0_AUTO_BASE_ADRR, LED_D1_AUTO_BASE_ADRR,
+	LED_D2_AUTO_BASE_ADRR
+};
+
+static const char *time_name_array[MAX_TIME] =3D {
+	TIME0, TIME1, TIME2, TIME3, TIME4, TIME5, TIME6, TIME7, TIME8,
+	TIME9, TIME10, TIME11, TIME12, TIME13, TIME14, TIME15
+};
+
+static const char *led_playback_time_arr[MAX_TIME] =3D {
+	"0 time", "1 time", "2 times", "3 times", "4 times", "5 times",
+	"6 times", "7 times", "8 times", "9 times", "10 times", "11 times",
+	"12 times", "13 times", "14 times", "infinite times"
+};
+
+static const char *aeu_name_array[MAX_AEU] =3D {AEU1, AEU2, AEU3};
+
+static const struct lp5812_specific_regs regs =3D {
+	.enable_reg            =3D CHIP_EN_REG,
+	.reset_reg             =3D RESET_REG,
+	.update_cmd_reg        =3D CMD_UPDATE_REG,
+	.start_cmd_reg         =3D CMD_START_REG,
+	.stop_cmd_reg          =3D CMD_STOP_REG,
+	.pause_cmd_reg         =3D CMD_PAUSE_REG,
+	.continue_cmd_reg      =3D CMD_CONTINUE_REG,
+	.fault_clear_reg       =3D FAULT_CLEAR_REG,
+	.tsd_config_status_reg =3D TSD_CONFIG_STAT_REG,
+};
+
+static void led_kobj_release(struct kobject *kobj)
+{
+	kfree(kobj);
+}
+
+static void aeu_kobj_release(struct kobject *kobj)
+{
+	kfree(kobj);
+}
+
+static const struct kobj_type led_ktype =3D {
+	.release =3D led_kobj_release,
+	.sysfs_ops =3D &kobj_sysfs_ops,
+};
+
+static const struct kobj_type aeu_ktype =3D {
+	.release =3D aeu_kobj_release,
+	.sysfs_ops =3D &kobj_sysfs_ops,
+};
+
+static int lp5812_write(struct lp5812_chip *chip, u16 reg, u8 val)
+{
+	int ret;
+	u8 extracted_bits; /* save 9th and 8th bit of reg address */
+	struct i2c_msg msg;
+	u8 buf[2] =3D {(u8)(reg & 0xFF), val};
+
+	extracted_bits =3D (reg >> 8) & 0x03;
+	msg.addr =3D (chip->i2c_cl->addr << 2) | extracted_bits;
+	msg.flags =3D 0;
+	msg.len =3D sizeof(buf);
+	msg.buf =3D buf;
+
+	ret =3D i2c_transfer(chip->i2c_cl->adapter, &msg, 1);
+	if (ret !=3D 1) {
+		dev_err(chip->dev, "i2c write error, register 0x%02X, ret=3D%d\n", reg, =
ret);
+		ret =3D ret < 0 ? ret : -EIO;
+	} else {
+		ret =3D 0;
+	}
+
+	return ret;
+}
+
+static int lp5812_read(struct lp5812_chip *chip, u16 reg, u8 *val)
+{
+	int ret;
+	u8 ret_val;  /* lp5812_chip return value */
+	u8 extracted_bits; /* save 9th and 8th bit of reg address */
+	u8 converted_reg;  /* extracted 8bit from reg */
+	struct i2c_msg msgs[2];
+
+	extracted_bits =3D (reg >> 8) & 0x03;
+	converted_reg =3D (u8)(reg & 0xFF);
+
+	msgs[0].addr =3D (chip->i2c_cl->addr << 2) | extracted_bits;
+	msgs[0].flags =3D 0;
+	msgs[0].len =3D 1;
+	msgs[0].buf =3D &converted_reg;
+
+	msgs[1].addr =3D (chip->i2c_cl->addr << 2) | extracted_bits;
+	msgs[1].flags =3D I2C_M_RD;
+	msgs[1].len =3D 1;
+	msgs[1].buf =3D &ret_val;
+
+	ret =3D i2c_transfer(chip->i2c_cl->adapter, msgs, 2);
+	if (ret !=3D 2) {
+		dev_err(chip->dev, "Read register 0x%02X error, ret=3D%d\n", reg, ret);
+		*val =3D 0;
+		ret =3D ret < 0 ? ret : -EIO;
+	} else {
+		*val =3D ret_val;
+		ret =3D 0;
+	}
+
+	return ret;
+}
+
+static int lp5812_update_bit(struct lp5812_chip *chip, u16 reg, u8 mask, u=
8 val)
+{
+	int ret;
+	u8 tmp;
+
+	ret =3D lp5812_read(chip, reg, &tmp);
+	if (ret)
+		return ret;
+
+	tmp &=3D ~mask;
+	tmp |=3D val & mask;
+
+	return lp5812_write(chip, reg, tmp);
+}
+
+static int lp5812_read_tsd_config_status(struct lp5812_chip *chip, u8 *reg=
_val)
+{
+	int ret =3D 0;
+
+	if (!reg_val)
+		return -1;
+
+	ret =3D lp5812_read(chip, chip->regs->tsd_config_status_reg, reg_val);
+
+	return ret;
+}
+
+static int lp5812_update_regs_config(struct lp5812_chip *chip)
+{
+	int ret;
+	u8 reg_val; /* save register value */
+
+	/* Send update command to update config setting */
+	ret =3D lp5812_write(chip, chip->regs->update_cmd_reg, UPDATE_CMD_VAL);
+	if (ret)
+		return ret;
+	/* check if the configuration is proper */
+	ret =3D lp5812_read_tsd_config_status(chip, &reg_val);
+	if (ret =3D=3D 0)
+		return (int)(reg_val & 0x01);
+
+	return ret;
+}
+
+static int lp5812_read_lod_status(struct lp5812_chip *chip, int led_number=
, u8 *val)
+{
+	int ret =3D 0;
+	u16 reg =3D 0;
+	u8 reg_val =3D 0;
+
+	if (!val)
+		return -1;
+
+	if (led_number < 0x8)
+		reg =3D LOD_STAT_1_REG;
+	else
+		reg =3D LOD_STAT_2_REG;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*val =3D (reg_val & (1 << (led_number % 8))) ? 1 : 0;
+
+	return ret;
+}
+
+static int lp5812_read_lsd_status(struct lp5812_chip *chip, int led_number=
, u8 *val)
+{
+	int ret =3D 0;
+	u16 reg =3D 0;
+	u8 reg_val =3D 0;
+
+	if (!val)
+		return -1;
+
+	if (led_number < 0x8)
+		reg =3D LSD_STAT_1_REG;
+	else
+		reg =3D LSD_STAT_2_REG;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*val =3D (reg_val & (1 << (led_number % 8))) ? 1 : 0;
+
+	return ret;
+}
+
+static int lp5812_read_auto_pwm_value(struct lp5812_chip *chip, int led_nu=
mber,
+		u8 *val)
+{
+	int ret =3D 0;
+	u16 reg =3D 0;
+	u8 reg_val =3D 0;
+
+	reg =3D AUTO_PWM_BASE_ADDR + led_number;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*val =3D reg_val;
+
+	return ret;
+}
+
+static int lp5812_read_aep_status(struct lp5812_chip *chip, int led_number=
, u8 *val)
+{
+	int ret =3D 0;
+	u16 reg;
+	u8 reg_val;
+
+	switch (led_number / 2) {
+	case 0:
+		reg =3D AEP_STATUS_0_REG; // LED_0 and LED_1
+		break;
+	case 1:
+		reg =3D AEP_STATUS_1_REG; // LED_2 and LED_3
+		break;
+	case 2:
+		reg =3D AEP_STATUS_2_REG; // LED_A0 and LED_A1
+		break;
+	case 3:
+		reg =3D AEP_STATUS_3_REG; // LED_A2 and LED_B0
+		break;
+	case 4:
+		reg =3D AEP_STATUS_4_REG; // LED_B1 and LED_B2
+		break;
+	case 5:
+		reg =3D AEP_STATUS_5_REG; // LED_C0 and LED_C1
+		break;
+	case 6:
+		reg =3D AEP_STATUS_6_REG; // LED_C2 and LED_D0
+		break;
+	case 7:
+		reg =3D AEP_STATUS_7_REG; // LED_D1 and LED_D2
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*val =3D (led_number % 2) ? ((reg_val >> 3) & 0x07) : (reg_val & 0x07);
+
+	return ret;
+}
+
+static int lp5812_enable_disable(struct lp5812_chip *chip, int enable)
+{
+	return lp5812_write(chip, chip->regs->enable_reg, (u8)enable);
+}
+
+static int lp5812_reset(struct lp5812_chip *chip)
+{
+	return lp5812_write(chip, chip->regs->reset_reg, RESET_REG_VAL);
+}
+
+static int lp5812_fault_clear(struct lp5812_chip *chip, u8 value)
+{
+	u8 reg_val;
+
+	if (value =3D=3D 0)
+		reg_val =3D LOD_CLEAR_VAL;
+	else if (value =3D=3D 1)
+		reg_val =3D LSD_CLEAR_VAL;
+	else if (value =3D=3D 2)
+		reg_val =3D TSD_CLEAR_VAL;
+	else if (value =3D=3D 3)
+		reg_val =3D FAULT_CLEAR_ALL;
+	else
+		return -EINVAL;
+
+	return lp5812_write(chip, chip->regs->fault_clear_reg, reg_val);
+}
+
+static void lp5812_dump_regs(struct lp5812_chip *chip, u16 from_reg, u16 t=
o_reg)
+{
+	u16 reg_addr;
+	u8 reg_val;
+
+	for (reg_addr =3D from_reg; reg_addr <=3D to_reg; reg_addr++)
+		lp5812_read(chip, reg_addr, &reg_val);
+}
+
+static int lp5812_device_command(struct lp5812_chip *chip, enum device_com=
mand command)
+{
+	switch (command) {
+	case UPDATE:
+		return lp5812_write(chip, chip->regs->update_cmd_reg,
+				UPDATE_CMD_VAL);
+	case START:
+		return lp5812_write(chip, chip->regs->start_cmd_reg,
+				START_CMD_VAL);
+	case STOP:
+		return lp5812_write(chip, chip->regs->stop_cmd_reg,
+				STOP_CMD_VAL);
+	case PAUSE:
+		return lp5812_write(chip, chip->regs->pause_cmd_reg,
+				PAUSE_CMD_VAL);
+	case CONTINUE:
+		return lp5812_write(chip, chip->regs->continue_cmd_reg,
+				CONTINUE_CMD_VAL);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int lp5812_set_pwm_dimming_scale(struct lp5812_chip *chip, int led_=
number,
+		enum pwm_dimming_scale scale)
+{
+	int ret =3D 0;
+	u16 reg;
+	u8 reg_val;
+
+	if (led_number <=3D 7)
+		reg =3D (u16)DEV_CONFIG5;
+	else
+		reg =3D (u16)DEV_CONFIG6;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret !=3D 0)
+		return ret;
+	if (scale =3D=3D LINEAR) {
+		reg_val &=3D ~(1 << (led_number % 8));
+		ret =3D lp5812_write(chip, reg, reg_val);
+		if (ret !=3D 0)
+			return ret;
+	} else {
+		reg_val |=3D (1 << (led_number % 8));
+		ret =3D lp5812_write(chip, reg, reg_val);
+		if (ret !=3D 0)
+			return ret;
+	}
+
+	ret =3D lp5812_update_regs_config(chip);
+
+	return ret;
+}
+
+static int lp5812_get_pwm_dimming_scale(struct lp5812_chip *chip,
+		int led_number, enum pwm_dimming_scale *scale)
+{
+	int ret =3D 0;
+	u16 reg;
+	u8 reg_val;
+
+	if (led_number < 0x8)
+		reg =3D DEV_CONFIG5;
+	else
+		reg =3D DEV_CONFIG6;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*scale =3D (reg_val & (1 << (led_number % 8))) ? EXPONENTIAL : LINEAR;
+
+	return 0;
+}
+
+static int lp5812_set_phase_align(struct lp5812_chip *chip, int led_number,
+		int phase_align_val)
+{
+	int ret;
+	int bit_pos;
+	u16 reg;
+	u8 reg_val;
+
+	reg =3D DEV_CONFIG7 + (led_number / 4);
+	bit_pos =3D (led_number % 4) * 2;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+	reg_val |=3D (phase_align_val << bit_pos);
+	ret =3D lp5812_write(chip, reg, reg_val);
+	if (ret !=3D 0)
+		return ret;
+	ret =3D lp5812_update_regs_config(chip);
+
+	return ret;
+}
+
+static int lp5812_get_phase_align(struct lp5812_chip *chip, int led_number,
+		int *phase_align_val)
+{
+	int ret;
+	int bit_pos;
+	u16 reg;
+	u8 reg_val;
+
+	reg =3D DEV_CONFIG7 + (led_number / 4);
+	bit_pos =3D (led_number % 4) * 2;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*phase_align_val =3D (reg_val >> bit_pos) & 0x03;
+
+	return ret;
+}
+
+static int lp5812_get_led_mode(struct lp5812_chip *chip,
+		int led_number, enum control_mode *mode)
+{
+	int ret =3D 0;
+	u16 reg;
+	u8 reg_val;
+
+	if (led_number < 0x8)
+		reg =3D DEV_CONFIG3;
+	else
+		reg =3D DEV_CONFIG4;
+
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret)
+		return ret;
+
+	*mode =3D (reg_val & (1 << (led_number % 8))) ? AUTONOMOUS : MANUAL;
+
+	return 0;
+}
+
+static int lp5812_set_led_mode(struct lp5812_chip *chip, int led_number,
+		enum control_mode mode)
+{
+	int ret =3D 0;
+	u16 reg;
+	u8 reg_val;
+
+	if (led_number <=3D 7)
+		reg =3D (u16)DEV_CONFIG3;
+	else
+		reg =3D (u16)DEV_CONFIG4;
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret !=3D 0)
+		return ret;
+	if (mode =3D=3D MANUAL) {
+		reg_val &=3D ~(1 << (led_number % 8));
+		ret =3D lp5812_write(chip, reg, reg_val);
+		if (ret !=3D 0)
+			return ret;
+	} else {
+		reg_val |=3D (1 << (led_number % 8));
+		ret =3D lp5812_write(chip, reg, reg_val);
+		if (ret !=3D 0)
+			return ret;
+	}
+
+	ret =3D lp5812_update_regs_config(chip);
+
+	return ret;
+}
+
+static int lp5812_manual_dc_pwm_control(struct lp5812_chip *chip,
+		int led_number, u8 val, enum dimming_type dimming_type)
+{
+	int ret;
+	u16 led_base_reg;
+
+	if (dimming_type =3D=3D ANALOG)
+		led_base_reg =3D (u16)MANUAL_DC_LED_0_REG;
+	else
+		led_base_reg =3D (u16)MANUAL_PWM_LED_0_REG;
+	ret =3D lp5812_write(chip, led_base_reg + led_number, val);
+
+	return ret;
+}
+
+static int lp5812_manual_dc_pwm_read(struct lp5812_chip *chip,
+		int led_number, u8 *val, enum dimming_type dimming_type)
+{
+	int ret;
+	u16 led_base_reg;
+
+	if (dimming_type =3D=3D ANALOG)
+		led_base_reg =3D (u16)MANUAL_DC_LED_0_REG;
+	else
+		led_base_reg =3D (u16)MANUAL_PWM_LED_0_REG;
+	ret =3D lp5812_read(chip, led_base_reg + led_number, val);
+
+	return ret;
+}
+
+static int lp5812_autonomous_dc_pwm_control(struct lp5812_chip *chip,
+		int led_number, u8 val, enum dimming_type dimming_type)
+{
+	int ret;
+	u16 led_base_reg;
+
+	led_base_reg =3D (u16)AUTO_DC_LED_0_REG;
+	ret =3D lp5812_write(chip, led_base_reg + led_number, val);
+
+	return ret;
+}
+
+static int lp5812_autonomous_dc_pwm_read(struct lp5812_chip *chip,
+		int led_number, u8 *val, enum dimming_type dimming_type)
+{
+	int ret;
+	u16 led_base_reg;
+
+	led_base_reg =3D (u16)AUTO_DC_LED_0_REG;
+	ret =3D lp5812_read(chip, led_base_reg + led_number, val);
+
+	return ret;
+}
+
+static int lp5812_disable_all_leds(struct lp5812_chip *chip)
+{
+	int ret =3D 0;
+
+	ret =3D lp5812_write(chip, (u16)LED_ENABLE_1_REG, 0x00);
+	if (ret !=3D 0)
+		return ret;
+	ret =3D lp5812_write(chip, (u16)LED_ENABLE_2_REG, 0x00);
+	if (ret !=3D 0)
+		return ret;
+
+	return ret;
+}
+
+static int lp5812_get_drive_mode_scan_order(struct lp5812_chip *chip)
+{
+	u8 val;
+	int ret =3D 0;
+
+	/* get led mode */
+	ret =3D lp5812_read(chip, (u16)DEV_CONFIG1, &val);
+	if (ret !=3D 0)
+		return ret;
+	chip->u_drive_mode.drive_mode_val =3D val;
+
+	/* get scan order */
+	ret =3D lp5812_read(chip, (u16)DEV_CONFIG2, &val);
+	if (ret !=3D 0)
+		return ret;
+	chip->u_scan_order.scan_order_val =3D val;
+
+	return ret;
+}
+
+static int lp5812_set_drive_mode_scan_order(struct lp5812_chip *chip)
+{
+	u8 val;
+	int ret =3D 0;
+
+	/* Set led mode */
+	val =3D chip->u_drive_mode.drive_mode_val;
+	ret =3D lp5812_write(chip, (u16)DEV_CONFIG1, val);
+	if (ret !=3D 0)
+		return ret;
+
+	/* Setup scan order */
+	val =3D chip->u_scan_order.scan_order_val;
+	ret =3D lp5812_write(chip, (u16)DEV_CONFIG2, val);
+	if (ret !=3D 0)
+		return ret;
+
+	return ret;
+}
+
+static int lp5812_initialize(struct lp5812_chip *chip)
+{
+	int ret =3D 0;
+
+	/* wait for 1 ms */
+	usleep_range(1000, 1100);
+
+	/* enable the lp5812 */
+	ret =3D lp5812_enable_disable(chip, 1);
+	if (ret !=3D 0) {
+		dev_err(chip->dev, "lp5812_enable_disable failed\n");
+		return ret;
+	}
+	ret =3D lp5812_set_drive_mode_scan_order(chip);
+	if (ret !=3D 0) {
+		dev_err(chip->dev, "lp5812_set_drive_mode_scan_order failed\n");
+		return ret;
+	}
+
+	/* Set lsd_threshold =3D 3h to avoid incorrect LSD detection */
+	ret =3D lp5812_write(chip, (u16)DEV_CONFIG12, 0x0B);
+	if (ret !=3D 0) {
+		dev_err(chip->dev, "write 0x0B to DEV_CONFIG12 failed\n");
+		return ret;
+	}
+
+	/* Send update command to complete configuration settings */
+	ret =3D lp5812_update_regs_config(chip);
+	if (ret !=3D 0) {
+		dev_err(chip->dev, "lp5812_update_regs_config failed\n");
+		return ret;
+	}
+
+	/* Enable LED_A0 for testing */
+	ret =3D lp5812_write(chip, (u16)LED_ENABLE_1_REG, 0x20);
+	if (ret !=3D 0) {
+		dev_err(chip->dev, "write 0x10 to LED_ENABLE_1_REG failed\n");
+		return ret;
+	}
+	/* set max DC current for LED_A0 */
+	ret =3D lp5812_write(chip, (u16)0x35, 0x80);
+	if (ret !=3D 0)
+		dev_err(chip->dev, "set max DC current for LED_A0 failed\n");
+
+	/* set 100% pwm cycle for LED_A0 */
+	ret =3D lp5812_write(chip, (u16)0x45, 0x80);
+	if (ret !=3D 0)
+		dev_err(chip->dev, "set 100 percent pwm cycle for LED_A0 failed\n");
+
+	return ret;
+}
+
+static int led_set_autonomous_animation_config(struct lp5812_led *led)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_chip *chip =3D led->priv;
+
+	/* Set start/end pause */
+	reg =3D led->anim_base_addr + AUTO_PAUSE;
+	ret =3D lp5812_write(chip, reg, led->start_stop_pause_time.time_val);
+	if (ret)
+		return ret;
+
+	/* Set led playback and AEU selection */
+	reg =3D led->anim_base_addr + AUTO_PLAYBACK;
+	ret =3D lp5812_write(chip, reg, led->led_playback.led_playback_val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int led_get_autonomous_animation_config(struct lp5812_led *led)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_chip *chip =3D led->priv;
+
+	/* Get start/end pause value */
+	reg =3D led->anim_base_addr + AUTO_PAUSE;
+	ret =3D lp5812_read(chip, reg, &led->start_stop_pause_time.time_val);
+	if (ret)
+		return ret;
+
+	/* Get led playback and AEU selection values */
+	reg =3D led->anim_base_addr + AUTO_PLAYBACK;
+	ret =3D lp5812_read(chip, reg, &led->led_playback.led_playback_val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static u16 get_aeu_pwm_register(struct anim_engine_unit *aeu,
+		enum pwm_slope_time_num pwm_num)
+{
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+
+	switch (pwm_num) {
+	case PWM1:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_PWM_1;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_PWM_1;
+		else
+			reg =3D led->anim_base_addr + AEU3_PWM_1;
+		break;
+	case PWM2:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_PWM_2;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_PWM_2;
+		else
+			reg =3D led->anim_base_addr + AEU3_PWM_2;
+		break;
+	case PWM3:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_PWM_3;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_PWM_3;
+		else
+			reg =3D led->anim_base_addr + AEU3_PWM_3;
+		break;
+	case PWM4:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_PWM_4;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_PWM_4;
+		else
+			reg =3D led->anim_base_addr + AEU3_PWM_4;
+		break;
+	case PWM5:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_PWM_5;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_PWM_5;
+		else
+			reg =3D led->anim_base_addr + AEU3_PWM_5;
+		break;
+	default:
+		reg =3D led->anim_base_addr;
+		break;
+	}
+
+	return reg;
+}
+
+static u16 get_aeu_slope_time_register(struct anim_engine_unit *aeu,
+		enum pwm_slope_time_num slope_time_num)
+{
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+
+	switch (slope_time_num) {
+	case SLOPE_T1:
+	case SLOPE_T2:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_T12;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_T12;
+		else
+			reg =3D led->anim_base_addr + AEU3_T12;
+		break;
+	case SLOPE_T3:
+	case SLOPE_T4:
+		if (aeu->aeu_number =3D=3D 1)
+			reg =3D led->anim_base_addr + AEU1_T34;
+		else if (aeu->aeu_number =3D=3D 2)
+			reg =3D led->anim_base_addr + AEU2_T34;
+		else
+			reg =3D led->anim_base_addr + AEU3_T34;
+		break;
+	default:
+		reg =3D led->anim_base_addr;
+		break;
+	}
+
+	return reg;
+}
+
+static u16 get_aeu_playback_time_register(struct anim_engine_unit *aeu)
+{
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+
+	if (aeu->aeu_number =3D=3D 1)
+		reg =3D led->anim_base_addr + AEU1_PLAYBACK;
+	else if (aeu->aeu_number =3D=3D 2)
+		reg =3D led->anim_base_addr + AEU2_PLAYBACK;
+	else
+		reg =3D led->anim_base_addr + AEU3_PLAYBACK;
+
+	return reg;
+}
+
+static int led_aeu_pwm_set_val(struct anim_engine_unit *aeu, u8 val,
+		enum pwm_slope_time_num pwm_num)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_pwm_register(aeu, pwm_num);
+	ret =3D lp5812_write(chip, reg, val);
+
+	return ret;
+}
+
+static int led_aeu_pwm_get_val(struct anim_engine_unit *aeu, u8 *val,
+		enum pwm_slope_time_num pwm_num)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_pwm_register(aeu, pwm_num);
+	ret =3D lp5812_read(chip, reg, val);
+
+	return ret;
+}
+
+static int led_aeu_slope_time_set_val(struct anim_engine_unit *aeu, u8 val,
+		enum pwm_slope_time_num slope_time_num)
+{
+	int ret;
+	u16 reg;
+	union time slope_time_val;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_slope_time_register(aeu, slope_time_num);
+
+	/* get original value of slope time */
+	ret =3D lp5812_read(chip, reg, &slope_time_val.time_val);
+	if (ret !=3D 0)
+		return ret;
+
+	/* Update new value for slope time*/
+	if (slope_time_num =3D=3D SLOPE_T1 || slope_time_num =3D=3D SLOPE_T3)
+		slope_time_val.s_time.first =3D val;
+	if (slope_time_num =3D=3D SLOPE_T2 || slope_time_num =3D=3D SLOPE_T4)
+		slope_time_val.s_time.second =3D val;
+
+	/* Save updated value to hardware */
+	ret =3D lp5812_write(chip, reg, slope_time_val.time_val);
+
+	return ret;
+}
+
+static int led_aeu_slope_time_get_val(struct anim_engine_unit *aeu, u8 *va=
l,
+		enum pwm_slope_time_num slope_time_num)
+{
+	int ret =3D 0;
+	u16 reg;
+	union time slope_time_val;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_slope_time_register(aeu, slope_time_num);
+	/* get slope time value */
+	ret =3D lp5812_read(chip, reg, &slope_time_val.time_val);
+	if (ret !=3D 0)
+		return ret;
+
+	if (slope_time_num =3D=3D SLOPE_T1 || slope_time_num =3D=3D SLOPE_T3)
+		*val =3D slope_time_val.s_time.first;
+
+	if (slope_time_num =3D=3D SLOPE_T2 || slope_time_num =3D=3D SLOPE_T4)
+		*val =3D slope_time_val.s_time.second;
+
+	return ret;
+}
+
+static int led_aeu_playback_time_set_val(struct anim_engine_unit *aeu, u8 =
val)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_playback_time_register(aeu);
+	ret =3D lp5812_write(chip, reg, val);
+
+	return ret;
+}
+
+static int led_aeu_playback_time_get_val(struct anim_engine_unit *aeu, u8 =
*val)
+{
+	int ret;
+	u16 reg;
+	struct lp5812_led *led =3D aeu->led;
+	struct lp5812_chip *chip =3D led->priv;
+
+	reg =3D get_aeu_playback_time_register(aeu);
+	ret =3D lp5812_read(chip, reg, val);
+
+	return ret;
+}
+
+static int aeu_create_sysfs_group(struct anim_engine_unit *aeu)
+{
+	int ret =3D 0;
+
+	ret =3D kobject_add(&aeu->kobj, &aeu->led->kobj, "%s",
+			aeu->aeu_name);
+	if (ret)
+		return ret;
+	ret =3D sysfs_create_group(&aeu->kobj, &aeu->attr_group);
+	aeu->enabled =3D 1;
+
+	return ret;
+}
+
+static void aeu_remove_sysfs_group(struct anim_engine_unit *aeu)
+{
+	aeu->enabled =3D 0;
+	sysfs_remove_group(&aeu->kobj, &aeu->attr_group);
+	kobject_del(&aeu->kobj);
+}
+
+/* Function to remove multiple sysfs group of AEU in one led */
+static void aeu_remove_multi_sysfs_groups(struct lp5812_led *led)
+{
+	int i;
+
+	for (i =3D 0; i < led->total_aeu; i++) {
+		if (led->aeu[i].enabled)
+			aeu_remove_sysfs_group(&led->aeu[i]);
+	}
+}
+
+/*
+ * Function to create multi sysfs group for specific led. it will
+ * check the mode of led and AEU number selection to create
+ * corresponding AEU interfaces
+ */
+static int aeu_create_multi_sysfs_groups(struct lp5812_led *led)
+{
+	int i;
+	int aeu_select;
+	enum control_mode mode;
+	struct lp5812_chip *chip =3D led->priv;
+
+	if (lp5812_get_led_mode(chip, led->led_number, &mode))
+		return -EIO;
+	if (mode =3D=3D AUTONOMOUS) {
+		if (led_get_autonomous_animation_config(led))
+			return -EIO;
+		aeu_select =3D led->led_playback.s_led_playback.aeu_selection;
+		if (aeu_select =3D=3D 3)
+			aeu_select =3D 2;
+		for (i =3D 0; i < aeu_select + 1; i++)
+			aeu_create_sysfs_group(&led->aeu[i]);
+	}
+
+	return 0;
+}
+
+static int lp5812_create_sysfs_group(struct lp5812_led *led)
+{
+	int ret =3D 0;
+
+	ret =3D kobject_add(&led->kobj, &led->priv->dev->kobj, "%s",
+			led->led_name);
+	if (ret)
+		return ret;
+	ret =3D sysfs_create_group(&led->kobj, &led->attr_group);
+	if (ret)
+		return ret;
+	led->is_sysfs_created =3D 1;
+
+	return ret;
+}
+
+static void lp5812_remove_sysfs_group(struct lp5812_led *led)
+{
+	int i;
+
+	/* remove sysfs group of AEU */
+	for (i =3D 0; i < led->total_aeu; i++) {
+		if (led->aeu[i].enabled)
+			aeu_remove_sysfs_group(&led->aeu[i]);
+	}
+	led->is_sysfs_created =3D 0;
+	sysfs_remove_group(&led->kobj, &led->attr_group);
+	kobject_del(&led->kobj);
+}
+
+static ssize_t device_enable_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	int enable;
+	int ret;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	ret =3D kstrtoint(buf, 0, &enable);
+	if (ret)
+		return ret;
+
+	if (enable !=3D 0 && enable !=3D 1)
+		return -EINVAL;
+
+	/* set to hardware */
+	mutex_lock(&chip->lock);
+	if (lp5812_enable_disable(chip, enable)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return len;
+}
+
+static ssize_t device_enable_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	u8 enable;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&chip->lock);
+	if (lp5812_read(chip, chip->regs->enable_reg, &enable)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return sysfs_emit(buf, "%d\n", enable);
+}
+
+static ssize_t device_command_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&chip->lock);
+	if (sysfs_streq(buf, "update")) {
+		lp5812_device_command(chip, UPDATE);
+	} else if (sysfs_streq(buf, "start")) {
+		lp5812_device_command(chip, START);
+	} else if (sysfs_streq(buf, "stop")) {
+		lp5812_device_command(chip, STOP);
+	} else if (sysfs_streq(buf, "pause")) {
+		lp5812_device_command(chip, PAUSE);
+	} else if (sysfs_streq(buf, "continue")) {
+		lp5812_device_command(chip, CONTINUE);
+	} else {
+		mutex_unlock(&chip->lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&chip->lock);
+
+	return len;
+}
+
+static ssize_t device_reset_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	int reset;
+	int ret;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	ret =3D kstrtoint(buf, 0, &reset);
+	if (ret)
+		return ret;
+
+	if (reset !=3D 1)
+		return -EINVAL;
+
+	/* reset hardware */
+	mutex_lock(&chip->lock);
+	lp5812_reset(chip);
+	msleep(1000);
+	/* set back manual mode as default for all LEDs */
+	lp5812_write(chip, (u16)DEV_CONFIG3, 0x00);
+	lp5812_write(chip, (u16)DEV_CONFIG4, 0x00);
+	lp5812_update_regs_config(chip);
+	mutex_unlock(&chip->lock);
+
+	/* Update sysfs based on default mode when hardware reseted*/
+	ret =3D lp5812_init_dev_config(chip, "direct_mode", 1);
+	if (ret) {
+		dev_err(dev, "%s: lp5812_init_dev_config failed\n",
+			__func__);
+		return ret;
+	}
+
+	return len;
+}
+
+static ssize_t fault_clear_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	int fault_clear;
+	int ret;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	ret =3D kstrtoint(buf, 0, &fault_clear);
+	if (ret)
+		return ret;
+
+	if (fault_clear !=3D 0 && fault_clear !=3D 1 && fault_clear !=3D 2 &&
+			fault_clear !=3D 3) {
+		return -EINVAL;
+	}
+	mutex_lock(&chip->lock);
+	if (lp5812_fault_clear(chip, fault_clear)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return len;
+}
+
+static void lp5812_create_sysfs_via_led_name(struct lp5812_chip *chip,
+		const char *led_name)
+{
+	int i;
+
+	for (i =3D 0; i < chip->total_leds; i++) {
+		if (sysfs_streq(led_name, chip->leds[i].led_name)) {
+			lp5812_create_sysfs_group(&chip->leds[i]);
+
+			/*
+			 * create AEU interface if current led mode is
+			 * autonomous
+			 */
+			aeu_create_multi_sysfs_groups(&chip->leds[i]);
+		}
+	}
+}
+
+static void set_mix_sel_led(struct lp5812_chip *chip, int mix_sel_led)
+{
+	if (mix_sel_led =3D=3D 0) {
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 1;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 0;
+	}
+	if (mix_sel_led =3D=3D 1) {
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 1;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 0;
+	}
+	if (mix_sel_led =3D=3D 2) {
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 1;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 0;
+	}
+	if (mix_sel_led =3D=3D 3) {
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 1;
+	}
+}
+
+static void parse_drive_mode(struct lp5812_chip *chip, char *str)
+{
+	int ret;
+	char *sub_str;
+	int tcm_scan_num, mix_scan_num, mix_sel_led;
+	int scan_oder0, scan_oder1, scan_oder2, scan_oder3;
+
+	sub_str =3D strsep(&str, ":");
+	if (sysfs_streq(sub_str, "direct_mode")) {
+		chip->u_drive_mode.s_drive_mode.led_mode =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 0;
+	}
+	if (sysfs_streq(sub_str, "tcmscan")) {
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_0 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_1 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_2 =3D 0;
+		chip->u_drive_mode.s_drive_mode.mix_sel_led_3 =3D 0;
+
+		/* Get tcm scan number */
+		sub_str =3D strsep(&str, ":");
+		ret =3D kstrtoint(sub_str, 0, &tcm_scan_num);
+		if (ret)
+			return;
+		chip->u_drive_mode.s_drive_mode.led_mode =3D tcm_scan_num;
+		switch (tcm_scan_num) {
+		case TCM_1_SCAN:
+			/* Get scan_oder0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			break;
+		case TCM_2_SCAN:
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			/* Get scan_order1 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder1);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			chip->u_scan_order.s_scan_order.scan_order_1 =3D
+				scan_oder1;
+			break;
+		case TCM_3_SCAN:
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			/* Get scan_order1 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder1);
+			if (ret)
+				return;
+			/* Get scan_order2 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder2);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			chip->u_scan_order.s_scan_order.scan_order_1 =3D
+				scan_oder1;
+			chip->u_scan_order.s_scan_order.scan_order_2 =3D
+				scan_oder2;
+			break;
+		case TCM_4_SCAN:
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			/* Get scan_order1 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder1);
+			if (ret)
+				return;
+			/* Get scan_order2 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder2);
+			if (ret)
+				return;
+			/* Get scan_order3 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder3);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			chip->u_scan_order.s_scan_order.scan_order_1 =3D
+				scan_oder1;
+			chip->u_scan_order.s_scan_order.scan_order_2 =3D
+				scan_oder2;
+			chip->u_scan_order.s_scan_order.scan_order_3 =3D
+				scan_oder3;
+			break;
+		default:
+			break;
+		}
+	}
+	if (sysfs_streq(sub_str, "mixscan")) {
+		/* Get mix scan number */
+		sub_str =3D strsep(&str, ":");
+		ret =3D kstrtoint(sub_str, 0, &mix_scan_num);
+		if (ret)
+			return;
+		chip->u_drive_mode.s_drive_mode.led_mode =3D mix_scan_num + 4;
+		/* Get mix_sel_led */
+		sub_str =3D strsep(&str, ":");
+		ret =3D kstrtoint(sub_str, 0, &mix_sel_led);
+		if (ret)
+			return;
+		set_mix_sel_led(chip, mix_sel_led);
+		if (mix_scan_num =3D=3D 1) {
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+		}
+		if (mix_scan_num =3D=3D 2) {
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			/* Get scan_order1 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder1);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			chip->u_scan_order.s_scan_order.scan_order_1 =3D
+				scan_oder1;
+		}
+		if (mix_scan_num =3D=3D 3) {
+			/* Get scan_order0 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder0);
+			if (ret)
+				return;
+			/* Get scan_order1 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder1);
+			if (ret)
+				return;
+			/* Get scan_order2 */
+			sub_str =3D strsep(&str, ":");
+			ret =3D kstrtoint(sub_str, 0, &scan_oder2);
+			if (ret)
+				return;
+			chip->u_scan_order.s_scan_order.scan_order_0 =3D
+				scan_oder0;
+			chip->u_scan_order.s_scan_order.scan_order_1 =3D
+				scan_oder1;
+			chip->u_scan_order.s_scan_order.scan_order_2 =3D
+				scan_oder2;
+		}
+	}
+}
+
+static void leds_remove_existed_sysfs(struct lp5812_chip *chip)
+{
+	int i;
+
+	for (i =3D 0; i < chip->total_leds; i++) {
+		if (chip->leds[i].is_sysfs_created)
+			lp5812_remove_sysfs_group(&chip->leds[i]);
+	}
+}
+
+static int lp5812_init_dev_config(struct lp5812_chip *chip,
+		const char *drive_mode, int rm_led_sysfs)
+{
+	int i;
+	int num_drive_mode;
+	int is_valid_arg =3D 0;
+	char *str;
+	const char **led_ptr;
+
+	str =3D kstrdup(drive_mode, GFP_KERNEL);
+	if (!str)
+		return -ENOMEM;
+
+	num_drive_mode =3D ARRAY_SIZE(chip_leds_map);
+	for (i =3D 0; i < num_drive_mode; ++i) {
+		if (sysfs_streq(str, chip_leds_map[i].drive_mode)) {
+			if (rm_led_sysfs)
+				leds_remove_existed_sysfs(chip);
+			parse_drive_mode(chip, str);
+			led_ptr =3D chip_leds_map[i].led_arr;
+			while (*led_ptr) {
+				lp5812_create_sysfs_via_led_name(chip,
+						*led_ptr);
+				++led_ptr;
+			}
+			is_valid_arg =3D 1;
+			break;
+		}
+	}
+	kfree(str);
+	if (!is_valid_arg)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t dev_config_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	int ret;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_init_dev_config(chip, buf, 1);
+	if (ret) {
+		dev_err(chip->dev, "%s: lp5812_init_dev_config failed\n",
+			__func__);
+		goto out;
+	}
+
+	/* set drive mode and scan order to hardware */
+	ret =3D lp5812_set_drive_mode_scan_order(chip);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+	ret =3D lp5812_update_regs_config(chip);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+
+	/* disable all LEDs that were previously enabled */
+	ret =3D lp5812_disable_all_leds(chip);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+	mutex_unlock(&chip->lock);
+
+	return len;
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static char *parse_dev_config_info(struct lp5812_chip *chip)
+{
+	char *tmp;
+	int order_0, order_1, order_2, order_3;
+	char *scan_order_0 =3D " scan_order_0: ";
+	char *scan_order_1 =3D " scan_order_1: ";
+	char *scan_order_2 =3D " scan_order_2: ";
+	char *scan_order_3 =3D " scan_order_3: ";
+
+	tmp =3D kmalloc(128, GFP_KERNEL);
+	if (!tmp)
+		return NULL;
+
+	order_0 =3D chip->u_scan_order.s_scan_order.scan_order_0;
+	order_1 =3D chip->u_scan_order.s_scan_order.scan_order_1;
+	order_2 =3D chip->u_scan_order.s_scan_order.scan_order_2;
+	order_3 =3D chip->u_scan_order.s_scan_order.scan_order_3;
+
+	switch (chip->u_drive_mode.s_drive_mode.led_mode) {
+	case DIRECT_MODE:
+		sprintf(tmp, "%s", "Mode: direct mode;");
+		break;
+	case TCM_1_SCAN:
+		sprintf(tmp, "%s%s%d%s", "Mode: tcm 1 scan;", scan_order_0,
+			order_0, ";");
+		break;
+	case TCM_2_SCAN:
+		sprintf(tmp, "%s%s%d%s%s%d%s", "Mode: tcm 2 scan;",
+		scan_order_0, order_0, ";", scan_order_1, order_1, ";");
+		break;
+	case TCM_3_SCAN:
+		sprintf(tmp, "%s%s%d%s%s%d%s%s%d%s", "Mode: tcm 3 scan;",
+			scan_order_0, order_0, ";", scan_order_1, order_1,
+			";", scan_order_2, order_2, ";");
+		break;
+	case TCM_4_SCAN:
+		sprintf(tmp, "%s%s%d%s%s%d%s%s%d%s%s%d%s", "Mode: tcm 4 scan;",
+			scan_order_0, order_0, ";", scan_order_1, order_1, ";",
+			scan_order_2, order_2, ";", scan_order_3, order_3, ";");
+		break;
+	case MIX_1_SCAN:
+		sprintf(tmp, "%s%s%d%s", "Mode: mix 1 scan;", scan_order_0,
+			order_0, ";");
+		break;
+	case MIX_2_SCAN:
+		sprintf(tmp, "%s%s%d%s%s%d%s", "Mode: mix 2 scan;",
+		scan_order_0, order_0, ";", scan_order_1, order_1, ";");
+		break;
+	case MIX_3_SCAN:
+		sprintf(tmp, "%s%s%d%s%s%d%s%s%d%s", "Mode: mix 3 scan;",
+			scan_order_0, order_0, ";", scan_order_1, order_1,
+			";", scan_order_2, order_2, ";");
+		break;
+	}
+
+	if (chip->u_drive_mode.s_drive_mode.mix_sel_led_0)
+		strcat(tmp, " Direct output: OUT0;");
+	else if (chip->u_drive_mode.s_drive_mode.mix_sel_led_1)
+		strcat(tmp, " Direct output: OUT1;");
+	else if (chip->u_drive_mode.s_drive_mode.mix_sel_led_2)
+		strcat(tmp, " Direct output: OUT2;");
+	else if (chip->u_drive_mode.s_drive_mode.mix_sel_led_3)
+		strcat(tmp, " Direct output: OUT3;");
+	else
+		strcat(tmp, " Direct output: None;");
+	return tmp;
+}
+
+static ssize_t dev_config_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int num_drive_mode;
+	char *mode_info;
+	char *total_str;
+	size_t total_length;
+	char *const_str =3D "\nPlease select below valid drive mode:\n";
+	char *const_ex_str =3D "For Ex: echo tcmscan:1:0 > dev_config\n";
+	int ret =3D 0;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	/* get drive mode and scan order */
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_get_drive_mode_scan_order(chip);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	mode_info =3D parse_dev_config_info(chip);
+	if (!mode_info)
+		return -ENOMEM;
+
+	num_drive_mode =3D ARRAY_SIZE(chip_leds_map);
+	total_length =3D strlen(mode_info) + strlen(const_str) +
+			strlen(const_ex_str) + 1;
+	for (i =3D 0; i < num_drive_mode; ++i) {
+		total_length +=3D strlen(chip_leds_map[i].drive_mode) +
+					strlen("\n");
+	}
+
+	total_str =3D kmalloc(total_length, GFP_KERNEL);
+	if (!total_str)
+		return -ENOMEM;
+
+	sprintf(total_str, "%s%s%s", mode_info, const_str, const_ex_str);
+	for (i =3D 0; i < num_drive_mode; ++i) {
+		strcat(total_str, chip_leds_map[i].drive_mode);
+		strcat(total_str, "\n");
+	}
+
+	ret =3D sysfs_emit(buf, "%s", total_str);
+	kfree(mode_info);
+	kfree(total_str);
+
+	return ret;
+}
+
+static ssize_t tsd_config_status_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 reg_val;
+	int tsd_stat;
+	int config_stat;
+	struct lp5812_chip *chip =3D i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read(chip, (u16)TSD_CONFIG_STAT_REG, &reg_val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+	tsd_stat =3D (reg_val >> 1) & 0x01;
+	config_stat =3D reg_val & 0x01;
+
+	return sysfs_emit(buf, "%d %d\n", tsd_stat, config_stat);
+}
+
+static LP5812_DEV_ATTR_RW(device_enable);
+static LP5812_DEV_ATTR_RW(dev_config);
+static LP5812_DEV_ATTR_WO(device_command);
+static LP5812_DEV_ATTR_WO(device_reset);
+static LP5812_DEV_ATTR_WO(fault_clear);
+static LP5812_DEV_ATTR_RO(tsd_config_status);
+
+static struct attribute *lp5812_chip_attributes[] =3D {
+	&dev_attr_device_enable.attr,
+	&dev_attr_device_command.attr,
+	&dev_attr_device_reset.attr,
+	&dev_attr_fault_clear.attr,
+	&dev_attr_dev_config.attr,
+	&dev_attr_tsd_config_status.attr,
+	NULL
+};
+
+static ssize_t led_enable_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	u8 val =3D 0;
+	u16 reg =3D 0;
+	u8 reg_val =3D 0;
+	int ret =3D 0;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	if (led->led_number < 0x8)
+		reg =3D LED_ENABLE_1_REG;
+	else
+		reg =3D LED_ENABLE_2_REG;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	val =3D (reg_val & (1 << (led->led_number % 8))) ? 1 : 0;
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t led_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int val =3D 0;
+	u16 reg =3D 0;
+	u8 reg_val =3D 0;
+	int ret =3D 0;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	ret =3D kstrtoint(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val !=3D 0 && val !=3D 1)
+		return -EINVAL;
+
+	if (led->led_number < 0x8)
+		reg =3D LED_ENABLE_1_REG;
+	else
+		reg =3D LED_ENABLE_2_REG;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read(chip, reg, &reg_val);
+	if (ret =3D=3D 0) {
+		if (val =3D=3D 0) {
+			ret =3D lp5812_write(chip, reg,
+				reg_val & (~(1 << (led->led_number % 8))));
+		} else {
+			ret =3D lp5812_write(chip, reg,
+				reg_val | (1 << (led->led_number % 8)));
+		}
+	}
+	mutex_unlock(&chip->lock);
+
+	if (ret)
+		return -EIO;
+
+	return count;
+}
+
+static ssize_t led_mode_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	enum control_mode mode;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_get_led_mode(chip, led->led_number, &mode);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%s\n", AUTONOMOUS =3D=3D mode ? "autonomous" : "m=
anual");
+}
+
+static ssize_t led_mode_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	enum control_mode val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	if (sysfs_streq(buf, "manual")) {
+		/* Remove AEU sysfs interface for current led in manual mode */
+		aeu_remove_multi_sysfs_groups(led);
+		val =3D MANUAL;
+	} else if (sysfs_streq(buf, "autonomous")) {
+		val =3D AUTONOMOUS;
+	} else {
+		return -EINVAL;
+	}
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_set_led_mode(chip, led->led_number, val);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+
+	ret =3D aeu_create_multi_sysfs_groups(led);
+	if (ret) {
+		dev_err(chip->dev, "aeu_create_multi_sysfs_groups() failed\n");
+		goto out;
+	}
+
+	mutex_unlock(&chip->lock);
+	return count;
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static ssize_t led_manual_dc_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int manual_dc;
+	int ret;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	ret =3D kstrtoint(buf, 0, &manual_dc);
+	if (ret)
+		return ret;
+
+	if (manual_dc < 0 || manual_dc > 255)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_manual_dc_pwm_control(chip, led->led_number,
+		manual_dc, ANALOG)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t led_manual_dc_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	u8 val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_manual_dc_pwm_read(chip, led->led_number, &val, ANALOG)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t led_manual_pwm_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int manual_pwm;
+	int ret;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	ret =3D kstrtoint(buf, 0, &manual_pwm);
+	if (ret)
+		return ret;
+
+	if (manual_pwm < 0 || manual_pwm > 255)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_manual_dc_pwm_control(chip, led->led_number,
+		manual_pwm, PWM)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t led_manual_pwm_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	u8 val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_manual_dc_pwm_read(chip, led->led_number, &val, PWM)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t led_auto_dc_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int auto_dc;
+	int ret;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	ret =3D kstrtoint(buf, 0, &auto_dc);
+	if (ret)
+		return ret;
+
+	if (auto_dc < 0 || auto_dc > 255)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_autonomous_dc_pwm_control(chip, led->led_number,
+			auto_dc, ANALOG)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t led_auto_dc_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	u8 val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	if (lp5812_autonomous_dc_pwm_read(chip, led->led_number,
+			&val, ANALOG)) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static int parse_autonomous_animation_config(struct lp5812_led *led,
+		const char *user_buf)
+{
+	int ret;
+	int i;
+	char *str;
+	char *sub_str;
+	int aeu_select, start_pause_time, stop_pause_time, led_playback_time;
+
+	str =3D kstrdup(user_buf, GFP_KERNEL);
+	if (!str)
+		return -ENOMEM;
+
+	/* parse aeu_select */
+	sub_str =3D strsep(&str, ":");
+	ret =3D kstrtoint(sub_str, 0, &aeu_select);
+	if (ret)
+		goto free_str;
+	if (aeu_select < 1 || aeu_select > 3) {
+		ret =3D -EINVAL;
+		goto free_str;
+	}
+
+	aeu_remove_multi_sysfs_groups(led);
+
+	for (i =3D 0; i < aeu_select; i++)
+		aeu_create_sysfs_group(&led->aeu[i]);
+	led->led_playback.s_led_playback.aeu_selection =3D aeu_select - 1;
+
+	/* parse start_pause_time */
+	sub_str =3D strsep(&str, ":");
+	if (sub_str) {
+		ret =3D kstrtoint(sub_str, 0, &start_pause_time);
+		if (ret)
+			goto free_str;
+		if (start_pause_time < 0 || start_pause_time > 15) {
+			ret =3D -EINVAL;
+			goto free_str;
+		}
+		led->start_stop_pause_time.s_time.second =3D start_pause_time;
+	} else {
+		led->start_stop_pause_time.s_time.second =3D 15;
+	}
+
+	/* parse stop_pause_time */
+	sub_str =3D strsep(&str, ":");
+	if (sub_str) {
+		ret =3D kstrtoint(sub_str, 0, &stop_pause_time);
+		if (ret)
+			goto free_str;
+		if (stop_pause_time < 0 || stop_pause_time > 15) {
+			ret =3D -EINVAL;
+			goto free_str;
+		}
+		led->start_stop_pause_time.s_time.first =3D stop_pause_time;
+	} else {
+		led->start_stop_pause_time.s_time.first =3D 15;
+	}
+
+	/* parse led_playback_time */
+	sub_str =3D strsep(&str, ":");
+	if (sub_str) {
+		ret =3D kstrtoint(sub_str, 0, &led_playback_time);
+		if (ret)
+			goto free_str;
+		if (led_playback_time < 0 || led_playback_time > 15) {
+			ret =3D -EINVAL;
+			goto free_str;
+		}
+		led->led_playback.s_led_playback.led_playback_time =3D
+			led_playback_time;
+	} else {
+		led->led_playback.s_led_playback.led_playback_time =3D 15;
+	}
+
+	kfree(str);
+	return 0;
+
+free_str:
+	kfree(str);
+	return ret;
+}
+
+static ssize_t led_auto_animation_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	enum control_mode led_mode;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	/* First check the mode of led is manual or autonomous */
+	ret =3D lp5812_get_led_mode(chip, led->led_number, &led_mode);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+
+	if (led_mode =3D=3D AUTONOMOUS) {
+		ret =3D parse_autonomous_animation_config(led, buf);
+		if (ret) {
+			dev_err(chip->dev, "parse_autonomous_animation_config failed\n");
+			goto out;
+		}
+		/* Write data to hardware */
+		ret =3D led_set_autonomous_animation_config(led);
+		if (ret) {
+			ret =3D -EIO;
+			goto out;
+		}
+	}
+	mutex_unlock(&chip->lock);
+	return count;
+
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static ssize_t led_auto_animation_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+	char tmp_str[256] =3D {};
+	char usage[128] =3D {};
+	char *aeu_select =3D "AEU Select: ";
+	char *start_pause_time =3D "Start pause time: ";
+	char *stop_pause_time =3D "; Stop pause time: ";
+	char *led_playback_time =3D "; LED Playback time: ";
+	int aeu_selection, playback_time, start_pause, stop_pause;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	sprintf(usage, "%s%s",
+	"Command usage: echo (aeu number):(start pause time):",
+	"(stop pause time):(playback time) > autonomous_animation");
+
+	mutex_lock(&chip->lock);
+	ret =3D led_get_autonomous_animation_config(led);
+	if (ret) {
+		ret =3D -EIO;
+		goto out;
+	}
+
+	/* parse config and feedback to userspace */
+	aeu_selection =3D led->led_playback.s_led_playback.aeu_selection;
+	playback_time =3D led->led_playback.s_led_playback.led_playback_time;
+	start_pause =3D led->start_stop_pause_time.s_time.second;
+	stop_pause =3D led->start_stop_pause_time.s_time.first;
+	if (aeu_selection =3D=3D ONLY_AEU1) {
+		sprintf(tmp_str, "%s%s%s%s%s%s%s%s\n", aeu_select,
+		"Only use AEU1; ", start_pause_time,
+		time_name_array[start_pause], stop_pause_time,
+		time_name_array[stop_pause], led_playback_time,
+		led_playback_time_arr[playback_time]);
+	} else if (aeu_selection =3D=3D AEU1_AEU2) {
+		sprintf(tmp_str, "%s%s%s%s%s%s%s%s\n", aeu_select,
+		"Use AEU1 and AEU2; ", start_pause_time,
+		time_name_array[start_pause], stop_pause_time,
+		time_name_array[stop_pause], led_playback_time,
+		led_playback_time_arr[playback_time]);
+	} else {
+		sprintf(tmp_str, "%s%s%s%s%s%s%s%s\n", aeu_select,
+		"Use AEU1,AEU2 and AEU3; ", start_pause_time,
+		time_name_array[start_pause], stop_pause_time,
+		time_name_array[stop_pause], led_playback_time,
+		led_playback_time_arr[playback_time]);
+	}
+	strcat(tmp_str, usage);
+	mutex_unlock(&chip->lock);
+	return sysfs_emit(buf, "%s\n", tmp_str);
+
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static ssize_t led_lod_lsd_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	u8 lsd_status, lod_status;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read_lsd_status(chip, led->led_number, &lsd_status);
+	if (ret) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+
+	ret =3D lp5812_read_lod_status(chip, led->led_number, &lod_status);
+	if (ret) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return sysfs_emit(buf, "%d %d\n", lod_status, lsd_status);
+}
+
+static ssize_t led_auto_pwm_val_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	u8 auto_pwm_val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read_auto_pwm_value(chip, led->led_number, &auto_pwm_val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%d\n", auto_pwm_val);
+}
+
+static ssize_t led_aep_status_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	u8 aep_status;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_read_aep_status(chip, led->led_number, &aep_status);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%d\n", aep_status);
+}
+
+static ssize_t led_pwm_phase_align_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int val, ret;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	if (sysfs_streq(buf, "forward"))
+		val =3D 0;
+	else if (sysfs_streq(buf, "middle"))
+		val =3D 2;
+	else if (sysfs_streq(buf, "backward"))
+		val =3D 3;
+	else
+		return -EINVAL;
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_set_phase_align(chip, led->led_number, val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return count;
+}
+
+static ssize_t led_pwm_phase_align_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int val, ret;
+	char *str;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_get_phase_align(chip, led->led_number, &val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+	if (val =3D=3D 0 || val =3D=3D 1)
+		str =3D "forward";
+	else if (val =3D=3D 2)
+		str =3D "middle";
+	else
+		str =3D "backward";
+
+	return sysfs_emit(buf, "%s\n", str);
+}
+
+static ssize_t led_pwm_dimming_scale_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	enum pwm_dimming_scale val;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	if (sysfs_streq(buf, "linear"))
+		val =3D LINEAR;
+	else if (sysfs_streq(buf, "exponential"))
+		val =3D EXPONENTIAL;
+	else
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_set_pwm_dimming_scale(chip, led->led_number, val);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return count;
+}
+
+static ssize_t led_pwm_dimming_scale_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	enum pwm_dimming_scale scale;
+	struct lp5812_led *led =3D to_lp5812_led(kobj);
+	struct lp5812_chip *chip =3D led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D lp5812_get_pwm_dimming_scale(chip, led->led_number, &scale);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%s\n",
+		scale =3D=3D EXPONENTIAL ? "exponential" : "linear");
+}
+
+static LP5812_KOBJ_ATTR_RW(enable, led_enable_show, led_enable_store);
+static LP5812_KOBJ_ATTR_RW(mode, led_mode_show, led_mode_store);
+static LP5812_KOBJ_ATTR_RW(manual_dc, led_manual_dc_show,
+		led_manual_dc_store);
+static LP5812_KOBJ_ATTR_RW(manual_pwm, led_manual_pwm_show,
+		led_manual_pwm_store);
+static LP5812_KOBJ_ATTR_RW(autonomous_dc, led_auto_dc_show,
+		led_auto_dc_store);
+static LP5812_KOBJ_ATTR_RW(autonomous_animation, led_auto_animation_show,
+		led_auto_animation_store);
+static LP5812_KOBJ_ATTR_RW(pwm_phase_align, led_pwm_phase_align_show,
+		led_pwm_phase_align_store);
+static LP5812_KOBJ_ATTR_RW(pwm_dimming_scale, led_pwm_dimming_scale_show,
+		led_pwm_dimming_scale_store);
+static LP5812_KOBJ_ATTR_RO(lod_lsd, led_lod_lsd_show);
+static LP5812_KOBJ_ATTR_RO(auto_pwm_val, led_auto_pwm_val_show);
+static LP5812_KOBJ_ATTR_RO(aep_status, led_aep_status_show);
+
+static struct attribute *led_kobj_attributes[] =3D {
+	&kobj_attr_enable.attr,
+	&kobj_attr_mode.attr,
+	&kobj_attr_manual_dc.attr,
+	&kobj_attr_manual_pwm.attr,
+	&kobj_attr_autonomous_dc.attr,
+	&kobj_attr_autonomous_animation.attr,
+	&kobj_attr_lod_lsd.attr,
+	&kobj_attr_auto_pwm_val.attr,
+	&kobj_attr_aep_status.attr,
+	&kobj_attr_pwm_phase_align.attr,
+	&kobj_attr_pwm_dimming_scale.attr,
+	NULL
+};
+
+static ssize_t aeu_pwm_store(struct kobject *kobj, struct kobj_attribute *=
attr,
+		const char *buf, size_t count, enum pwm_slope_time_num pwm_channel)
+{
+	int val =3D 0;
+	int ret =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	ret =3D kstrtoint(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+	if (val < 0 || val > 255)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_pwm_set_val(aeu, val, pwm_channel);
+	if (ret !=3D 0) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t aeu_pwm_show(struct kobject *kobj, struct kobj_attribute *a=
ttr,
+		char *buf, enum pwm_slope_time_num pwm_channel)
+{
+	int ret =3D 0;
+	u8 val =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_pwm_get_val(aeu, &val, pwm_channel);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t aeu_pwm1_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_pwm_store(kobj, attr, buf, count, PWM1);
+}
+
+static ssize_t aeu_pwm1_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_pwm_show(kobj, attr, buf, PWM1);
+}
+
+static ssize_t aeu_pwm2_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_pwm_store(kobj, attr, buf, count, PWM2);
+}
+
+static ssize_t aeu_pwm2_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_pwm_show(kobj, attr, buf, PWM2);
+}
+
+static ssize_t aeu_pwm3_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_pwm_store(kobj, attr, buf, count, PWM3);
+}
+
+static ssize_t aeu_pwm3_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_pwm_show(kobj, attr, buf, PWM3);
+}
+
+static ssize_t aeu_pwm4_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_pwm_store(kobj, attr, buf, count, PWM4);
+}
+
+static ssize_t aeu_pwm4_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_pwm_show(kobj, attr, buf, PWM4);
+}
+
+static ssize_t aeu_pwm5_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_pwm_store(kobj, attr, buf, count, PWM5);
+}
+
+static ssize_t aeu_pwm5_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_pwm_show(kobj, attr, buf, PWM5);
+}
+
+static ssize_t aeu_slope_time_store(struct kobject *kobj, struct kobj_attr=
ibute *attr,
+		const char *buf, size_t count, enum pwm_slope_time_num slope_time_num)
+{
+	int val =3D 0;
+	int ret =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	ret =3D kstrtoint(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val < 0x00 || val > 0x0F)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_slope_time_set_val(aeu, val, slope_time_num);
+	if (ret !=3D 0) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t aeu_slope_time_show(struct kobject *kobj, struct kobj_attri=
bute *attr,
+		char *buf, enum pwm_slope_time_num slope_time_num)
+{
+	int ret =3D 0;
+	u8 val =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_slope_time_get_val(aeu, &val, slope_time_num);
+	mutex_unlock(&chip->lock);
+	if (ret)
+		return -EIO;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t aeu_slope_time_t1_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_slope_time_store(kobj, attr, buf, count, SLOPE_T1);
+}
+
+static ssize_t aeu_slope_time_t1_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_slope_time_show(kobj, attr, buf, SLOPE_T1);
+}
+
+static ssize_t aeu_slope_time_t2_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_slope_time_store(kobj, attr, buf, count, SLOPE_T2);
+}
+
+static ssize_t aeu_slope_time_t2_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_slope_time_show(kobj, attr, buf, SLOPE_T2);
+}
+
+static ssize_t aeu_slope_time_t3_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_slope_time_store(kobj, attr, buf, count, SLOPE_T3);
+}
+
+static ssize_t aeu_slope_time_t3_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_slope_time_show(kobj, attr, buf, SLOPE_T3);
+}
+
+static ssize_t aeu_slope_time_t4_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return aeu_slope_time_store(kobj, attr, buf, count, SLOPE_T4);
+}
+
+static ssize_t aeu_slope_time_t4_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return aeu_slope_time_show(kobj, attr, buf, SLOPE_T4);
+}
+
+static ssize_t aeu_playback_time_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int val =3D 0;
+	int ret =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	ret =3D kstrtoint(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val < 0 || val > 3)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_playback_time_set_val(aeu, val);
+	if (ret !=3D 0) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+
+static ssize_t aeu_playback_time_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int ret =3D 0;
+	u8 val =3D 0;
+	struct anim_engine_unit *aeu =3D to_anim_engine_unit(kobj);
+	struct lp5812_chip *chip =3D aeu->led->priv;
+
+	mutex_lock(&chip->lock);
+	ret =3D led_aeu_playback_time_get_val(aeu, &val);
+	if (ret !=3D 0) {
+		mutex_unlock(&chip->lock);
+		return -EIO;
+	}
+	mutex_unlock(&chip->lock);
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static LP5812_KOBJ_ATTR_RW(pwm1, aeu_pwm1_show, aeu_pwm1_store);
+static LP5812_KOBJ_ATTR_RW(pwm2, aeu_pwm2_show, aeu_pwm2_store);
+static LP5812_KOBJ_ATTR_RW(pwm3, aeu_pwm3_show, aeu_pwm3_store);
+static LP5812_KOBJ_ATTR_RW(pwm4, aeu_pwm4_show, aeu_pwm4_store);
+static LP5812_KOBJ_ATTR_RW(pwm5, aeu_pwm5_show, aeu_pwm5_store);
+static LP5812_KOBJ_ATTR_RW(slope_time_t1, aeu_slope_time_t1_show,
+		aeu_slope_time_t1_store);
+static LP5812_KOBJ_ATTR_RW(slope_time_t2, aeu_slope_time_t2_show,
+		aeu_slope_time_t2_store);
+static LP5812_KOBJ_ATTR_RW(slope_time_t3, aeu_slope_time_t3_show,
+		aeu_slope_time_t3_store);
+static LP5812_KOBJ_ATTR_RW(slope_time_t4, aeu_slope_time_t4_show,
+		aeu_slope_time_t4_store);
+static LP5812_KOBJ_ATTR_RW(playback_time, aeu_playback_time_show,
+		aeu_playback_time_store);
+
+static struct attribute *aeu_kobj_attributes[] =3D {
+	&kobj_attr_pwm1.attr,
+	&kobj_attr_pwm2.attr,
+	&kobj_attr_pwm3.attr,
+	&kobj_attr_pwm4.attr,
+	&kobj_attr_pwm5.attr,
+	&kobj_attr_slope_time_t1.attr,
+	&kobj_attr_slope_time_t2.attr,
+	&kobj_attr_slope_time_t3.attr,
+	&kobj_attr_slope_time_t4.attr,
+	&kobj_attr_playback_time.attr,
+	NULL
+};
+
+static void aeu_init_properties(struct lp5812_led *led)
+{
+	int i;
+
+	for (i =3D 0; i < MAX_AEU; i++) {
+		led->aeu[i].aeu_name =3D aeu_name_array[i];
+		led->aeu[i].aeu_number =3D i + 1;
+		led->aeu[i].led =3D led;
+		led->aeu[i].enabled =3D 0;
+		led->aeu[i].attr_group.attrs =3D aeu_kobj_attributes;
+		kobject_init(&led->aeu[i].kobj, &aeu_ktype);
+	}
+}
+
+static int lp5812_probe(struct i2c_client *client)
+{
+	struct lp5812_chip *chip;
+	int i;
+	int ret;
+	u8 val;
+
+	chip =3D devm_kzalloc(&client->dev, sizeof(struct lp5812_chip),
+			GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+	mutex_init(&chip->lock);
+	chip->i2c_cl =3D client;
+	chip->dev =3D &client->dev;
+	chip->regs =3D &regs;
+	chip->command =3D NONE;
+	chip->total_leds =3D MAX_LEDS;
+	chip->attr_group.name =3D "lp5812_chip_setup";
+	chip->attr_group.attrs =3D lp5812_chip_attributes;
+	chip->chip_leds_map =3D chip_leds_map;
+	chip->u_drive_mode.drive_mode_val =3D 0x10;
+	chip->u_scan_order.scan_order_val =3D 0x00;
+
+	/* initialize property for each led */
+	for (i =3D 0; i < MAX_LEDS; i++) {
+		chip->leds[i].led_name =3D led_name_array[i];
+		chip->leds[i].led_number =3D i;
+		chip->leds[i].anim_base_addr =3D anim_base_addr_array[i];
+		chip->leds[i].enable =3D 0; /* LED disable as default */
+		chip->leds[i].mode =3D MANUAL; /* manual mode as default */
+		chip->leds[i].priv =3D chip;
+		chip->leds[i].total_aeu =3D MAX_AEU;
+		chip->leds[i].led_playback.led_playback_val =3D 0;
+		chip->leds[i].start_stop_pause_time.time_val =3D 0;
+		/* sysfs for this led not be created */
+		chip->leds[i].is_sysfs_created =3D 0;
+		chip->leds[i].attr_group.attrs =3D led_kobj_attributes;
+		kobject_init(&chip->leds[i].kobj, &led_ktype);
+
+		/* init animation engine unit properties */
+		aeu_init_properties(&chip->leds[i]);
+
+		/* set autonomous animation config as default for all LEDs */
+		led_set_autonomous_animation_config(&chip->leds[i]);
+	}
+
+	i2c_set_clientdata(client, chip);
+
+	ret =3D sysfs_create_group(&chip->dev->kobj, &chip->attr_group);
+	if (ret)
+		return dev_err_probe(chip->dev, ret, "sysfs_create_group failed\n");
+
+	ret =3D lp5812_init_dev_config(chip, "tcmscan:4:0:1:2:3", 0);
+	if (ret)
+		return dev_err_probe(chip->dev, ret, "%s: lp5812_init_dev_config failed\=
n",
+			__func__);
+
+	ret =3D lp5812_initialize(chip);
+	if (ret)
+		return dev_err_probe(chip->dev, ret, "lp5812 initialize failed\n");
+
+	/* code to verify i2c read/write ok or not */
+	lp5812_read(chip, (u16)DEV_CONFIG2, &val);
+
+	lp5812_write(chip, (u16)LED_A1_AUTO_BASE_ADRR, 0x14);
+	lp5812_read(chip, (u16)LED_A1_AUTO_BASE_ADRR, &val);
+	/* End code to verify i2c read/write*/
+
+	return 0;
+}
+
+static void lp5812_remove(struct i2c_client *client)
+{
+	struct lp5812_chip *chip =3D i2c_get_clientdata(client);
+
+	mutex_destroy(&chip->lock);
+	leds_remove_existed_sysfs(chip);
+	sysfs_remove_group(&chip->dev->kobj, &chip->attr_group);
+
+	lp5812_disable_all_leds(chip);
+
+	lp5812_enable_disable(chip, 0);
+}
+
+static const struct i2c_device_id lp5812_id[] =3D {
+	{ "lp5812" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, lp5812_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp5812_match[] =3D {
+	{ .compatible =3D "ti,lp5812", },
+	{/* NULL */}
+};
+
+MODULE_DEVICE_TABLE(of, of_lp5812_match);
+#endif
+
+static struct i2c_driver lp5812_driver =3D {
+	.driver =3D {
+		.name   =3D "lp5812",
+		.of_match_table =3D of_match_ptr(of_lp5812_match),
+	},
+	.probe          =3D lp5812_probe,
+	.remove         =3D lp5812_remove,
+	.id_table       =3D lp5812_id
+};
+
+module_i2c_driver(lp5812_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP5812 LED Driver");
+MODULE_AUTHOR("Jared Zhou");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lp5812.h b/drivers/leds/leds-lp5812.h
new file mode 100644
index 000000000000..2c26d7cbcc2f
--- /dev/null
+++ b/drivers/leds/leds-lp5812.h
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * LP5812 Driver Header
+ *
+ * Copyright (C) 2025 Texas Instruments
+ *
+ * Author: Jared Zhou <jared-zhou@ti.com>
+ */
+
+#ifndef _LEDS_LP5812_H_
+#define _LEDS_LP5812_H_
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/sysfs.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/leds.h>
+#include <linux/delay.h>
+
+#define CHIP_EN_REG                     0x00
+
+#define DEV_CONFIG0                     0x01
+#define DEV_CONFIG1                     0x02
+#define DEV_CONFIG2                     0x03
+#define DEV_CONFIG3                     0x04
+#define DEV_CONFIG4                     0x05
+#define DEV_CONFIG5                     0x06
+#define DEV_CONFIG6                     0x07
+#define DEV_CONFIG7                     0x08
+#define DEV_CONFIG8                     0x09
+#define DEV_CONFIG9                     0x0A
+#define DEV_CONFIG10                    0x0B
+#define DEV_CONFIG11                    0x0c
+#define DEV_CONFIG12                    0x0D
+
+#define CMD_UPDATE_REG                  0x10
+#define CMD_START_REG                   0x11
+#define CMD_STOP_REG                    0x12
+#define CMD_PAUSE_REG                   0x13
+#define CMD_CONTINUE_REG                0x14
+
+#define LED_ENABLE_1_REG                0x20
+#define LED_ENABLE_2_REG                0x21
+
+#define FAULT_CLEAR_REG                 0x22
+#define RESET_REG                       0x23
+
+#define MANUAL_DC_LED_0_REG             0x30
+#define MANUAL_PWM_LED_0_REG            0x40
+#define AUTO_DC_LED_0_REG               0x50
+
+/* value for register */
+#define UPDATE_CMD_VAL                  0x55
+#define START_CMD_VAL                   0xFF
+#define STOP_CMD_VAL                    0xAA
+#define PAUSE_CMD_VAL                   0x33
+#define CONTINUE_CMD_VAL                0xCC
+
+#define CHIP_ENABLE                     0x01
+#define CHIP_DISABLE                    0x00
+
+#define FAULT_CLEAR_ALL                 0x07
+#define TSD_CLEAR_VAL                   0x04
+#define LSD_CLEAR_VAL                   0x02
+#define LOD_CLEAR_VAL                   0x01
+#define RESET_REG_VAL                   0x66
+
+#define LED0_AUTO_BASE_ADRR             0x80
+#define LED1_AUTO_BASE_ADRR             0x9A
+#define LED2_AUTO_BASE_ADRR             0xB4
+#define LED3_AUTO_BASE_ADRR             0xCE
+#define LED_A0_AUTO_BASE_ADRR           0xE8
+#define LED_A1_AUTO_BASE_ADRR           0x102
+#define LED_A2_AUTO_BASE_ADRR           0x11C
+#define LED_B0_AUTO_BASE_ADRR           0x136
+#define LED_B1_AUTO_BASE_ADRR           0x150
+#define LED_B2_AUTO_BASE_ADRR           0x16A
+#define LED_C0_AUTO_BASE_ADRR           0x184
+#define LED_C1_AUTO_BASE_ADRR           0x19E
+#define LED_C2_AUTO_BASE_ADRR           0x1B8
+#define LED_D0_AUTO_BASE_ADRR           0x1D2
+#define LED_D1_AUTO_BASE_ADRR           0x1EC
+#define LED_D2_AUTO_BASE_ADRR           0x206
+
+/* Flag Registers */
+#define TSD_CONFIG_STAT_REG             0x300
+#define LOD_STAT_1_REG                  0x301
+#define LOD_STAT_2_REG                  0x302
+#define LSD_STAT_1_REG                  0x303
+#define LSD_STAT_2_REG                  0x304
+
+#define AUTO_PWM_BASE_ADDR              0x305
+
+#define AEP_STATUS_0_REG                0x315
+#define AEP_STATUS_1_REG                0x316
+#define AEP_STATUS_2_REG                0x317
+#define AEP_STATUS_3_REG                0x318
+#define AEP_STATUS_4_REG                0x319
+#define AEP_STATUS_5_REG                0x31A
+#define AEP_STATUS_6_REG                0x31B
+#define AEP_STATUS_7_REG                0x31C
+
+#define LED0                            "led_0"
+#define LED1                            "led_1"
+#define LED2                            "led_2"
+#define LED3                            "led_3"
+#define LED_A0                          "led_A0"
+#define LED_A1                          "led_A1"
+#define LED_A2                          "led_A2"
+#define LED_B0                          "led_B0"
+#define LED_B1                          "led_B1"
+#define LED_B2                          "led_B2"
+#define LED_C0                          "led_C0"
+#define LED_C1                          "led_C1"
+#define LED_C2                          "led_C2"
+#define LED_D0                          "led_D0"
+#define LED_D1                          "led_D1"
+#define LED_D2                          "led_D2"
+
+/* Below define time for (start/stop/slope time) */
+#define TIME0                           "no time"
+#define TIME1                           "0.09s"
+#define TIME2                           "0.18s"
+#define TIME3                           "0.36s"
+#define TIME4                           "0.54s"
+#define TIME5                           "0.80s"
+#define TIME6                           "1.07s"
+#define TIME7                           "1.52s"
+#define TIME8                           "2.06s"
+#define TIME9                           "2.50s"
+#define TIME10                          "3.04s"
+#define TIME11                          "4.02s"
+#define TIME12                          "5.01s"
+#define TIME13                          "5.99s"
+#define TIME14                          "7.06s"
+#define TIME15                          "8.05s"
+/* End define time for (start/stop/slope time) */
+
+#define AEU1                            "AEU1"
+#define AEU2                            "AEU2"
+#define AEU3                            "AEU3"
+
+#define MAX_LEDS                        16
+#define MAX_TIME                        16
+#define MAX_AEU                         3
+
+#define LP5812_DEV_ATTR_RW(name)   \
+	DEVICE_ATTR_RW(name)
+#define LP5812_DEV_ATTR_RO(name)          \
+	DEVICE_ATTR_RO(name)
+#define LP5812_DEV_ATTR_WO(name)         \
+	DEVICE_ATTR_WO(name)
+
+#define LP5812_KOBJ_ATTR(_name, _mode, _show, _store) \
+	struct kobj_attribute kobj_attr_##_name =3D __ATTR(_name, _mode, _show, _=
store)
+#define LP5812_KOBJ_ATTR_RW(name, show, store) \
+	LP5812_KOBJ_ATTR(name, 0644, show, store)
+#define LP5812_KOBJ_ATTR_RO(name, show) \
+	LP5812_KOBJ_ATTR(name, 0444, show, NULL)
+#define LP5812_KOBJ_ATTR_WO(name, store) \
+	LP5812_KOBJ_ATTR(name, 0200, NULL, store)
+
+enum pwm_slope_time_num {
+	PWM1 =3D 1,
+	PWM2,
+	PWM3,
+	PWM4,
+	PWM5,
+	SLOPE_T1,
+	SLOPE_T2,
+	SLOPE_T3,
+	SLOPE_T4
+};
+
+enum dimming_type {
+	ANALOG,
+	PWM
+};
+
+enum pwm_dimming_scale {
+	LINEAR =3D 0,
+	EXPONENTIAL
+};
+
+enum control_mode {
+	MANUAL =3D 0,
+	AUTONOMOUS
+};
+
+enum device_command {
+	NONE,
+	UPDATE,
+	START,
+	STOP,
+	PAUSE,
+	CONTINUE
+};
+
+enum animation_addr {
+	AUTO_PAUSE =3D 0,
+	AUTO_PLAYBACK,
+	AEU1_PWM_1,
+	AEU1_PWM_2,
+	AEU1_PWM_3,
+	AEU1_PWM_4,
+	AEU1_PWM_5,
+	AEU1_T12,
+	AEU1_T34,
+	AEU1_PLAYBACK,
+	AEU2_PWM_1,
+	AEU2_PWM_2,
+	AEU2_PWM_3,
+	AEU2_PWM_4,
+	AEU2_PWM_5,
+	AEU2_T12,
+	AEU2_T34,
+	AEU2_PLAYBACK,
+	AEU3_PWM_1,
+	AEU3_PWM_2,
+	AEU3_PWM_3,
+	AEU3_PWM_4,
+	AEU3_PWM_5,
+	AEU3_T12,
+	AEU3_T34,
+	AEU3_PLAYBACK
+};
+
+enum drive_mode {
+	DIRECT_MODE =3D 0,
+	TCM_1_SCAN,
+	TCM_2_SCAN,
+	TCM_3_SCAN,
+	TCM_4_SCAN,
+	MIX_1_SCAN,
+	MIX_2_SCAN,
+	MIX_3_SCAN
+};
+
+enum aeu_select {
+	ONLY_AEU1,
+	AEU1_AEU2,
+	AEU1_AEU2_AEU3
+};
+
+union time {
+	struct {
+		u8 first:4;
+		u8 second:4;
+	} __packed s_time;
+	u8 time_val;
+}; /* type for start/stop pause time and slope time */
+
+union led_playback {
+	struct {
+		u8 led_playback_time:4;
+		u8 aeu_selection:2;
+		u8 reserved:2;
+	} __packed s_led_playback;
+	u8 led_playback_val;
+};
+
+union scan_order {
+	struct {
+		u8 scan_order_0:2;
+		u8 scan_order_1:2;
+		u8 scan_order_2:2;
+		u8 scan_order_3:2;
+	} __packed s_scan_order;
+	u8 scan_order_val;
+};
+
+union drive_mode_info {
+	struct {
+		u8 mix_sel_led_0:1;
+		u8 mix_sel_led_1:1;
+		u8 mix_sel_led_2:1;
+		u8 mix_sel_led_3:1;
+		u8 led_mode:3;
+		u8 pwm_fre:1;
+	} __packed s_drive_mode;
+	u8 drive_mode_val;
+};
+
+struct drive_mode_led_map {
+	const char *drive_mode;
+	const char **led_arr;
+};
+
+struct lp5812_specific_regs {
+	u16 enable_reg;
+	u16 reset_reg;
+	u16 update_cmd_reg;
+	u16 start_cmd_reg;
+	u16 stop_cmd_reg;
+	u16 pause_cmd_reg;
+	u16 continue_cmd_reg;
+	u16 fault_clear_reg;
+	u16 tsd_config_status_reg;
+};
+
+struct anim_engine_unit {
+	struct kobject                        kobj;
+	struct lp5812_led                     *led;
+	struct attribute_group                attr_group;
+	const char                            *aeu_name;
+	int                                   aeu_number; /* start from 1 */
+
+	/* To know led using this AEU or not*/
+	int                                   enabled;
+};
+
+struct lp5812_led {
+	struct kobject                        kobj;
+	struct lp5812_chip                    *priv;
+	struct attribute_group                attr_group;
+	int                                   enable;
+	enum control_mode                     mode;
+	enum dimming_type                     dimming_type;
+	u8                                    lod_lsd;
+	u8                                    auto_pwm;
+	u8                                    aep_status;
+	u16                                   anim_base_addr;
+	int                                   led_number; /* start from 0 */
+	int                                   is_sysfs_created;
+	const char                            *led_name;
+
+	union led_playback                    led_playback;
+	union time                            start_stop_pause_time;
+
+	int                                   total_aeu;
+	struct anim_engine_unit               aeu[MAX_AEU];
+};
+
+struct lp5812_chip {
+	struct i2c_client                     *i2c_cl;
+	struct mutex                          lock; /* Protects access to device =
registers */
+	struct device                         *dev;
+	struct attribute_group                attr_group;
+	const struct lp5812_specific_regs     *regs;
+	const struct drive_mode_led_map       *chip_leds_map;
+	enum device_command                   command;
+	int                                   total_leds;
+	union scan_order                      u_scan_order;
+	union drive_mode_info                 u_drive_mode;
+
+	struct lp5812_led                     leds[MAX_LEDS]; /* MAX 16 LEDs */
+};
+
+#endif /*_LEDS_LP5812_H_*/
--=20
2.25.1