blob: e0d9328e734315696497a5a49194c57f4c5fba18 [file] [log] [blame] [edit]
#include <amlogic/leds-aw210xx.h>
#include <amlogic/leds-main.h>
#include <linux/ctype.h>
#include <dm.h>
static struct udevice *g_led_devp;
static enum Aw210XX_INIT_STATUS led_init_ready;
#define CAL_FILE_LEN 2048
void reg_write(struct udevice *led_devp, uint reg, int mask, int val)
{
int old_val;
int new_val;
old_val = dm_i2c_reg_read(led_devp, reg);
if (old_val < 0) {
pr_err("LED: read %d reg failed\n", reg);
return;
}
new_val = (old_val & ~mask) | (val & mask);
dm_i2c_reg_write(led_devp, reg, new_val);
}
static void led_turn_off_write(struct udevice *led_devp, unsigned int led_gcfg)
{
int i;
for (i = 0; i < 3; ++i) {
reg_write(led_devp, AW210XX_REG_SL00 + (led_gcfg * 3) + i,
AW210XX_SLXX_MASK, 0);
reg_write(led_devp, AW210XX_REG_BR00L + (led_gcfg * 3 * 2) + i * 2,
AW210XX_BRXX_MASK, 0);
}
reg_write(led_devp, AW210XX_REG_UPDATE, AW210XX_UPDATE_MASK,
AW210XX_UPDATE_ENABLE);
}
static void led_cal_setting_write(unsigned int *cal_settings,
struct udevice *led_devp, unsigned int led_index)
{
int i;
if (!led_devp || !cal_settings)
return;
for(i = 0; i < 3; ++i) {
// color
reg_write(led_devp, AW210XX_REG_SL00 + (led_index * 3) + i, AW210XX_SLXX_MASK,
cal_settings[i]);
// brightness
reg_write(led_devp, AW210XX_REG_BR00L + (led_index * 3 * 2) + i * 2,
AW210XX_BRXX_MASK, cal_settings[3]);
}
reg_write(led_devp, AW210XX_REG_UPDATE, AW210XX_UPDATE_MASK,
AW210XX_UPDATE_ENABLE);
}
static int get_n_dot_data(const char *buffer,
const char *start_label, const char *end_label, char *data, size_t data_size)
{
const char *start = strstr(buffer, start_label);
const char *end = strstr(start, end_label);
int length;
if (!start || !end) {
pr_err("LED: cannot found n_dot data\n");
return -1;
}
length = end - start;
if (length >= data_size)
length = data_size - 1;
memcpy(data, start, length);
data[length] = '\0';
return 0;
}
static void parse_and_convert(const char *p, unsigned long settings_[], int len)
{
const char *ptr = p;
char num_str[16];
int i = 0;
int j = 0;
while (*ptr != '\0' && i < N_CAL_SETTINGS) {
memset(num_str, 0, sizeof(num_str));
j = 0;
while (*ptr != ',' && *ptr != ';' && *ptr != '\0')
num_str[j++] = *ptr++;
settings_[i++] = simple_strtoul(num_str, NULL, 10);
if (*ptr == ',' || *ptr == ';')
ptr++;
}
}
void aw210xx_get_cal_settings(unsigned int *settings, const char color_header[],
unsigned int led_index)
{
int i;
unsigned long settings_[N_CAL_SETTINGS];
const char *p;
char rgbw_buf[CAL_FILE_LEN];
const int label_len = strlen("four_dot_0") + 1;
char start_label[label_len];
char end_label[label_len];
const unsigned int max_chars = CAL_FILE_LEN;
char buf[max_chars];
if (get_cal_string(buf, max_chars))
return;
snprintf(start_label, sizeof(start_label), "%s%d", "four_dot_", led_index);
if (led_index < AW210XX_NUM_LEDS - 1)
snprintf(end_label, sizeof(end_label), "%s%d", "four_dot_", led_index + 1);
else
snprintf(end_label, strlen("Debug") + 1, "%s%c", "Debug", '\0');
if (get_n_dot_data(buf, start_label, end_label, rgbw_buf, sizeof(rgbw_buf)))
return;
p = strstr(rgbw_buf, color_header);
if (!p) {
pr_err("LED: \"%s\" not found in cal file segment: %s\n",
color_header, buf);
return;
}
p += strlen(color_header);
parse_and_convert(p, settings_, sizeof(p));
for (i = 0; i < N_CAL_SETTINGS; ++i)
settings[i] = (unsigned int)settings_[i];
}
void turn_on_white_led(struct udevice *led_devp, unsigned int led_index)
{
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
char color_header[] = "White:";
aw210xx_get_cal_settings(cal_settings, color_header, led_index);
pr_info("LED: index(%d) show %s%d,%d,%d;%d\n", led_index, color_header,
cal_settings[0], cal_settings[1], cal_settings[2], cal_settings[3]);
led_cal_setting_write(cal_settings, led_devp, led_index);
}
void turn_on_yellow_led(struct udevice *led_devp, unsigned int led_index)
{
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
char color_header[] = "NestYellow:";
aw210xx_get_cal_settings(cal_settings, color_header, led_index);
pr_info("LED: index(%d) show %s%d,%d,%d;%d\n", led_index, color_header,
cal_settings[0], cal_settings[1], cal_settings[2], cal_settings[3]);
led_cal_setting_write(cal_settings, led_devp, led_index);
}
void turn_on_green_led(struct udevice *led_devp, unsigned int led_index)
{
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
char color_header[] = "NestGreen:";
aw210xx_get_cal_settings(cal_settings, color_header, led_index);
pr_info("LED: index(%d) show %s%d,%d,%d;%d\n", led_index, color_header,
cal_settings[0], cal_settings[1], cal_settings[2], cal_settings[3]);
led_cal_setting_write(cal_settings, led_devp, led_index);
}
void turn_on_blue_led(struct udevice *led_devp, unsigned int led_index)
{
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
char color_header[] = "NestBlue:";
aw210xx_get_cal_settings(cal_settings, color_header, led_index);
pr_info("LED: index(%d) show %s%d,%d,%d;%d\n", led_index, color_header,
cal_settings[0], cal_settings[1], cal_settings[2], cal_settings[3]);
led_cal_setting_write(cal_settings, led_devp, led_index);
}
void turn_on_orange_led(struct udevice *led_devp, unsigned int led_index)
{
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
char color_header[] = "NestOrange:";
aw210xx_get_cal_settings(cal_settings, color_header, led_index);
pr_info("LED: index(%d) show %s%d,%d,%d;%d\n", led_index, color_header,
cal_settings[0], cal_settings[1], cal_settings[2], cal_settings[3]);
led_cal_setting_write(cal_settings, led_devp, led_index);
}
void blink_yellow_led(struct udevice *led_devp) {
unsigned int cal_settings[N_CAL_SETTINGS] = {0, 0, 0, 0};
int i;
reg_write(led_devp, AW210XX_REG_GCFG, AW210XX_GE_MASK,
AW210XX_GE2_SET);
reg_write(led_devp, AW210XX_REG_ABMCFG, AW210XX_PATCFG_MASK,
AW210XX_PATE_ENABLE | AW210XX_PATMD_AUTO);
reg_write(led_devp, AW210XX_REG_ABMT0, AW210XX_PAT_RISE_MASK,
AW210XX_PAT_RISE_TIME);
reg_write(led_devp, AW210XX_REG_ABMT1, AW210XX_PAT_FALL_MASK,
AW210XX_PAT_FALL_TIME);
reg_write(led_devp, AW210XX_REG_ABMT0, AW210XX_PAT_ON_MASK,
AW210XX_PAT_ON_TIME);
reg_write(led_devp, AW210XX_REG_ABMT1, AW210XX_PAT_OFF_MASK,
AW210XX_PAT_OFF_TIME);
// HW design: LED7 is blue, LED8 is red, LED9 is green
// datasheet: in group mode
// GSLR: for ledx(x=1,4,7)
// GSLG: for ledx(x=2,5,8)
// GSLB: for ledx(x=3,6,9)
for(i = 0; i < 3; ++i) {
if (i == 2) {
// blue led
reg_write(led_devp, AW210XX_REG_GSLR, AW210XX_SLXX_MASK,
cal_settings[i]);
} else {
// red led and green led
reg_write(led_devp, AW210XX_REG_GSLG + i,
AW210XX_SLXX_MASK, cal_settings[i]);
}
}
// set brightness range
reg_write(led_devp, AW210XX_REG_GBRH, AW210XX_BRXX_MASK,
AW210xx_MAX_BRIGHTNESS);
reg_write(led_devp, AW210XX_REG_GBRL, AW210XX_BRXX_MASK,
AW210xx_MIN_BRIGHTNESS);
// start run
reg_write(led_devp, AW210XX_REG_ABMGO, AW210XX_PAT_RUN_MASK,
AW210XX_PAT_RUN);
}
int aw210xx_sys_led_init(void)
{
int ret = 0;
struct udevice *led_devp = NULL;
if (led_init_ready)
return ret;
led_init_ready = INIT_DONE;
ret = i2c_get_chip_for_busnum(AW210XX_I2C_BUS_NUM, AW210XX_I2C_LED_REG, 1, &led_devp);
if (ret) {
pr_info("LED: i2c can't get bus 0x%x\n", AW210XX_I2C_LED_REG);
led_init_ready = INIT_NO_USED;
return ret;
}
reg_write(led_devp, AW210XX_REG_RESET, AW210XX_RESET_MASK,
AW210XX_RESET_CHIP);
reg_write(led_devp, AW210XX_REG_GCR, AW210XX_BIT_CHIPEN_MASK,
AW210XX_BIT_CHIPEN_ENABLE);
reg_write(led_devp, AW210XX_REG_GCR, AW210XX_BIT_APSE_MASK,
AW210XX_BIT_APSE_ENABLE);
reg_write(led_devp, AW210XX_REG_GCCR, AW210XX_GLOBAL_CURRENT_MASK,
AW210xx_MAX_CURRENT);
reg_write(led_devp, AW210XX_REG_PHCR, AW210XX_PDE_MASK,
AW210XX_PDE_ENABLE);
g_led_devp = led_devp;
return ret;
}
int aw210xx_turn_on_led(enum LED_ANIMATION led_animation, unsigned int led_index)
{
int ret;
struct udevice *led_devp;
if (check_read_cal_done() == -1)
return -1;
if (led_init_ready == INIT_NO_USED)
return -1;
if (led_init_ready != INIT_DONE) {
ret = aw210xx_sys_led_init();
if (ret)
return ret;
}
if (led_index >= AW210XX_NUM_LEDS) {
pr_err("LED: %s Can't to find led_index: %d\n",
__func__, led_index);
return -1;
}
led_devp = g_led_devp;
switch(led_animation) {
case WHITE:
turn_on_white_led(led_devp, led_index);
break;
case YELLOW:
turn_on_yellow_led(led_devp, led_index);
break;
case GREEN:
turn_on_green_led(led_devp, led_index);
break;
case BLUE:
turn_on_blue_led(led_devp, led_index);
break;
case ORANGE:
turn_on_orange_led(led_devp, led_index);
break;
default:
pr_err("LED: %s unknown led animation\n", __func__);
}
return 0;
}
int aw210xx_turn_off_led(unsigned int led_index)
{
int ret;
struct udevice *led_devp;
if (led_init_ready == INIT_NO_USED)
return -1;
if (led_init_ready != INIT_DONE) {
ret = aw210xx_sys_led_init();
if (ret)
return ret;
}
if (led_index >= AW210XX_NUM_LEDS) {
pr_err("LED: %s Can't to find led_index: %d\n",
__func__, led_index);
return -1;
}
led_devp = g_led_devp;
led_turn_off_write(led_devp, led_index);
return 0;
}
void aw210xx_uboot_init_led_status(void)
{
int i;
for (i = 0; i < AW210XX_NUM_LEDS; ++i)
aw210xx_turn_off_led(i);
// show group index is 1/2 led
const int indexs[] = {1, 2};
int length = sizeof(indexs) / sizeof(indexs[0]);
for (i = 0; i < length; ++i)
aw210xx_turn_on_led(WHITE, indexs[i]);
}
void aw210xx_start_kernel_led_status(void)
{
int i;
for (i = 0; i < AW210XX_NUM_LEDS; ++i)
aw210xx_turn_off_led(i);
// show all leds
for (i = 0; i < AW210XX_NUM_LEDS; ++i)
aw210xx_turn_on_led(WHITE, i);
}
void aw210xx_udisk_update_led_status(void)
{
int i;
// show 1/2 green led
for (i = 0; i < AW210XX_NUM_LEDS; ++i) {
if (i == 1 || i == 2)
aw210xx_turn_on_led(GREEN, i);
else
aw210xx_turn_off_led(i);
}
}