blob: 53f54ef34e144b782e39cbbd2de5950063ae716d [file]
#include <amlogic/led_aw2015.h>
#include <dm.h>
#if defined(CONFIG_C2_VENUS_P1) || defined(CONFIG_C2_VENUS_P2) || \
defined(CONFIG_C2_VENUS_BX) || defined(CONFIG_C2_BSV3_P1)
#define CAL_FILENAME "status_led_calibration_LUT.txt"
#define AW2015_MAX_CURRENT AW2015_LED_25_5mA
#else
#define CAL_FILENAME "led_calibration_LUT.txt"
#define AW2015_MAX_CURRENT AW2015_LED_12_25mA
#endif
// Copied from
// https://eureka-partner-review.googlesource.com/c/amlogic/u-boot/+/149113
// Reads "led_calibration_LUT.txt" from the factory partition.
// Forwards errors, returns 0 on success.
int get_cal_string(char *buf, int len)
{
int ret;
loff_t len_read;
const loff_t seek = 21;
ret = fs_set_blk_dev("mmc", "1:4", FS_TYPE_EXT);
if (ret) {
pr_err("LED: fs_set_blk_dev error=%d\n", ret);
return ret;
}
// Leave space for terminator
ret = fs_read(CAL_FILENAME, buf, seek, len - 1,
&len_read);
if (ret) {
pr_err("LED: fs_read error=%d\n", ret);
return ret;
}
// fs_read does not add a null terminator
buf[len_read] = '\0';
return 0;
}
// Copied from
// https://eureka-partner-review.googlesource.com/c/amlogic/u-boot/+/149113
// Gets calibration settings for the status LED:
// pwm_r, pwm_g, pwm_b, current_r, current_g, current_b
// On failure, |settings| is not modified.
void get_cal_settings(unsigned int *settings, const char color_header[])
{
int i;
unsigned long settings_[N_CAL_SETTINGS];
// Max length to cover all color lines
// 9 colors * (color_name + ":4095:" + xxx[,|;|\n] * 6)
// 9 * (14 + 6 + 4 * 6 )
const unsigned int max_chars = 396;
// Add one for null terminator
char buf[max_chars];
if (get_cal_string(&buf, max_chars))
return;
const char *p, *p_next;
p = strstr(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);
for (i = 0; i < N_CAL_SETTINGS; ++i) {
unsigned long value = simple_strtoul(p, &p_next, 10);
if (value > 255) {
pr_err("LED: value %d is too large: %lu\n",
settings_[i]);
return;
}
settings_[i] = value;
// Skip check that |p_next| is valid on last iteration
if (i == 5)
break;
// Is there not a separator, or not another number
if ((*p_next != ',' && *p_next != ';') ||
!isdigit(p_next[1])) {
// Is this the first current
if (i == 3) {
// One current instead of RGB current
settings_[4] = value;
settings_[5] = value;
break;
}
// Invalid
pr_err("LED calibration file is malformed\n");
return;
}
p = p_next + 1;
}
for (i = 0; i < N_CAL_SETTINGS; ++i)
settings[i] = (unsigned int)settings_[i];
}
void reg_write(struct udevice *led_devp, int 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);
}
void sys_led_init(enum LED_ANIMATION led_animation)
{
#ifdef CONFIG_SYS_I2C_MESON
int ret;
struct udevice *led_devp = NULL;
ret = i2c_get_chip_for_busnum(BUS_NUM, I2C_LED_REG, 1, &led_devp);
if (ret) {
pr_err("LED: i2c get bus fail\n");
return;
}
reg_write(led_devp, AW2015_REG_RSTIDR, AW2015_LED_RSTIDR_MASK,
AW2015_LED_RSTIDR_RESET);
reg_write(led_devp, AW2015_REG_GCR, AW2015_LED_CHIPEN_MASK,
AW2015_LED_CHIP_ENABLE);
reg_write(led_devp, AW2015_REG_GCR, AW2015_LED_CHARGE_DISABLE_MASK,
AW2015_LED_CHARGE_DISABLE);
reg_write(led_devp, AW2015_REG_IMAX, AW2015_LED_IMX_MASK,
AW2015_MAX_CURRENT);
reg_write(led_devp, AW2015_REG_LEDCTR, AW2015_LED_PWMLOG_MASK,
AW2015_LED_PWMLOG_LEANER);
reg_write(led_devp, AW2015_REG_LEDEN, AW2015_LED_LEDEN_MASK,
AW2015_LED_LEDEN_TURN_ON_ALL);
switch(led_animation) {
case WHITE:
turn_on_white_led(led_devp);
break;
case YELLOW:
turn_on_yellow_led(led_devp);
break;
case BLINK_YELLOW:
blink_yellow_led(led_devp);
break;
default:
pr_err("LED: unknown led animation\n");
}
#endif // CONFIG_SYS_I2C_MESON
}
void turn_on_white_led(struct udevice *led_devp) {
unsigned int cal_settings[N_CAL_SETTINGS] =
{128, 0, 128, 100, 100, 100};
int i;
get_cal_settings(&cal_settings, "White:4095:");
for(i = 0; i < 3; ++i) {
reg_write(led_devp, AW2015_REG_LCFG1 + i, AW2015_LED_LEDMD_MASK,
AW2015_LED_ON_MODE);
reg_write(led_devp, AW2015_REG_PWM1 + i, AW2015_LED_PWM_MASK,
cal_settings[i]);
reg_write(led_devp, AW2015_REG_ILED1 + i, AW2015_LED_ILED_MASK,
cal_settings[3+i]);
}
}
void turn_on_yellow_led(struct udevice *led_devp) {
unsigned int cal_settings[N_CAL_SETTINGS] =
{255, 255, 255, 0xff, 0xef, 0};
int i;
get_cal_settings(&cal_settings, "NestYellow:4095:");
for(i = 0; i < 3; ++i) {
reg_write(led_devp, AW2015_REG_LCFG1 + i, AW2015_LED_LEDMD_MASK,
AW2015_LED_ON_MODE);
reg_write(led_devp, AW2015_REG_PWM1 + i, AW2015_LED_PWM_MASK,
cal_settings[i]);
reg_write(led_devp, AW2015_REG_ILED1 + i, AW2015_LED_ILED_MASK,
cal_settings[3+i]);
}
}
void blink_yellow_led(struct udevice *led_devp) {
int i;
int val;
turn_on_yellow_led(led_devp);
for(i = 0; i < 3; ++i) {
reg_write(led_devp, AW2015_REG_PAT1_T1 + i * PAT_FIELD_NUMBER,
AW2015_LED_PAT_TRISE_MASK, 0x0);
reg_write(led_devp, AW2015_REG_PAT1_T2 + i * PAT_FIELD_NUMBER,
AW2015_LED_PAT_TFALL_MASK, 0x0);
reg_write(led_devp, AW2015_REG_PAT1_T1 + i * PAT_FIELD_NUMBER,
AW2015_LED_PAT_TON_MASK,
AW2015_LED_PAT_ON_1SEC);
reg_write(led_devp, AW2015_REG_PAT1_T2 + i * PAT_FIELD_NUMBER,
AW2015_LED_PAT_TOFF_MASK,
AW2015_LED_PAT_OFF_1SEC);
reg_write(led_devp, AW2015_REG_PAT1_T4 + i * PAT_FIELD_NUMBER,
AW2015_LED_PAT_CTR_MASK,
AW2015_LED_PAT_CTR_INF_LOOP);
reg_write(led_devp, AW2015_REG_LCFG1 + i,
AW2015_LED_LEDMD_MASK,
AW2015_LED_BLINK_MODE);
}
reg_write(led_devp, AW2015_REG_PATRUN, AW2015_LED_PATRUN_MASK,
AW2015_LED_PATRUN_START_ALL);
}