| #include "util.h" |
| #include "i2c_driver.h" |
| |
| #define AW21018_REG_GCR 0x20 |
| #define AW21018_REG_BRIGHTNESS_BASE 0x21 |
| #define AW21018_REG_UPDATE 0x45 |
| #define AW21018_REG_SL_BASE 0x46 |
| #define AW21018_REG_GCCR 0x58 |
| #define AW21018_REG_GCR2 0x61 |
| #define AW21018_REG_DEVICE_RESET 0x70 |
| |
| #define AW21018_CHIPID 0x02 |
| |
| #define AW21018_REG_GCR_CHIPEN_BIT (1 << 0) |
| #define AW21018_REG_GCR_AUTO_POWER_BIT (1 << 7) |
| |
| #define AW21018_REG_GCR2_RGBMD_BIT (1 << 0) |
| #define AW21018_REG_GCR2_SBMD_BIT (1 << 1) |
| |
| /* Global Current is read from the DTS entry by the kernel driver. |
| * Here in the bootloader we will just hardcode it. */ |
| #define AW21018_GLOBAL_CURRENT 0x66 |
| |
| #define AW21018_MAX_LED_CHANNELS 18 |
| #define AW21018_MAX_LED_GROUPS 6 |
| |
| extern int lgpl_printf(const char *format, ...); |
| extern int i2c_master_write_and_read(int id, int target_addr, unsigned char* send_buff, int send_len, unsigned char* recv_buff, int recv_len); |
| |
| int diag_i2c_led_aw210xx_detect_driver(int master_id, unsigned char slave_addr) { |
| unsigned char tx_buf = AW21018_REG_DEVICE_RESET; |
| unsigned char rx_buf = 0xFF; |
| int ret; |
| lgpl_printf("%s: master_id: %d slave_addr: 0x%x\n", __func__, master_id, slave_addr); |
| |
| ret = i2c_master_write_and_read(master_id, slave_addr, &tx_buf, 1, &rx_buf, 1); |
| if (ret != 0) { |
| lgpl_printf("%s: failed to read reset register.\n", __func__); |
| return ret; |
| } |
| |
| if (AW21018_CHIPID != rx_buf) { |
| lgpl_printf("%s: chip ID did not match expected value: %d (expected: %d).\n", __func__, |
| rx_buf, AW21018_CHIPID); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void aw210xx_write_register(int master_id, unsigned char slave_addr, |
| unsigned char reg, unsigned char val) |
| { |
| unsigned char tx_buf[2]; |
| int ret; |
| |
| tx_buf[0] = reg; |
| tx_buf[1] = val; |
| |
| ret = i2c_master_write_and_read(master_id, slave_addr, tx_buf, 2, 0, 0); |
| if (ret != 0) { |
| lgpl_printf("%s: failed to set register 0x%x: error = 0x%x.\n", __func__, reg, ret); |
| } |
| } |
| |
| static void aw210xx_set_update_register(int master_id, unsigned char slave_addr) { |
| aw210xx_write_register(master_id, slave_addr, AW21018_REG_UPDATE, 0x00); |
| } |
| |
| static void aw210xx_set_brightness_registers(int master_id, |
| unsigned char slave_addr, unsigned char *reg_data, size_t size) |
| { |
| unsigned char tx_buf[AW21018_MAX_LED_GROUPS + 1]; |
| int len = 1; |
| size_t i; |
| int ret; |
| |
| /* The bootloader only operates the driver in Single Byte Configuration mode. |
| * Only allow setting the first 6 BR registers */ |
| tx_buf[0] = AW21018_REG_BRIGHTNESS_BASE; |
| |
| for (i = 1; i < AW21018_MAX_LED_GROUPS + 1; i++) { |
| if (i <= size) { |
| tx_buf[i] = reg_data[i - 1]; |
| len++; |
| } else { |
| tx_buf[i] = 0; |
| } |
| } |
| |
| ret = i2c_master_write_and_read(master_id, slave_addr, tx_buf, len, 0, 0); |
| if (ret != 0) { |
| lgpl_printf("%s: failed to set brightness registers: error = 0x%x.\n", __func__, ret); |
| } |
| |
| aw210xx_set_update_register(master_id, slave_addr); |
| } |
| |
| static void aw210xx_set_sl_registers(int master_id, |
| unsigned char slave_addr, unsigned char *reg_data, size_t size) |
| { |
| unsigned char tx_buf[AW21018_MAX_LED_CHANNELS + 1]; |
| int len = 1; |
| size_t i; |
| int ret; |
| |
| tx_buf[0] = AW21018_REG_SL_BASE; |
| |
| for (i = 1; i < AW21018_MAX_LED_CHANNELS + 1; i++) { |
| if (i <= size) { |
| tx_buf[i] = reg_data[i - 1]; |
| len++; |
| } else { |
| tx_buf[i] = 0; |
| } |
| } |
| |
| ret = i2c_master_write_and_read(master_id, slave_addr, tx_buf, len, 0, 0); |
| if (ret != 0) { |
| lgpl_printf("%s: failed to set SL registers: error = 0x%x.\n", __func__, ret); |
| } |
| |
| aw210xx_set_update_register(master_id, slave_addr); |
| } |
| |
| int diag_i2c_led_aw210xx_init(int master_id, unsigned char slave_addr) { |
| lgpl_printf("%s: master_id: %d slave_addr: 0x%x\n", __func__, master_id, slave_addr); |
| |
| // Set CHIPEN to put AS21018 into active mode |
| aw210xx_write_register(master_id, slave_addr, AW21018_REG_GCR, AW21018_REG_GCR_CHIPEN_BIT); |
| |
| // Set RGB Mode and Single Byte Configuration mode |
| aw210xx_write_register(master_id, slave_addr, AW21018_REG_GCR2, |
| (AW21018_REG_GCR2_RGBMD_BIT | AW21018_REG_GCR2_SBMD_BIT)); |
| |
| // Set the global current control register to predefined value |
| aw210xx_write_register(master_id, slave_addr, AW21018_REG_GCCR, AW21018_GLOBAL_CURRENT); |
| |
| // Set Auto power saving mode |
| aw210xx_write_register(master_id, slave_addr, AW21018_REG_GCR, |
| (AW21018_REG_GCR_AUTO_POWER_BIT | AW21018_REG_GCR_CHIPEN_BIT)); |
| |
| return 0; |
| } |
| |
| int diag_i2c_led_aw210xx_set_frame(int master_id, unsigned char slave_addr, |
| unsigned char *frame, size_t size) |
| { |
| unsigned char br_buf[AW21018_MAX_LED_GROUPS] = {0}; |
| unsigned char sl_buf[AW21018_MAX_LED_CHANNELS] = {0}; |
| size_t br_index = 0; |
| size_t sl_index = 0; |
| size_t i = 0; |
| |
| lgpl_printf("%s: master_id: %d slave_addr: 0x%x frame: 0x%x size: %d\n", __func__, |
| master_id, slave_addr, frame, size); |
| |
| // The frame should contain at least 8 bytes for brightness |
| if (size < 8) { |
| return -1; |
| } |
| |
| /* First 8 bytes of the frame represent the brightness of 8 LEDs |
| * AW21018 only supports 6 LED BR registers in RGBMD so we only copy |
| * the first 6 values from the frame */ |
| for (i = 0; i < AW21018_MAX_LED_GROUPS; i++) { |
| br_buf[br_index++] = frame[i]; |
| } |
| |
| /* Remaining 24 bytes of the frame represent RGB values. |
| * AW21018 only supports 18 channels so we only copy the first 18 |
| * bytes of data */ |
| for (i = 8; ((i < size) && (sl_index < AW21018_MAX_LED_CHANNELS)); i+=3) { |
| sl_buf[sl_index++] = frame[i]; |
| sl_buf[sl_index++] = frame[i + 1]; |
| sl_buf[sl_index++] = frame[i + 2]; |
| } |
| |
| aw210xx_set_brightness_registers(master_id, slave_addr, br_buf, br_index); |
| aw210xx_set_sl_registers(master_id, slave_addr, sl_buf, sl_index); |
| |
| return 0; |
| } |