// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ /* * leds-aw9523.c aw9523 led module * * Version: 1.0.0 * * Copyright (c) 2017 AWINIC Technology CO., LTD * * Author: Nick Li * * 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; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "leds-aw9523b.h" #include /****************************************************** * * Marco * ******************************************************/ #define AW9523_I2C_NAME "aw9523_led" #define MESON_LEDS_CDEV_NAME "i2c_leds" #define AW9523_VERSION "v2.0.0" #define AW_I2C_RETRIES 3 #define AW_I2C_RETRY_DELAY 1 #define AW_READ_CHIPID_RETRIES 1 #define AW9523_COLORS_COUNT 3 #define AW9523_MAX_IO 15 #define REG_INPUT_P0 0x00 #define REG_INPUT_P1 0x01 #define REG_OUTPUT_P0 0x02 #define REG_OUTPUT_P1 0x03 #define REG_CONFIG_P0 0x04 #define REG_CONFIG_P1 0x05 #define REG_INT_P0 0x06 #define REG_INT_P1 0x07 #define REG_ID 0x10 #define REG_CTRL 0x11 #define REG_WORK_MODE_P0 0x12 #define REG_WORK_MODE_P1 0x13 #define REG_DIM00 0x20 #define REG_DIM01 0x21 #define REG_DIM02 0x22 #define REG_DIM03 0x23 #define REG_DIM04 0x24 #define REG_DIM05 0x25 #define REG_DIM06 0x26 #define REG_DIM07 0x27 #define REG_DIM08 0x28 #define REG_DIM09 0x29 #define REG_DIM10 0x2a #define REG_DIM11 0x2b #define REG_DIM12 0x2c #define REG_DIM13 0x2d #define REG_DIM14 0x2e #define REG_DIM15 0x2f #define REG_SWRST 0x7F #define REGISTER_OFFSET 0x20 /* aw9523 register read/write access*/ #define REG_NONE_ACCESS 0 #define REG_RD_ACCESS BIT(0) #define REG_WR_ACCESS BIT(1) #define AW9523_REG_MAX (0xFF) const unsigned char aw9523_reg_access[AW9523_REG_MAX] = { [REG_INPUT_P0] = REG_RD_ACCESS, [REG_INPUT_P1] = REG_RD_ACCESS, [REG_OUTPUT_P0] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_OUTPUT_P1] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_CONFIG_P0] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_CONFIG_P1] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_INT_P0] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_INT_P1] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_ID] = REG_RD_ACCESS, [REG_CTRL] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_WORK_MODE_P0] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_WORK_MODE_P1] = REG_RD_ACCESS | REG_WR_ACCESS, [REG_DIM01] = REG_WR_ACCESS, [REG_DIM00] = REG_WR_ACCESS, [REG_DIM02] = REG_WR_ACCESS, [REG_DIM03] = REG_WR_ACCESS, [REG_DIM04] = REG_WR_ACCESS, [REG_DIM05] = REG_WR_ACCESS, [REG_DIM06] = REG_WR_ACCESS, [REG_DIM07] = REG_WR_ACCESS, [REG_DIM08] = REG_WR_ACCESS, [REG_DIM09] = REG_WR_ACCESS, [REG_DIM10] = REG_WR_ACCESS, [REG_DIM11] = REG_WR_ACCESS, [REG_DIM12] = REG_WR_ACCESS, [REG_DIM13] = REG_WR_ACCESS, [REG_DIM14] = REG_WR_ACCESS, [REG_DIM15] = REG_WR_ACCESS, [REG_SWRST] = REG_WR_ACCESS, }; #define MAX_I2C_BUFFER_SIZE 65536 #define AW9523_ID 0x23 struct meson_aw9523_io { unsigned int r_io; unsigned int g_io; unsigned int b_io; }; struct meson_aw9523_colors { unsigned char red; unsigned char green; unsigned char blue; }; struct meson_aw9523 { struct i2c_client *i2c; struct device *dev; struct led_classdev cdev; struct work_struct brightness_work; struct work_struct leds_work; struct workqueue_struct *leds_wq; int reset_gpio; unsigned char chipid; int imax; int platform; struct meson_aw9523_io *io; struct meson_aw9523_colors *colors; int led_counts; int ignore_led_suspend; }; struct meson_aw9523 *aw9523; static int aw9523_i2c_write(struct meson_aw9523 *aw9523, unsigned char reg_addr, unsigned char reg_data); static int aw9523_reset(struct meson_aw9523 *aw9523); static int *all_data[32] = {0};//[2+16*N]: N command,each command consists of 16 bits static int length_data[32] = {0}; static unsigned int state_mode; /*2:S905X2 SEI520, 3:S905X3 SEI600TID; 4:S905X3 SEI610/SEI610F, 6:SEI610FPT, 8:SEI810CPR*/ static unsigned int platform_id; static unsigned int hwen_id = 3, isel_id = 3; static unsigned int led_value; static int time_val, cycles_val, instruct_val; static u8 tmp_w[1024][16] = {}; static unsigned int tmp_color[5] = {}; static int aw9523_i2c_writes(struct i2c_client *client, unsigned char sreg_addr, uint8_t length, uint8_t *bufs) { int ret = -1; struct i2c_msg msg; int i, retries = 0; u8 data[64]; data[0] = sreg_addr; for (i = 1; i <= length; i++) data[i] = bufs[i - 1]; msg.addr = client->addr; msg.flags = !I2C_M_RD; msg.len = length + 1; msg.buf = data; while (retries < 3) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret >= 0) { ret = 1; break; } retries++; } if (retries >= 3) pr_err("%s error...retyies = %d\n", __func__, retries); return ret; } static void leds_aw9523b_work_func(struct work_struct *work) { int i, j; if (cycles_val == 0) { for (j = 0; j < instruct_val; j++) { aw9523_i2c_writes(aw9523->i2c, REG_DIM00, 16, tmp_w[j]); usleep_range(1000 * (time_val - 10), 1000 * (time_val + 10)); if (state_mode == 0) break; } if (state_mode != 0) queue_work(aw9523->leds_wq, &aw9523->leds_work); } else { for (i = 1; i <= cycles_val; i++) { for (j = 0; j < instruct_val; j++) { aw9523_i2c_writes(aw9523->i2c, REG_DIM00, 16, tmp_w[j]); usleep_range(1000 * (time_val - 10), 1000 * (time_val + 10)); if (state_mode == 0) break; } if (state_mode == 0) break; } } } static void init_leds_mode(int leds_mode) { int offset, i, j; if (leds_mode > MAX_LED_MODE - 1) { dev_err(aw9523->dev, "%s erro,out of led mode range\n", __func__); return; } time_val = *(all_data[leds_mode] + 0); cycles_val = *((all_data[leds_mode]) + 1); instruct_val = (length_data[leds_mode] - 2) / 16; state_mode = 0; usleep_range(80000, 100000); for (i = 0; i < instruct_val; i++) { for (j = 0; j < 16; j++) { offset = 16 * i + j; tmp_w[i][j] = *((all_data[leds_mode]) + offset + 2); } } state_mode = leds_mode; queue_work(aw9523->leds_wq, &aw9523->leds_work); } static void meson_aw9523_set_colors(unsigned int *colors, unsigned int counts) { struct meson_aw9523_colors *color; struct meson_aw9523_io *io; unsigned char color_data[AW9523_MAX_IO]; unsigned int i; memset(color_data, 0, sizeof(color_data)); for (i = 0; i < counts; i++) { io = &aw9523->io[i]; color = &aw9523->colors[i]; color->blue = colors[i] & 0xff; color->green = (colors[i] >> 8) & 0xff; color->red = (colors[i] >> 16) & 0xff; color_data[io->b_io] = color->blue; color_data[io->g_io] = color->green; color_data[io->r_io] = color->red; } aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data); } static void meson_aw9523_set_colors_again(struct meson_aw9523 *aw9523) { struct meson_aw9523_colors *color; struct meson_aw9523_io *io; unsigned char color_data[AW9523_MAX_IO]; unsigned int i; for (i = 0; i < aw9523->led_counts; i++) { io = &aw9523->io[i]; color = &aw9523->colors[i]; color_data[io->b_io] = color->blue; color_data[io->g_io] = color->green; color_data[io->r_io] = color->red; } aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data); } static void init_leds_config(void) { aw9523_reset(aw9523); //aw9523_i2c_write(aw9523, 0x7F, 0x00);//0x7F SW_RSTN /*0x12 P0 mode, <0x00: all PWM mode> */ aw9523_i2c_write(aw9523, 0x12, 0x00); /*0x13 P1 mode, <0x00: all PWM mode, 0xF0: P1_7~P1_4 IO mode>*/ aw9523_i2c_write(aw9523, 0x13, 0x00); /* * GPOMD<0x00: Open-Drain, * 0x01: Push-Pull> + ISEL <0x00:Imax, * 0x11: 1/4-Imax,0x10: 2/4-Imax, 0x01: 3/4-Imax> */ aw9523_i2c_write(aw9523, 0x11, 0x11); } static void init_leds_data(void) { all_data[0] = (int *)&data_mode0; all_data[1] = (int *)&data_mode1; all_data[2] = (int *)&data_mode2; all_data[3] = (int *)&data_mode3; all_data[4] = (int *)&data_mode4; all_data[5] = (int *)&data_mode5; all_data[6] = (int *)&data_mode6; all_data[7] = (int *)&data_mode7; all_data[8] = (int *)&data_mode8; all_data[9] = (int *)&data_mode9; all_data[10] = (int *)&data_mode10; all_data[11] = (int *)&data_mode11; length_data[0] = ARRAY_SIZE(data_mode0); length_data[1] = ARRAY_SIZE(data_mode1); length_data[2] = ARRAY_SIZE(data_mode2); length_data[3] = ARRAY_SIZE(data_mode3); length_data[4] = ARRAY_SIZE(data_mode4); length_data[5] = ARRAY_SIZE(data_mode5); length_data[6] = ARRAY_SIZE(data_mode6); length_data[7] = ARRAY_SIZE(data_mode7); length_data[8] = ARRAY_SIZE(data_mode8); length_data[9] = ARRAY_SIZE(data_mode9); length_data[10] = ARRAY_SIZE(data_mode10); length_data[11] = ARRAY_SIZE(data_mode11); /*add X3 SEI600TID */ all_data[12] = (int *)&data_mode12; length_data[12] = ARRAY_SIZE(data_mode12); /*add X4 SEI810CPR */ all_data[13] = (int *)&data_mode13; length_data[13] = ARRAY_SIZE(data_mode13); all_data[14] = (int *)&data_mode14; length_data[14] = ARRAY_SIZE(data_mode14); init_leds_config(); if (platform_id == 3) { init_leds_mode(12); aw9523_i2c_write(aw9523, REG_OUTPUT_P1, 0x00); } else if (platform_id == 2) { init_leds_mode(1); } else if (platform_id == 4) { init_leds_mode(1); } else if (platform_id == 8) { init_leds_mode(13); } } static int aw9523_i2c_write(struct meson_aw9523 *aw9523, unsigned char reg_addr, unsigned char reg_data) { int ret = -1; unsigned char cnt = 0; while (cnt < AW_I2C_RETRIES) { ret = i2c_smbus_write_byte_data(aw9523->i2c, reg_addr, reg_data); if (ret < 0) { pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret); cnt++; msleep(AW_I2C_RETRY_DELAY); } else { break; } } return ret; } static int aw9523_i2c_read(struct meson_aw9523 *aw9523, unsigned char reg_addr, unsigned int *reg_data) { int ret = -1; unsigned char cnt = 0; while (cnt < AW_I2C_RETRIES) { ret = i2c_smbus_read_byte_data(aw9523->i2c, reg_addr); if (ret < 0) { pr_debug("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret); } else { *reg_data = ret; break; } cnt++; msleep(AW_I2C_RETRY_DELAY); } return ret; } static void aw9523_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { struct meson_aw9523 *aw9523 = container_of(cdev, struct meson_aw9523, cdev); aw9523->cdev.brightness = brightness; } static int aw9523_parse_dt(struct device *dev, struct meson_aw9523 *aw9523, struct device_node *np) { int ret; aw9523->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); if (gpio_is_valid(aw9523->reset_gpio)) { ret = devm_gpio_request(dev, aw9523->reset_gpio, "reset-gpio"); if (ret) { dev_err(dev, "failed to request gpio\n"); return ret; } gpio_direction_output(aw9523->reset_gpio, 1); } if (of_property_read_bool(np, "ignore-led-suspend")) aw9523->ignore_led_suspend = 1; else aw9523->ignore_led_suspend = 0; return 0; } static int aw9523_reset(struct meson_aw9523 *aw9523) { if (gpio_is_valid(aw9523->reset_gpio)) { gpio_set_value_cansleep(aw9523->reset_gpio, 0); msleep(20); gpio_set_value_cansleep(aw9523->reset_gpio, 1); msleep(20); } else { aw9523_i2c_write(aw9523, 0x7F, 0x00); //0x7F SW_RSTN // dev_err(aw9523->dev, "%s: failed\n", __func__); } return 0; } static int aw9523_off(struct meson_aw9523 *aw9523) { pr_debug("Tiger]%s enter,Line:%d\n", __func__, __LINE__); if (gpio_is_valid(aw9523->reset_gpio)) { gpio_set_value_cansleep(aw9523->reset_gpio, 0); msleep(20); } else { aw9523_i2c_write(aw9523, 0x7F, 0x00); //0x7F SW_RSTN // dev_err(aw9523->dev, "%s: failed\n", __func__); } return 0; } static int aw9523_read_chipid(struct meson_aw9523 *aw9523) { int ret = -1; unsigned char cnt = 0; unsigned int reg_val = 0; while (cnt < AW_READ_CHIPID_RETRIES) { ret = aw9523_i2c_read(aw9523, REG_ID, ®_val); if (ret < 0) dev_dbg(aw9523->dev, "%s: failed to read register aw9523_REG_ID: %d retry: %d\n", __func__, ret, cnt); switch (reg_val) { case AW9523_ID: pr_debug("Tiger]%s aw9523 detected\n", __func__); aw9523->chipid = AW9523_ID; return 0; default: break; } cnt++; } return -EINVAL; } static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int state_mode; int ret; cancel_work_sync(&aw9523->leds_work); flush_workqueue(aw9523->leds_wq); ret = kstrtouint(buf, 10, &state_mode); if (ret) dev_err(dev, "%s error\n", __func__); pr_debug("Tiger]%s enter,Line:%d...state_mode=%d\n", __func__, __LINE__, state_mode); init_leds_mode(state_mode); return count; } static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; len += snprintf(buf + len, PAGE_SIZE - len, "current mode:%d\n", state_mode); return len; } static ssize_t reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[2] = {0, 0}; if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) aw9523_i2c_write(aw9523, (unsigned char)databuf[0], (unsigned char)databuf[1]); return count; } static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); ssize_t len = 0; unsigned char i = 0; unsigned int reg_val = 0; for (i = 0; i < AW9523_REG_MAX; i++) { if (!(aw9523_reg_access[i] & REG_RD_ACCESS)) continue; aw9523_i2c_read(aw9523, i, ®_val); len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x\n", i, reg_val); } return len; } static ssize_t hwen_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[1] = {0}; if (kstrtoint(buf, 16, &databuf[0]) == 0) { if (databuf[0] == 1) aw9523_reset(aw9523); else if (databuf[0] == 2)//sw-reset aw9523_i2c_write(aw9523, 0x7F, 0x00);//0x7F SW_RSTN else if (databuf[0] == 3)//sw-init init_leds_data(); else aw9523_off(aw9523); hwen_id = databuf[0]; } return count; } static ssize_t hwen_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; len += snprintf(buf + len, PAGE_SIZE - len, "hwen=%d\n", hwen_id); return len; } static ssize_t isel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[1] = {0}; if (kstrtoint(buf, 16, &databuf[0]) == 0) { isel_id = databuf[0]; aw9523_i2c_write(aw9523, 0x11, databuf[0]); } return count; } static ssize_t isel_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; len += snprintf(buf + len, PAGE_SIZE - len, "isel=%d\n", isel_id); return len; } static ssize_t colors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int colors[4]; int ret; ret = sscanf(buf, "%x %x %x %x", &colors[0], &colors[1], &colors[2], &colors[3]); if (ret != 4) dev_err(dev, " enter,Line:...set led colors fail ret: %d\n", ret); if (ret == 4) { meson_aw9523_set_colors(colors, 4); tmp_color[0] = colors[0]; tmp_color[1] = colors[1]; tmp_color[2] = colors[2]; tmp_color[3] = colors[3]; } else if (ret == 2) { //for android 11, param: index, color if (colors[0] < aw9523->led_counts) tmp_color[colors[0]] = colors[1]; meson_aw9523_set_colors(tmp_color, 4); } return count; } static ssize_t colors_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; len += snprintf(buf + len, PAGE_SIZE - len, "current mode:%d\n", state_mode); return len; } static ssize_t led1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[1] = {0}; if (kstrtoint(buf, 16, &databuf[0]) == 0) { if (databuf[0] >= 1) databuf[0] = 1; if (platform_id == 3) databuf[0] = !databuf[0]; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0xE0) | (databuf[0] << 4)); dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value); aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value); } return count; } static ssize_t led1_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0x10) >> 4); if (platform_id == 3) led_value = !led_value; len += snprintf(buf + len, PAGE_SIZE - len, "led1_value=%d\n", led_value); return len; } static ssize_t led2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[1] = {0}; if (kstrtoint(buf, 16, &databuf[0]) == 0) { if (databuf[0] >= 1) databuf[0] = 1; if (platform_id == 3) databuf[0] = !databuf[0]; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0xD0) | (databuf[0] << 5)); dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value); aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value); } return count; } static ssize_t led2_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0x20) >> 5); if (platform_id == 3) led_value = !led_value; len += snprintf(buf + len, PAGE_SIZE - len, "led2_value=%d\n", led_value); return len; } static ssize_t led3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev); unsigned int databuf[1] = {0}; if (kstrtoint(buf, 16, &databuf[0]) == 0) { if (databuf[0] >= 1) databuf[0] = 1; if (platform_id == 3) databuf[0] = !databuf[0]; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0xB0) | (databuf[0] << 6)); dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value); aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value); } return count; } static ssize_t led3_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value); led_value = ((led_value & 0x40) >> 6); if (platform_id == 3) led_value = !led_value; len += snprintf(buf + len, PAGE_SIZE - len, "led3_value=%d\n", led_value); return len; } static int meson_aw9523_set_singlecolors(u32 ledid, u32 led_color) { struct meson_aw9523_colors *color; struct meson_aw9523_io *io; if (ledid > 3) { pr_debug(" Exceed the maximum number of leds!\n"); return 0; } io = &aw9523->io[ledid]; color = &aw9523->colors[ledid]; color->blue = led_color & 0xff; color->green = (led_color >> 8) & 0xff; color->red = (led_color >> 16) & 0xff; aw9523_i2c_write(aw9523, io->r_io + REGISTER_OFFSET, color->red); aw9523_i2c_write(aw9523, io->g_io + REGISTER_OFFSET, color->green); aw9523_i2c_write(aw9523, io->b_io + REGISTER_OFFSET, color->blue); return 0; } static ssize_t single_colors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 ledid, color; int ret; ret = sscanf(buf, "%d %x", &ledid, &color); if (ret != 2) { pr_debug(" enter,Line:...set led colors fail!\n"); return count; } meson_aw9523_set_singlecolors(ledid, color); return count; } static DEVICE_ATTR_RW(reg); static DEVICE_ATTR_RW(hwen); static DEVICE_ATTR_RW(mode); static DEVICE_ATTR_RW(isel); static DEVICE_ATTR_RW(colors); static DEVICE_ATTR_WO(single_colors); static DEVICE_ATTR_RW(led1); static DEVICE_ATTR_RW(led2); static DEVICE_ATTR_RW(led3); static struct attribute *aw9523_attributes[] = { &dev_attr_reg.attr, &dev_attr_hwen.attr, &dev_attr_mode.attr, &dev_attr_isel.attr, &dev_attr_colors.attr, &dev_attr_led1.attr, &dev_attr_led2.attr, &dev_attr_led3.attr, &dev_attr_single_colors.attr, NULL }; static struct attribute_group aw9523_attribute_group = { .attrs = aw9523_attributes }; static int meson_aw9523_suspend(struct device *dev) { unsigned char color_data[AW9523_MAX_IO] = { 0 }; if (aw9523->ignore_led_suspend) return 0; aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data); pr_debug("Tiger]aw9523b suspend\n"); return 0; } static int meson_aw9523_resume(struct device *dev) { if (aw9523->ignore_led_suspend) return 0; meson_aw9523_set_colors_again(aw9523); pr_debug("Tiger]aw9523b resume\n"); return 0; } static int aw9523_parse_led_cdev(struct meson_aw9523 *aw9523, struct device_node *np) { struct device_node *temp; unsigned int colors[AW9523_COLORS_COUNT]; int ret = -1; int i = 0; for_each_child_of_node(np, temp) { ret = of_property_read_u32_array(temp, "default_colors", colors, AW9523_COLORS_COUNT); if (ret < 0) { dev_err(aw9523->dev, "Failure reading default colors ret = %d\n", ret); goto free_pdata; } ret = of_property_read_u32(temp, "r_io_number", &aw9523->io[i].r_io); if (ret < 0) { dev_err(aw9523->dev, "Failure reading imax ret = %d\n", ret); goto free_pdata; } ret = of_property_read_u32(temp, "g_io_number", &aw9523->io[i].g_io); if (ret < 0) { dev_err(aw9523->dev, "Failure reading brightness ret = %d\n", ret); goto free_pdata; } ret = of_property_read_u32(temp, "b_io_number", &aw9523->io[i].b_io); if (ret < 0) { dev_err(aw9523->dev, "Failure reading max brightness ret = %d\n", ret); goto free_pdata; } aw9523->colors[i].red = colors[0]; aw9523->colors[i].green = colors[1]; aw9523->colors[i].blue = colors[2]; i++; } ret = of_property_read_u32(np, "platform", &platform_id); if (ret < 0) dev_err(aw9523->dev, "Tiger] %s:%d default platform_id=%d\n", __func__, __LINE__, platform_id); dev_dbg(aw9523->dev, "Tiger] %s:%d set platform_id=%d\n", __func__, __LINE__, platform_id); aw9523->cdev.name = MESON_LEDS_CDEV_NAME; aw9523->cdev.brightness = 0; aw9523->cdev.max_brightness = 255; aw9523->cdev.brightness_set = aw9523_set_brightness; ret = led_classdev_register(aw9523->dev, &aw9523->cdev); if (ret) { dev_err(aw9523->dev, "unable to register led ret=%d\n", ret); goto free_pdata; } ret = sysfs_create_group(&aw9523->cdev.dev->kobj, &aw9523_attribute_group); if (ret) { dev_err(aw9523->dev, "led sysfs ret: %d\n", ret); goto free_class; } return 0; free_class: led_classdev_unregister(&aw9523->cdev); free_pdata: return ret; } static int aw9523_i2c_probe(struct i2c_client *i2c) { struct device_node *np = i2c->dev.of_node; int ret; if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL)) { dev_err(&i2c->dev, "check_functionality failed\n"); return -EIO; } aw9523 = devm_kzalloc(&i2c->dev, sizeof(struct meson_aw9523), GFP_KERNEL); if (!aw9523) return -ENOMEM; aw9523_parse_dt(&i2c->dev, aw9523, np); aw9523->dev = &i2c->dev; aw9523->i2c = i2c; aw9523->led_counts = device_get_child_node_count(&i2c->dev); aw9523->io = devm_kcalloc(&i2c->dev, aw9523->led_counts, sizeof(struct meson_aw9523_io), GFP_KERNEL); if (!aw9523->io) return -ENOMEM; aw9523->colors = devm_kcalloc(&i2c->dev, aw9523->led_counts, sizeof(struct meson_aw9523_colors), GFP_KERNEL); if (!aw9523->colors) return -ENOMEM; i2c_set_clientdata(i2c, aw9523); /* aw9523 chip id */ ret = aw9523_read_chipid(aw9523); if (ret < 0) { dev_info(&i2c->dev, "there is no aw9523. Exit probe process\n"); return 0; } dev_set_drvdata(&i2c->dev, aw9523); ret = aw9523_parse_led_cdev(aw9523, np); if (ret < 0) { dev_err(&i2c->dev, "%s error creating led class dev\n", __func__); goto err_sysfs; } INIT_WORK(&aw9523->leds_work, leds_aw9523b_work_func); aw9523->leds_wq = create_singlethread_workqueue("leds_wq"); if (!aw9523->leds_wq) { dev_err(&i2c->dev, "create leds_work->input_wq fail!\n"); return -ENOMEM; } queue_work(aw9523->leds_wq, &aw9523->leds_work); init_leds_data(); return 0; err_sysfs: return ret; } static void aw9523_i2c_remove(struct i2c_client *i2c) { struct meson_aw9523 *aw9523 = i2c_get_clientdata(i2c); sysfs_remove_group(&aw9523->cdev.dev->kobj, &aw9523_attribute_group); led_classdev_unregister(&aw9523->cdev); aw9523_reset(aw9523); cancel_work_sync(&aw9523->leds_work); destroy_workqueue(aw9523->leds_wq); } static const struct i2c_device_id aw9523_i2c_id[] = { { AW9523_I2C_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id); static const struct of_device_id aw9523_dt_match[] = { { .compatible = "amlogic,aw9523b_led" }, { }, }; static SIMPLE_DEV_PM_OPS(meson_aw9523_pm, meson_aw9523_suspend, meson_aw9523_resume); static struct i2c_driver meson_aw9523_driver = { .driver = { .name = AW9523_I2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(aw9523_dt_match), .probe_type = PROBE_PREFER_ASYNCHRONOUS, .pm = &meson_aw9523_pm, }, .probe = aw9523_i2c_probe, .remove = aw9523_i2c_remove, .id_table = aw9523_i2c_id, }; int led_aw9523_init(void) { return i2c_add_driver(&meson_aw9523_driver); } void __exit led_aw9523_exit(void) { return i2c_del_driver(&meson_aw9523_driver); }