136a87f37SMartin Kurbanov // SPDX-License-Identifier: GPL-2.0 236a87f37SMartin Kurbanov /* 3634fea79SGeorge Stark * Awinic AW20036/AW20054/AW20072/AW20108 LED driver 436a87f37SMartin Kurbanov * 536a87f37SMartin Kurbanov * Copyright (c) 2023, SberDevices. All Rights Reserved. 636a87f37SMartin Kurbanov * 736a87f37SMartin Kurbanov * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru> 836a87f37SMartin Kurbanov */ 936a87f37SMartin Kurbanov 1036a87f37SMartin Kurbanov #include <linux/bitfield.h> 1136a87f37SMartin Kurbanov #include <linux/bits.h> 1236a87f37SMartin Kurbanov #include <linux/container_of.h> 13d882762fSDmitry Rokosov #include <linux/gpio/consumer.h> 1436a87f37SMartin Kurbanov #include <linux/i2c.h> 1536a87f37SMartin Kurbanov #include <linux/leds.h> 1636a87f37SMartin Kurbanov #include <linux/mod_devicetable.h> 1736a87f37SMartin Kurbanov #include <linux/module.h> 1836a87f37SMartin Kurbanov #include <linux/mutex.h> 1936a87f37SMartin Kurbanov #include <linux/regmap.h> 2036a87f37SMartin Kurbanov #include <linux/time.h> 2136a87f37SMartin Kurbanov #include <linux/units.h> 2236a87f37SMartin Kurbanov 2336a87f37SMartin Kurbanov #define AW200XX_DIM_MAX (BIT(6) - 1) 2436a87f37SMartin Kurbanov #define AW200XX_FADE_MAX (BIT(8) - 1) 2536a87f37SMartin Kurbanov #define AW200XX_IMAX_DEFAULT_uA 60000 2636a87f37SMartin Kurbanov #define AW200XX_IMAX_MAX_uA 160000 2736a87f37SMartin Kurbanov #define AW200XX_IMAX_MIN_uA 3300 2836a87f37SMartin Kurbanov 2936a87f37SMartin Kurbanov /* Page 0 */ 3036a87f37SMartin Kurbanov #define AW200XX_REG_PAGE0_BASE 0xc000 3136a87f37SMartin Kurbanov 3236a87f37SMartin Kurbanov /* Select page register */ 3336a87f37SMartin Kurbanov #define AW200XX_REG_PAGE 0xF0 3436a87f37SMartin Kurbanov #define AW200XX_PAGE_MASK (GENMASK(7, 6) | GENMASK(2, 0)) 3536a87f37SMartin Kurbanov #define AW200XX_PAGE_SHIFT 0 3636a87f37SMartin Kurbanov #define AW200XX_NUM_PAGES 6 3736a87f37SMartin Kurbanov #define AW200XX_PAGE_SIZE 256 3836a87f37SMartin Kurbanov #define AW200XX_REG(page, reg) \ 3936a87f37SMartin Kurbanov (AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg)) 4036a87f37SMartin Kurbanov #define AW200XX_REG_MAX \ 4136a87f37SMartin Kurbanov AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1) 4236a87f37SMartin Kurbanov #define AW200XX_PAGE0 0 4336a87f37SMartin Kurbanov #define AW200XX_PAGE1 1 4436a87f37SMartin Kurbanov #define AW200XX_PAGE2 2 4536a87f37SMartin Kurbanov #define AW200XX_PAGE3 3 4636a87f37SMartin Kurbanov #define AW200XX_PAGE4 4 4736a87f37SMartin Kurbanov #define AW200XX_PAGE5 5 4836a87f37SMartin Kurbanov 4936a87f37SMartin Kurbanov /* Chip ID register */ 5036a87f37SMartin Kurbanov #define AW200XX_REG_IDR AW200XX_REG(AW200XX_PAGE0, 0x00) 5136a87f37SMartin Kurbanov #define AW200XX_IDR_CHIPID 0x18 5236a87f37SMartin Kurbanov 5336a87f37SMartin Kurbanov /* Sleep mode register */ 5436a87f37SMartin Kurbanov #define AW200XX_REG_SLPCR AW200XX_REG(AW200XX_PAGE0, 0x01) 5536a87f37SMartin Kurbanov #define AW200XX_SLPCR_ACTIVE 0x00 5636a87f37SMartin Kurbanov 5736a87f37SMartin Kurbanov /* Reset register */ 5836a87f37SMartin Kurbanov #define AW200XX_REG_RSTR AW200XX_REG(AW200XX_PAGE0, 0x02) 5936a87f37SMartin Kurbanov #define AW200XX_RSTR_RESET 0x01 6036a87f37SMartin Kurbanov 6136a87f37SMartin Kurbanov /* Global current configuration register */ 6236a87f37SMartin Kurbanov #define AW200XX_REG_GCCR AW200XX_REG(AW200XX_PAGE0, 0x03) 6336a87f37SMartin Kurbanov #define AW200XX_GCCR_IMAX_MASK GENMASK(7, 4) 6436a87f37SMartin Kurbanov #define AW200XX_GCCR_IMAX(x) ((x) << 4) 6536a87f37SMartin Kurbanov #define AW200XX_GCCR_ALLON BIT(3) 6636a87f37SMartin Kurbanov 6736a87f37SMartin Kurbanov /* Fast clear display control register */ 6836a87f37SMartin Kurbanov #define AW200XX_REG_FCD AW200XX_REG(AW200XX_PAGE0, 0x04) 6936a87f37SMartin Kurbanov #define AW200XX_FCD_CLEAR 0x01 7036a87f37SMartin Kurbanov 7136a87f37SMartin Kurbanov /* Display size configuration */ 7236a87f37SMartin Kurbanov #define AW200XX_REG_DSIZE AW200XX_REG(AW200XX_PAGE0, 0x80) 7336a87f37SMartin Kurbanov #define AW200XX_DSIZE_COLUMNS_MAX 12 7436a87f37SMartin Kurbanov 7536a87f37SMartin Kurbanov #define AW200XX_LED2REG(x, columns) \ 7636a87f37SMartin Kurbanov ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns)))) 7736a87f37SMartin Kurbanov 78adfd4621SMartin Kurbanov /* DIM current configuration register on page 1 */ 79adfd4621SMartin Kurbanov #define AW200XX_REG_DIM_PAGE1(x, columns) \ 80adfd4621SMartin Kurbanov AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns)) 81adfd4621SMartin Kurbanov 8236a87f37SMartin Kurbanov /* 8336a87f37SMartin Kurbanov * DIM current configuration register (page 4). 8436a87f37SMartin Kurbanov * The even address for current DIM configuration. 8536a87f37SMartin Kurbanov * The odd address for current FADE configuration 8636a87f37SMartin Kurbanov */ 8736a87f37SMartin Kurbanov #define AW200XX_REG_DIM(x, columns) \ 8836a87f37SMartin Kurbanov AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2) 8936a87f37SMartin Kurbanov #define AW200XX_REG_DIM2FADE(x) ((x) + 1) 90150bca53SGeorge Stark #define AW200XX_REG_FADE2DIM(fade) \ 91150bca53SGeorge Stark DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX) 9236a87f37SMartin Kurbanov 9336a87f37SMartin Kurbanov /* 9436a87f37SMartin Kurbanov * Duty ratio of display scan (see p.15 of datasheet for formula): 9536a87f37SMartin Kurbanov * duty = (592us / 600.5us) * (1 / (display_rows + 1)) 9636a87f37SMartin Kurbanov * 9736a87f37SMartin Kurbanov * Multiply to 1000 (MILLI) to improve the accuracy of calculations. 9836a87f37SMartin Kurbanov */ 9936a87f37SMartin Kurbanov #define AW200XX_DUTY_RATIO(rows) \ 10036a87f37SMartin Kurbanov (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI) 10136a87f37SMartin Kurbanov 10236a87f37SMartin Kurbanov struct aw200xx_chipdef { 10336a87f37SMartin Kurbanov u32 channels; 10436a87f37SMartin Kurbanov u32 display_size_rows_max; 10536a87f37SMartin Kurbanov u32 display_size_columns; 10636a87f37SMartin Kurbanov }; 10736a87f37SMartin Kurbanov 10836a87f37SMartin Kurbanov struct aw200xx_led { 10936a87f37SMartin Kurbanov struct led_classdev cdev; 11036a87f37SMartin Kurbanov struct aw200xx *chip; 11136a87f37SMartin Kurbanov int dim; 11236a87f37SMartin Kurbanov u32 num; 11336a87f37SMartin Kurbanov }; 11436a87f37SMartin Kurbanov 11536a87f37SMartin Kurbanov struct aw200xx { 11636a87f37SMartin Kurbanov const struct aw200xx_chipdef *cdef; 11736a87f37SMartin Kurbanov struct i2c_client *client; 11836a87f37SMartin Kurbanov struct regmap *regmap; 11936a87f37SMartin Kurbanov struct mutex mutex; 12036a87f37SMartin Kurbanov u32 num_leds; 12136a87f37SMartin Kurbanov u32 display_rows; 122d882762fSDmitry Rokosov struct gpio_desc *hwen; 123ff861ca9SKees Cook struct aw200xx_led leds[] __counted_by(num_leds); 12436a87f37SMartin Kurbanov }; 12536a87f37SMartin Kurbanov 12636a87f37SMartin Kurbanov static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, 12736a87f37SMartin Kurbanov char *buf) 12836a87f37SMartin Kurbanov { 12936a87f37SMartin Kurbanov struct led_classdev *cdev = dev_get_drvdata(dev); 13036a87f37SMartin Kurbanov struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); 13136a87f37SMartin Kurbanov int dim = led->dim; 13236a87f37SMartin Kurbanov 13336a87f37SMartin Kurbanov if (dim < 0) 13436a87f37SMartin Kurbanov return sysfs_emit(buf, "auto\n"); 13536a87f37SMartin Kurbanov 13636a87f37SMartin Kurbanov return sysfs_emit(buf, "%d\n", dim); 13736a87f37SMartin Kurbanov } 13836a87f37SMartin Kurbanov 13936a87f37SMartin Kurbanov static ssize_t dim_store(struct device *dev, struct device_attribute *devattr, 14036a87f37SMartin Kurbanov const char *buf, size_t count) 14136a87f37SMartin Kurbanov { 14236a87f37SMartin Kurbanov struct led_classdev *cdev = dev_get_drvdata(dev); 14336a87f37SMartin Kurbanov struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); 14436a87f37SMartin Kurbanov struct aw200xx *chip = led->chip; 14536a87f37SMartin Kurbanov u32 columns = chip->cdef->display_size_columns; 14636a87f37SMartin Kurbanov int dim; 14736a87f37SMartin Kurbanov ssize_t ret; 14836a87f37SMartin Kurbanov 14936a87f37SMartin Kurbanov if (sysfs_streq(buf, "auto")) { 15036a87f37SMartin Kurbanov dim = -1; 15136a87f37SMartin Kurbanov } else { 15236a87f37SMartin Kurbanov ret = kstrtoint(buf, 0, &dim); 15336a87f37SMartin Kurbanov if (ret) 15436a87f37SMartin Kurbanov return ret; 15536a87f37SMartin Kurbanov 15636a87f37SMartin Kurbanov if (dim > AW200XX_DIM_MAX) 15736a87f37SMartin Kurbanov return -EINVAL; 15836a87f37SMartin Kurbanov } 15936a87f37SMartin Kurbanov 16036a87f37SMartin Kurbanov mutex_lock(&chip->mutex); 16136a87f37SMartin Kurbanov 16236a87f37SMartin Kurbanov if (dim >= 0) { 16336a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, 164adfd4621SMartin Kurbanov AW200XX_REG_DIM_PAGE1(led->num, columns), 165adfd4621SMartin Kurbanov dim); 16636a87f37SMartin Kurbanov if (ret) 16736a87f37SMartin Kurbanov goto out_unlock; 16836a87f37SMartin Kurbanov } 16936a87f37SMartin Kurbanov 17036a87f37SMartin Kurbanov led->dim = dim; 17136a87f37SMartin Kurbanov ret = count; 17236a87f37SMartin Kurbanov 17336a87f37SMartin Kurbanov out_unlock: 17436a87f37SMartin Kurbanov mutex_unlock(&chip->mutex); 17536a87f37SMartin Kurbanov return ret; 17636a87f37SMartin Kurbanov } 17736a87f37SMartin Kurbanov static DEVICE_ATTR_RW(dim); 17836a87f37SMartin Kurbanov 17936a87f37SMartin Kurbanov static struct attribute *dim_attrs[] = { 18036a87f37SMartin Kurbanov &dev_attr_dim.attr, 18136a87f37SMartin Kurbanov NULL 18236a87f37SMartin Kurbanov }; 18336a87f37SMartin Kurbanov ATTRIBUTE_GROUPS(dim); 18436a87f37SMartin Kurbanov 18536a87f37SMartin Kurbanov static int aw200xx_brightness_set(struct led_classdev *cdev, 18636a87f37SMartin Kurbanov enum led_brightness brightness) 18736a87f37SMartin Kurbanov { 18836a87f37SMartin Kurbanov struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); 18936a87f37SMartin Kurbanov struct aw200xx *chip = led->chip; 19036a87f37SMartin Kurbanov int dim; 19136a87f37SMartin Kurbanov u32 reg; 19236a87f37SMartin Kurbanov int ret; 19336a87f37SMartin Kurbanov 19436a87f37SMartin Kurbanov mutex_lock(&chip->mutex); 19536a87f37SMartin Kurbanov 19636a87f37SMartin Kurbanov reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns); 19736a87f37SMartin Kurbanov 19836a87f37SMartin Kurbanov dim = led->dim; 19936a87f37SMartin Kurbanov if (dim < 0) 200150bca53SGeorge Stark dim = AW200XX_REG_FADE2DIM(brightness); 20136a87f37SMartin Kurbanov 20236a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, reg, dim); 20336a87f37SMartin Kurbanov if (ret) 20436a87f37SMartin Kurbanov goto out_unlock; 20536a87f37SMartin Kurbanov 20636a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, 20736a87f37SMartin Kurbanov AW200XX_REG_DIM2FADE(reg), brightness); 20836a87f37SMartin Kurbanov 20936a87f37SMartin Kurbanov out_unlock: 21036a87f37SMartin Kurbanov mutex_unlock(&chip->mutex); 21136a87f37SMartin Kurbanov 21236a87f37SMartin Kurbanov return ret; 21336a87f37SMartin Kurbanov } 21436a87f37SMartin Kurbanov 21536a87f37SMartin Kurbanov static u32 aw200xx_imax_from_global(const struct aw200xx *const chip, 21636a87f37SMartin Kurbanov u32 global_imax_uA) 21736a87f37SMartin Kurbanov { 21836a87f37SMartin Kurbanov u64 led_imax_uA; 21936a87f37SMartin Kurbanov 22036a87f37SMartin Kurbanov /* 22136a87f37SMartin Kurbanov * The output current of each LED (see p.14 of datasheet for formula): 22236a87f37SMartin Kurbanov * Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty 22336a87f37SMartin Kurbanov * 22436a87f37SMartin Kurbanov * The value of duty is determined by the following formula: 22536a87f37SMartin Kurbanov * duty = (592us / 600.5us) * (1 / (display_rows + 1)) 22636a87f37SMartin Kurbanov * 22736a87f37SMartin Kurbanov * Calculated for the maximum values of fade and dim. 22836a87f37SMartin Kurbanov * We divide by 1000 because we earlier multiplied by 1000 to improve 22936a87f37SMartin Kurbanov * accuracy when calculating the duty. 23036a87f37SMartin Kurbanov */ 23136a87f37SMartin Kurbanov led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows); 23236a87f37SMartin Kurbanov do_div(led_imax_uA, MILLI); 23336a87f37SMartin Kurbanov 23436a87f37SMartin Kurbanov return led_imax_uA; 23536a87f37SMartin Kurbanov } 23636a87f37SMartin Kurbanov 23736a87f37SMartin Kurbanov static u32 aw200xx_imax_to_global(const struct aw200xx *const chip, 23836a87f37SMartin Kurbanov u32 led_imax_uA) 23936a87f37SMartin Kurbanov { 24036a87f37SMartin Kurbanov u32 duty = AW200XX_DUTY_RATIO(chip->display_rows); 24136a87f37SMartin Kurbanov 24236a87f37SMartin Kurbanov /* The output current of each LED (see p.14 of datasheet for formula) */ 24336a87f37SMartin Kurbanov return (led_imax_uA * 1000U) / duty; 24436a87f37SMartin Kurbanov } 24536a87f37SMartin Kurbanov 24636a87f37SMartin Kurbanov #define AW200XX_IMAX_MULTIPLIER1 10000 24736a87f37SMartin Kurbanov #define AW200XX_IMAX_MULTIPLIER2 3333 24836a87f37SMartin Kurbanov #define AW200XX_IMAX_BASE_VAL1 0 24936a87f37SMartin Kurbanov #define AW200XX_IMAX_BASE_VAL2 8 25036a87f37SMartin Kurbanov 25136a87f37SMartin Kurbanov /* 25236a87f37SMartin Kurbanov * The AW200XX has a 4-bit register (GCCR) to configure the global current, 25336a87f37SMartin Kurbanov * which ranges from 3.3mA to 160mA. The following table indicates the values 25436a87f37SMartin Kurbanov * of the global current, divided into two parts: 25536a87f37SMartin Kurbanov * 25636a87f37SMartin Kurbanov * +-----------+-----------------+-----------+-----------------+ 25736a87f37SMartin Kurbanov * | reg value | global max (mA) | reg value | global max (mA) | 25836a87f37SMartin Kurbanov * +-----------+-----------------+-----------+-----------------+ 25936a87f37SMartin Kurbanov * | 0 | 10 | 8 | 3.3 | 26036a87f37SMartin Kurbanov * | 1 | 20 | 9 | 6.7 | 26136a87f37SMartin Kurbanov * | 2 | 30 | 10 | 10 | 26236a87f37SMartin Kurbanov * | 3 | 40 | 11 | 13.3 | 26336a87f37SMartin Kurbanov * | 4 | 60 | 12 | 20 | 26436a87f37SMartin Kurbanov * | 5 | 80 | 13 | 26.7 | 26536a87f37SMartin Kurbanov * | 6 | 120 | 14 | 40 | 26636a87f37SMartin Kurbanov * | 7 | 160 | 15 | 53.3 | 26736a87f37SMartin Kurbanov * +-----------+-----------------+-----------+-----------------+ 26836a87f37SMartin Kurbanov * 26936a87f37SMartin Kurbanov * The left part with a multiplier of 10, and the right part with a multiplier 27036a87f37SMartin Kurbanov * of 3.3. 27136a87f37SMartin Kurbanov * So we have two formulas to calculate the global current: 27236a87f37SMartin Kurbanov * for the left part of the table: 27336a87f37SMartin Kurbanov * imax = coefficient * 10 27436a87f37SMartin Kurbanov * 27536a87f37SMartin Kurbanov * for the right part of the table: 27636a87f37SMartin Kurbanov * imax = coefficient * 3.3 27736a87f37SMartin Kurbanov * 27836a87f37SMartin Kurbanov * The coefficient table consists of the following values: 27936a87f37SMartin Kurbanov * 1, 2, 3, 4, 6, 8, 12, 16. 28036a87f37SMartin Kurbanov */ 28136a87f37SMartin Kurbanov static int aw200xx_set_imax(const struct aw200xx *const chip, 28236a87f37SMartin Kurbanov u32 led_imax_uA) 28336a87f37SMartin Kurbanov { 28436a87f37SMartin Kurbanov u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA); 285*d0532248SColin Ian King static const u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16}; 28636a87f37SMartin Kurbanov u32 gccr_imax = UINT_MAX; 28736a87f37SMartin Kurbanov u32 cur_imax = 0; 28836a87f37SMartin Kurbanov int i; 28936a87f37SMartin Kurbanov 29036a87f37SMartin Kurbanov for (i = 0; i < ARRAY_SIZE(coeff_table); i++) { 29136a87f37SMartin Kurbanov u32 imax; 29236a87f37SMartin Kurbanov 29336a87f37SMartin Kurbanov /* select closest ones */ 29436a87f37SMartin Kurbanov imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1; 29536a87f37SMartin Kurbanov if (g_imax_uA >= imax && imax > cur_imax) { 29636a87f37SMartin Kurbanov cur_imax = imax; 29736a87f37SMartin Kurbanov gccr_imax = i + AW200XX_IMAX_BASE_VAL1; 29836a87f37SMartin Kurbanov } 29936a87f37SMartin Kurbanov 30036a87f37SMartin Kurbanov imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2; 30136a87f37SMartin Kurbanov imax = DIV_ROUND_CLOSEST(imax, 100) * 100; 30236a87f37SMartin Kurbanov if (g_imax_uA >= imax && imax > cur_imax) { 30336a87f37SMartin Kurbanov cur_imax = imax; 30436a87f37SMartin Kurbanov gccr_imax = i + AW200XX_IMAX_BASE_VAL2; 30536a87f37SMartin Kurbanov } 30636a87f37SMartin Kurbanov } 30736a87f37SMartin Kurbanov 30836a87f37SMartin Kurbanov if (gccr_imax == UINT_MAX) 30936a87f37SMartin Kurbanov return -EINVAL; 31036a87f37SMartin Kurbanov 31136a87f37SMartin Kurbanov return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, 31236a87f37SMartin Kurbanov AW200XX_GCCR_IMAX_MASK, 31336a87f37SMartin Kurbanov AW200XX_GCCR_IMAX(gccr_imax)); 31436a87f37SMartin Kurbanov } 31536a87f37SMartin Kurbanov 31636a87f37SMartin Kurbanov static int aw200xx_chip_reset(const struct aw200xx *const chip) 31736a87f37SMartin Kurbanov { 31836a87f37SMartin Kurbanov int ret; 31936a87f37SMartin Kurbanov 32036a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET); 32136a87f37SMartin Kurbanov if (ret) 32236a87f37SMartin Kurbanov return ret; 32336a87f37SMartin Kurbanov 324d883a5abSGeorge Stark /* According to the datasheet software reset takes at least 1ms */ 325d883a5abSGeorge Stark fsleep(1000); 326d883a5abSGeorge Stark 32736a87f37SMartin Kurbanov regcache_mark_dirty(chip->regmap); 32836a87f37SMartin Kurbanov return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); 32936a87f37SMartin Kurbanov } 33036a87f37SMartin Kurbanov 33136a87f37SMartin Kurbanov static int aw200xx_chip_init(const struct aw200xx *const chip) 33236a87f37SMartin Kurbanov { 33336a87f37SMartin Kurbanov int ret; 33436a87f37SMartin Kurbanov 33536a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE, 33636a87f37SMartin Kurbanov chip->display_rows - 1); 33736a87f37SMartin Kurbanov if (ret) 33836a87f37SMartin Kurbanov return ret; 33936a87f37SMartin Kurbanov 34036a87f37SMartin Kurbanov ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR, 34136a87f37SMartin Kurbanov AW200XX_SLPCR_ACTIVE); 34236a87f37SMartin Kurbanov if (ret) 34336a87f37SMartin Kurbanov return ret; 34436a87f37SMartin Kurbanov 34536a87f37SMartin Kurbanov return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, 34636a87f37SMartin Kurbanov AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON); 34736a87f37SMartin Kurbanov } 34836a87f37SMartin Kurbanov 34936a87f37SMartin Kurbanov static int aw200xx_chip_check(const struct aw200xx *const chip) 35036a87f37SMartin Kurbanov { 35136a87f37SMartin Kurbanov struct device *dev = &chip->client->dev; 35236a87f37SMartin Kurbanov u32 chipid; 35336a87f37SMartin Kurbanov int ret; 35436a87f37SMartin Kurbanov 35536a87f37SMartin Kurbanov ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid); 35636a87f37SMartin Kurbanov if (ret) 35736a87f37SMartin Kurbanov return dev_err_probe(dev, ret, "Failed to read chip ID\n"); 35836a87f37SMartin Kurbanov 35936a87f37SMartin Kurbanov if (chipid != AW200XX_IDR_CHIPID) 36036a87f37SMartin Kurbanov return dev_err_probe(dev, -ENODEV, 36136a87f37SMartin Kurbanov "Chip reported wrong ID: %x\n", chipid); 36236a87f37SMartin Kurbanov 36336a87f37SMartin Kurbanov return 0; 36436a87f37SMartin Kurbanov } 36536a87f37SMartin Kurbanov 366d882762fSDmitry Rokosov static void aw200xx_enable(const struct aw200xx *const chip) 367d882762fSDmitry Rokosov { 368d882762fSDmitry Rokosov gpiod_set_value_cansleep(chip->hwen, 1); 369d882762fSDmitry Rokosov 370d882762fSDmitry Rokosov /* 371d882762fSDmitry Rokosov * After HWEN pin set high the chip begins to load the OTP information, 372d882762fSDmitry Rokosov * which takes 200us to complete. About 200us wait time is needed for 373d882762fSDmitry Rokosov * internal oscillator startup and display SRAM initialization. After 374d882762fSDmitry Rokosov * display SRAM initialization, the registers in page1 to page5 can be 375d882762fSDmitry Rokosov * configured via i2c interface. 376d882762fSDmitry Rokosov */ 377d882762fSDmitry Rokosov fsleep(400); 378d882762fSDmitry Rokosov } 379d882762fSDmitry Rokosov 380d882762fSDmitry Rokosov static void aw200xx_disable(const struct aw200xx *const chip) 381d882762fSDmitry Rokosov { 382d882762fSDmitry Rokosov return gpiod_set_value_cansleep(chip->hwen, 0); 383d882762fSDmitry Rokosov } 384d882762fSDmitry Rokosov 3852b8db572SGeorge Stark static int aw200xx_probe_get_display_rows(struct device *dev, 3862b8db572SGeorge Stark struct aw200xx *chip) 3872b8db572SGeorge Stark { 3882b8db572SGeorge Stark struct fwnode_handle *child; 3892b8db572SGeorge Stark u32 max_source = 0; 3902b8db572SGeorge Stark 3912b8db572SGeorge Stark device_for_each_child_node(dev, child) { 3922b8db572SGeorge Stark u32 source; 3932b8db572SGeorge Stark int ret; 3942b8db572SGeorge Stark 3952b8db572SGeorge Stark ret = fwnode_property_read_u32(child, "reg", &source); 3962b8db572SGeorge Stark if (ret || source >= chip->cdef->channels) 3972b8db572SGeorge Stark continue; 3982b8db572SGeorge Stark 3992b8db572SGeorge Stark max_source = max(max_source, source); 4002b8db572SGeorge Stark } 4012b8db572SGeorge Stark 4022b8db572SGeorge Stark if (max_source == 0) 4032b8db572SGeorge Stark return -EINVAL; 4042b8db572SGeorge Stark 4052b8db572SGeorge Stark chip->display_rows = max_source / chip->cdef->display_size_columns + 1; 4062b8db572SGeorge Stark 4072b8db572SGeorge Stark return 0; 4082b8db572SGeorge Stark } 4092b8db572SGeorge Stark 41036a87f37SMartin Kurbanov static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) 41136a87f37SMartin Kurbanov { 41236a87f37SMartin Kurbanov struct fwnode_handle *child; 41336a87f37SMartin Kurbanov u32 current_min, current_max, min_uA; 41436a87f37SMartin Kurbanov int ret; 41536a87f37SMartin Kurbanov int i; 41636a87f37SMartin Kurbanov 4172b8db572SGeorge Stark ret = aw200xx_probe_get_display_rows(dev, chip); 41836a87f37SMartin Kurbanov if (ret) 41936a87f37SMartin Kurbanov return dev_err_probe(dev, ret, 4202b8db572SGeorge Stark "No valid led definitions found\n"); 42136a87f37SMartin Kurbanov 42236a87f37SMartin Kurbanov current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); 42336a87f37SMartin Kurbanov current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); 42436a87f37SMartin Kurbanov min_uA = UINT_MAX; 42536a87f37SMartin Kurbanov i = 0; 42636a87f37SMartin Kurbanov 42736a87f37SMartin Kurbanov device_for_each_child_node(dev, child) { 42836a87f37SMartin Kurbanov struct led_init_data init_data = {}; 42936a87f37SMartin Kurbanov struct aw200xx_led *led; 43036a87f37SMartin Kurbanov u32 source, imax; 43136a87f37SMartin Kurbanov 43236a87f37SMartin Kurbanov ret = fwnode_property_read_u32(child, "reg", &source); 43336a87f37SMartin Kurbanov if (ret) { 43436a87f37SMartin Kurbanov dev_err(dev, "Missing reg property\n"); 43536a87f37SMartin Kurbanov chip->num_leds--; 43636a87f37SMartin Kurbanov continue; 43736a87f37SMartin Kurbanov } 43836a87f37SMartin Kurbanov 43936a87f37SMartin Kurbanov if (source >= chip->cdef->channels) { 44036a87f37SMartin Kurbanov dev_err(dev, "LED reg %u out of range (max %u)\n", 44136a87f37SMartin Kurbanov source, chip->cdef->channels); 44236a87f37SMartin Kurbanov chip->num_leds--; 44336a87f37SMartin Kurbanov continue; 44436a87f37SMartin Kurbanov } 44536a87f37SMartin Kurbanov 44636a87f37SMartin Kurbanov ret = fwnode_property_read_u32(child, "led-max-microamp", 44736a87f37SMartin Kurbanov &imax); 44836a87f37SMartin Kurbanov if (ret) { 44936a87f37SMartin Kurbanov dev_info(&chip->client->dev, 45036a87f37SMartin Kurbanov "DT property led-max-microamp is missing\n"); 45136a87f37SMartin Kurbanov } else if (imax < current_min || imax > current_max) { 45236a87f37SMartin Kurbanov dev_err(dev, "Invalid value %u for led-max-microamp\n", 45336a87f37SMartin Kurbanov imax); 45436a87f37SMartin Kurbanov chip->num_leds--; 45536a87f37SMartin Kurbanov continue; 45636a87f37SMartin Kurbanov } else { 45736a87f37SMartin Kurbanov min_uA = min(min_uA, imax); 45836a87f37SMartin Kurbanov } 45936a87f37SMartin Kurbanov 46036a87f37SMartin Kurbanov led = &chip->leds[i]; 46136a87f37SMartin Kurbanov led->dim = -1; 46236a87f37SMartin Kurbanov led->num = source; 46336a87f37SMartin Kurbanov led->chip = chip; 46436a87f37SMartin Kurbanov led->cdev.brightness_set_blocking = aw200xx_brightness_set; 465150bca53SGeorge Stark led->cdev.max_brightness = AW200XX_FADE_MAX; 46636a87f37SMartin Kurbanov led->cdev.groups = dim_groups; 46736a87f37SMartin Kurbanov init_data.fwnode = child; 46836a87f37SMartin Kurbanov 46936a87f37SMartin Kurbanov ret = devm_led_classdev_register_ext(dev, &led->cdev, 47036a87f37SMartin Kurbanov &init_data); 47136a87f37SMartin Kurbanov if (ret) { 47236a87f37SMartin Kurbanov fwnode_handle_put(child); 47336a87f37SMartin Kurbanov break; 47436a87f37SMartin Kurbanov } 47536a87f37SMartin Kurbanov 47636a87f37SMartin Kurbanov i++; 47736a87f37SMartin Kurbanov } 47836a87f37SMartin Kurbanov 47936a87f37SMartin Kurbanov if (!chip->num_leds) 48036a87f37SMartin Kurbanov return -EINVAL; 48136a87f37SMartin Kurbanov 48236a87f37SMartin Kurbanov if (min_uA == UINT_MAX) { 48336a87f37SMartin Kurbanov min_uA = aw200xx_imax_from_global(chip, 48436a87f37SMartin Kurbanov AW200XX_IMAX_DEFAULT_uA); 48536a87f37SMartin Kurbanov } 48636a87f37SMartin Kurbanov 48736a87f37SMartin Kurbanov return aw200xx_set_imax(chip, min_uA); 48836a87f37SMartin Kurbanov } 48936a87f37SMartin Kurbanov 49036a87f37SMartin Kurbanov static const struct regmap_range_cfg aw200xx_ranges[] = { 49136a87f37SMartin Kurbanov { 49236a87f37SMartin Kurbanov .name = "aw200xx", 49336a87f37SMartin Kurbanov .range_min = 0, 49436a87f37SMartin Kurbanov .range_max = AW200XX_REG_MAX, 49536a87f37SMartin Kurbanov .selector_reg = AW200XX_REG_PAGE, 49636a87f37SMartin Kurbanov .selector_mask = AW200XX_PAGE_MASK, 49736a87f37SMartin Kurbanov .selector_shift = AW200XX_PAGE_SHIFT, 49836a87f37SMartin Kurbanov .window_start = 0, 49936a87f37SMartin Kurbanov .window_len = AW200XX_PAGE_SIZE, 50036a87f37SMartin Kurbanov }, 50136a87f37SMartin Kurbanov }; 50236a87f37SMartin Kurbanov 50336a87f37SMartin Kurbanov static const struct regmap_range aw200xx_writeonly_ranges[] = { 50436a87f37SMartin Kurbanov regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX), 50536a87f37SMartin Kurbanov }; 50636a87f37SMartin Kurbanov 50736a87f37SMartin Kurbanov static const struct regmap_access_table aw200xx_readable_table = { 50836a87f37SMartin Kurbanov .no_ranges = aw200xx_writeonly_ranges, 50936a87f37SMartin Kurbanov .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges), 51036a87f37SMartin Kurbanov }; 51136a87f37SMartin Kurbanov 51236a87f37SMartin Kurbanov static const struct regmap_range aw200xx_readonly_ranges[] = { 51336a87f37SMartin Kurbanov regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR), 51436a87f37SMartin Kurbanov }; 51536a87f37SMartin Kurbanov 51636a87f37SMartin Kurbanov static const struct regmap_access_table aw200xx_writeable_table = { 51736a87f37SMartin Kurbanov .no_ranges = aw200xx_readonly_ranges, 51836a87f37SMartin Kurbanov .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges), 51936a87f37SMartin Kurbanov }; 52036a87f37SMartin Kurbanov 52136a87f37SMartin Kurbanov static const struct regmap_config aw200xx_regmap_config = { 52236a87f37SMartin Kurbanov .reg_bits = 8, 52336a87f37SMartin Kurbanov .val_bits = 8, 52436a87f37SMartin Kurbanov .max_register = AW200XX_REG_MAX, 52536a87f37SMartin Kurbanov .ranges = aw200xx_ranges, 52636a87f37SMartin Kurbanov .num_ranges = ARRAY_SIZE(aw200xx_ranges), 52736a87f37SMartin Kurbanov .rd_table = &aw200xx_readable_table, 52836a87f37SMartin Kurbanov .wr_table = &aw200xx_writeable_table, 52965e9b513SMark Brown .cache_type = REGCACHE_MAPLE, 53096b43a10SGeorge Stark .disable_locking = true, 53136a87f37SMartin Kurbanov }; 53236a87f37SMartin Kurbanov 53336a87f37SMartin Kurbanov static int aw200xx_probe(struct i2c_client *client) 53436a87f37SMartin Kurbanov { 53536a87f37SMartin Kurbanov const struct aw200xx_chipdef *cdef; 53636a87f37SMartin Kurbanov struct aw200xx *chip; 53736a87f37SMartin Kurbanov int count; 53836a87f37SMartin Kurbanov int ret; 53936a87f37SMartin Kurbanov 54036a87f37SMartin Kurbanov cdef = device_get_match_data(&client->dev); 54136a87f37SMartin Kurbanov if (!cdef) 54236a87f37SMartin Kurbanov return -ENODEV; 54336a87f37SMartin Kurbanov 54436a87f37SMartin Kurbanov count = device_get_child_node_count(&client->dev); 54536a87f37SMartin Kurbanov if (!count || count > cdef->channels) 54636a87f37SMartin Kurbanov return dev_err_probe(&client->dev, -EINVAL, 54736a87f37SMartin Kurbanov "Incorrect number of leds (%d)", count); 54836a87f37SMartin Kurbanov 54936a87f37SMartin Kurbanov chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), 55036a87f37SMartin Kurbanov GFP_KERNEL); 55136a87f37SMartin Kurbanov if (!chip) 55236a87f37SMartin Kurbanov return -ENOMEM; 55336a87f37SMartin Kurbanov 55436a87f37SMartin Kurbanov chip->cdef = cdef; 55536a87f37SMartin Kurbanov chip->num_leds = count; 55636a87f37SMartin Kurbanov chip->client = client; 55736a87f37SMartin Kurbanov i2c_set_clientdata(client, chip); 55836a87f37SMartin Kurbanov 55936a87f37SMartin Kurbanov chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config); 56036a87f37SMartin Kurbanov if (IS_ERR(chip->regmap)) 56136a87f37SMartin Kurbanov return PTR_ERR(chip->regmap); 56236a87f37SMartin Kurbanov 563d882762fSDmitry Rokosov chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", 564d882762fSDmitry Rokosov GPIOD_OUT_HIGH); 565d882762fSDmitry Rokosov if (IS_ERR(chip->hwen)) 566d882762fSDmitry Rokosov return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), 567d882762fSDmitry Rokosov "Cannot get enable GPIO"); 568d882762fSDmitry Rokosov 569d882762fSDmitry Rokosov aw200xx_enable(chip); 570d882762fSDmitry Rokosov 57136a87f37SMartin Kurbanov ret = aw200xx_chip_check(chip); 57236a87f37SMartin Kurbanov if (ret) 57336a87f37SMartin Kurbanov return ret; 57436a87f37SMartin Kurbanov 57536a87f37SMartin Kurbanov mutex_init(&chip->mutex); 57636a87f37SMartin Kurbanov 57736a87f37SMartin Kurbanov /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */ 57836a87f37SMartin Kurbanov mutex_lock(&chip->mutex); 57936a87f37SMartin Kurbanov 58036a87f37SMartin Kurbanov ret = aw200xx_chip_reset(chip); 58136a87f37SMartin Kurbanov if (ret) 58236a87f37SMartin Kurbanov goto out_unlock; 58336a87f37SMartin Kurbanov 58436a87f37SMartin Kurbanov ret = aw200xx_probe_fw(&client->dev, chip); 58536a87f37SMartin Kurbanov if (ret) 58636a87f37SMartin Kurbanov goto out_unlock; 58736a87f37SMartin Kurbanov 58836a87f37SMartin Kurbanov ret = aw200xx_chip_init(chip); 58936a87f37SMartin Kurbanov 59036a87f37SMartin Kurbanov out_unlock: 591d882762fSDmitry Rokosov if (ret) 592d882762fSDmitry Rokosov aw200xx_disable(chip); 593d882762fSDmitry Rokosov 59436a87f37SMartin Kurbanov mutex_unlock(&chip->mutex); 59536a87f37SMartin Kurbanov return ret; 59636a87f37SMartin Kurbanov } 59736a87f37SMartin Kurbanov 59836a87f37SMartin Kurbanov static void aw200xx_remove(struct i2c_client *client) 59936a87f37SMartin Kurbanov { 60036a87f37SMartin Kurbanov struct aw200xx *chip = i2c_get_clientdata(client); 60136a87f37SMartin Kurbanov 60236a87f37SMartin Kurbanov aw200xx_chip_reset(chip); 603d882762fSDmitry Rokosov aw200xx_disable(chip); 60436a87f37SMartin Kurbanov mutex_destroy(&chip->mutex); 60536a87f37SMartin Kurbanov } 60636a87f37SMartin Kurbanov 60736a87f37SMartin Kurbanov static const struct aw200xx_chipdef aw20036_cdef = { 60836a87f37SMartin Kurbanov .channels = 36, 60936a87f37SMartin Kurbanov .display_size_rows_max = 3, 61036a87f37SMartin Kurbanov .display_size_columns = 12, 61136a87f37SMartin Kurbanov }; 61236a87f37SMartin Kurbanov 61336a87f37SMartin Kurbanov static const struct aw200xx_chipdef aw20054_cdef = { 61436a87f37SMartin Kurbanov .channels = 54, 61536a87f37SMartin Kurbanov .display_size_rows_max = 6, 61636a87f37SMartin Kurbanov .display_size_columns = 9, 61736a87f37SMartin Kurbanov }; 61836a87f37SMartin Kurbanov 61936a87f37SMartin Kurbanov static const struct aw200xx_chipdef aw20072_cdef = { 62036a87f37SMartin Kurbanov .channels = 72, 62136a87f37SMartin Kurbanov .display_size_rows_max = 6, 62236a87f37SMartin Kurbanov .display_size_columns = 12, 62336a87f37SMartin Kurbanov }; 62436a87f37SMartin Kurbanov 625634fea79SGeorge Stark static const struct aw200xx_chipdef aw20108_cdef = { 626634fea79SGeorge Stark .channels = 108, 627634fea79SGeorge Stark .display_size_rows_max = 9, 628634fea79SGeorge Stark .display_size_columns = 12, 629634fea79SGeorge Stark }; 630634fea79SGeorge Stark 63136a87f37SMartin Kurbanov static const struct i2c_device_id aw200xx_id[] = { 63236a87f37SMartin Kurbanov { "aw20036" }, 63336a87f37SMartin Kurbanov { "aw20054" }, 63436a87f37SMartin Kurbanov { "aw20072" }, 635634fea79SGeorge Stark { "aw20108" }, 63636a87f37SMartin Kurbanov {} 63736a87f37SMartin Kurbanov }; 63836a87f37SMartin Kurbanov MODULE_DEVICE_TABLE(i2c, aw200xx_id); 63936a87f37SMartin Kurbanov 64036a87f37SMartin Kurbanov static const struct of_device_id aw200xx_match_table[] = { 64136a87f37SMartin Kurbanov { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, 64236a87f37SMartin Kurbanov { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, 64336a87f37SMartin Kurbanov { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, 644634fea79SGeorge Stark { .compatible = "awinic,aw20108", .data = &aw20108_cdef, }, 64536a87f37SMartin Kurbanov {} 64636a87f37SMartin Kurbanov }; 64736a87f37SMartin Kurbanov MODULE_DEVICE_TABLE(of, aw200xx_match_table); 64836a87f37SMartin Kurbanov 64936a87f37SMartin Kurbanov static struct i2c_driver aw200xx_driver = { 65036a87f37SMartin Kurbanov .driver = { 65136a87f37SMartin Kurbanov .name = "aw200xx", 65236a87f37SMartin Kurbanov .of_match_table = aw200xx_match_table, 65336a87f37SMartin Kurbanov }, 65407a476e0SUwe Kleine-König .probe = aw200xx_probe, 65536a87f37SMartin Kurbanov .remove = aw200xx_remove, 65636a87f37SMartin Kurbanov .id_table = aw200xx_id, 65736a87f37SMartin Kurbanov }; 65836a87f37SMartin Kurbanov module_i2c_driver(aw200xx_driver); 65936a87f37SMartin Kurbanov 66036a87f37SMartin Kurbanov MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>"); 66136a87f37SMartin Kurbanov MODULE_DESCRIPTION("AW200XX LED driver"); 66236a87f37SMartin Kurbanov MODULE_LICENSE("GPL"); 663