1 // SPDX-License-Identifier: GPL-2.0
3 * Awinic AW20036/AW20054/AW20072 LED driver
5 * Copyright (c) 2023, SberDevices. All Rights Reserved.
7 * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru>
10 #include <linux/bitfield.h>
11 #include <linux/bits.h>
12 #include <linux/container_of.h>
13 #include <linux/i2c.h>
14 #include <linux/leds.h>
15 #include <linux/mod_devicetable.h>
16 #include <linux/module.h>
17 #include <linux/mutex.h>
18 #include <linux/regmap.h>
19 #include <linux/time.h>
20 #include <linux/units.h>
22 #define AW200XX_DIM_MAX (BIT(6) - 1)
23 #define AW200XX_FADE_MAX (BIT(8) - 1)
24 #define AW200XX_IMAX_DEFAULT_uA 60000
25 #define AW200XX_IMAX_MAX_uA 160000
26 #define AW200XX_IMAX_MIN_uA 3300
29 #define AW200XX_REG_PAGE0_BASE 0xc000
31 /* Select page register */
32 #define AW200XX_REG_PAGE 0xF0
33 #define AW200XX_PAGE_MASK (GENMASK(7, 6) | GENMASK(2, 0))
34 #define AW200XX_PAGE_SHIFT 0
35 #define AW200XX_NUM_PAGES 6
36 #define AW200XX_PAGE_SIZE 256
37 #define AW200XX_REG(page, reg) \
38 (AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg))
39 #define AW200XX_REG_MAX \
40 AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1)
41 #define AW200XX_PAGE0 0
42 #define AW200XX_PAGE1 1
43 #define AW200XX_PAGE2 2
44 #define AW200XX_PAGE3 3
45 #define AW200XX_PAGE4 4
46 #define AW200XX_PAGE5 5
48 /* Chip ID register */
49 #define AW200XX_REG_IDR AW200XX_REG(AW200XX_PAGE0, 0x00)
50 #define AW200XX_IDR_CHIPID 0x18
52 /* Sleep mode register */
53 #define AW200XX_REG_SLPCR AW200XX_REG(AW200XX_PAGE0, 0x01)
54 #define AW200XX_SLPCR_ACTIVE 0x00
57 #define AW200XX_REG_RSTR AW200XX_REG(AW200XX_PAGE0, 0x02)
58 #define AW200XX_RSTR_RESET 0x01
60 /* Global current configuration register */
61 #define AW200XX_REG_GCCR AW200XX_REG(AW200XX_PAGE0, 0x03)
62 #define AW200XX_GCCR_IMAX_MASK GENMASK(7, 4)
63 #define AW200XX_GCCR_IMAX(x) ((x) << 4)
64 #define AW200XX_GCCR_ALLON BIT(3)
66 /* Fast clear display control register */
67 #define AW200XX_REG_FCD AW200XX_REG(AW200XX_PAGE0, 0x04)
68 #define AW200XX_FCD_CLEAR 0x01
70 /* Display size configuration */
71 #define AW200XX_REG_DSIZE AW200XX_REG(AW200XX_PAGE0, 0x80)
72 #define AW200XX_DSIZE_COLUMNS_MAX 12
74 #define AW200XX_LED2REG(x, columns) \
75 ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns))))
77 /* DIM current configuration register on page 1 */
78 #define AW200XX_REG_DIM_PAGE1(x, columns) \
79 AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns))
82 * DIM current configuration register (page 4).
83 * The even address for current DIM configuration.
84 * The odd address for current FADE configuration
86 #define AW200XX_REG_DIM(x, columns) \
87 AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2)
88 #define AW200XX_REG_DIM2FADE(x) ((x) + 1)
91 * Duty ratio of display scan (see p.15 of datasheet for formula):
92 * duty = (592us / 600.5us) * (1 / (display_rows + 1))
94 * Multiply to 1000 (MILLI) to improve the accuracy of calculations.
96 #define AW200XX_DUTY_RATIO(rows) \
97 (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI)
99 struct aw200xx_chipdef {
101 u32 display_size_rows_max;
102 u32 display_size_columns;
106 struct led_classdev cdev;
107 struct aw200xx *chip;
113 const struct aw200xx_chipdef *cdef;
114 struct i2c_client *client;
115 struct regmap *regmap;
119 struct aw200xx_led leds[];
122 static ssize_t dim_show(struct device *dev, struct device_attribute *devattr,
125 struct led_classdev *cdev = dev_get_drvdata(dev);
126 struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
130 return sysfs_emit(buf, "auto\n");
132 return sysfs_emit(buf, "%d\n", dim);
135 static ssize_t dim_store(struct device *dev, struct device_attribute *devattr,
136 const char *buf, size_t count)
138 struct led_classdev *cdev = dev_get_drvdata(dev);
139 struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
140 struct aw200xx *chip = led->chip;
141 u32 columns = chip->cdef->display_size_columns;
145 if (sysfs_streq(buf, "auto")) {
148 ret = kstrtoint(buf, 0, &dim);
152 if (dim > AW200XX_DIM_MAX)
156 mutex_lock(&chip->mutex);
159 ret = regmap_write(chip->regmap,
160 AW200XX_REG_DIM_PAGE1(led->num, columns),
170 mutex_unlock(&chip->mutex);
173 static DEVICE_ATTR_RW(dim);
175 static struct attribute *dim_attrs[] = {
179 ATTRIBUTE_GROUPS(dim);
181 static int aw200xx_brightness_set(struct led_classdev *cdev,
182 enum led_brightness brightness)
184 struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
185 struct aw200xx *chip = led->chip;
190 mutex_lock(&chip->mutex);
192 reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns);
197 brightness / (AW200XX_FADE_MAX / AW200XX_DIM_MAX),
200 ret = regmap_write(chip->regmap, reg, dim);
204 ret = regmap_write(chip->regmap,
205 AW200XX_REG_DIM2FADE(reg), brightness);
208 mutex_unlock(&chip->mutex);
213 static u32 aw200xx_imax_from_global(const struct aw200xx *const chip,
219 * The output current of each LED (see p.14 of datasheet for formula):
220 * Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty
222 * The value of duty is determined by the following formula:
223 * duty = (592us / 600.5us) * (1 / (display_rows + 1))
225 * Calculated for the maximum values of fade and dim.
226 * We divide by 1000 because we earlier multiplied by 1000 to improve
227 * accuracy when calculating the duty.
229 led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows);
230 do_div(led_imax_uA, MILLI);
235 static u32 aw200xx_imax_to_global(const struct aw200xx *const chip,
238 u32 duty = AW200XX_DUTY_RATIO(chip->display_rows);
240 /* The output current of each LED (see p.14 of datasheet for formula) */
241 return (led_imax_uA * 1000U) / duty;
244 #define AW200XX_IMAX_MULTIPLIER1 10000
245 #define AW200XX_IMAX_MULTIPLIER2 3333
246 #define AW200XX_IMAX_BASE_VAL1 0
247 #define AW200XX_IMAX_BASE_VAL2 8
250 * The AW200XX has a 4-bit register (GCCR) to configure the global current,
251 * which ranges from 3.3mA to 160mA. The following table indicates the values
252 * of the global current, divided into two parts:
254 * +-----------+-----------------+-----------+-----------------+
255 * | reg value | global max (mA) | reg value | global max (mA) |
256 * +-----------+-----------------+-----------+-----------------+
257 * | 0 | 10 | 8 | 3.3 |
258 * | 1 | 20 | 9 | 6.7 |
259 * | 2 | 30 | 10 | 10 |
260 * | 3 | 40 | 11 | 13.3 |
261 * | 4 | 60 | 12 | 20 |
262 * | 5 | 80 | 13 | 26.7 |
263 * | 6 | 120 | 14 | 40 |
264 * | 7 | 160 | 15 | 53.3 |
265 * +-----------+-----------------+-----------+-----------------+
267 * The left part with a multiplier of 10, and the right part with a multiplier
269 * So we have two formulas to calculate the global current:
270 * for the left part of the table:
271 * imax = coefficient * 10
273 * for the right part of the table:
274 * imax = coefficient * 3.3
276 * The coefficient table consists of the following values:
277 * 1, 2, 3, 4, 6, 8, 12, 16.
279 static int aw200xx_set_imax(const struct aw200xx *const chip,
282 u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA);
283 u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16};
284 u32 gccr_imax = UINT_MAX;
288 for (i = 0; i < ARRAY_SIZE(coeff_table); i++) {
291 /* select closest ones */
292 imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1;
293 if (g_imax_uA >= imax && imax > cur_imax) {
295 gccr_imax = i + AW200XX_IMAX_BASE_VAL1;
298 imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2;
299 imax = DIV_ROUND_CLOSEST(imax, 100) * 100;
300 if (g_imax_uA >= imax && imax > cur_imax) {
302 gccr_imax = i + AW200XX_IMAX_BASE_VAL2;
306 if (gccr_imax == UINT_MAX)
309 return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,
310 AW200XX_GCCR_IMAX_MASK,
311 AW200XX_GCCR_IMAX(gccr_imax));
314 static int aw200xx_chip_reset(const struct aw200xx *const chip)
318 ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET);
322 regcache_mark_dirty(chip->regmap);
323 return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR);
326 static int aw200xx_chip_init(const struct aw200xx *const chip)
330 ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE,
331 chip->display_rows - 1);
335 ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR,
336 AW200XX_SLPCR_ACTIVE);
340 return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,
341 AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON);
344 static int aw200xx_chip_check(const struct aw200xx *const chip)
346 struct device *dev = &chip->client->dev;
350 ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid);
352 return dev_err_probe(dev, ret, "Failed to read chip ID\n");
354 if (chipid != AW200XX_IDR_CHIPID)
355 return dev_err_probe(dev, -ENODEV,
356 "Chip reported wrong ID: %x\n", chipid);
361 static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip)
363 struct fwnode_handle *child;
364 u32 current_min, current_max, min_uA;
368 ret = device_property_read_u32(dev, "awinic,display-rows",
369 &chip->display_rows);
371 return dev_err_probe(dev, ret,
372 "Failed to read 'display-rows' property\n");
374 if (!chip->display_rows ||
375 chip->display_rows > chip->cdef->display_size_rows_max) {
376 return dev_err_probe(dev, -EINVAL,
377 "Invalid leds display size %u\n",
381 current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA);
382 current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA);
386 device_for_each_child_node(dev, child) {
387 struct led_init_data init_data = {};
388 struct aw200xx_led *led;
391 ret = fwnode_property_read_u32(child, "reg", &source);
393 dev_err(dev, "Missing reg property\n");
398 if (source >= chip->cdef->channels) {
399 dev_err(dev, "LED reg %u out of range (max %u)\n",
400 source, chip->cdef->channels);
405 ret = fwnode_property_read_u32(child, "led-max-microamp",
408 dev_info(&chip->client->dev,
409 "DT property led-max-microamp is missing\n");
410 } else if (imax < current_min || imax > current_max) {
411 dev_err(dev, "Invalid value %u for led-max-microamp\n",
416 min_uA = min(min_uA, imax);
419 led = &chip->leds[i];
423 led->cdev.brightness_set_blocking = aw200xx_brightness_set;
424 led->cdev.groups = dim_groups;
425 init_data.fwnode = child;
427 ret = devm_led_classdev_register_ext(dev, &led->cdev,
430 fwnode_handle_put(child);
440 if (min_uA == UINT_MAX) {
441 min_uA = aw200xx_imax_from_global(chip,
442 AW200XX_IMAX_DEFAULT_uA);
445 return aw200xx_set_imax(chip, min_uA);
448 static const struct regmap_range_cfg aw200xx_ranges[] = {
452 .range_max = AW200XX_REG_MAX,
453 .selector_reg = AW200XX_REG_PAGE,
454 .selector_mask = AW200XX_PAGE_MASK,
455 .selector_shift = AW200XX_PAGE_SHIFT,
457 .window_len = AW200XX_PAGE_SIZE,
461 static const struct regmap_range aw200xx_writeonly_ranges[] = {
462 regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX),
465 static const struct regmap_access_table aw200xx_readable_table = {
466 .no_ranges = aw200xx_writeonly_ranges,
467 .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges),
470 static const struct regmap_range aw200xx_readonly_ranges[] = {
471 regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR),
474 static const struct regmap_access_table aw200xx_writeable_table = {
475 .no_ranges = aw200xx_readonly_ranges,
476 .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges),
479 static const struct regmap_config aw200xx_regmap_config = {
482 .max_register = AW200XX_REG_MAX,
483 .ranges = aw200xx_ranges,
484 .num_ranges = ARRAY_SIZE(aw200xx_ranges),
485 .rd_table = &aw200xx_readable_table,
486 .wr_table = &aw200xx_writeable_table,
487 .cache_type = REGCACHE_RBTREE,
490 static int aw200xx_probe(struct i2c_client *client)
492 const struct aw200xx_chipdef *cdef;
493 struct aw200xx *chip;
497 cdef = device_get_match_data(&client->dev);
501 count = device_get_child_node_count(&client->dev);
502 if (!count || count > cdef->channels)
503 return dev_err_probe(&client->dev, -EINVAL,
504 "Incorrect number of leds (%d)", count);
506 chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count),
512 chip->num_leds = count;
513 chip->client = client;
514 i2c_set_clientdata(client, chip);
516 chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config);
517 if (IS_ERR(chip->regmap))
518 return PTR_ERR(chip->regmap);
520 ret = aw200xx_chip_check(chip);
524 mutex_init(&chip->mutex);
526 /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */
527 mutex_lock(&chip->mutex);
529 ret = aw200xx_chip_reset(chip);
533 ret = aw200xx_probe_fw(&client->dev, chip);
537 ret = aw200xx_chip_init(chip);
540 mutex_unlock(&chip->mutex);
544 static void aw200xx_remove(struct i2c_client *client)
546 struct aw200xx *chip = i2c_get_clientdata(client);
548 aw200xx_chip_reset(chip);
549 mutex_destroy(&chip->mutex);
552 static const struct aw200xx_chipdef aw20036_cdef = {
554 .display_size_rows_max = 3,
555 .display_size_columns = 12,
558 static const struct aw200xx_chipdef aw20054_cdef = {
560 .display_size_rows_max = 6,
561 .display_size_columns = 9,
564 static const struct aw200xx_chipdef aw20072_cdef = {
566 .display_size_rows_max = 6,
567 .display_size_columns = 12,
570 static const struct i2c_device_id aw200xx_id[] = {
576 MODULE_DEVICE_TABLE(i2c, aw200xx_id);
578 static const struct of_device_id aw200xx_match_table[] = {
579 { .compatible = "awinic,aw20036", .data = &aw20036_cdef, },
580 { .compatible = "awinic,aw20054", .data = &aw20054_cdef, },
581 { .compatible = "awinic,aw20072", .data = &aw20072_cdef, },
584 MODULE_DEVICE_TABLE(of, aw200xx_match_table);
586 static struct i2c_driver aw200xx_driver = {
589 .of_match_table = aw200xx_match_table,
591 .probe = aw200xx_probe,
592 .remove = aw200xx_remove,
593 .id_table = aw200xx_id,
595 module_i2c_driver(aw200xx_driver);
597 MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>");
598 MODULE_DESCRIPTION("AW200XX LED driver");
599 MODULE_LICENSE("GPL");