| #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); |
| } |
| } |