diff --git a/src/kernel/linux/v4.4/arch/arm64/boot/dts/mediatek/aud8516p1v2_consys_slc_128_yocto.dts b/src/kernel/linux/v4.4/arch/arm64/boot/dts/mediatek/aud8516p1v2_consys_slc_128_yocto.dts
index d1723a2b5..8b6fcccfb 100755
--- a/src/kernel/linux/v4.4/arch/arm64/boot/dts/mediatek/aud8516p1v2_consys_slc_128_yocto.dts
+++ b/src/kernel/linux/v4.4/arch/arm64/boot/dts/mediatek/aud8516p1v2_consys_slc_128_yocto.dts
@@ -443,12 +443,42 @@
reset-gpio = <&pio 7 0>;
irq-gpio = <&pio 6 0>;
status = "okay";
- aw20036,led {
- aw20036,name = "aw20036_led";
- aw20036,imax = <1>;
- aw20036,brightness = <128>;
- aw20036,max_brightness = <255>;
- };
+ imax = <5>; /* 80mA */
+ max-brightness = <255>;
+ brightness = <80>;
+ gamma-table = <
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10,
+ 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17,
+ 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 26,
+ 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38,
+ 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80, 81, 82, 84, 85, 86,
+ 87, 89, 90, 91, 92, 94, 95, 96, 98, 99,100,102,103,104,106,107,
+ 109,110,112,113,114,116,117,119,120,122,123,125,126,128,130,131,
+ 133,134,136,138,139,141,143,144,146,148,149,151,153,154,156,158,
+ 160,161,163,165,167,169,170,172,174,176,178,180,182,183,185,187,
+ 189,191,193,195,197,199,201,203,205,207,209,211,213,215,218,220,
+ 222,224,226,228,230,233,235,237,239,241,244,246,248,250,253,255,
+ >;
+ brightness-levels = <
+ 0 40 42 44 46 48 50 52 54 56
+ 58 60 62 64 66 68 70 72 74 76
+ 78 80 82 84 86 88 90 92 94 98
+ 100 102 104 106 108 110 112 114 116 118
+ 120 122 124 126 128 130 132 134 136 138
+ 140 142 144 146 148 150 152 154 156 158
+ 160 162 164 166 168 170 172 174 176 178
+ 180 182 184 186 188 190 192 194 196 198
+ 200 202 204 206 208 210 212 214 216 218
+ 220 222 224 226 228 230 232 234 236 238
+ 255
+ >;
+ default-brightness-level = <100>;
+
};
cht8305_dev@38 {
compatible = "sensylink,cht8305_dev";
diff --git a/src/kernel/linux/v4.4/drivers/leds/Kconfig b/src/kernel/linux/v4.4/drivers/leds/Kconfig
index b1ab8bdf8..3b6fc7a71 100644
--- a/src/kernel/linux/v4.4/drivers/leds/Kconfig
+++ b/src/kernel/linux/v4.4/drivers/leds/Kconfig
@@ -29,6 +29,17 @@ config LEDS_CLASS_FLASH
for the flash related features of a LED device. It can be built
as a module.
+config LEDS_CLASS_AVS_UX
+ tristate "Amazon Alexa Voice Service LED UX Class Support"
+ depends on LED_CLASS
+ help
+ This option enables the amazon alexa voice service led sysfs class in
+ /sys/class/leds.
+ It wrapps LED Class and adds AVS ux LEDS specific sysfs attributes and
+ kernel internal API to it. You'll need this to provide support for AVS
+ releated features of a LED device. It can be built as a module.
+
+
comment "LED drivers"
config LEDS_88PM860X
diff --git a/src/kernel/linux/v4.4/drivers/leds/Makefile b/src/kernel/linux/v4.4/drivers/leds/Makefile
index 8c56aab57..442f2f6c5 100755
--- a/src/kernel/linux/v4.4/drivers/leds/Makefile
+++ b/src/kernel/linux/v4.4/drivers/leds/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
+obj-$((CONFIG_LEDS_CLASS_AVS_UX) += led-class-avsux.o leds-aw20036.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
diff --git a/src/kernel/linux/v4.4/drivers/leds/led-class-avsux.c b/src/kernel/linux/v4.4/drivers/leds/led-class-avsux.c
new file mode 100755
index 000000000..35a1d28f4
--- /dev/null
+++ b/src/kernel/linux/v4.4/drivers/leds/led-class-avsux.c
@@ -0,0 +1,458 @@
+/*
+ * Amazon alexa voice service LED UX class interface
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "leds.h"
+
+int avsux_timer_flag = 0;
+
+static void avsux_parse_rgb(const u32 rgb, struct led_rgb_colors *colors)
+{
+ colors->red = (rgb >> 16) & 0xff;
+ colors->green = (rgb >> 8) & 0xff;
+ colors->blue = rgb & 0xff;
+}
+
+static u32 avsux_dump_rgb(struct led_rgb_colors *colors)
+{
+ return (colors->red << 16) | (colors->green << 8) | colors->blue;
+}
+
+static int avsux_parse_pattern(struct led_avsux_animation *animation, char *buf, size_t len)
+{
+ struct led_avsux_pattern *pattern;
+ char *line, *duration, *hex;
+ u32 rgb;
+ int idx;
+ char *next_line = buf;
+
+ if (!buf)
+ return -1;
+ buf[len-1] = '\0';
+
+ while ((line = strsep(&next_line, "\r\n")) != NULL) {
+ if ((line - buf) >= len)
+ break;
+
+ if (*line == '\0' || *line == '#')
+ continue;
+
+ duration = strsep(&line, ":");
+ if (line) {
+ pattern = kzalloc(sizeof(struct led_avsux_pattern), GFP_KERNEL);
+ if (pattern) {
+ kstrtou32(duration, 10, &pattern->duration);
+ idx = 0;
+ while ((hex = strsep(&line, ",")) != NULL) {
+ kstrtou32(hex, 16, &rgb);
+ avsux_parse_rgb(rgb, &pattern->colors[idx++]);
+ if (idx >= MAX_NUM_LEDS)
+ break;
+ }
+
+ list_add_tail(&pattern->list, &animation->patterns);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void avsux_animation_free(struct led_avsux_animation *animation)
+{
+ struct list_head *pos, *p;
+ struct led_avsux_pattern *pattern;
+
+ if (!animation)
+ return;
+
+ list_for_each_safe(pos, p, &animation->patterns) {
+ pattern = list_entry(pos, struct led_avsux_pattern, list);
+ list_del(pos);
+ kfree(pattern);
+ }
+
+ kfree(animation);
+}
+
+static int avsux_animation_reset(struct led_classdev_avsux *auled_cdev)
+{
+ struct list_head *pos, *p;
+ struct led_avsux_animation *animation;
+
+ auled_cdev->cur_anime = NULL;
+
+ list_for_each_safe(pos, p, &auled_cdev->animations) {
+ animation = list_entry(pos, struct led_avsux_animation, list);
+ list_del(pos);
+ avsux_animation_free(animation);
+ }
+
+ return 0;
+}
+
+struct led_avsux_animation *led_avsux_create_animation(char *name, int priority,
+ u32 loop, u32 pause, struct led_avsux_pattern *patterns,
+ int num_pattern)
+{
+ struct led_avsux_animation *animation;
+ int i;
+
+ animation = kzalloc(sizeof(struct led_avsux_animation), GFP_KERNEL);
+ if (!animation)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&animation->patterns);
+
+ strncpy(animation->name, name, 32);
+ animation->priority = priority;
+ animation->loop = loop;
+ animation->pause = pause;
+
+ for (i = 0; i < num_pattern; i++)
+ list_add_tail(&patterns[i].list, &animation->patterns);
+
+ return animation;
+}
+EXPORT_SYMBOL_GPL(led_avsux_create_animation);
+
+void led_avsux_start_animation(struct led_classdev_avsux *auled_cdev,
+ struct led_avsux_animation *animation)
+{
+ if (!auled_cdev || !animation)
+ return;
+
+ auled_cdev->cur_anime = animation;
+ animation->cur_pattern = list_first_entry(&animation->patterns,
+ struct led_avsux_pattern, list);
+ animation->count = animation->loop;
+ hrtimer_start(&auled_cdev->anime_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+}
+EXPORT_SYMBOL_GPL(led_avsux_start_animation);
+
+static void avsux_animation_start(struct led_classdev_avsux *auled_cdev, const char *name)
+{
+ struct led_avsux_animation *animation;
+
+ if (hrtimer_active(&auled_cdev->anime_timer))
+ hrtimer_cancel(&auled_cdev->anime_timer);
+
+ list_for_each_entry(animation, &auled_cdev->animations, list) {
+ if (!strcmp(animation->name, name)) {
+ avsux_timer_flag = 1;
+ led_avsux_start_animation(auled_cdev, animation);
+ }
+ }
+}
+
+static ssize_t avsux_animation_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ const struct firmware *fw;
+ ssize_t ret = -EINVAL;
+ struct led_avsux_animation *animation;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (!strncmp(buf, "reset", 5)) {
+ if (hrtimer_active(&auled_cdev->anime_timer))
+ hrtimer_cancel(&auled_cdev->anime_timer);
+
+ avsux_animation_reset(auled_cdev);
+ ret = size;
+ goto unlock;
+ }
+
+ animation = kzalloc(sizeof(struct led_avsux_animation), GFP_KERNEL);
+ if (!animation) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ INIT_LIST_HEAD(&animation->patterns);
+
+ ret = sscanf(buf, "%s %s %d %d", animation->name, animation->anime_path,
+ &animation->loop, &animation->pause);
+ if (ret < 2) {
+ kfree(animation);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = request_firmware(&fw, animation->anime_path, dev);
+ if (ret) {
+ kfree(animation);
+ goto unlock;
+ }
+
+ ret = avsux_parse_pattern(animation, (char *)fw->data, fw->size);
+ if (ret == 0)
+ list_add_tail(&animation->list, &auled_cdev->animations);
+
+ release_firmware(fw);
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t avsux_animation_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ struct led_avsux_animation *animation;
+ ssize_t len = 0;
+
+ if (list_empty(&auled_cdev->animations))
+ return 0;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "Available animations:\n");
+ list_for_each_entry(animation, &auled_cdev->animations, list) {
+ len += snprintf(buf + len, PAGE_SIZE - len, "\t[%s]:[%s][%d][%d]\n",
+ animation->name, animation->anime_path,
+ animation->loop, animation->pause);
+ }
+
+ return len;
+}
+static DEVICE_ATTR_RW(avsux_animation);
+
+static ssize_t avsux_select_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ ssize_t ret = -EINVAL;
+ char name[32];
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = sscanf(buf, "%s", name);
+ if (ret < 1) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ avsux_animation_start(auled_cdev, name);
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t avsux_select_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ struct led_avsux_animation *animation = auled_cdev->cur_anime;
+ struct led_avsux_pattern *pattern;
+ ssize_t len = 0;
+
+ if (!animation)
+ return 0;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s[%s][%d][%d]:\n",
+ animation->name, animation->anime_path,
+ animation->loop, animation->pause);
+ list_for_each_entry(pattern, &animation->patterns, list) {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\t%8d:%06X,%06X,%06X,%06X,%06X,%06X,%06X,%06X,%06X,%06X,%06X,%06X\n", pattern->duration,
+ avsux_dump_rgb(&pattern->colors[0]), avsux_dump_rgb(&pattern->colors[1]),
+ avsux_dump_rgb(&pattern->colors[2]), avsux_dump_rgb(&pattern->colors[3]),
+ avsux_dump_rgb(&pattern->colors[4]), avsux_dump_rgb(&pattern->colors[5]),
+ avsux_dump_rgb(&pattern->colors[6]), avsux_dump_rgb(&pattern->colors[7]),
+ avsux_dump_rgb(&pattern->colors[8]), avsux_dump_rgb(&pattern->colors[9]),
+ avsux_dump_rgb(&pattern->colors[10]), avsux_dump_rgb(&pattern->colors[11]));
+ }
+
+ return len;
+}
+static DEVICE_ATTR_RW(avsux_select);
+
+static char *avsux_type_str(enum led_avsux_type type)
+{
+ switch (type) {
+ case AVS_UX_TYPE_SINGLE:
+ return "single";
+ case AVS_UX_TYPE_RADIAL:
+ return "radial";
+ case AVS_UX_TYPE_LINEAR:
+ return "linear";
+ }
+
+ return "unknow";
+}
+
+static ssize_t avsux_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ ssize_t len = 0;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "INFO:\n");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\tled name:[%s]\n",
+ led_cdev->name);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\tled type:[%s]\n",
+ avsux_type_str(auled_cdev->led_type));
+ len += snprintf(buf + len, PAGE_SIZE - len, "\tled nums:[%d]\n",
+ auled_cdev->num_leds);
+
+ return len;
+}
+static DEVICE_ATTR_RO(avsux_info);
+
+static struct attribute *led_avsux_select_attrs[] = {
+ &dev_attr_avsux_select.attr,
+ NULL,
+};
+
+static const struct attribute_group led_avsux_select_group = {
+ .attrs = led_avsux_select_attrs,
+};
+
+static struct attribute *led_avsux_animation_attrs[] = {
+ &dev_attr_avsux_animation.attr,
+ NULL,
+};
+
+static const struct attribute_group led_avsux_animation_group = {
+ .attrs = led_avsux_animation_attrs,
+};
+
+static struct attribute *led_avsux_info_attrs[] = {
+ &dev_attr_avsux_info.attr,
+ NULL,
+};
+
+static const struct attribute_group led_avsux_info_group = {
+ .attrs = led_avsux_info_attrs,
+};
+
+static void led_avsux_init_sysfs_groups(struct led_classdev_avsux *auled_cdev)
+{
+ struct led_classdev *led_cdev = &auled_cdev->led_cdev;
+ const struct attribute_group **avsux_groups = auled_cdev->sysfs_groups;
+
+ int num_sysfs_groups = 0;
+
+ avsux_groups[num_sysfs_groups++] = &led_avsux_select_group;
+ avsux_groups[num_sysfs_groups++] = &led_avsux_animation_group;
+ avsux_groups[num_sysfs_groups++] = &led_avsux_info_group;
+
+ led_cdev->groups = avsux_groups;
+}
+
+static enum hrtimer_restart led_avsux_animation_timer(struct hrtimer *timer)
+{
+ struct led_classdev_avsux *auled_cdev =
+ container_of(timer, struct led_classdev_avsux, anime_timer);
+ struct led_avsux_animation *animation = auled_cdev->cur_anime;
+ ktime_t interval = ms_to_ktime(animation->cur_pattern->duration);
+
+ auled_cdev->ops->pattern_set(auled_cdev, animation->cur_pattern);
+ if (list_is_last(&animation->cur_pattern->list, &animation->patterns)) {
+ animation->cur_pattern = list_first_entry(&animation->patterns, struct led_avsux_pattern, list);
+ if (animation->pause)
+ interval = ktime_add_ms(interval, animation->pause);
+
+ if (--animation->count == 0) {
+ avsux_timer_flag = 0;
+ printk("%s, HRTIMER_NORESTART\n", __func__);
+ return HRTIMER_NORESTART;
+ }
+ } else
+ animation->cur_pattern = list_next_entry(animation->cur_pattern, list);
+
+ hrtimer_forward_now(&auled_cdev->anime_timer, interval);
+
+ return HRTIMER_RESTART;
+}
+
+static int led_avsux_pattern_set_common(struct led_classdev_avsux *auled_cdev, struct led_avsux_pattern *pattern)
+{
+ struct led_classdev *led_cdev = &auled_cdev->led_cdev;
+
+ //led_set_brightness(led_cdev, LED_FULL);
+ led_set_brightness(led_cdev, led_cdev->brightness);
+
+ return 0;
+}
+
+static struct led_avsux_ops led_avsux_common_ops = {
+ .pattern_set = led_avsux_pattern_set_common,
+};
+
+int led_classdev_avsux_register(struct device *parent,
+ struct led_classdev_avsux *auled_cdev)
+{
+ struct led_classdev *led_cdev;
+
+ if (!auled_cdev)
+ return -EINVAL;
+
+ led_cdev = &auled_cdev->led_cdev;
+
+ if (led_cdev->flags & LED_DEV_CAP_AVS_UX) {
+ if (!auled_cdev->ops)
+ auled_cdev->ops = &led_avsux_common_ops;
+
+ INIT_LIST_HEAD(&auled_cdev->animations);
+ hrtimer_init(&auled_cdev->anime_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ auled_cdev->anime_timer.function = &led_avsux_animation_timer;
+
+ led_avsux_init_sysfs_groups(auled_cdev);
+ }
+
+ /* Register led class device */
+ return led_classdev_register(parent, led_cdev);
+}
+EXPORT_SYMBOL_GPL(led_classdev_avsux_register);
+
+void led_classdev_avsux_unregister(struct led_classdev_avsux *auled_cdev)
+{
+ if (!auled_cdev)
+ return;
+
+ led_classdev_unregister(&auled_cdev->led_cdev);
+}
+EXPORT_SYMBOL_GPL(led_classdev_avsux_unregister);
+
+MODULE_AUTHOR("Jason ");
+MODULE_DESCRIPTION("Amazon alexa voice service LED UX class interface");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.4/drivers/leds/leds-aw20036-bootup.h b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036-bootup.h
new file mode 100755
index 000000000..3d24a4aaa
--- /dev/null
+++ b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036-bootup.h
@@ -0,0 +1,157 @@
+#ifndef __AW20036_BOOTUP_H__
+#define __AW20036_BOOTUP_H__
+
+struct led_avsux_pattern bootup_patterns[] = {
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xFF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xEF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xDF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xCF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xBF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xAF},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x9F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x8F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x7F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x6F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x5F},{0x00, 0x00, 0x00},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x4F},{0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xFF},{0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xEF},{0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xDF},{0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xCF},{0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xBF},{0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0xAF},{0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x9F},{0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x8F},{0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x7F},{0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x6F},{0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x5F},{0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x4F},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+ {
+ .duration = 50,
+ .colors = {
+ {0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00},{0x00, 0x00, 0x00}
+ },
+ },
+};
+
+#endif
diff --git a/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.c b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.c
new file mode 100755
index 000000000..302b315ed
--- /dev/null
+++ b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.c
@@ -0,0 +1,436 @@
+/*
+ * AW20036 LED UX driver for amazon alexa voice service
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "leds-aw20036.h"
+#include "leds-aw20036-bootup.h"
+
+#define AW20036_I2C_NAME "aw20036_led"
+#define AW20036_VERSION "v2.0.0"
+#define AW20036_RESET_GPIO_NAME "aw20036_reset_gpio"
+
+struct led_avsux_animation *bootup_anime;
+extern int avsux_timer_flag;
+
+static void aw20036_brightness_work(struct work_struct *work)
+{
+ struct aw20036 *priv = container_of(work, struct aw20036, work);
+ struct led_classdev_avsux *auled_cdev = &priv->aucdev;
+ struct led_classdev *led_cdev = &auled_cdev->led_cdev;
+ struct led_avsux_pattern *pattern = priv->cur_pattern;
+ enum led_brightness value = led_cdev->brightness;
+ int act_value = 0;
+ int max = led_cdev->max_brightness;
+ u8 red, green, blue;
+ int i;
+#if 1
+ if (value > 100)
+ value = 100;
+#else
+ if (value > max)
+ value = max;
+#endif
+ if (value < 0)
+ value = 0;
+
+ if (priv->levels) {
+ act_value = priv->levels[value];
+ }
+
+ /* Set fade current */
+ regmap_write(priv->regmap, REG_PAGE, 0xC2);
+ for (i = 0; i < 12; i++) {
+ #if 1
+ red = pattern->colors[i].red * act_value / max;
+ green = pattern->colors[i].green * act_value / max;
+ blue = pattern->colors[i].blue * act_value / max;
+ #else
+ red = pattern->colors[i].red * max / LED_FULL;
+ green = pattern->colors[i].green * max / LED_FULL;
+ blue = pattern->colors[i].blue * max / LED_FULL;
+ #endif
+ if (priv->gamma_table) {
+ regmap_write(priv->regmap, 3*i, priv->gamma_table[red]);
+ regmap_write(priv->regmap, 3*i + 1, priv->gamma_table[green]);
+ regmap_write(priv->regmap, 3*i + 2, priv->gamma_table[blue]);
+ } else {
+ regmap_write(priv->regmap, 3*i, red);
+ regmap_write(priv->regmap, 3*i + 1, green);
+ regmap_write(priv->regmap, 3*i + 2, blue);
+ }
+ }
+}
+
+static void aw20036_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
+ struct led_avsux_animation *animation = auled_cdev->cur_anime;
+ struct aw20036 *priv = container_of(auled_cdev, struct aw20036, aucdev);
+
+ /* Save current pattern */
+ priv->cur_pattern = animation->cur_pattern;
+
+ schedule_work(&priv->work);
+}
+
+static ssize_t aw20036_store_setting(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct aw20036 *priv =
+ container_of(class, struct aw20036, cls);
+
+ if (kstrtoint(buf, 10, &value) || ((value != 0) && (value != 1)))
+ return -EINVAL;
+
+ if (!strcmp(attr->attr.name, "reset-gpio")) {
+ gpio_set_value_cansleep(priv->reset_gpio, value);
+ } else {
+ pr_err("Invalid write attr.\n");
+ }
+
+ return count;
+}
+
+static void aw20036_debug_class_release(struct class *cls)
+{
+ pr_info("aw20036_debug_class_release...\n");
+}
+
+static struct class_attribute aw20036_debug_class_attrs[] = {
+ __ATTR(reset-gpio, 0644, NULL, aw20036_store_setting),
+ __ATTR_NULL
+};
+
+static int aw20036_debug_reset_gpio(struct device *dev, struct aw20036 *priv)
+{
+ int ret = 0;
+
+ priv->cls.name = devm_kzalloc(dev, 20, GFP_KERNEL);
+ sprintf((char *)priv->cls.name, AW20036_RESET_GPIO_NAME);
+ priv->cls.class_attrs = aw20036_debug_class_attrs;
+ priv->cls.class_release = aw20036_debug_class_release;
+ ret = class_register(&priv->cls);
+ if (ret < 0)
+ dev_err(dev, "debug register class failed! (%d)\n", ret);
+
+ return ret;
+}
+
+static int aw20036_parse_dt(struct device *dev, struct aw20036 *aw20036,
+ struct device_node *np)
+{
+ int ret = 0, prop_size, length;
+ struct property *prop;
+ struct property *prop_level;
+
+ aw20036->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+ if (aw20036->reset_gpio < 0) {
+ dev_err(dev,
+ "%s: no reset gpio provided, will not HW reset device\n",
+ __func__);
+ return -1;
+ } else {
+ dev_info(dev, "%s: reset gpio provided ok\n", __func__);
+ }
+
+ ret = of_property_read_u32(np, "max-brightness", &aw20036->max_brightness);
+ if (ret < 0) {
+ dev_err(dev, "Failed read max brightness, use default!");
+ aw20036->max_brightness = LED_FULL;
+ }
+
+ ret = of_property_read_u32(np, "brightness", &aw20036->brightness);
+ if (ret < 0) {
+ dev_err(dev, "Failed read max brightness, use default!");
+ aw20036->brightness = LED_FULL;
+ }
+
+ ret = of_property_read_u32(np, "imax", &aw20036->imax);
+ if (ret < 0) {
+ dev_err(dev, "Failed read global max current, use default!");
+ aw20036->imax = 3; /* 40mA */
+ }
+
+ prop = of_find_property(np, "gamma-table", &prop_size);
+ if (prop) {
+ aw20036->gamma_table = devm_kzalloc(dev, prop_size, GFP_KERNEL);
+ if (!aw20036->gamma_table)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "gamma-table",
+ aw20036->gamma_table,
+ prop_size / sizeof(u32));
+ if (ret < 0) {
+ dev_err(dev, "Get LED gamma table failed!\n");
+ return ret;
+ }
+ } else {
+ dev_info(dev, "No LED gamma table found!\n");
+ aw20036->gamma_table = NULL;
+ }
+
+ prop_level = of_find_property(np, "brightness-levels", &length);
+ if (prop_level) {
+ aw20036->levels = devm_kzalloc(dev, length, GFP_KERNEL);
+ if (!aw20036->levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "brightness-levels",
+ aw20036->levels,
+ length / sizeof(u32));
+
+ if (ret < 0) {
+ dev_err(dev, "Get LED brightness-levels failed!\n");
+ return ret;
+ }
+ } else {
+ dev_info(dev, "No LED brightness levels table found!\n");
+ aw20036->levels = NULL;
+ }
+
+ return 0;
+}
+
+static int aw20036_hw_reset(struct aw20036 *aw20036) {
+ if (aw20036 && gpio_is_valid(aw20036->reset_gpio)) {
+ gpio_set_value_cansleep(aw20036->reset_gpio, 0);
+ msleep(1);
+ gpio_set_value_cansleep(aw20036->reset_gpio, 1);
+ usleep_range(2000, 2500);
+ } else {
+ dev_err(aw20036->dev, "hw_reset...failed\n");
+ }
+ dev_info(aw20036->dev, "hw_reset...ok.\n");
+ return 0;
+}
+
+static int aw20036_read_aw20036id(struct aw20036 *aw20036) {
+ int ret = -1;
+ unsigned char cnt = 0;
+ unsigned int reg_val = 0;
+
+ regmap_write(aw20036->regmap, REG_PAGE, 0xC0);
+
+ while (cnt < AW_READ_CHIPID_RETRIES) {
+ ret = regmap_read(aw20036->regmap, REG_CHIPID, ®_val);
+ if (reg_val == AW20036_CHIPID) {
+ dev_info(aw20036->dev, "This Chip is 'AW20036' REG_ID: '0x%x'\n",
+ reg_val);
+ return 0;
+ } else if (ret < 0) {
+ dev_err(aw20036->dev,
+ "%s: failed to AW20036_REG_ID: %d\n", __func__,
+ ret);
+ return -EIO;
+ } else {
+ cnt++;
+ dev_info(aw20036->dev,
+ "This Chip read register REG_ID: 0x%x\n",
+ reg_val);
+ }
+ msleep(AW_READ_CHIPID_RETRY_DELAY);
+ }
+ return -EINVAL;
+}
+
+static int aw20036_led_init(struct aw20036 *aw20036) {
+ int i = 0;
+
+ regmap_write(aw20036->regmap, REG_PAGE, 0xC0);
+ regmap_write(aw20036->regmap, REG_SWRST, 0x01);
+ msleep(2);
+ regmap_write(aw20036->regmap, REG_GCCR, (aw20036->imax << 4) | 0x8);
+ /* 3x12 LEDs */
+ regmap_write(aw20036->regmap, REG_SIZE, 0x2);
+ /* Active mode */
+ regmap_write(aw20036->regmap, REG_WORK_MODE, 0x00);
+
+ /* Set DIM current */
+ regmap_write(aw20036->regmap, REG_PAGE, 0xC1);
+ for (i = 0; i < 36; i+=3) {
+ regmap_write(aw20036->regmap, i, 0x3f);
+ regmap_write(aw20036->regmap, i + 1, 0x3f);
+ regmap_write(aw20036->regmap, i + 2, 0x2f);
+ }
+
+ /* Set fade current */
+ regmap_write(aw20036->regmap, REG_PAGE, 0xC2);
+ for (i = 0; i <= 0x23; i++)
+ regmap_write(aw20036->regmap, i, 0);
+
+ dev_info(aw20036->dev, "aw20036_led_init...ok\n");
+
+ return 0;
+}
+
+static int aw20036_led_release(struct aw20036 *aw20036) {
+ regmap_write(aw20036->regmap, REG_PAGE, 0xC0);
+ regmap_write(aw20036->regmap, REG_SWRST, 0x01);
+
+ return 0;
+}
+
+static const struct regmap_config aw20036_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int aw20036_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id) {
+ struct aw20036 *aw20036 = NULL;
+ struct device_node *np = i2c->dev.of_node;
+ struct led_classdev *led_cdev;
+ int ret = -1;
+
+ aw20036 = devm_kzalloc(&i2c->dev, sizeof(struct aw20036), GFP_KERNEL);
+ if (aw20036 == NULL)
+ return -ENOMEM;
+
+ ret = aw20036_debug_reset_gpio(&i2c->dev, aw20036);
+ if (ret < 0)
+ return ret;
+
+ aw20036->i2c = i2c;
+ aw20036->dev = &i2c->dev;
+
+ if (np) {
+ ret = aw20036_parse_dt(&i2c->dev, aw20036, np);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "%s: failed to parse device tree node\n",
+ __func__);
+ return -EIO;
+ }
+ } else {
+ aw20036->reset_gpio = -1;
+ }
+
+ if (gpio_is_valid(aw20036->reset_gpio)) {
+ ret = devm_gpio_request_one(&i2c->dev, aw20036->reset_gpio,
+ GPIOF_OUT_INIT_LOW, "aw20036_rst");
+ if (ret) {
+ dev_err(&i2c->dev, "%s: rst request failed\n",
+ __func__);
+ return -EIO;
+ }
+ }
+
+ aw20036->regmap = devm_regmap_init_i2c(i2c, &aw20036_regmap);
+ if (IS_ERR(aw20036->regmap)) {
+ ret = PTR_ERR(aw20036->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ aw20036_hw_reset(aw20036);
+ ret = aw20036_read_aw20036id(aw20036);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s: aw20036_read_aw20036id failed ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ aw20036_led_init(aw20036);
+
+ INIT_WORK(&aw20036->work, aw20036_brightness_work);
+
+ aw20036->aucdev.led_type = AVS_UX_TYPE_RADIAL,
+ aw20036->aucdev.num_leds = 12,
+ led_cdev = &aw20036->aucdev.led_cdev;
+ led_cdev->name = AW20036_I2C_NAME;
+ led_cdev->max_brightness = aw20036->max_brightness;
+ led_cdev->brightness = aw20036->brightness;
+ led_cdev->brightness_set = aw20036_brightness_set;
+ led_cdev->flags |= LED_DEV_CAP_AVS_UX;
+
+ ret = led_classdev_avsux_register(&i2c->dev, &aw20036->aucdev);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Register avsux led failed:%d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, aw20036);
+
+ /* Run bootup animation */
+ avsux_timer_flag = 1;
+ bootup_anime = led_avsux_create_animation("bootup", 0, 0, 0,
+ bootup_patterns, ARRAY_SIZE(bootup_patterns));
+ if (!IS_ERR(bootup_anime)) {
+ printk("############### bootup ###############\n");
+ led_avsux_start_animation(&aw20036->aucdev, bootup_anime);
+ }
+
+ return 0;
+}
+
+static int aw20036_i2c_remove(struct i2c_client *i2c) {
+ struct aw20036 *aw20036 = i2c_get_clientdata(i2c);
+
+ aw20036_led_release(aw20036);
+
+ if (gpio_is_valid(aw20036->reset_gpio)) {
+ devm_gpio_free(&i2c->dev, aw20036->reset_gpio);
+ }
+
+ led_classdev_avsux_unregister(&aw20036->aucdev);
+
+ if (!IS_ERR(bootup_anime))
+ kfree(bootup_anime);
+
+ devm_kfree(&i2c->dev, aw20036);
+ aw20036 = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id aw20036_i2c_id[] = {
+ {AW20036_I2C_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, aw20036_i2c_id);
+
+static const struct of_device_id aw20036_dt_match[] = {
+ {.compatible = "awinic,aw20036_led"},
+ {},
+};
+
+static struct i2c_driver aw20036_i2c_driver = {
+ .driver = {
+ .name = AW20036_I2C_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(aw20036_dt_match),
+ },
+ .probe = aw20036_i2c_probe,
+ .remove = aw20036_i2c_remove,
+ .id_table = aw20036_i2c_id,
+};
+
+module_i2c_driver(aw20036_i2c_driver);
+
+MODULE_DESCRIPTION("AW20036 LED Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.h b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.h
new file mode 100755
index 000000000..6ae2064bc
--- /dev/null
+++ b/src/kernel/linux/v4.4/drivers/leds/leds-aw20036.h
@@ -0,0 +1,80 @@
+#ifndef __AW20036_H__
+#define __AW20036_H__
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define REG_CHIPID 0x00
+#define REG_WORK_MODE 0x01
+#define REG_SWRST 0x02
+#define REG_GCCR 0x03
+#define REG_FCS 0x04
+#define REG_CLKSYS 0x05
+#define REG_FLTCFG1 0x09
+#define REG_FLTCFG2 0x0A
+#define REG_ISRFLT 0x0B
+#define REG_LEDON0 0x31
+#define REG_LEDON1 0x32
+#define REG_LEDON2 0x33
+#define REG_LEDON3 0x34
+#define REG_LEDON4 0x35
+#define REG_LEDON5 0x36
+#define REG_PATE 0x43
+#define REG_FADEH0 0x44
+#define REG_FADEH1 0x45
+#define REG_FADEH2 0x46
+#define REG_FADEL0 0x47
+#define REG_FADEL1 0x48
+#define REG_FADEL2 0x49
+#define REG_PAT0T0 0x4A
+#define REG_PAT0T1 0x4B
+#define REG_PAT0T2 0x4C
+#define REG_PAT0T3 0x4D
+#define REG_PAT1T0 0x4E
+#define REG_PAT1T1 0x4F
+#define REG_PAT1T2 0x50
+#define REG_PAT1T3 0x51
+#define REG_PAT2T0 0x52
+#define REG_PAT2T1 0x53
+#define REG_PAT2T2 0x54
+#define REG_PAT2T3 0x55
+#define REG_PAT0CFG 0x56
+#define REG_PAT1CFG 0x57
+#define REG_PAT2CFG 0x58
+#define REG_PATGO 0x59
+#define REG_SIZE 0x80
+#define REG_PAGE 0xF0
+
+#define AW_I2C_RETRIES 2
+#define AW_I2C_RETRY_DELAY 1
+#define AW_READ_CHIPID_RETRIES 2
+#define AW_READ_CHIPID_RETRY_DELAY 1
+
+#define AW20036_RSTR 0x01
+#define AW20036_CHIPID 0x18
+
+struct aw20036 {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct led_classdev_avsux aucdev;
+ struct regmap *regmap;
+ struct work_struct work;
+ struct led_avsux_pattern *cur_pattern;
+ struct class cls;
+ unsigned max_brightness;
+ unsigned brightness;
+ unsigned imax;
+ u32 *gamma_table;
+ u32 *levels;
+ int reset_gpio;
+};
+
+#endif
diff --git a/src/kernel/linux/v4.4/drivers/leds/leds.h b/src/kernel/linux/v4.4/drivers/leds/leds.h
index 4238fbc31..04019704e 100644
--- a/src/kernel/linux/v4.4/drivers/leds/leds.h
+++ b/src/kernel/linux/v4.4/drivers/leds/leds.h
@@ -16,11 +16,20 @@
#include
#include
+#ifdef CONFIG_LEDS_CLASS_AVS_UX
+extern int avsux_timer_flag;
+#endif
+
static inline void led_set_brightness_async(struct led_classdev *led_cdev,
enum led_brightness value)
{
value = min(value, led_cdev->max_brightness);
led_cdev->brightness = value;
+#ifdef CONFIG_LEDS_CLASS_AVS_UX
+ if (!avsux_timer_flag){
+ return;
+ }
+#endif
if (!(led_cdev->flags & LED_SUSPENDED))
led_cdev->brightness_set(led_cdev, value);
diff --git a/src/kernel/linux/v4.4/include/linux/led-class-avsux.h b/src/kernel/linux/v4.4/include/linux/led-class-avsux.h
new file mode 100755
index 000000000..a70b0349c
--- /dev/null
+++ b/src/kernel/linux/v4.4/include/linux/led-class-avsux.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __LINUX_AVS_UX_LED_CLASS_H__
+#define __LINUX_AVS_UX_LED_CLASS_H__
+
+#include
+#include
+#include
+
+struct device_node;
+struct led_classdev_avsux;
+
+#define MAX_NUM_LEDS 12
+#define LED_AVS_UX_SYSFS_GROUPS_SIZE 5
+
+enum led_avsux_type {
+ AVS_UX_TYPE_SINGLE = 0,
+ AVS_UX_TYPE_RADIAL,
+ AVS_UX_TYPE_LINEAR,
+};
+
+struct led_rgb_colors {
+ u8 red;
+ u8 green;
+ u8 blue;
+};
+
+struct led_avsux_pattern {
+ struct list_head list;
+ struct led_rgb_colors colors[MAX_NUM_LEDS];
+ u32 duration;
+};
+
+struct led_avsux_animation {
+ char name[32];
+ char anime_path[256];
+ struct list_head list;
+ struct list_head patterns;
+ struct led_avsux_pattern *cur_pattern;
+ int priority;
+ u32 loop, pause;
+ u32 count;
+};
+
+struct led_avsux_ops {
+ int (*pattern_set)(struct led_classdev_avsux *auled_cdev, struct led_avsux_pattern *pattern);
+};
+
+struct led_classdev_avsux {
+ struct led_classdev led_cdev;
+ struct led_avsux_ops *ops;
+ enum led_avsux_type led_type;
+ int num_leds;
+ struct hrtimer anime_timer;
+ struct list_head animations;
+ struct led_avsux_animation *cur_anime;
+
+ const struct attribute_group *sysfs_groups[LED_AVS_UX_SYSFS_GROUPS_SIZE];
+};
+
+static inline struct led_classdev_avsux *lcdev_to_aucdev(
+ struct led_classdev *lcdev)
+{
+ return container_of(lcdev, struct led_classdev_avsux, led_cdev);
+}
+
+extern int led_classdev_avsux_register(struct device *parent,
+ struct led_classdev_avsux *auled_cdev);
+
+extern void led_classdev_avsux_unregister(struct led_classdev_avsux *auled_cdev);
+
+extern struct led_avsux_animation *led_avsux_create_animation(char *name, int priority,
+ u32 loop, u32 pause, struct led_avsux_pattern *patterns,
+ int num_pattern);
+extern void led_avsux_start_animation(struct led_classdev_avsux *auled_cdev, struct led_avsux_animation *animation);
+
+#endif
diff --git a/src/kernel/linux/v4.4/include/linux/leds.h b/src/kernel/linux/v4.4/include/linux/leds.h
index fa359c79c..323e0cec0 100644
--- a/src/kernel/linux/v4.4/include/linux/leds.h
+++ b/src/kernel/linux/v4.4/include/linux/leds.h
@@ -48,6 +48,7 @@ struct led_classdev {
#define SET_BRIGHTNESS_ASYNC (1 << 21)
#define SET_BRIGHTNESS_SYNC (1 << 22)
#define LED_DEV_CAP_FLASH (1 << 23)
+#define LED_DEV_CAP_AVS_UX (1 << 24)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */