| /* |
| * leds-aw210xx.c |
| * |
| * Copyright (c) 2021 AWINIC Technology CO., LTD |
| * |
| * Author: hushanping <hushanping@awinic.com> |
| * |
| * 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 <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/i2c.h> |
| #include <linux/of_gpio.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/firmware.h> |
| #include <linux/slab.h> |
| #include <linux/version.h> |
| #include <linux/input.h> |
| #include <linux/interrupt.h> |
| #include <linux/debugfs.h> |
| #include <linux/miscdevice.h> |
| #include <linux/uaccess.h> |
| #include <linux/leds.h> |
| #include <linux/leds-aw210xx.h> |
| #include <linux/leds-aw210xx-reg.h> |
| |
| /****************************************************** |
| * |
| * Marco |
| * |
| ******************************************************/ |
| #define AW210XX_DRIVER_VERSION "V0.1.0" |
| #define AW_I2C_RETRIES 5 |
| #define AW_I2C_RETRY_DELAY 1 |
| #define AW_READ_CHIPID_RETRIES 2 |
| #define AW_READ_CHIPID_RETRY_DELAY 1 |
| #define AW210XX_CFG_NAME_MAX 64 |
| |
| /****************************************************** |
| * |
| * aw210xx led parameter |
| * |
| ******************************************************/ |
| aw210xx_cfg_t aw210xx_cfg_array[] = { |
| {aw210xx_group_cfg_led_off, sizeof(aw210xx_group_cfg_led_off)}, |
| {aw21018_group_all_leds_on, sizeof(aw21018_group_all_leds_on)}, |
| {aw21018_group_red_leds_on, sizeof(aw21018_group_red_leds_on)}, |
| {aw21018_group_green_leds_on, sizeof(aw21018_group_green_leds_on)}, |
| {aw21018_group_blue_leds_on, sizeof(aw21018_group_blue_leds_on)}, |
| {aw21018_group_breath_leds_on, sizeof(aw21018_group_breath_leds_on)}, |
| {aw21012_group_all_leds_on, sizeof(aw21012_group_all_leds_on)}, |
| {aw21012_group_red_leds_on, sizeof(aw21012_group_red_leds_on)}, |
| {aw21012_group_green_leds_on, sizeof(aw21012_group_green_leds_on)}, |
| {aw21012_group_blue_leds_on, sizeof(aw21012_group_blue_leds_on)}, |
| {aw21012_group_breath_leds_on, sizeof(aw21012_group_breath_leds_on)}, |
| {aw21009_group_all_leds_on, sizeof(aw21009_group_all_leds_on)}, |
| {aw21009_group_red_leds_on, sizeof(aw21009_group_red_leds_on)}, |
| {aw21009_group_green_leds_on, sizeof(aw21009_group_green_leds_on)}, |
| {aw21009_group_blue_leds_on, sizeof(aw21009_group_blue_leds_on)}, |
| {aw21009_group_breath_leds_on, sizeof(aw21009_group_breath_leds_on)} |
| }; |
| static char aw210xx_cfg_name[][AW210XX_CFG_NAME_MAX] = { |
| {"aw210xx_group_cfg_led_off"}, |
| {"aw21018_group_all_leds_on"}, |
| {"aw21018_group_red_leds_on"}, |
| {"aw21018_group_green_leds_on"}, |
| {"aw21018_group_blue_leds_on"}, |
| {"aw21018_group_breath_leds_on"}, |
| {"aw21012_group_all_leds_on"}, |
| {"aw21012_group_red_leds_on"}, |
| {"aw21012_group_green_leds_on"}, |
| {"aw21012_group_blue_leds_on"}, |
| {"aw21012_group_breath_leds_on"}, |
| {"aw21009_group_all_leds_on"}, |
| {"aw21009_group_red_leds_on"}, |
| {"aw21009_group_green_leds_on"}, |
| {"aw21009_group_blue_leds_on"}, |
| {"aw21009_group_breath_leds_on"}, |
| }; |
| |
| /****************************************************** |
| * |
| * aw210xx i2c write/read |
| * |
| ******************************************************/ |
| static int aw210xx_i2c_write(struct aw210xx *aw210xx, |
| 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(aw210xx->i2c, |
| reg_addr, reg_data); |
| if (ret < 0) |
| AW_ERR("i2c_write cnt=%d ret=%d\n", cnt, ret); |
| else |
| break; |
| cnt++; |
| usleep_range(AW_I2C_RETRY_DELAY * 1000, |
| AW_I2C_RETRY_DELAY * 1000 + 500); |
| } |
| |
| return ret; |
| } |
| |
| static int aw210xx_i2c_read(struct aw210xx *aw210xx, |
| unsigned char reg_addr, unsigned char *reg_data) |
| { |
| int ret = -1; |
| unsigned char cnt = 0; |
| |
| while (cnt < AW_I2C_RETRIES) { |
| ret = i2c_smbus_read_byte_data(aw210xx->i2c, reg_addr); |
| if (ret < 0) { |
| AW_ERR("i2c_read cnt=%d ret=%d\n", cnt, ret); |
| } else { |
| *reg_data = ret; |
| break; |
| } |
| cnt++; |
| usleep_range(AW_I2C_RETRY_DELAY * 1000, |
| AW_I2C_RETRY_DELAY * 1000 + 500); |
| } |
| |
| return ret; |
| } |
| |
| static int aw210xx_i2c_write_bits(struct aw210xx *aw210xx, |
| unsigned char reg_addr, unsigned int mask, |
| unsigned char reg_data) |
| { |
| unsigned char reg_val; |
| |
| aw210xx_i2c_read(aw210xx, reg_addr, ®_val); |
| reg_val &= mask; |
| reg_val |= reg_data; |
| aw210xx_i2c_write(aw210xx, reg_addr, reg_val); |
| |
| return 0; |
| } |
| |
| static int aw210xx_led_write_block_data(struct aw210xx *aw210xx, |
| unsigned char reg_addr, unsigned char len, const void *val) |
| { |
| int ret; |
| int cnt = 0; |
| |
| while (cnt < AW_I2C_RETRIES) { |
| ret = i2c_smbus_write_i2c_block_data(aw210xx->i2c, |
| reg_addr, len, val); |
| if (ret < 0) |
| AW_ERR("i2c_write_block cnt=%d ret=%d\n", cnt, ret); |
| else |
| break; |
| cnt++; |
| usleep_range(AW_I2C_RETRY_DELAY * 1000, |
| AW_I2C_RETRY_DELAY * 1000 + 500); |
| } |
| |
| return ret; |
| } |
| |
| /***************************************************** |
| * led Interface: set effect |
| *****************************************************/ |
| static void aw210xx_update_cfg_array(struct aw210xx *aw210xx, |
| uint8_t *p_cfg_data, uint32_t cfg_size) |
| { |
| unsigned int i = 0; |
| |
| for (i = 0; i < cfg_size; i += 2) |
| aw210xx_i2c_write(aw210xx, p_cfg_data[i], p_cfg_data[i + 1]); |
| } |
| |
| void aw210xx_cfg_update(struct aw210xx *aw210xx) |
| { |
| AW_LOG("aw210xx->effect = %d", aw210xx->effect); |
| |
| aw210xx_update_cfg_array(aw210xx, |
| aw210xx_cfg_array[aw210xx->effect].p, |
| aw210xx_cfg_array[aw210xx->effect].count); |
| } |
| |
| void aw210xx_uvlo_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_UVCR, |
| AW210XX_BIT_UVPD_MASK, |
| AW210XX_BIT_UVPD_DISENA); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_UVCR, |
| AW210XX_BIT_UVDIS_MASK, |
| AW210XX_BIT_UVDIS_DISENA); |
| } else { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_UVCR, |
| AW210XX_BIT_UVPD_MASK, |
| AW210XX_BIT_UVPD_ENABLE); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_UVCR, |
| AW210XX_BIT_UVDIS_MASK, |
| AW210XX_BIT_UVDIS_ENABLE); |
| } |
| } |
| |
| void aw210xx_sbmd_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR2, |
| AW210XX_BIT_SBMD_MASK, |
| AW210XX_BIT_SBMD_ENABLE); |
| aw210xx->sdmd_flag = 1; |
| } else { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR2, |
| AW210XX_BIT_SBMD_MASK, |
| AW210XX_BIT_SBMD_DISENA); |
| aw210xx->sdmd_flag = 0; |
| } |
| } |
| |
| void aw210xx_rgbmd_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR2, |
| AW210XX_BIT_RGBMD_MASK, |
| AW210XX_BIT_RGBMD_ENABLE); |
| aw210xx->rgbmd_flag = 1; |
| } else { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR2, |
| AW210XX_BIT_RGBMD_MASK, |
| AW210XX_BIT_RGBMD_DISENA); |
| aw210xx->rgbmd_flag = 0; |
| } |
| } |
| |
| void aw210xx_apse_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_APSE_MASK, |
| AW210XX_BIT_APSE_ENABLE); |
| } else { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_APSE_MASK, |
| AW210XX_BIT_APSE_DISENA); |
| } |
| } |
| |
| /***************************************************** |
| * aw210xx led function set |
| *****************************************************/ |
| int32_t aw210xx_osc_pwm_set(struct aw210xx *aw210xx) |
| { |
| switch (aw210xx->osc_clk) { |
| case CLK_FRQ_16M: |
| AW_LOG("osc is 16MHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_16MHz); |
| break; |
| case CLK_FRQ_8M: |
| AW_LOG("osc is 8MHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_8MHz); |
| break; |
| case CLK_FRQ_1M: |
| AW_LOG("osc is 1MHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_1MHz); |
| break; |
| case CLK_FRQ_512k: |
| AW_LOG("osc is 512KHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_512kHz); |
| break; |
| case CLK_FRQ_256k: |
| AW_LOG("osc is 256KHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_256kHz); |
| break; |
| case CLK_FRQ_125K: |
| AW_LOG("osc is 125KHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_125kHz); |
| break; |
| case CLK_FRQ_62_5K: |
| AW_LOG("osc is 62.5KHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_62_5kHz); |
| break; |
| case CLK_FRQ_31_25K: |
| AW_LOG("osc is 31.25KHz!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CLKFRQ_MASK, |
| AW210XX_BIT_CLKFRQ_31_25kHz); |
| break; |
| default: |
| AW_LOG("this clk_pwm is unsupported!\n"); |
| return -AW210XX_CLK_MODE_UNSUPPORT; |
| } |
| |
| return 0; |
| } |
| |
| int32_t aw210xx_br_res_set(struct aw210xx *aw210xx) |
| { |
| switch (aw210xx->br_res) { |
| case BR_RESOLUTION_8BIT: |
| AW_LOG("br resolution select 8bit!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_PWMRES_MASK, |
| AW210XX_BIT_PWMRES_8BIT); |
| break; |
| case BR_RESOLUTION_9BIT: |
| AW_LOG("br resolution select 9bit!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_PWMRES_MASK, |
| AW210XX_BIT_PWMRES_9BIT); |
| break; |
| case BR_RESOLUTION_12BIT: |
| AW_LOG("br resolution select 12bit!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_PWMRES_MASK, |
| AW210XX_BIT_PWMRES_12BIT); |
| break; |
| case BR_RESOLUTION_9_AND_3_BIT: |
| AW_LOG("br resolution select 9+3bit!\n"); |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_PWMRES_MASK, |
| AW210XX_BIT_PWMRES_9_AND_3_BIT); |
| break; |
| default: |
| AW_LOG("this br_res is unsupported!\n"); |
| return -AW210XX_CLK_MODE_UNSUPPORT; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************** |
| * aw210xx debug interface set |
| *****************************************************/ |
| static int aw210xx_update(struct aw210xx *aw210xx) |
| { |
| return aw210xx_i2c_write(aw210xx, AW210XX_REG_UPDATE, AW210XX_UPDATE_BR_SL); |
| } |
| |
| void aw210xx_global_set(struct aw210xx *aw210xx) |
| { |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_GCCR, aw210xx->glo_current); |
| } |
| void aw210xx_current_set(struct aw210xx *aw210xx) |
| { |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCCR, aw210xx->set_current); |
| } |
| |
| /***************************************************** |
| * |
| * aw210xx led cfg |
| * |
| *****************************************************/ |
| static void aw210xx_brightness_work(struct work_struct *work) |
| { |
| struct aw210xx *aw210xx = container_of(work, struct aw210xx, |
| brightness_work); |
| |
| AW_LOG("enter\n"); |
| |
| if (aw210xx->cdev.brightness > aw210xx->cdev.max_brightness) |
| aw210xx->cdev.brightness = aw210xx->cdev.max_brightness; |
| |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCCR, aw210xx->cdev.brightness); |
| } |
| |
| static void aw210xx_set_brightness(struct led_classdev *cdev, |
| enum led_brightness brightness) |
| { |
| struct aw210xx *aw210xx = container_of(cdev, struct aw210xx, cdev); |
| |
| aw210xx->cdev.brightness = brightness; |
| |
| schedule_work(&aw210xx->brightness_work); |
| } |
| |
| /***************************************************** |
| * aw210xx basic function set |
| *****************************************************/ |
| void aw210xx_chipen_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CHIPEN_MASK, |
| AW210XX_BIT_CHIPEN_ENABLE); |
| } else { |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_GCR, |
| AW210XX_BIT_CHIPEN_MASK, |
| AW210XX_BIT_CHIPEN_DISENA); |
| } |
| } |
| |
| static int aw210xx_hw_enable(struct aw210xx *aw210xx, bool flag) |
| { |
| AW_LOG("enter\n"); |
| |
| if (aw210xx && gpio_is_valid(aw210xx->enable_gpio)) { |
| if (flag) { |
| gpio_set_value_cansleep(aw210xx->enable_gpio, 1); |
| usleep_range(2000, 2500); |
| } else { |
| gpio_set_value_cansleep(aw210xx->enable_gpio, 0); |
| } |
| } else { |
| AW_ERR("failed\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int32_t aw210xx_group_gcfg_set(struct aw210xx *aw210xx, bool flag) |
| { |
| if (flag) { |
| switch (aw210xx->chipid) { |
| case AW21018_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21018_GROUP_ENABLE); |
| return 0; |
| case AW21012_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21012_GROUP_ENABLE); |
| return 0; |
| case AW21009_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21009_GROUP_ENABLE); |
| return 0; |
| default: |
| AW_LOG("%s: chip is unsupported device!\n", |
| __func__); |
| return -AW210XX_CHIPID_FAILD; |
| } |
| } else { |
| switch (aw210xx->chipid) { |
| case AW21018_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21018_GROUP_DISABLE); |
| return 0; |
| case AW21012_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21012_GROUP_DISABLE); |
| return 0; |
| case AW21009_CHIPID: |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_GCFG, |
| AW21009_GROUP_DISABLE); |
| return 0; |
| default: |
| AW_LOG("%s: chip is unsupported device!\n", |
| __func__); |
| return -AW210XX_CHIPID_FAILD; |
| } |
| } |
| } |
| |
| static int aw210xx_led_init(struct aw210xx *aw210xx) |
| { |
| AW_LOG("enter\n"); |
| |
| aw210xx->sdmd_flag = 0; |
| aw210xx->rgbmd_flag = 0; |
| /* chip enable */ |
| aw210xx_chipen_set(aw210xx, true); |
| /* SBMD (single byte mode) disable */ |
| aw210xx_sbmd_set(aw210xx, false); |
| /* RGBMD disable */ |
| aw210xx_rgbmd_set(aw210xx, false); |
| /* group set disable */ |
| aw210xx_group_gcfg_set(aw210xx, false); |
| /* clk_pwm selsect */ |
| aw210xx_osc_pwm_set(aw210xx); |
| /* br_res select */ |
| aw210xx_br_res_set(aw210xx); |
| /* global set */ |
| aw210xx_global_set(aw210xx); |
| /* under voltage lock out */ |
| aw210xx_uvlo_set(aw210xx, true); |
| /* APSE (auto power saving) enable */ |
| aw210xx_apse_set(aw210xx, true); |
| |
| return 0; |
| } |
| |
| void aw210xx_singleled_set(struct aw210xx *aw210xx, |
| uint32_t rgb_reg, |
| uint32_t rgb_sl, |
| uint32_t rgb_br) |
| { |
| /* chip enable */ |
| aw210xx_chipen_set(aw210xx, true); |
| /* global set */ |
| aw210xx->set_current = rgb_br; |
| aw210xx_current_set(aw210xx); |
| /* group set disable */ |
| aw210xx_group_gcfg_set(aw210xx, false); |
| |
| aw210xx_sbmd_set(aw210xx, true); |
| aw210xx_rgbmd_set(aw210xx, false); |
| aw210xx_uvlo_set(aw210xx, true); |
| |
| /* set sl */ |
| aw210xx->rgbcolor = rgb_sl & 0xff; |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_SL00 + rgb_reg, |
| aw210xx->rgbcolor); |
| |
| /* br set */ |
| if (aw210xx->sdmd_flag == 1) { |
| if (aw210xx->rgbmd_flag == 1) { |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00L + rgb_reg, |
| rgb_br); |
| } else { |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00L + rgb_reg, |
| rgb_br); |
| } |
| } else { |
| if (aw210xx->rgbmd_flag == 1) { |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00L + rgb_reg, |
| rgb_br); |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00H + rgb_reg, |
| rgb_br); |
| } else { |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00L + rgb_reg, |
| rgb_br); |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00H + rgb_reg, |
| rgb_br); |
| } |
| } |
| /* update */ |
| aw210xx_update(aw210xx); |
| } |
| |
| /***************************************************** |
| * open short detect |
| *****************************************************/ |
| void aw210xx_open_detect_cfg(struct aw210xx *aw210xx) |
| { |
| /*enable open detect*/ |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_OSDCR, AW210XX_OPEN_DETECT_EN); |
| /*set DCPWM = 1*/ |
| aw210xx_i2c_write_bits(aw210xx, AW210XX_REG_SSCR, |
| AW210XX_DCPWM_SET_MASK, |
| AW210XX_DCPWM_SET); |
| /*set Open threshold = 0.2v*/ |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_OSDCR, |
| AW210XX_OPEN_THRESHOLD_SET_MASK, |
| AW210XX_OPEN_THRESHOLD_SET); |
| } |
| |
| void aw210xx_short_detect_cfg(struct aw210xx *aw210xx) |
| { |
| |
| /*enable short detect*/ |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_OSDCR, AW210XX_SHORT_DETECT_EN); |
| /*set DCPWM = 1*/ |
| aw210xx_i2c_write_bits(aw210xx, AW210XX_REG_SSCR, |
| AW210XX_DCPWM_SET_MASK, |
| AW210XX_DCPWM_SET); |
| /*set Short threshold = 1v*/ |
| aw210xx_i2c_write_bits(aw210xx, |
| AW210XX_REG_OSDCR, |
| AW210XX_SHORT_THRESHOLD_SET_MASK, |
| AW210XX_SHORT_THRESHOLD_SET); |
| } |
| |
| void aw210xx_open_short_dis(struct aw210xx *aw210xx) |
| { |
| aw210xx_i2c_write(aw210xx, AW210XX_REG_OSDCR, AW210XX_OPEN_SHORT_DIS); |
| /*SET DCPWM = 0*/ |
| aw210xx_i2c_write_bits(aw210xx, AW210XX_REG_SSCR, |
| AW210XX_DCPWM_SET_MASK, |
| AW210XX_DCPWM_CLEAN); |
| } |
| void aw210xx_open_short_detect(struct aw210xx *aw210xx, |
| int32_t detect_flg, u8 *reg_val) |
| { |
| /*config for open shor detect*/ |
| if (detect_flg == AW210XX_OPEN_DETECT) |
| aw210xx_open_detect_cfg(aw210xx); |
| else if (detect_flg == AW210XX_SHORT_DETECT) |
| aw210xx_short_detect_cfg(aw210xx); |
| /*read detect result*/ |
| aw210xx_i2c_read(aw210xx, AW210XX_REG_OSST0, ®_val[0]); |
| aw210xx_i2c_read(aw210xx, AW210XX_REG_OSST1, ®_val[1]); |
| aw210xx_i2c_read(aw210xx, AW210XX_REG_OSST2, ®_val[2]); |
| /*close for open short detect*/ |
| aw210xx_open_short_dis(aw210xx); |
| } |
| |
| /****************************************************** |
| * |
| * sys group attribute: reg |
| * |
| ******************************************************/ |
| static ssize_t aw210xx_reg_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, |
| size_t len) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| uint32_t databuf[2] = { 0, 0 }; |
| |
| if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { |
| if (aw210xx_reg_access[(uint8_t)databuf[0]] & REG_WR_ACCESS) |
| aw210xx_i2c_write(aw210xx, (uint8_t)databuf[0], |
| (uint8_t)databuf[1]); |
| } |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_reg_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| ssize_t len = 0; |
| unsigned int i = 0; |
| unsigned char reg_val = 0; |
| uint8_t br_max = 0; |
| uint8_t sl_val = 0; |
| |
| aw210xx_i2c_read(aw210xx, AW210XX_REG_GCR, ®_val); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "reg:0x%02x=0x%02x\n", AW210XX_REG_GCR, reg_val); |
| switch (aw210xx->chipid) { |
| case AW21018_CHIPID: |
| br_max = AW210XX_REG_BR17H; |
| sl_val = AW210XX_REG_SL17; |
| break; |
| case AW21012_CHIPID: |
| br_max = AW210XX_REG_BR11H; |
| sl_val = AW210XX_REG_SL11; |
| break; |
| case AW21009_CHIPID: |
| br_max = AW210XX_REG_BR08H; |
| sl_val = AW210XX_REG_SL08; |
| break; |
| default: |
| AW_LOG("chip is unsupported device!\n"); |
| return len; |
| } |
| |
| for (i = AW210XX_REG_BR00L; i <= br_max; i++) { |
| if (!(aw210xx_reg_access[i] & REG_RD_ACCESS)) |
| continue; |
| aw210xx_i2c_read(aw210xx, i, ®_val); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "reg:0x%02x=0x%02x\n", i, reg_val); |
| } |
| for (i = AW210XX_REG_SL00; i <= sl_val; i++) { |
| if (!(aw210xx_reg_access[i] & REG_RD_ACCESS)) |
| continue; |
| aw210xx_i2c_read(aw210xx, i, ®_val); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "reg:0x%02x=0x%02x\n", i, reg_val); |
| } |
| for (i = AW210XX_REG_GCCR; i <= AW210XX_REG_GCFG; i++) { |
| if (!(aw210xx_reg_access[i] & REG_RD_ACCESS)) |
| continue; |
| aw210xx_i2c_read(aw210xx, i, ®_val); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "reg:0x%02x=0x%02x\n", i, reg_val); |
| } |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_hwen_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t len) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| int rc; |
| unsigned int val = 0; |
| |
| rc = kstrtouint(buf, 0, &val); |
| if (rc < 0) |
| return rc; |
| |
| if (val > 0) |
| aw210xx_hw_enable(aw210xx, true); |
| else |
| aw210xx_hw_enable(aw210xx, false); |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_hwen_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| ssize_t len = 0; |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, "hwen=%d\n", |
| gpio_get_value(aw210xx->enable_gpio)); |
| return len; |
| } |
| |
| static ssize_t aw210xx_effect_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| ssize_t len = 0; |
| unsigned int i; |
| |
| for (i = 0; i < (sizeof(aw210xx_cfg_array) / |
| sizeof(aw210xx_cfg_t)); i++) { |
| len += snprintf(buf + len, PAGE_SIZE - len, "effect[%d]: %s\n", |
| i, aw210xx_cfg_name[i]); |
| } |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, "current effect[%d]: %s\n", |
| aw210xx->effect, aw210xx_cfg_name[aw210xx->effect]); |
| return len; |
| } |
| |
| static ssize_t aw210xx_effect_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t len) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| int rc; |
| unsigned int val = 0; |
| |
| rc = kstrtouint(buf, 10, &val); |
| if (rc < 0) |
| return rc; |
| if ((val >= (sizeof(aw210xx_cfg_array) / |
| sizeof(aw210xx_cfg_t))) || (val < 0)) { |
| pr_err("%s, store effect num error.\n", __func__); |
| return -EINVAL; |
| } |
| |
| aw210xx->effect = val; |
| pr_info("%s, line%d,val = %d\n", __func__, __LINE__, val); |
| aw210xx_cfg_update(aw210xx); |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_rgbcolor_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t len) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| uint32_t rgb_num = 0; |
| uint32_t rgb_data = 0; |
| |
| if (sscanf(buf, "%x %x", &rgb_num, &rgb_data) == 2) { |
| aw210xx_chipen_set(aw210xx, true); |
| aw210xx_sbmd_set(aw210xx, true); |
| aw210xx_rgbmd_set(aw210xx, true); |
| aw210xx_global_set(aw210xx); |
| aw210xx_uvlo_set(aw210xx, true); |
| |
| /* set sl */ |
| aw210xx->rgbcolor = (rgb_data & 0xff0000) >> 16; |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_SL00 + (uint8_t)rgb_num * 3, |
| aw210xx->rgbcolor); |
| |
| aw210xx->rgbcolor = (rgb_data & 0x00ff00) >> 8; |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_SL00 + (uint8_t)rgb_num * 3 + 1, |
| aw210xx->rgbcolor); |
| |
| aw210xx->rgbcolor = (rgb_data & 0x0000ff); |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_SL00 + (uint8_t)rgb_num * 3 + 2, |
| aw210xx->rgbcolor); |
| |
| /* br set */ |
| aw210xx_i2c_write(aw210xx, |
| AW210XX_REG_BR00L + (uint8_t)rgb_num, |
| AW210XX_GLOBAL_DEFAULT_SET); |
| |
| /* update */ |
| aw210xx_update(aw210xx); |
| } |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_singleled_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t len) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| uint32_t led_num = 0; |
| uint32_t rgb_data = 0; |
| uint32_t rgb_brightness = 0; |
| |
| if (sscanf(buf, "%x %x %x", &led_num, &rgb_data, &rgb_brightness) == 3) { |
| if (aw210xx->chipid == AW21018_CHIPID) { |
| if (led_num > AW21018_LED_NUM) |
| led_num = AW21018_LED_NUM; |
| } else if (aw210xx->chipid == AW21012_CHIPID) { |
| if (led_num > AW21012_LED_NUM) |
| led_num = AW21012_LED_NUM; |
| } else { |
| if (led_num > AW21009_LED_NUM) |
| led_num = AW21009_LED_NUM; |
| } |
| aw210xx_singleled_set(aw210xx, led_num, rgb_data, rgb_brightness); |
| } |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_opdetect_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| ssize_t len = 0; |
| int i = 0; |
| uint8_t reg_val[3] = {0}; |
| |
| aw210xx_open_short_detect(aw210xx, AW210XX_OPEN_DETECT, reg_val); |
| for (i = 0; i < sizeof(reg_val); i++) |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "OSST%d:%#x\n", i, reg_val[i]); |
| |
| return len; |
| } |
| |
| static ssize_t aw210xx_stdetect_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| ssize_t len = 0; |
| int i = 0; |
| uint8_t reg_val[3] = {0}; |
| |
| aw210xx_open_short_detect(aw210xx, AW210XX_SHORT_DETECT, reg_val); |
| for (i = 0; i < sizeof(reg_val); i++) |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "OSST%d:%#x\n", i, reg_val[i]); |
| return len; |
| } |
| |
| /* Function to store scale and brightness control values of all LEDs. |
| * Takes 16 values. First 8 are [0-255], latter 8 are [0-4095]. |
| * If #values < 16, the remaining values are set to 0 as default. |
| * Writes all the values in one block write. |
| * Input Format: |
| * "iref0 iref1 iref2 iref3 .. iref7 brt0 brt1 brt2 brt3 .. brt7" |
| */ |
| static ssize_t aw210xx_iref_pwm_all_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| struct aw210xx *aw210xx = container_of(led_cdev, struct aw210xx, cdev); |
| int chars_read = 0; |
| int idx = 0; |
| int ret; |
| // brightness is 12bits |
| u16 value; |
| u16 values[16]; |
| const int num_brightness_bytes = 9 * 2; |
| u8 output[num_brightness_bytes]; |
| |
| memset(values, 0, sizeof(values)); |
| memset(output, 0, sizeof(output)); |
| |
| while (sscanf(buf, "%u%n", &value, &chars_read) == 1) { |
| if (idx == sizeof(values)) { |
| dev_err(dev, "%s: too many values received\n", |
| __func__); |
| return -EINVAL; |
| } |
| values[idx] = value; |
| idx++; |
| buf += chars_read; |
| } |
| if (idx == 0) { |
| dev_err(dev, "%s: no valid values received\n", __func__); |
| return -EINVAL; |
| } |
| |
| // write SL (current) |
| for (idx = 0; idx < 8; idx++) { |
| output[idx] = values[idx]; |
| } |
| ret = aw210xx_led_write_block_data(aw210xx, |
| AW210XX_REG_SL00, 8, output); |
| if (ret < 0) { |
| dev_err(dev, |
| "%s: failed to write SL values to all leds, err: %d\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| // write Brightness (PWM) |
| memset(output, 0, sizeof(output)); |
| for (idx = 0; idx < 8; idx++) { |
| output[idx * 2] = values[8 + idx] & 0xFF; |
| output[idx * 2 + 1] = (values[8 + idx] >> 8) & 0x0F; |
| } |
| ret = aw210xx_led_write_block_data(aw210xx, |
| AW210XX_REG_BR00L, 16, output); |
| if (ret < 0) { |
| dev_err(dev, |
| "%s: failed to write BR values to all leds, err: %d\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| /* update */ |
| ret = aw210xx_update(aw210xx); |
| if (ret < 0) { |
| dev_err(dev, "%s: failed to update LED, err: %d\n", __func__, ret); |
| return ret; |
| } |
| return count; |
| } |
| |
| static DEVICE_ATTR(reg, 0664, aw210xx_reg_show, aw210xx_reg_store); |
| static DEVICE_ATTR(hwen, 0664, aw210xx_hwen_show, aw210xx_hwen_store); |
| static DEVICE_ATTR(effect, 0664, aw210xx_effect_show, aw210xx_effect_store); |
| static DEVICE_ATTR(rgbcolor, 0664, NULL, aw210xx_rgbcolor_store); |
| static DEVICE_ATTR(singleled, 0664, NULL, aw210xx_singleled_store); |
| static DEVICE_ATTR(opdetect, 0664, aw210xx_opdetect_show, NULL); |
| static DEVICE_ATTR(stdetect, 0664, aw210xx_stdetect_show, NULL); |
| static DEVICE_ATTR(iref_pwm_all, 0664, NULL, aw210xx_iref_pwm_all_store); |
| |
| static struct attribute *aw210xx_attributes[] = { |
| &dev_attr_reg.attr, |
| &dev_attr_hwen.attr, |
| &dev_attr_effect.attr, |
| &dev_attr_rgbcolor.attr, |
| &dev_attr_singleled.attr, |
| &dev_attr_opdetect.attr, |
| &dev_attr_stdetect.attr, |
| &dev_attr_iref_pwm_all.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group aw210xx_attribute_group = { |
| .attrs = aw210xx_attributes |
| }; |
| /****************************************************** |
| * |
| * led class dev |
| ******************************************************/ |
| |
| static int aw210xx_parse_led_cdev(struct aw210xx *aw210xx, |
| struct device_node *np) |
| { |
| int ret = -1; |
| struct device_node *temp; |
| |
| AW_LOG("enter\n"); |
| for_each_child_of_node(np, temp) { |
| ret = of_property_read_string(temp, "aw210xx,name", |
| &aw210xx->cdev.name); |
| if (ret < 0) { |
| dev_err(aw210xx->dev, |
| "Failure reading led name, ret = %d\n", ret); |
| goto free_pdata; |
| } |
| ret = of_property_read_u32(temp, "aw210xx,imax", |
| &aw210xx->imax); |
| if (ret < 0) { |
| dev_err(aw210xx->dev, |
| "Failure reading imax, ret = %d\n", ret); |
| goto free_pdata; |
| } |
| ret = of_property_read_u32(temp, "aw210xx,brightness", |
| &aw210xx->cdev.brightness); |
| if (ret < 0) { |
| dev_err(aw210xx->dev, |
| "Failure reading brightness, ret = %d\n", ret); |
| goto free_pdata; |
| } |
| ret = of_property_read_u32(temp, "aw210xx,max_brightness", |
| &aw210xx->cdev.max_brightness); |
| if (ret < 0) { |
| dev_err(aw210xx->dev, |
| "Failure reading max brightness, ret = %d\n", |
| ret); |
| goto free_pdata; |
| } |
| } |
| |
| INIT_WORK(&aw210xx->brightness_work, aw210xx_brightness_work); |
| aw210xx->cdev.brightness_set = aw210xx_set_brightness; |
| |
| ret = led_classdev_register(aw210xx->dev, &aw210xx->cdev); |
| if (ret) { |
| AW_ERR("unable to register led ret=%d\n", ret); |
| goto free_pdata; |
| } |
| |
| ret = sysfs_create_group(&aw210xx->cdev.dev->kobj, |
| &aw210xx_attribute_group); |
| if (ret) { |
| AW_ERR("led sysfs ret: %d\n", ret); |
| goto free_class; |
| } |
| |
| aw210xx_led_init(aw210xx); |
| |
| return 0; |
| |
| free_class: |
| led_classdev_unregister(&aw210xx->cdev); |
| free_pdata: |
| return ret; |
| } |
| |
| /***************************************************** |
| * |
| * check chip id and version |
| * |
| *****************************************************/ |
| static int aw210xx_read_chipid(struct aw210xx *aw210xx) |
| { |
| int ret = -1; |
| unsigned char cnt = 0; |
| unsigned char chipid = 0; |
| |
| while (cnt < AW_READ_CHIPID_RETRIES) { |
| ret = aw210xx_i2c_read(aw210xx, AW210XX_REG_RESET, &chipid); |
| if (ret < 0) { |
| AW_ERR("failed to read chipid: %d\n", ret); |
| } else { |
| aw210xx->chipid = chipid; |
| switch (aw210xx->chipid) { |
| case AW21018_CHIPID: |
| AW_LOG("AW21018, read chipid = 0x%02x!!\n", |
| chipid); |
| return 0; |
| case AW21012_CHIPID: |
| AW_LOG("AW21012, read chipid = 0x%02x!!\n", |
| chipid); |
| return 0; |
| case AW21009_CHIPID: |
| AW_LOG("AW21009, read chipid = 0x%02x!!\n", |
| chipid); |
| return 0; |
| default: |
| AW_LOG("chip is unsupported device id = %x\n", |
| chipid); |
| break; |
| } |
| } |
| cnt++; |
| usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, |
| AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); |
| } |
| |
| return -EINVAL; |
| } |
| |
| /***************************************************** |
| * |
| * device tree |
| * |
| *****************************************************/ |
| static int aw210xx_parse_dt(struct device *dev, struct aw210xx *aw210xx, |
| struct device_node *np) |
| { |
| int ret = -EINVAL; |
| |
| aw210xx->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); |
| if (aw210xx->enable_gpio < 0) { |
| aw210xx->enable_gpio = -1; |
| AW_ERR("no enable gpio provided, HW enable unsupported\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "osc_clk", |
| &aw210xx->osc_clk); |
| if (ret < 0) { |
| AW_ERR("no osc_clk provided, osc clk unsupported\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "br_res", |
| &aw210xx->br_res); |
| if (ret < 0) { |
| AW_ERR("brightness resolution unsupported\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "global_current", |
| &aw210xx->glo_current); |
| if (ret < 0) { |
| AW_ERR("global current resolution unsupported\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| /****************************************************** |
| * |
| * i2c driver |
| * |
| ******************************************************/ |
| static int aw210xx_i2c_probe(struct i2c_client *i2c, |
| const struct i2c_device_id *id) |
| { |
| struct aw210xx *aw210xx; |
| struct device_node *np = i2c->dev.of_node; |
| int ret; |
| |
| AW_LOG("enter\n"); |
| |
| if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { |
| AW_ERR("check_functionality failed\n"); |
| return -EIO; |
| } |
| |
| aw210xx = devm_kzalloc(&i2c->dev, sizeof(struct aw210xx), GFP_KERNEL); |
| if (aw210xx == NULL) |
| return -ENOMEM; |
| |
| aw210xx->dev = &i2c->dev; |
| aw210xx->i2c = i2c; |
| i2c_set_clientdata(i2c, aw210xx); |
| |
| /* aw210xx parse device tree */ |
| if (np) { |
| ret = aw210xx_parse_dt(&i2c->dev, aw210xx, np); |
| if (ret) { |
| AW_ERR("failed to parse device tree node\n"); |
| goto err_parse_dt; |
| } |
| } |
| |
| if (gpio_is_valid(aw210xx->enable_gpio)) { |
| ret = devm_gpio_request(&i2c->dev, aw210xx->enable_gpio, "aw210xx_en"); |
| if (ret) { |
| AW_ERR("enable gpio request failed\n"); |
| goto err_gpio_request; |
| } |
| } |
| |
| /* hardware enable */ |
| aw210xx_hw_enable(aw210xx, true); |
| |
| /* aw210xx identify */ |
| ret = aw210xx_read_chipid(aw210xx); |
| if (ret < 0) { |
| AW_ERR("aw210xx_read_chipid failed ret=%d\n", ret); |
| goto err_id; |
| } |
| |
| dev_set_drvdata(&i2c->dev, aw210xx); |
| aw210xx_parse_led_cdev(aw210xx, np); |
| if (ret < 0) { |
| AW_ERR("error creating led class dev\n"); |
| goto err_sysfs; |
| } |
| |
| AW_LOG("probe completed!\n"); |
| |
| return 0; |
| |
| err_sysfs: |
| err_id: |
| devm_gpio_free(&i2c->dev, aw210xx->enable_gpio); |
| err_gpio_request: |
| err_parse_dt: |
| devm_kfree(&i2c->dev, aw210xx); |
| aw210xx = NULL; |
| return ret; |
| } |
| |
| static int aw210xx_i2c_remove(struct i2c_client *i2c) |
| { |
| struct aw210xx *aw210xx = i2c_get_clientdata(i2c); |
| |
| AW_LOG("enter\n"); |
| sysfs_remove_group(&aw210xx->cdev.dev->kobj, &aw210xx_attribute_group); |
| led_classdev_unregister(&aw210xx->cdev); |
| if (gpio_is_valid(aw210xx->enable_gpio)) |
| devm_gpio_free(&i2c->dev, aw210xx->enable_gpio); |
| devm_kfree(&i2c->dev, aw210xx); |
| aw210xx = NULL; |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id aw210xx_i2c_id[] = { |
| {AW210XX_I2C_NAME, 0}, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(i2c, aw210xx_i2c_id); |
| |
| static const struct of_device_id aw210xx_dt_match[] = { |
| {.compatible = "awinic,aw210xx_led"}, |
| {} |
| }; |
| |
| static struct i2c_driver aw210xx_i2c_driver = { |
| .driver = { |
| .name = AW210XX_I2C_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(aw210xx_dt_match), |
| }, |
| .probe = aw210xx_i2c_probe, |
| .remove = aw210xx_i2c_remove, |
| .id_table = aw210xx_i2c_id, |
| }; |
| |
| static int __init aw210xx_i2c_init(void) |
| { |
| int ret = 0; |
| |
| AW_LOG("enter, aw210xx driver version %s\n", AW210XX_DRIVER_VERSION); |
| |
| ret = i2c_add_driver(&aw210xx_i2c_driver); |
| if (ret) { |
| AW_ERR("failed to register aw210xx driver!\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| module_init(aw210xx_i2c_init); |
| |
| static void __exit aw210xx_i2c_exit(void) |
| { |
| i2c_del_driver(&aw210xx_i2c_driver); |
| } |
| module_exit(aw210xx_i2c_exit); |
| |
| MODULE_DESCRIPTION("AW210XX LED Driver"); |
| MODULE_LICENSE("GPL v2"); |