From 51cad7bc9d688a533696be1c19d6ac7e5a651472 Mon Sep 17 00:00:00 2001 From: wk Date: Wed, 22 Jun 2022 22:57:52 +0800 Subject: [PATCH] =?UTF-8?q?[debug]=20=E6=B7=BB=E5=8A=A0=E7=AD=89=E6=95=88?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aud8516p1v2_consys_slc_128_yocto.dts | 42 +- src/kernel/linux/v4.4/drivers/leds/Kconfig | 11 + src/kernel/linux/v4.4/drivers/leds/Makefile | 1 + .../linux/v4.4/drivers/leds/led-class-avsux.c | 458 ++++++++++++++++++ .../v4.4/drivers/leds/leds-aw20036-bootup.h | 157 ++++++ .../linux/v4.4/drivers/leds/leds-aw20036.c | 436 +++++++++++++++++ .../linux/v4.4/drivers/leds/leds-aw20036.h | 80 +++ src/kernel/linux/v4.4/drivers/leds/leds.h | 9 + .../v4.4/include/linux/led-class-avsux.h | 90 ++++ src/kernel/linux/v4.4/include/linux/leds.h | 1 + 10 files changed, 1279 insertions(+), 6 deletions(-) create mode 100755 src/kernel/linux/v4.4/drivers/leds/led-class-avsux.c create mode 100755 src/kernel/linux/v4.4/drivers/leds/leds-aw20036-bootup.h create mode 100755 src/kernel/linux/v4.4/drivers/leds/leds-aw20036.c create mode 100755 src/kernel/linux/v4.4/drivers/leds/leds-aw20036.h create mode 100755 src/kernel/linux/v4.4/include/linux/led-class-avsux.h 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 */