kk-dev #1

Open
Ghost wants to merge 11 commits from kk-dev into un_sdk_patch
10 changed files with 1279 additions and 6 deletions
Showing only changes of commit feac7f1cf1 - Show all commits

View File

@ -430,12 +430,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>;
};
};

View File

@ -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

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/led-class-avsux.h>
#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 <yzhuq@qq.com>");
MODULE_DESCRIPTION("Amazon alexa voice service LED UX class interface");
MODULE_LICENSE("GPL v2");

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include <linux/led-class-avsux.h>
#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, &reg_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");

View File

@ -0,0 +1,80 @@
#ifndef __AW20036_H__
#define __AW20036_H__
#include <linux/leds.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/led-class-avsux.h>
#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

View File

@ -16,11 +16,20 @@
#include <linux/rwsem.h>
#include <linux/leds.h>
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __LINUX_AVS_UX_LED_CLASS_H__
#define __LINUX_AVS_UX_LED_CLASS_H__
#include <linux/leds.h>
#include <linux/hrtimer.h>
#include <linux/list.h>
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

View File

@ -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 */