/* * drivers/leds/is31fl3736.c * Driver for ISSI is31fl3736 of I2C LED controllers * * Copyright (C) 2018 Allwinner Technology Limited. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "leds-is31fl3736.h" #define IS31FL3736_WRITE_BIT 0x00 #define IS31FL3736_READ_BIT 0x01 #define IS31FL3736_ACK_CHECK_EN 1 #define IS31FL3736_READ_ACK 0x0 /*!< I2C ack value */ #define IS31FL3736_READ_NACK 0x1 /*!< I2C nack value */ #define IS31FL3736_CMD_WRITE_EN 0xC5 /*!< magic num */ #define IS31FL3736_I2C_ID 0xA0 /*!< I2C Addr,up to ADDR1/ADDR2 pin*/ /* Used to indicate a device has no such register */ #define IS31FL37XX_REG_NONE 0xFF /*!< max channel num in the PWM mode */ #define IS31FL3736_PWM_CHANNEL_MAX (0XBE) /*!< in LED matrix mode, there are 8 current sources channel */ #define IS31FL3736_CSX_MAX (8) #define IS31FL3736_CSX_MAX_MASK (0xff) /*!< in LED matrix mode, there are 12 switch channel */ #define IS31FL3736_SWY_MAX (12) #define IS31FL3736_SWY_MAX_MASK (0xfff) #define IS31FL3736_PAGE(i) (i) /*!< range 0 ~ 3 */ enum is31fl3736_t1_t3_time { IS31FL3736_t1_t3_time_0 = 0,/*!< t1/t3, 0.21s */ IS31FL3736_t1_t3_time_1 = 0x01, /*!< t1/t3, 0.42s */ IS31FL3736_t1_t3_time_2 = 0x02, /*!< t1/t3, 0.84s */ IS31FL3736_t1_t3_time_3 = 0x03, /*!< t1/t3, 1.68s*/ IS31FL3736_t1_t3_time_4 = 0x04, /*!< t1/t3, 3.36s */ IS31FL3736_t1_t3_time_5 = 0x05, /*!< t1/t3, 6.72s */ IS31FL3736_t1_t3_time_6 = 0x06, /*!< t1/t3, 13.44s */ IS31FL3736_t1_t3_time_7 = 0x07, /*!< t1/t3, 26.88s */ IS31FL3736_t1_t3_time_MAX, }; enum is31fl3736_t2_time { IS31FL3736_t2_time_0 = 0,/*!< t2, 0s */ IS31FL3736_t2_time_1 = 0x01, /*!< t2, 0.21s */ IS31FL3736_t2_time_2 = 0x02, /*!< t2, 0.42s */ IS31FL3736_t2_time_3 = 0x03, /*!< t2, 0.84s */ IS31FL3736_t2_time_4 = 0x04, /*!< t2, 1.68s */ IS31FL3736_t2_time_5 = 0x05, /*!< t2, 3.36s */ IS31FL3736_t2_time_6 = 0x06, /*!< t2, 6.72s */ IS31FL3736_t2_time_7 = 0x07, /*!< t2, 13.44s */ IS31FL3736_t2_time_8 = 0x08, /*!< t2, 26.88s */ IS31FL3736_t2_time_MAX, }; enum is31fl3736_t4_time { IS31FL3736_t4_time_0 = 0,/*!< t4, 0s */ IS31FL3736_t4_time_1 = 0x01, /*!< t4, 0.21s */ IS31FL3736_t4_time_2 = 0x02, /*!< t4, 0.42s */ IS31FL3736_t4_time_3 = 0x03, /*!< t4, 0.84s */ IS31FL3736_t4_time_4 = 0x04, /*!< t4, 1.68s */ IS31FL3736_t4_time_5 = 0x05, /*!< t4, 3.36s */ IS31FL3736_t4_time_6 = 0x06, /*!< t4, 6.72s */ IS31FL3736_t4_time_7 = 0x07, /*!< t4, 13.44s */ IS31FL3736_t4_time_8 = 0x08, /*!< t4, 26.88s */ IS31FL3736_t4_time_9 = 0x09, /*!< t4, 53.76s */ IS31FL3736_t4_time_10 = 0x10, /*!< t4, 107.52s */ IS31FL3736_t4_time_MAX, }; enum is31fl3736_led_state { IS31FL3736_LED_OFF = 0, /*!< The resistor value */ IS31FL3736_LED_ON = 1, /*!< The resistor value */ IS31FL3736_LED_MAX, }; enum is31fl3736_auto_breath_mode { IS31FL3736_PWM_MODE = 0, /*!< The resistor value */ IS31FL3736_ABM1 = 1, /*!< The resistor value */ IS31FL3736_ABM2 = 2, /*!< The resistor value */ IS31FL3736_ABM3 = 3, /*!< The resistor value */ IS31FL3736_ABM_MAX, }; struct is31fl3736_led_data { struct led_classdev cdev; u8 channel; /* 1-based, max */ u8 sw_ch; u8 cs_ch; /*Auto breath control time set*/ u8 rise_time; /*t1*/ u8 hold_time; /*t2*/ u8 fall_time; /*t3*/ u8 off_time; /*t4*/ u8 cur_duty; /*pwm duty*/ u32 loop;/*blink time, if 0, endless blink*/ enum is31fl3736_led_state cur_state; enum is31fl3736_auto_breath_mode cur_mode; struct is31fl3736_priv *priv; }; struct is31fl3736_priv { u32 power_gpio; u32 shdn_gpio; u32 int_gpio; u16 sw_y[IS31FL3736_SWY_MAX]; u16 num_leds; u8 cs_max; u8 sw_max; struct i2c_client *client; struct mutex mutex; struct is31fl3736_led_data leds[0]; }; #define IS31Fl3736_ATTR_RW(_name) \ DEVICE_ATTR(_name, 0644, is31fl3736_led_show_##_name, \ is31fl3736_led_store_##_name) static int is31fl3736_read_byte(struct is31fl3736_priv *priv, u8 reg, u8 *rt_value) { int ret; u8 read_cmd[3] = { 0 }; u8 cmd_len = 0; read_cmd[0] = reg; cmd_len = 1; if (priv->client->adapter == NULL) pr_err("is31fl3736_read client->adapter==NULL\n"); ret = i2c_master_send(priv->client, read_cmd, cmd_len); if (ret != cmd_len) { pr_err("is31fl3736_read error1\n"); return -1; } ret = i2c_master_recv(priv->client, rt_value, 1); if (ret != 1) { pr_err("is31fl3736_read error2, ret = %d.\n", ret); return -1; } return 0; } static int is31fl3736_write_byte(struct is31fl3736_priv *priv, u8 reg, u8 value) { int ret = 0; u8 write_cmd[2] = { 0 }; write_cmd[0] = reg; write_cmd[1] = value; ret = i2c_master_send(priv->client, write_cmd, 2); if (ret != 2) { pr_err("is31fl3736_write error->[REG-0x%02x,val-0x%02x]\n", reg, value); return -1; } return 0; } /** * @brief change led channels on/off state */ static int is31fl3736_set_led_state(struct is31fl3736_priv *priv, u8 cs_x, u8 sw_y, enum is31fl3736_led_state state) { u8 reg, reg_val; int ret = 0; pr_debug("%s: cs: %d, sw: %d, state: %d\n", __func__, cs_x, sw_y, state); if (cs_x > IS31FL3736_CSX_MAX || cs_x < 1) { pr_err("%s: cs channel error, cs_x: %d\n", __func__, cs_x); return -EINVAL; } if (sw_y > IS31FL3736_SWY_MAX || sw_y < 1) { pr_err("%s: sw channel error, sw_y: %d\n", __func__, sw_y); return -EINVAL; } ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(0)); if (ret) return ret; reg = (sw_y - 1) * 2 + ((cs_x - 1) / 4); if (state == IS31FL3736_LED_ON) *(priv->sw_y + sw_y - 1) |= state << (cs_x - 1) * 2; else *(priv->sw_y + sw_y - 1) &= state << (cs_x - 1) * 2; if (cs_x > 4) reg_val = (*(priv->sw_y + sw_y - 1) >> 8) & 0xff;//high 8 bit else reg_val = *(priv->sw_y + sw_y - 1) & 0xff;//low 8 bit pr_debug("%s: reg 0x%02x, val 0x%02x\n", __func__, reg, reg_val); return is31fl3736_write_byte(priv, reg, reg_val); } /** * @brief change channels pwm duty register */ static int is31fl3736_set_pwm_duty(struct is31fl3736_priv *priv, u8 cs_x, u8 sw_y, u8 duty) { u8 reg; int ret = 0; if (cs_x > IS31FL3736_CSX_MAX || cs_x < 1) { pr_err("%s: cs channel error, cs_x: %d\n", __func__, cs_x); return -EINVAL; } if (sw_y > IS31FL3736_SWY_MAX || sw_y < 1) { pr_err("%s: sw channel error, sw_y: %d\n", __func__, sw_y); return -EINVAL; } ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(1)); if (ret) return ret; reg = 0x10 * (sw_y - 1) + 0x02 * (cs_x - 1); pr_debug("%s: reg 0x%02x, duty %d\n", __func__, reg, duty); return is31fl3736_write_byte(priv, reg, duty); //pwm-duty } /** * @brief change the breath mode for each led bit */ static int is31fl3736_set_breath_mode(struct is31fl3736_priv *priv, u8 cs_x, u8 sw_y, enum is31fl3736_auto_breath_mode mode) { u8 reg; int ret = 0; if (cs_x > IS31FL3736_CSX_MAX || cs_x < 1) { pr_err("%s: cs channel error, cs_x: %d\n", __func__, cs_x); return -EINVAL; } if (sw_y > IS31FL3736_SWY_MAX || sw_y < 1) { pr_err("%s: sw channel error, sw_y: %d\n", __func__, sw_y); return -EINVAL; } ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(2)); if (ret) return ret; reg = 0x10 * (sw_y - 1) + 0x02 * (cs_x - 1); pr_debug("%s: reg 0x%02x, mode %d\n", __func__, reg, mode); return is31fl3736_write_byte(priv, reg, mode); //ABM-x } /** * @brief set led time delay for fade and holdtime */ static int is31fl3736_set_breath_time(struct is31fl3736_priv *priv, u8 rise, u8 hold, u8 fall, u8 off, enum is31fl3736_auto_breath_mode mode) { int ret; u8 regval; pr_debug("%s: set led breah mode:%d time: t1: %d, t2: %d, t3: %d, t3: %d\n", __func__, mode, rise, hold, fall, off); if (mode == IS31FL3736_PWM_MODE) return -EINVAL; ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(3)); if (ret) return ret; regval = ((rise & IS31FL3736_ABM_T1_V) << IS31FL3736_ABM_T1_S) | ((hold & IS31FL3736_ABM_T2_V) << IS31FL3736_ABM_T2_S); pr_debug("%s: reg: 0x%0x, val: %d\n", __func__, IS31FL3736_REG_PG3_FADE_IN(mode), regval); ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_FADE_IN(mode), regval); if (ret) return ret; regval = ((fall & IS31FL3736_ABM_T3_V) << IS31FL3736_ABM_T3_S) | ((off & IS31FL3736_ABM_T4_V) << IS31FL3736_ABM_T4_S); pr_debug("%s: reg: 0x%0x, val: %d\n", __func__, IS31FL3736_REG_PG3_FADE_OUT(mode), regval); ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_FADE_OUT(mode), regval); return ret; } /** * @brief set led loop time for brath mode */ static int is31fl3736_set_breath_loop(struct is31fl3736_priv *priv, u32 loop, enum is31fl3736_auto_breath_mode mode) { int ret; pr_debug("%s: set led blink loop times, mode: %d, loop: %d\n", __func__, mode, loop); if (mode == IS31FL3736_PWM_MODE) return -EINVAL; ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(3)); if (ret) return ret; /*LTA, high 4 bit*/ ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_LOOP1(mode), (loop >> 8) & IS31FL3736_ABM_LTA_V); if (ret) return ret; /*LTB, low 8 bits, total loop time: LTA*256 + LTB*/ ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_LOOP2(mode), loop & IS31FL3736_ABM_LTB_V); return ret; } /** * @brief set led blink for brath mode, begin or stop blink, just influent the ARMx mode led */ static int is31fl3736_set_breath_blink(struct is31fl3736_priv *priv, int enable) { int ret; pr_debug("%s: set led blink enable: %d\n", __func__, enable); ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(3)); if (ret) return ret; if (enable) { is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CONFIG, 0x03); //update registers ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_UPDATE, 0x00); } else { //disable auto breath mode ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CONFIG, 0x01); } return ret; } /** * @brief reset all regs to default */ static int is31fl3736_reset_regs(struct is31fl3736_priv *priv) { int ret; u8 reg; ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(3)); if (ret) return ret; ret = is31fl3736_read_byte(priv, IS31FL3736_REG_PG3_RESET, ®); if (ret) return ret; return 0; } /** * @brief software shutdown the chip */ static int is31fl3736_software_shutdown(struct is31fl3736_priv *priv, bool enable) { int ret; ret = is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); if (ret) return ret; ret = is31fl3736_write_byte(priv, IS31FL3736_REG_CMD, IS31FL3736_PAGE(3)); if (ret) return ret; if (enable) { //software shutdown ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CONFIG, 0x00); } else { //normal state ret = is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CONFIG, 0x03); } return ret; } /** * @brief init the chip to normal state */ static int is31fl3736_init_regs(struct is31fl3736_priv *priv) { is31fl3736_write_byte(priv, IS31FL3736_RET_CMD_LOCK, IS31FL3736_CMD_WRITE_EN); is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CONFIG, 0x03); is31fl3736_write_byte(priv, IS31FL3736_REG_PG3_CURR, 0x0ff); return 0; } /** * @brief set the led chanel brightness */ static int is31fl3736_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); int ret; enum is31fl3736_led_state state; dev_dbg(led_cdev->dev, "%s: %d, cs: %d, sw: %d\n", __func__, brightness, led_data->cs_ch, led_data->sw_ch); if (led_data->cur_mode != IS31FL3736_PWM_MODE) { dev_info(led_cdev->dev, "%s: current mode is not PWM mode\n", __func__); return -EPERM; } if (brightness < 0 || brightness > 0xff) { dev_err(led_cdev->dev, "%s: brightness: %d is invalid\n", __func__, brightness); return -EINVAL; } state = brightness ? IS31FL3736_LED_ON : IS31FL3736_LED_OFF; is31fl3736_set_led_state(led_data->priv, led_data->cs_ch, led_data->sw_ch, IS31FL3736_LED_ON); led_data->cur_state = state; ret = is31fl3736_set_pwm_duty(led_data->priv, led_data->cs_ch, led_data->sw_ch, brightness); return ret; } static ssize_t is31fl3736_led_show_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); ssize_t ret = 0; mutex_lock(&led_data->priv->mutex); switch (led_data->cur_mode) { case IS31FL3736_PWM_MODE: ret += sprintf(buf, "PWM\n"); break; case IS31FL3736_ABM1: ret += sprintf(buf, "ABM1\n"); break; case IS31FL3736_ABM2: ret += sprintf(buf, "ABM2\n"); break; case IS31FL3736_ABM3: ret += sprintf(buf, "ABM3\n"); break; default: ret += sprintf(buf, "NONE\n"); break; } mutex_unlock(&led_data->priv->mutex); return ret; } static ssize_t is31fl3736_led_store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); enum is31fl3736_auto_breath_mode mode; int ret; mutex_lock(&led_data->priv->mutex); if (!strncmp(buf, "PWM", 3)) mode = IS31FL3736_PWM_MODE; else if (!strncmp(buf, "ABM1", 4)) mode = IS31FL3736_ABM1; else if (!strncmp(buf, "ABM2", 4)) mode = IS31FL3736_ABM2; else if (!strncmp(buf, "ABM3", 4)) mode = IS31FL3736_ABM3; else mode = IS31FL3736_PWM_MODE; ret = is31fl3736_set_breath_mode(led_data->priv, led_data->cs_ch, led_data->sw_ch, mode); if (!ret) led_data->cur_mode = mode; mutex_unlock(&led_data->priv->mutex); return size; } static ssize_t is31fl3736_led_show_state(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); ssize_t ret = 0; mutex_lock(&led_data->priv->mutex); switch (led_data->cur_state) { case IS31FL3736_LED_OFF: ret += sprintf(buf, "OFF\n"); break; case IS31FL3736_LED_ON: ret += sprintf(buf, "ON\n"); break; default: ret += sprintf(buf, "NONE\n"); break; } mutex_unlock(&led_data->priv->mutex); return ret; } static ssize_t is31fl3736_led_store_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); enum is31fl3736_led_state state; int ret; mutex_lock(&led_data->priv->mutex); if (!strncmp(buf, "OFF", 3) || !strncmp(buf, "off", 3)) state = IS31FL3736_LED_OFF; else if (!strncmp(buf, "ON", 2) || !strncmp(buf, "on", 2)) state = IS31FL3736_LED_ON; else state = IS31FL3736_LED_OFF; ret = is31fl3736_set_led_state(led_data->priv, led_data->cs_ch, led_data->sw_ch, state); if (!ret) led_data->cur_state = state; mutex_unlock(&led_data->priv->mutex); return size; } static ssize_t is31fl3736_led_show_time(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); return snprintf(buf, PAGE_SIZE, "%d %d %d %d\n", led_data->rise_time, led_data->hold_time, led_data->fall_time, led_data->off_time); } static ssize_t is31fl3736_led_store_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); int ret, rise_time, hold_time, fall_time, off_time; ret = sscanf(buf, "%d %d %d %d", &rise_time, &hold_time, &fall_time, &off_time); pr_debug("%s, rise: %d, hold: %d, fall: %d, off: %d\n", __func__, rise_time, hold_time, fall_time, off_time); if (led_data->cur_mode == IS31FL3736_PWM_MODE) { dev_info(led_cdev->dev, "%s: current mode is PWM mode\n", __func__); return -EPERM; } if ((rise_time < 0 || rise_time >= IS31FL3736_t1_t3_time_MAX) || (hold_time < 0 || hold_time >= IS31FL3736_t2_time_MAX) || (fall_time < 0 || fall_time >= IS31FL3736_t1_t3_time_MAX) || (off_time < 0 || off_time >= IS31FL3736_t4_time_MAX)) { dev_info(led_cdev->dev, "%s: breath time paras invalid\n", __func__); return -EINVAL; } mutex_lock(&led_data->priv->mutex); ret = is31fl3736_set_breath_time(led_data->priv, rise_time, hold_time, fall_time, off_time, led_data->cur_mode); if (!ret) { led_data->rise_time = rise_time;/*t1, fade in*/ led_data->hold_time = hold_time;/*t2, on*/ led_data->fall_time = fall_time;/*t3, fade out*/ led_data->off_time = off_time; /*t4, off*/ } mutex_unlock(&led_data->priv->mutex); return size; } static ssize_t is31fl3736_led_show_loop(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); return sprintf(buf, "%d\n", led_data->loop); } static ssize_t is31fl3736_led_store_loop(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); unsigned long loop; int ret; if (kstrtoul(buf, 10, &loop)) return -EINVAL; if (loop < 0) loop = 0; pr_debug("%s, set breath loop time: %ld", __func__, loop); mutex_lock(&led_data->priv->mutex); ret = is31fl3736_set_breath_loop(led_data->priv, (u32)loop, led_data->cur_mode); if (!ret) led_data->loop = (u32)loop; mutex_unlock(&led_data->priv->mutex); return size; } static ssize_t is31fl3736_led_show_blink(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t is31fl3736_led_store_blink(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct is31fl3736_led_data *led_data = container_of(led_cdev, struct is31fl3736_led_data, cdev); unsigned long blink; int ret; ret = kstrtoul(buf, 10, &blink); if (ret) return ret; pr_debug("%s, blink time: %ld", __func__, blink); mutex_lock(&led_data->priv->mutex); is31fl3736_set_breath_blink(led_data->priv, (int)blink); mutex_unlock(&led_data->priv->mutex); return size; } static IS31Fl3736_ATTR_RW(mode); static IS31Fl3736_ATTR_RW(state); static IS31Fl3736_ATTR_RW(time); static IS31Fl3736_ATTR_RW(loop); static IS31Fl3736_ATTR_RW(blink); static struct attribute *is31fl3736_led_attributes[] = { &dev_attr_mode.attr, &dev_attr_state.attr, &dev_attr_time.attr, &dev_attr_loop.attr, &dev_attr_blink.attr, NULL, }; static struct attribute_group is31fl3736_led_attribute_group = { .attrs = is31fl3736_led_attributes }; static const struct attribute_group *is31fl3736_led_attribute_groups[] = { &is31fl3736_led_attribute_group, NULL }; static inline size_t sizeof_is31fl3736_priv(int num_leds) { return sizeof(struct is31fl3736_priv) + (sizeof(struct is31fl3736_led_data) * num_leds); } static int is31fl3736_parse_child_dt(const struct device *dev, const struct device_node *child, struct is31fl3736_led_data *led_data) { struct led_classdev *cdev = &led_data->cdev; int ret = 0; u32 reg; u32 sw, cs; if (of_property_read_string(child, "label", &cdev->name)) cdev->name = child->name; ret = of_property_read_u32(child, "reg", ®); if (ret || reg < 1) { dev_err(dev, "Child node %s does not have a valid reg property\n", child->full_name); return -EINVAL; } led_data->channel = reg; pr_debug("%s, line:%d, led channel: %d, name: %s\n", __func__, __LINE__, reg, child->name); ret = of_property_read_u32(child, "sw_location", &sw); if (ret || sw < 1 || sw > led_data->priv->sw_max) { dev_err(dev, "Child node %s does not have a valid sw location property\n", child->full_name); return -EINVAL; } led_data->sw_ch = sw; ret = of_property_read_u32(child, "cs_location", &cs); if (ret || cs < 1 || cs > led_data->priv->cs_max) { dev_err(dev, "Child node %s does not have a valid cs location property\n", child->full_name); return -EINVAL; } led_data->cs_ch = cs; of_property_read_string(child, "linux,default-trigger", &cdev->default_trigger); cdev->brightness_set_blocking = is31fl3736_brightness_set; cdev->brightness = LED_OFF; cdev->groups = is31fl3736_led_attribute_groups; return 0; } static struct is31fl3736_led_data *is31fl3736_find_led_data( struct is31fl3736_priv *priv, u8 channel) { size_t i; for (i = 0; i < priv->num_leds; i++) { if (priv->leds[i].channel == channel) return &priv->leds[i]; } return NULL; } static int is31fl3736_parse_dt(struct device *dev, struct is31fl3736_priv *priv) { struct device_node *child; struct gpio_config config; int ret = 0; /*gpio power enable*/ priv->power_gpio = of_get_named_gpio_flags(dev->of_node, "gpio-power", 0, (enum of_gpio_flags *)&config); pr_debug("%s, line:%d, power_gpio: %d!\n", __func__, __LINE__, priv->power_gpio); /*gpio shdn enable*/ priv->shdn_gpio = of_get_named_gpio_flags(dev->of_node, "gpio-shdn", 0, (enum of_gpio_flags *)&config); pr_debug("%s, line:%d, shdn_gpio: %d!\n", __func__, __LINE__, priv->shdn_gpio); for_each_child_of_node(dev->of_node, child) { struct is31fl3736_led_data *led_data = &priv->leds[priv->num_leds]; const struct is31fl3736_led_data *other_led_data; led_data->priv = priv; ret = is31fl3736_parse_child_dt(dev, child, led_data); if (ret) goto err; /* Detect if channel is already in use by another child */ other_led_data = is31fl3736_find_led_data(priv, led_data->channel); if (other_led_data) { dev_err(dev, "%s and %s both attempting to use channel %d\n", led_data->cdev.name, other_led_data->cdev.name, led_data->channel); goto err; } ret = devm_led_classdev_register(dev, &led_data->cdev); if (ret) { dev_err(dev, "failed to register PWM led for %s: %d\n", led_data->cdev.name, ret); goto err; } priv->num_leds++; } return 0; err: of_node_put(child); return ret; } static const struct of_device_id of_is31fl3736_match[] = { { .compatible = "issi,is31fl3736", }, {}, }; MODULE_DEVICE_TABLE(of, of_is31fl3736_match); static int is31fl3736_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct of_device_id *of_dev_id; struct device *dev = &client->dev; struct is31fl3736_priv *priv; int count; int ret = 0; pr_debug("%s, line:%d, i2c device id: %s!\n", __func__, __LINE__, id->name); of_dev_id = of_match_device(of_is31fl3736_match, dev); if (!of_dev_id) return -EINVAL; count = of_get_child_count(dev->of_node); if (!count) return -EINVAL; pr_debug("%s, line:%d, led count: %d!\n", __func__, __LINE__, count); priv = devm_kzalloc(dev, sizeof_is31fl3736_priv(count), GFP_KERNEL); if (!priv) return -ENOMEM; priv->client = client; priv->cs_max = IS31FL3736_CSX_MAX; priv->sw_max = IS31FL3736_SWY_MAX; i2c_set_clientdata(client, priv); ret = is31fl3736_parse_dt(dev, priv); if (ret) return ret; if (gpio_is_valid(priv->shdn_gpio)) { ret = gpio_request(priv->shdn_gpio, "shdn gpio"); if (!ret) { gpio_direction_output(priv->shdn_gpio, 1); gpio_set_value(priv->shdn_gpio, 1); pr_info("%s, line:%d, enable shdn gpio: %d!\n", __func__, __LINE__, priv->shdn_gpio); msleep(20); } else { pr_err("%s, line:%d, failed request shdn gpio: %d!\n", __func__, __LINE__, priv->shdn_gpio); } } if (gpio_is_valid(priv->power_gpio)) { ret = gpio_request(priv->power_gpio, "power gpio"); if (!ret) { gpio_direction_output(priv->power_gpio, 1); gpio_set_value(priv->power_gpio, 1); pr_info("%s, line:%d, power gpio: %d!\n", __func__, __LINE__, priv->power_gpio); msleep(20); } else { pr_err("%s, line:%d, failed request power gpio: %d!\n", __func__, __LINE__, priv->power_gpio); } } mutex_init(&priv->mutex); is31fl3736_reset_regs(priv); is31fl3736_init_regs(priv); pr_info("%s, line:%d, is31fl3736_probe succeed!\n", __func__, __LINE__); return 0; } static int is31fl3736_remove(struct i2c_client *client) { struct is31fl3736_priv *priv = i2c_get_clientdata(client); return is31fl3736_reset_regs(priv); } /* * i2c-core (and modalias) requires that id_table be properly filled, * even though it is not used for DeviceTree based instantiation. */ static const struct i2c_device_id is31fl3736_id[] = { { "is31fl3736" }, {}, }; MODULE_DEVICE_TABLE(i2c, is31fl3736_id); static struct i2c_driver is31fl3736_driver = { .driver = { .name = "is31fl3736", .of_match_table = of_is31fl3736_match, }, .probe = is31fl3736_probe, .remove = is31fl3736_remove, .id_table = is31fl3736_id, }; module_i2c_driver(is31fl3736_driver); MODULE_AUTHOR("xudong"); MODULE_DESCRIPTION("ISSI IS31FL3736 LED driver"); MODULE_LICENSE("GPL v2");