Yuji Sasaki has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/33431
Change subject: TEMP: NOT FOR REVIEW: google/mistral: Add LED calibration ......................................................................
TEMP: NOT FOR REVIEW: google/mistral: Add LED calibration
Add LED calibration for LP5562.
BUG=b:130764817 TEST=Run on DUT, verify LED color and brightness changed with led_calibration entry in the VPD.
Change-Id: Iff3ab55cdd12a8e8fe7549bce99a88ed0ef01534 --- M src/drivers/i2c/lp5562/Makefile.inc M src/drivers/i2c/lp5562/led_lp5562.c A src/drivers/i2c/lp5562/led_lp5562_calibration.c A src/drivers/i2c/lp5562/led_lp5562_calibration.h M src/drivers/i2c/lp5562/led_lp5562_programs.c M src/drivers/i2c/lp5562/led_lp5562_programs.h M src/mainboard/google/mistral/Makefile.inc A src/mainboard/google/mistral/led_calibration.c A src/mainboard/google/mistral/led_calibration.h M src/mainboard/google/mistral/verstage.c 10 files changed, 748 insertions(+), 32 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/31/33431/1
diff --git a/src/drivers/i2c/lp5562/Makefile.inc b/src/drivers/i2c/lp5562/Makefile.inc index bfdb408..308898e 100644 --- a/src/drivers/i2c/lp5562/Makefile.inc +++ b/src/drivers/i2c/lp5562/Makefile.inc @@ -1,5 +1,6 @@ verstage-$(CONFIG_DRIVERS_I2C_LED_LP5562) += led_lp5562.c verstage-$(CONFIG_DRIVERS_I2C_LED_LP5562) += led_lp5562_programs.c +verstage-$(CONFIG_DRIVERS_I2C_LED_LP5562) += led_lp5562_calibration.c
ramstage-$(CONFIG_DRIVERS_I2C_LED_LP5562) += led_lp5562.c ramstage-$(CONFIG_DRIVERS_I2C_LED_LP5562) += led_lp5562_programs.c diff --git a/src/drivers/i2c/lp5562/led_lp5562.c b/src/drivers/i2c/lp5562/led_lp5562.c index 6eafee3..268eac0 100644 --- a/src/drivers/i2c/lp5562/led_lp5562.c +++ b/src/drivers/i2c/lp5562/led_lp5562.c @@ -290,6 +290,14 @@ enable_reg |= LP5562_ENABLE_ALL_HOLD; ledc_write_enable(ledc, enable_reg);
+ /* Configure LED current */ + ledc_write(ledc, LP5562_CURRENT_B, + &program_desc->engine_program[0].led_current, 1); + ledc_write(ledc, LP5562_CURRENT_G, + &program_desc->engine_program[1].led_current, 1); + ledc_write(ledc, LP5562_CURRENT_R, + &program_desc->engine_program[2].led_current, 1); + /* All engines in LOAD mode. */ ledc_write_opmode(ledc, LP5562_OPMODE_ALL_LOAD);
diff --git a/src/drivers/i2c/lp5562/led_lp5562_calibration.c b/src/drivers/i2c/lp5562/led_lp5562_calibration.c new file mode 100644 index 0000000..ec11719 --- /dev/null +++ b/src/drivers/i2c/lp5562/led_lp5562_calibration.c @@ -0,0 +1,174 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <console/console.h> +#include <string.h> +#include "led_lp5562_programs.h" +#include "led_lp5562_calibration.h" + +static int calculate_step_time(int duration, int increment) +{ +/* + * On LP5562 ramp instruction, duration (D, msec) is + * D = T * step_time * increment * 1000 + * where T = 0.49msec(1/2048Hz) or 15.6msec(1/64Hz) + * Thus, + * step_time = D/(increment*T)/1000 + * = (D*F)/increment/1000 + * Use F=2048 for short duration, F=64 for long duration. + */ + int step_time = ((duration * 2048) / increment) / 1000; + if (step_time > 63) { + step_time = ((duration * 64) / increment) / 1000; + if (step_time > 63) { + return -1; /* Overflow */ + } + step_time |= 0x40; + } + return step_time; +} + +static void calibrate_set_pwm_instruction( + const struct lp5562_calibration_code_map* code_map, + uint8_t *code, + int offset, + int intensity) +{ + /* + * Calibrate set_pwm instruction: + * Just to replace PWM value in the code. + */ + code[offset+0] = 0x40; + code[offset+1] = intensity; +} + +static void calibrate_ramp_instruction( + const struct lp5562_calibration_code_map* code_map, + uint8_t *code, + int offset, + int intensity) +{ + int intensity_reminder = intensity; + + /* + * Calibrate ramp instruction: + * We have to re-calculate PWM increment and step time in the code. + * Because LP5562 only can handle PWM increment in 7-bit range + * (+2 to +128) per instruction, we usually use 2 consecutive + * ramp instructions to implement full 8-bit PWM swing. + * In that case we have to replace multiple PWM values / + * and step times. + */ + for (int i = 0; i < code_map->ramp_num; i++) { + int ramp_sign = 0; + int increment = + code_map->ramp_instructions[i].ramp_division; + int duration = + code_map->ramp_instructions[i].ramp_duration; + + /* + * Calculate PWM increment for this instruction. + */ + if (increment < 0) { + increment = -increment; + ramp_sign = 0x80; + } + + /* + * To handle division remainder, we track remaining + * intensity and plase the leftover at the last of + * consecutive ramp instructions. + * (if there is only one ramp instruction, we always + * use the intensity as PWM increment). + */ + if ((i == (code_map->ramp_num - 1))) { + increment = intensity_reminder; + } else { + increment = ((intensity * increment) / 256); + intensity_reminder -= increment; + } + + /* + * If there is no need to ramp PWM value, + * substitute the code with "set_pwm 0" instruction + * (0x4000) as placeholder. + */ + if (increment == 0) { + code[offset+0] = 0x40; + code[offset+1] = 0x00; + } else { + int step_time = + calculate_step_time(duration, increment); + + /* + * LP5562 ramp/wait duration is correlated with + * increment. Smaller the increment, shorter the + * duration. + * If single ramp/wait instruction could not handle + * specified duration, try to incrase increment. + */ + while (step_time < 0) { + increment++; + step_time = calculate_step_time( + duration, increment); + } + + increment = ramp_sign | (increment - 1); + code[offset+0] = step_time; + code[offset+1] = increment; + } + offset += 2; + } +} + +int led_lp5562_calibrate(const struct lp5562_calibration_data* cal_data, + struct lp5562_calibrated_color* calibrated_colors) +{ + const struct lp5562_calibration_code_map* + code_map = cal_data->code_map; + + while (code_map->code_type != invalid) { + int bgr_index = code_map->component; + int rgb_index = (2 - bgr_index); + int offset = (code_map->code_offset % NPROGSIZE) * 2; + uint8_t *code = cal_data->pattern-> + engine_program[bgr_index].program_text; + + cal_data->pattern->engine_program[bgr_index].led_current = + calibrated_colors[cal_data->color_index]. + current_values[rgb_index]; + + /* + * Calculate calibrated LED intensity (0-255) + * +50 is to round off the fraction. + */ + int intensity = + calibrated_colors[cal_data->color_index]. + pwm_values[rgb_index]; + if (cal_data->relative_brightness != 100) { + intensity = (intensity * + cal_data->relative_brightness + 50) / 100; + } + + switch (code_map->code_type) { + + case set_pwm: + calibrate_set_pwm_instruction( + code_map, code, offset, intensity); + break; + + case ramp: + calibrate_ramp_instruction( + code_map, code, offset, intensity); + break; + + default: + break; + } + + code_map++; + } + return 0; +} + diff --git a/src/drivers/i2c/lp5562/led_lp5562_calibration.h b/src/drivers/i2c/lp5562/led_lp5562_calibration.h new file mode 100644 index 0000000..5d8bee9 --- /dev/null +++ b/src/drivers/i2c/lp5562/led_lp5562_calibration.h @@ -0,0 +1,74 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LED_LP5562_CALIBRATION_H_ +#define LED_LP5562_CALIBRATION_H_ + +/* Size of LP5562 program memry, as in 16-bit word steps */ +#define NPROGSIZE 16 + +/* Number of consecutive ramp instructions */ +#define NMAXRAMPS 2 + +/* LP5562 color component number, associated with engine number */ +enum lp5562_components { + blue = 0, + green, + red +}; + +/* LP5562 code type for calibration (PWM value replacement) */ +enum lp5562_code_types { + invalid = 0, + set_pwm, + ramp +}; + +/* + * Structure to indicate where / how to replace PWM value + * in a LP5562 program code. + */ +struct lp5562_calibration_code_map { + enum lp5562_components component; + enum lp5562_code_types code_type; + /* Code offset, as in 16bit/word binary */ + uint8_t code_offset; + /* Number of consecutive ramp instructions */ + uint8_t ramp_num; + struct { + /* RAMP duration, in msec */ + uint16_t ramp_duration; + /* PWM division, -128 to +128 */ + int16_t ramp_division; + } ramp_instructions[NMAXRAMPS]; +}; + +/* + * Structure to store calibration data for a pattern. + */ +struct lp5562_calibration_data { + TiLp5562Program *pattern; + /* Index to calibrated color table */ + int color_index; + /* 0-100 % */ + int relative_brightness; + const struct lp5562_calibration_code_map* code_map; +}; + +/* + * Structure to store calibration data. + */ +struct lp5562_calibrated_color { + uint8_t pwm_values[3]; + uint8_t current_values[3]; +}; + +int led_lp5562_calibrate(const struct lp5562_calibration_data* cal_data, + struct lp5562_calibrated_color* calibrated_colors); + + +extern const struct lp5562_calibration_data mistral_calibration_database[]; + +#endif // LED_LP5562_CALIBRATION_H_ + diff --git a/src/drivers/i2c/lp5562/led_lp5562_programs.c b/src/drivers/i2c/lp5562/led_lp5562_programs.c index e837dfd..adb40bc 100644 --- a/src/drivers/i2c/lp5562/led_lp5562_programs.c +++ b/src/drivers/i2c/lp5562/led_lp5562_programs.c @@ -25,7 +25,9 @@ * LED behavior. */
+#include <stddef.h> #include "drivers/i2c/lp5562/led_lp5562_programs.h" +#include "drivers/i2c/lp5562/led_lp5562_calibration.h"
/**************************************************************** * LED ring program definitions for different patterns. @@ -59,51 +61,64 @@ */
/* RGB set to 000000, resulting in all LEDs off. */ -static const uint8_t solid_00_text[] = { +static uint8_t solid_00_text[] = { 0x40, 0, 0xc0, 0x00 };
-/* Rgb set to 128, resulting in a brightish white color. */ -static const uint8_t solid_FF_text[] = { - 0x40, 255, 0xc0, 0x00 -}; - -static const TiLp5562Program solid_000000_program = { +static TiLp5562Program solid_000000_program = { { { /* Engine1:Blue */ solid_00_text, sizeof(solid_00_text), 0, - }, + LED_LP5562_DEFAULT_CURRENT + }, { /* Engine2:Green */ solid_00_text, sizeof(solid_00_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { /* Engine3:Red */ solid_00_text, sizeof(solid_00_text), 0, + LED_LP5562_DEFAULT_CURRENT }, } };
-static const TiLp5562Program solid_FFFFFF_program = { +static uint8_t solid_FF_b_text[] = { + 0x40, 255, 0xc0, 0x00, +}; + +static uint8_t solid_FF_g_text[] = { + 0x40, 255, 0xc0, 0x00 +}; + +static uint8_t solid_FF_r_text[] = { + 0x40, 255, 0xc0, 0x00 +}; + +static TiLp5562Program solid_FFFFFF_program = { { { /* Engine1:Blue */ - solid_FF_text, - sizeof(solid_FF_text), + solid_FF_b_text, + sizeof(solid_FF_b_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { /* Engine2:Green */ - solid_FF_text, - sizeof(solid_FF_text), + solid_FF_g_text, + sizeof(solid_FF_g_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { /* Engine3:Red */ - solid_FF_text, - sizeof(solid_FF_text), + solid_FF_r_text, + sizeof(solid_FF_r_text), 0, + LED_LP5562_DEFAULT_CURRENT }, } }; @@ -124,34 +139,37 @@ 11 21 C000 end */
-static const uint8_t fdr_press1_b_text[] = { +static uint8_t fdr_press1_b_text[] = { 0x40, 0, 0xc0, 0x00, };
-static const uint8_t fdr_press1_g_text[] = { +static uint8_t fdr_press1_g_text[] = { 0x40, 204, 0xc0, 0x00 };
-static const uint8_t fdr_press1_r_text[] = { +static uint8_t fdr_press1_r_text[] = { 0x40, 255, 0xc0, 0x00 };
-static const TiLp5562Program fdr_press1_program = { +static TiLp5562Program fdr_press1_program = { { { fdr_press1_b_text, sizeof(fdr_press1_b_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { fdr_press1_g_text, sizeof(fdr_press1_g_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { fdr_press1_r_text, sizeof(fdr_press1_r_text), 0, + LED_LP5562_DEFAULT_CURRENT }, } }; @@ -183,37 +201,40 @@ 22 26 0000 gotostart */
-static const uint8_t blink_recovery1_b_text[] = { +static uint8_t blink_recovery1_b_text[] = { 0xe2, 0x00, 0x40, 0, 0xe2, 0x00, 0x40, 0, 0x00, 0x00, };
-static const uint8_t blink_recovery1_g_text[] = { +static uint8_t blink_recovery1_g_text[] = { 0xe2, 0x00, 0x40, 204, 0xe2, 0x00, 0x40, 0, 0x00, 0x00, };
-static const uint8_t blink_recovery1_r_text[] = { +static uint8_t blink_recovery1_r_text[] = { 0xe0, 0x06, 0x40, 255, 0x53, 0x00, 0xe0, 0x06, 0x40, 0, 0x53, 0x00, 0x00, 0x00 };
-static const TiLp5562Program blink_recovery1_program = { +static TiLp5562Program blink_recovery1_program = { { { blink_recovery1_b_text, sizeof(blink_recovery1_b_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { blink_recovery1_g_text, sizeof(blink_recovery1_g_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { blink_recovery1_r_text, sizeof(blink_recovery1_r_text), 0, + LED_LP5562_DEFAULT_CURRENT }, } }; @@ -240,41 +261,40 @@ 17 23 C000 end */
-static const uint8_t preboot1_b_text[] = { +static uint8_t preboot1_b_text[] = { 0x40, 0x00, 0xe2, 0x00, 0x0d, 0x4c, 0xc0, 0x00 }; -static const uint8_t preboot1_g_text[] = { +static uint8_t preboot1_g_text[] = { 0x40, 0x00, 0xe2, 0x00, 0x0d, 0x4c, 0xc0, 0x00 }; -static const uint8_t preboot1_r_text[] = { +static uint8_t preboot1_r_text[] = { 0x40, 0x00, 0xe0, 0x06, 0x0d, 0x4c, 0xc0, 0x00 };
-static const TiLp5562Program preboot1_program = { +static TiLp5562Program preboot1_program = { { { preboot1_b_text, sizeof(preboot1_b_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { preboot1_g_text, sizeof(preboot1_g_text), 0, + LED_LP5562_DEFAULT_CURRENT }, { preboot1_r_text, sizeof(preboot1_r_text), 0, + LED_LP5562_DEFAULT_CURRENT }, } };
const Led5562StateProg led_lp5562_state_programs[] = { - /* - * for test purposes the blank screen program is set to blinking, will - * be changed soon. - */ {LED_ALL_OFF, {&solid_000000_program} }, {LED_RECOVERY_PUSHED, {&solid_FFFFFF_program} }, {LED_WIPEOUT_REQUEST, {&fdr_press1_program} }, @@ -282,3 +302,147 @@ {LED_NORMAL_BOOT, {&preboot1_program} }, {}, /* Empty record to mark the end of the table. */ }; + +/* + * Calibration code map for "solid" pattern. + * Set PWM values then stop. + */ +const struct lp5562_calibration_code_map mistral_code_map_solid[] = { + { + blue, + set_pwm, + 0x00, + 0, + { } + }, + { + green, + set_pwm, + 0x10, + 0, + { } + }, + { + red, + set_pwm, + 0x20, + 0, + { } + }, + { + 0, + invalid, + 0x00, + 0, + { } + } +}; + +/* + * Calibration code map for "blink" pattern. + * Set PWM values, wait, set PWMs to 0, wait, loop. + */ +const struct lp5562_calibration_code_map mistral_code_map_blink[] = { + { + blue, + set_pwm, + 0x01, + 0, + { } + }, + { + green, + set_pwm, + 0x11, + 0, + { } + }, + { + red, + set_pwm, + 0x21, + 0, + { } + }, + { + 0, + invalid, + 0x00, + 0, + { } + } +}; + +/* + * Calibration code map for "slow blink" pattern. + * Start from OFF, + * ramp up in 1sec, ramp down in 2sec, repeat forever. + */ + +/* + * Calibration code map for "ramp up" pattern. + * Start from OFF, ramp up in 0.5sec + */ +const struct lp5562_calibration_code_map mistral_code_map_ramp_up[] = { + { + blue, + ramp, + 0x02, + 1, + { {500, 256}, } + }, + { + green, + ramp, + 0x12, + 1, + { {500, 256}, } + }, + { + red, + ramp, + 0x22, + 1, + { {500, 256}, } + }, + { + 0, + invalid, + 0x00, + 0, + { } + } +}; + +const struct lp5562_calibration_data mistral_calibration_database[] = { + { + &solid_FFFFFF_program, + 0, /* White */ + 100, /* 30% brigheness */ + mistral_code_map_solid, + }, + { + &preboot1_program, + 0, /* White */ + 30, /* 30% brigheness */ + mistral_code_map_ramp_up, + }, + { + &blink_recovery1_program, + 1, /* Yellow */ + 100, /* 100% brightness */ + mistral_code_map_blink, + }, + { + &fdr_press1_program, + 1, /* Yellow */ + 100, /* 100% brightness */ + mistral_code_map_solid, + }, + { + NULL, + 0, + 0, + NULL + } +}; diff --git a/src/drivers/i2c/lp5562/led_lp5562_programs.h b/src/drivers/i2c/lp5562/led_lp5562_programs.h index e5373c0..54dd0b2 100644 --- a/src/drivers/i2c/lp5562/led_lp5562_programs.h +++ b/src/drivers/i2c/lp5562/led_lp5562_programs.h @@ -37,6 +37,9 @@ /* Number of LP5562 controllers on this implementation */ #define LED_LP5562_NUM_LED_CONTROLLERS 1
+/* Default LED current (x0.1 mA) */ +#define LED_LP5562_DEFAULT_CURRENT 120 + /* * Structure to describe an lp5562 program: pointer to the text of the * program, its size for each engines, and start address relative to @@ -45,9 +48,10 @@ */ typedef struct { struct { - const uint8_t *program_text; + uint8_t *program_text; uint8_t program_size; uint8_t engine_start_addr; + uint8_t led_current; } engine_program[LED_LP5562_NUM_OF_ENGINES]; } TiLp5562Program;
diff --git a/src/mainboard/google/mistral/Makefile.inc b/src/mainboard/google/mistral/Makefile.inc index ab81a73..f6dcb09 100644 --- a/src/mainboard/google/mistral/Makefile.inc +++ b/src/mainboard/google/mistral/Makefile.inc @@ -8,6 +8,7 @@ verstage-y += chromeos.c verstage-y += reset.c verstage-y += verstage.c +verstage-y += led_calibration.c
romstage-y += memlayout.ld romstage-y += chromeos.c diff --git a/src/mainboard/google/mistral/led_calibration.c b/src/mainboard/google/mistral/led_calibration.c new file mode 100644 index 0000000..661332a --- /dev/null +++ b/src/mainboard/google/mistral/led_calibration.c @@ -0,0 +1,266 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2019 Google LLC + * + * 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; version 2 of the License. + * + * 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. + */ + +#include <console/console.h> +#include <string.h> +#include <fmap.h> +#include "drivers/i2c/lp5562/led_lp5562_programs.h" +#include "drivers/i2c/lp5562/led_lp5562_calibration.h" +#include "led_calibration.h" + +/* Number of predefined colors */ +#define NDEFCOLORS 3 + +const char *color_names[NDEFCOLORS] = { + "white", + "yellow", + "red" +}; + +struct lp5562_calibrated_color calibrated_colors[NDEFCOLORS] = { + { {255, 255, 255}, {120, 120, 120} }, + { {255, 204, 0}, {120, 120, 120} }, + { {255, 0, 0}, {120, 120, 120} }, +}; + +static const char key_name[] = "led_calibration"; + +/* Some constants of VPD2.0 format */ +enum { + GOOGLE_VPD_2_0_OFFSET = 0x600, + VPD_TYPE_STRING = 0x01, + VPD_TYPE_INFO = 0xfe, +}; + +static int _strspn(const char *str, const char *spn); +static int _strcspn(const char *str, const char *spn); +static int _atoi(const char *str); +static int decode_vpd_length(uint8_t *vpd_ptr, int *offset_p, int max_len); +static int load_caldata_from_vpd(char *caldata_buff, int caldata_size); + +static int _strspn(const char *str, const char *spn) +{ + int ret = 0; + + while (*str != 0) { + const char *p = spn; + while (*p != 0) { + if (*str == *p) { + break; + } + p++; + } + if (*p == 0) { + break; + } + ret++; + str++; + } + return ret; +} + +static int _strcspn(const char *str, const char *spn) +{ + int ret = 0; + + while (*str != 0) { + const char *p = spn; + while (*p != 0) { + if (*str == *p++) { + return ret; + } + } + ret++; + str++; + } + return ret; +} + +static int _atoi(const char *str) +{ + int ret = 0; + + while (isdigit(*str)) { + ret *= 10; + ret += *str++ - '0'; + } + return ret; +} + + +/* Decode VPD length from given buffer */ +static int decode_vpd_length(uint8_t *vpd_ptr, int *offset_p, int max_len) +{ + int offset = *offset_p; + int len = 0; + uint8_t d; + + do { + d = vpd_ptr[offset++]; + len *= 128; + len += (d & 0x7f); + } while ((d & 0x80) && (offset < max_len)); + + if (d & 0x80) { + len = -1; + } + *offset_p = offset; + return len; +} + +/* Find and read LED calibration data from VPD */ +static int load_caldata_from_vpd(char *caldata_buff, int caldata_size) +{ + struct region_device rdev; + size_t offset; + size_t size; + ssize_t read_len; + int buff_offset; + int len; + uint8_t buff[256]; + int led_caldata_found = 0; + + if (fmap_locate_area_as_rdev("RO_VPD", &rdev)) { + return 0; + } + + /* Search from VPD2.0 fixed offset */ + offset = GOOGLE_VPD_2_0_OFFSET; + size = rdev.region.size; + while (!led_caldata_found && offset < size) { + uint8_t type; + + /* Read key length */ + read_len = rdev_readat(&rdev, buff, offset, 4); + if (read_len < 0) break; + type = buff[0]; + if ((type != VPD_TYPE_STRING) && (type != VPD_TYPE_INFO)) { + break; + } + buff_offset = 1; + len = decode_vpd_length(buff, &buff_offset, (size - offset)); + if (len < 0) break; + + offset += buff_offset; + if (len < sizeof(buff)) { + /* Read key */ + read_len = rdev_readat(&rdev, buff, offset, len); + if (read_len < 0) break; + buff[len] = 0; + if ((type == VPD_TYPE_STRING) && + (strcmp((char*)buff, key_name) == 0)) { + led_caldata_found = 1; + } + } + offset += len; + + /* Read value length */ + read_len = rdev_readat(&rdev, buff, offset, 4); + if (read_len < 0) break; + buff_offset = 0; + len = decode_vpd_length(buff, &buff_offset, (size - offset)); + if (len < 0) break; + + offset += buff_offset; + if (led_caldata_found) { + if (len >= caldata_size) { + printk(BIOS_ERR, + "LED_LP5562: Caldata too large(%d)\n", + len); + break; + } + + /* Read value */ + read_len = rdev_readat(&rdev, caldata_buff, offset, len); + if (read_len < 0) break; + led_caldata_found = 2; + } + offset += len; + } + return (led_caldata_found == 2); +} + +int calibrate_led(void) +{ + const char *separators = " ,;"; + char buff[256]; + char *p = buff; + int idx; + + if (!load_caldata_from_vpd(buff, sizeof(buff))) { + printk(BIOS_INFO, "LED_LP5562: Valid caldata not found\n"); + return 0; + } + + /* + * Parse calibration data + * Calibration data is list of entries separated by semicolon(;) + * An entry is list of values separated by commna(,) + * first value is ASCII string color name, + * second value is ASCII decimal string of current, + * 3rd, 4th, 5th values are brightness of Red, Green and Blue. + */ + while (*p != 0) { + int color; + int rgb; + int len; + + len = _strcspn(p, separators); + if (len < 1) { + break; + } + for (color = 0; color < NDEFCOLORS; color++) { + if (strncmp(color_names[color], p, len) == 0) { + break; + } + } + if (color >= NDEFCOLORS) { + len = _strcspn(p, ";"); + if (len < 0) { + break; + } + p += (len + 1); + continue; + } + + p += len; + p += _strspn(p, separators); + calibrated_colors[color].current_values[0] = + calibrated_colors[color].current_values[1] = + calibrated_colors[color].current_values[2] = _atoi(p); + + p += _strcspn(p, separators); + p += _strspn(p, separators); + for (rgb = 0; rgb < 3; rgb++) { + calibrated_colors[color].pwm_values[rgb] = _atoi(p); + p += _strcspn(p, separators); + p += _strspn(p, separators); + } + } + + /* + * Apply calibration data + */ + idx = 0; + while (mistral_calibration_database[idx].pattern != NULL) { + led_lp5562_calibrate( + &mistral_calibration_database[idx], + calibrated_colors); + idx++; + + } + return 0; +} + diff --git a/src/mainboard/google/mistral/led_calibration.h b/src/mainboard/google/mistral/led_calibration.h new file mode 100644 index 0000000..a9efe93 --- /dev/null +++ b/src/mainboard/google/mistral/led_calibration.h @@ -0,0 +1,22 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2019 Google LLC + * + * 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; version 2 of the License. + * + * 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. + */ + +#ifndef __COREBOOT_SRC_MAINBOARD_GOOGLE_MISTRAL_LED_CALIBRATION_H +#define __COREBOOT_SRC_MAINBOARD_GOOGLE_MISTRAL_LED_CALIBRATION_H + +extern int calibrate_led(void); + +#endif + diff --git a/src/mainboard/google/mistral/verstage.c b/src/mainboard/google/mistral/verstage.c index 08b5391..6a15de2 100644 --- a/src/mainboard/google/mistral/verstage.c +++ b/src/mainboard/google/mistral/verstage.c @@ -19,6 +19,7 @@ #include <soc/clock.h> #include <spi-generic.h> #include "board.h" +#include "led_calibration.h"
void verstage_mainboard_init(void) { @@ -30,4 +31,5 @@ CONFIG_DRIVER_TPM_SPI_CHIP, &spi)) { printk(BIOS_ERR, "Failed to setup TPM SPI slave\n"); } + calibrate_led(); }