xref: /linux/drivers/leds/leds-aw200xx.c (revision a23e1966932464e1c5226cb9ac4ce1d5fc10ba22)
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