]>
Commit | Line | Data |
---|---|---|
36a87f37 MK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
634fea79 | 3 | * Awinic AW20036/AW20054/AW20072/AW20108 LED driver |
36a87f37 MK |
4 | * |
5 | * Copyright (c) 2023, SberDevices. All Rights Reserved. | |
6 | * | |
7 | * Author: Martin Kurbanov <[email protected]> | |
8 | */ | |
9 | ||
10 | #include <linux/bitfield.h> | |
11 | #include <linux/bits.h> | |
12 | #include <linux/container_of.h> | |
d882762f | 13 | #include <linux/gpio/consumer.h> |
36a87f37 MK |
14 | #include <linux/i2c.h> |
15 | #include <linux/leds.h> | |
16 | #include <linux/mod_devicetable.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/regmap.h> | |
20 | #include <linux/time.h> | |
21 | #include <linux/units.h> | |
22 | ||
23 | #define AW200XX_DIM_MAX (BIT(6) - 1) | |
24 | #define AW200XX_FADE_MAX (BIT(8) - 1) | |
25 | #define AW200XX_IMAX_DEFAULT_uA 60000 | |
26 | #define AW200XX_IMAX_MAX_uA 160000 | |
27 | #define AW200XX_IMAX_MIN_uA 3300 | |
28 | ||
29 | /* Page 0 */ | |
30 | #define AW200XX_REG_PAGE0_BASE 0xc000 | |
31 | ||
32 | /* Select page register */ | |
33 | #define AW200XX_REG_PAGE 0xF0 | |
34 | #define AW200XX_PAGE_MASK (GENMASK(7, 6) | GENMASK(2, 0)) | |
35 | #define AW200XX_PAGE_SHIFT 0 | |
36 | #define AW200XX_NUM_PAGES 6 | |
37 | #define AW200XX_PAGE_SIZE 256 | |
38 | #define AW200XX_REG(page, reg) \ | |
39 | (AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg)) | |
40 | #define AW200XX_REG_MAX \ | |
41 | AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1) | |
42 | #define AW200XX_PAGE0 0 | |
43 | #define AW200XX_PAGE1 1 | |
44 | #define AW200XX_PAGE2 2 | |
45 | #define AW200XX_PAGE3 3 | |
46 | #define AW200XX_PAGE4 4 | |
47 | #define AW200XX_PAGE5 5 | |
48 | ||
49 | /* Chip ID register */ | |
50 | #define AW200XX_REG_IDR AW200XX_REG(AW200XX_PAGE0, 0x00) | |
51 | #define AW200XX_IDR_CHIPID 0x18 | |
52 | ||
53 | /* Sleep mode register */ | |
54 | #define AW200XX_REG_SLPCR AW200XX_REG(AW200XX_PAGE0, 0x01) | |
55 | #define AW200XX_SLPCR_ACTIVE 0x00 | |
56 | ||
57 | /* Reset register */ | |
58 | #define AW200XX_REG_RSTR AW200XX_REG(AW200XX_PAGE0, 0x02) | |
59 | #define AW200XX_RSTR_RESET 0x01 | |
60 | ||
61 | /* Global current configuration register */ | |
62 | #define AW200XX_REG_GCCR AW200XX_REG(AW200XX_PAGE0, 0x03) | |
63 | #define AW200XX_GCCR_IMAX_MASK GENMASK(7, 4) | |
64 | #define AW200XX_GCCR_IMAX(x) ((x) << 4) | |
65 | #define AW200XX_GCCR_ALLON BIT(3) | |
66 | ||
67 | /* Fast clear display control register */ | |
68 | #define AW200XX_REG_FCD AW200XX_REG(AW200XX_PAGE0, 0x04) | |
69 | #define AW200XX_FCD_CLEAR 0x01 | |
70 | ||
71 | /* Display size configuration */ | |
72 | #define AW200XX_REG_DSIZE AW200XX_REG(AW200XX_PAGE0, 0x80) | |
73 | #define AW200XX_DSIZE_COLUMNS_MAX 12 | |
74 | ||
75 | #define AW200XX_LED2REG(x, columns) \ | |
76 | ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns)))) | |
77 | ||
adfd4621 MK |
78 | /* DIM current configuration register on page 1 */ |
79 | #define AW200XX_REG_DIM_PAGE1(x, columns) \ | |
80 | AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns)) | |
81 | ||
36a87f37 MK |
82 | /* |
83 | * DIM current configuration register (page 4). | |
84 | * The even address for current DIM configuration. | |
85 | * The odd address for current FADE configuration | |
86 | */ | |
87 | #define AW200XX_REG_DIM(x, columns) \ | |
88 | AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2) | |
89 | #define AW200XX_REG_DIM2FADE(x) ((x) + 1) | |
150bca53 GS |
90 | #define AW200XX_REG_FADE2DIM(fade) \ |
91 | DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX) | |
36a87f37 MK |
92 | |
93 | /* | |
94 | * Duty ratio of display scan (see p.15 of datasheet for formula): | |
95 | * duty = (592us / 600.5us) * (1 / (display_rows + 1)) | |
96 | * | |
97 | * Multiply to 1000 (MILLI) to improve the accuracy of calculations. | |
98 | */ | |
99 | #define AW200XX_DUTY_RATIO(rows) \ | |
100 | (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI) | |
101 | ||
102 | struct aw200xx_chipdef { | |
103 | u32 channels; | |
104 | u32 display_size_rows_max; | |
105 | u32 display_size_columns; | |
106 | }; | |
107 | ||
108 | struct aw200xx_led { | |
109 | struct led_classdev cdev; | |
110 | struct aw200xx *chip; | |
111 | int dim; | |
112 | u32 num; | |
113 | }; | |
114 | ||
115 | struct aw200xx { | |
116 | const struct aw200xx_chipdef *cdef; | |
117 | struct i2c_client *client; | |
118 | struct regmap *regmap; | |
119 | struct mutex mutex; | |
120 | u32 num_leds; | |
121 | u32 display_rows; | |
d882762f | 122 | struct gpio_desc *hwen; |
ff861ca9 | 123 | struct aw200xx_led leds[] __counted_by(num_leds); |
36a87f37 MK |
124 | }; |
125 | ||
126 | static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, | |
127 | char *buf) | |
128 | { | |
129 | struct led_classdev *cdev = dev_get_drvdata(dev); | |
130 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
131 | int dim = led->dim; | |
132 | ||
133 | if (dim < 0) | |
134 | return sysfs_emit(buf, "auto\n"); | |
135 | ||
136 | return sysfs_emit(buf, "%d\n", dim); | |
137 | } | |
138 | ||
139 | static ssize_t dim_store(struct device *dev, struct device_attribute *devattr, | |
140 | const char *buf, size_t count) | |
141 | { | |
142 | struct led_classdev *cdev = dev_get_drvdata(dev); | |
143 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
144 | struct aw200xx *chip = led->chip; | |
145 | u32 columns = chip->cdef->display_size_columns; | |
146 | int dim; | |
147 | ssize_t ret; | |
148 | ||
149 | if (sysfs_streq(buf, "auto")) { | |
150 | dim = -1; | |
151 | } else { | |
152 | ret = kstrtoint(buf, 0, &dim); | |
153 | if (ret) | |
154 | return ret; | |
155 | ||
156 | if (dim > AW200XX_DIM_MAX) | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
160 | mutex_lock(&chip->mutex); | |
161 | ||
162 | if (dim >= 0) { | |
163 | ret = regmap_write(chip->regmap, | |
adfd4621 MK |
164 | AW200XX_REG_DIM_PAGE1(led->num, columns), |
165 | dim); | |
36a87f37 MK |
166 | if (ret) |
167 | goto out_unlock; | |
168 | } | |
169 | ||
170 | led->dim = dim; | |
171 | ret = count; | |
172 | ||
173 | out_unlock: | |
174 | mutex_unlock(&chip->mutex); | |
175 | return ret; | |
176 | } | |
177 | static DEVICE_ATTR_RW(dim); | |
178 | ||
179 | static struct attribute *dim_attrs[] = { | |
180 | &dev_attr_dim.attr, | |
181 | NULL | |
182 | }; | |
183 | ATTRIBUTE_GROUPS(dim); | |
184 | ||
185 | static int aw200xx_brightness_set(struct led_classdev *cdev, | |
186 | enum led_brightness brightness) | |
187 | { | |
188 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
189 | struct aw200xx *chip = led->chip; | |
190 | int dim; | |
191 | u32 reg; | |
192 | int ret; | |
193 | ||
194 | mutex_lock(&chip->mutex); | |
195 | ||
196 | reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns); | |
197 | ||
198 | dim = led->dim; | |
199 | if (dim < 0) | |
150bca53 | 200 | dim = AW200XX_REG_FADE2DIM(brightness); |
36a87f37 MK |
201 | |
202 | ret = regmap_write(chip->regmap, reg, dim); | |
203 | if (ret) | |
204 | goto out_unlock; | |
205 | ||
206 | ret = regmap_write(chip->regmap, | |
207 | AW200XX_REG_DIM2FADE(reg), brightness); | |
208 | ||
209 | out_unlock: | |
210 | mutex_unlock(&chip->mutex); | |
211 | ||
212 | return ret; | |
213 | } | |
214 | ||
215 | static u32 aw200xx_imax_from_global(const struct aw200xx *const chip, | |
216 | u32 global_imax_uA) | |
217 | { | |
218 | u64 led_imax_uA; | |
219 | ||
220 | /* | |
221 | * The output current of each LED (see p.14 of datasheet for formula): | |
222 | * Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty | |
223 | * | |
224 | * The value of duty is determined by the following formula: | |
225 | * duty = (592us / 600.5us) * (1 / (display_rows + 1)) | |
226 | * | |
227 | * Calculated for the maximum values of fade and dim. | |
228 | * We divide by 1000 because we earlier multiplied by 1000 to improve | |
229 | * accuracy when calculating the duty. | |
230 | */ | |
231 | led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows); | |
232 | do_div(led_imax_uA, MILLI); | |
233 | ||
234 | return led_imax_uA; | |
235 | } | |
236 | ||
237 | static u32 aw200xx_imax_to_global(const struct aw200xx *const chip, | |
238 | u32 led_imax_uA) | |
239 | { | |
240 | u32 duty = AW200XX_DUTY_RATIO(chip->display_rows); | |
241 | ||
242 | /* The output current of each LED (see p.14 of datasheet for formula) */ | |
243 | return (led_imax_uA * 1000U) / duty; | |
244 | } | |
245 | ||
246 | #define AW200XX_IMAX_MULTIPLIER1 10000 | |
247 | #define AW200XX_IMAX_MULTIPLIER2 3333 | |
248 | #define AW200XX_IMAX_BASE_VAL1 0 | |
249 | #define AW200XX_IMAX_BASE_VAL2 8 | |
250 | ||
251 | /* | |
252 | * The AW200XX has a 4-bit register (GCCR) to configure the global current, | |
253 | * which ranges from 3.3mA to 160mA. The following table indicates the values | |
254 | * of the global current, divided into two parts: | |
255 | * | |
256 | * +-----------+-----------------+-----------+-----------------+ | |
257 | * | reg value | global max (mA) | reg value | global max (mA) | | |
258 | * +-----------+-----------------+-----------+-----------------+ | |
259 | * | 0 | 10 | 8 | 3.3 | | |
260 | * | 1 | 20 | 9 | 6.7 | | |
261 | * | 2 | 30 | 10 | 10 | | |
262 | * | 3 | 40 | 11 | 13.3 | | |
263 | * | 4 | 60 | 12 | 20 | | |
264 | * | 5 | 80 | 13 | 26.7 | | |
265 | * | 6 | 120 | 14 | 40 | | |
266 | * | 7 | 160 | 15 | 53.3 | | |
267 | * +-----------+-----------------+-----------+-----------------+ | |
268 | * | |
269 | * The left part with a multiplier of 10, and the right part with a multiplier | |
270 | * of 3.3. | |
271 | * So we have two formulas to calculate the global current: | |
272 | * for the left part of the table: | |
273 | * imax = coefficient * 10 | |
274 | * | |
275 | * for the right part of the table: | |
276 | * imax = coefficient * 3.3 | |
277 | * | |
278 | * The coefficient table consists of the following values: | |
279 | * 1, 2, 3, 4, 6, 8, 12, 16. | |
280 | */ | |
281 | static int aw200xx_set_imax(const struct aw200xx *const chip, | |
282 | u32 led_imax_uA) | |
283 | { | |
284 | u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA); | |
d0532248 | 285 | static const u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16}; |
36a87f37 MK |
286 | u32 gccr_imax = UINT_MAX; |
287 | u32 cur_imax = 0; | |
288 | int i; | |
289 | ||
290 | for (i = 0; i < ARRAY_SIZE(coeff_table); i++) { | |
291 | u32 imax; | |
292 | ||
293 | /* select closest ones */ | |
294 | imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1; | |
295 | if (g_imax_uA >= imax && imax > cur_imax) { | |
296 | cur_imax = imax; | |
297 | gccr_imax = i + AW200XX_IMAX_BASE_VAL1; | |
298 | } | |
299 | ||
300 | imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2; | |
301 | imax = DIV_ROUND_CLOSEST(imax, 100) * 100; | |
302 | if (g_imax_uA >= imax && imax > cur_imax) { | |
303 | cur_imax = imax; | |
304 | gccr_imax = i + AW200XX_IMAX_BASE_VAL2; | |
305 | } | |
306 | } | |
307 | ||
308 | if (gccr_imax == UINT_MAX) | |
309 | return -EINVAL; | |
310 | ||
311 | return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, | |
312 | AW200XX_GCCR_IMAX_MASK, | |
313 | AW200XX_GCCR_IMAX(gccr_imax)); | |
314 | } | |
315 | ||
316 | static int aw200xx_chip_reset(const struct aw200xx *const chip) | |
317 | { | |
318 | int ret; | |
319 | ||
320 | ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET); | |
321 | if (ret) | |
322 | return ret; | |
323 | ||
d883a5ab GS |
324 | /* According to the datasheet software reset takes at least 1ms */ |
325 | fsleep(1000); | |
326 | ||
36a87f37 MK |
327 | regcache_mark_dirty(chip->regmap); |
328 | return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); | |
329 | } | |
330 | ||
331 | static int aw200xx_chip_init(const struct aw200xx *const chip) | |
332 | { | |
333 | int ret; | |
334 | ||
335 | ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE, | |
336 | chip->display_rows - 1); | |
337 | if (ret) | |
338 | return ret; | |
339 | ||
340 | ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR, | |
341 | AW200XX_SLPCR_ACTIVE); | |
342 | if (ret) | |
343 | return ret; | |
344 | ||
345 | return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, | |
346 | AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON); | |
347 | } | |
348 | ||
349 | static int aw200xx_chip_check(const struct aw200xx *const chip) | |
350 | { | |
351 | struct device *dev = &chip->client->dev; | |
352 | u32 chipid; | |
353 | int ret; | |
354 | ||
355 | ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid); | |
356 | if (ret) | |
357 | return dev_err_probe(dev, ret, "Failed to read chip ID\n"); | |
358 | ||
359 | if (chipid != AW200XX_IDR_CHIPID) | |
360 | return dev_err_probe(dev, -ENODEV, | |
361 | "Chip reported wrong ID: %x\n", chipid); | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
d882762f DR |
366 | static void aw200xx_enable(const struct aw200xx *const chip) |
367 | { | |
368 | gpiod_set_value_cansleep(chip->hwen, 1); | |
369 | ||
370 | /* | |
371 | * After HWEN pin set high the chip begins to load the OTP information, | |
372 | * which takes 200us to complete. About 200us wait time is needed for | |
373 | * internal oscillator startup and display SRAM initialization. After | |
374 | * display SRAM initialization, the registers in page1 to page5 can be | |
375 | * configured via i2c interface. | |
376 | */ | |
377 | fsleep(400); | |
378 | } | |
379 | ||
380 | static void aw200xx_disable(const struct aw200xx *const chip) | |
381 | { | |
382 | return gpiod_set_value_cansleep(chip->hwen, 0); | |
383 | } | |
384 | ||
2b8db572 GS |
385 | static int aw200xx_probe_get_display_rows(struct device *dev, |
386 | struct aw200xx *chip) | |
387 | { | |
388 | struct fwnode_handle *child; | |
389 | u32 max_source = 0; | |
390 | ||
391 | device_for_each_child_node(dev, child) { | |
392 | u32 source; | |
393 | int ret; | |
394 | ||
395 | ret = fwnode_property_read_u32(child, "reg", &source); | |
396 | if (ret || source >= chip->cdef->channels) | |
397 | continue; | |
398 | ||
399 | max_source = max(max_source, source); | |
400 | } | |
401 | ||
402 | if (max_source == 0) | |
403 | return -EINVAL; | |
404 | ||
405 | chip->display_rows = max_source / chip->cdef->display_size_columns + 1; | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
36a87f37 MK |
410 | static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) |
411 | { | |
412 | struct fwnode_handle *child; | |
413 | u32 current_min, current_max, min_uA; | |
414 | int ret; | |
415 | int i; | |
416 | ||
2b8db572 | 417 | ret = aw200xx_probe_get_display_rows(dev, chip); |
36a87f37 MK |
418 | if (ret) |
419 | return dev_err_probe(dev, ret, | |
2b8db572 | 420 | "No valid led definitions found\n"); |
36a87f37 MK |
421 | |
422 | current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); | |
423 | current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); | |
424 | min_uA = UINT_MAX; | |
425 | i = 0; | |
426 | ||
427 | device_for_each_child_node(dev, child) { | |
428 | struct led_init_data init_data = {}; | |
429 | struct aw200xx_led *led; | |
430 | u32 source, imax; | |
431 | ||
432 | ret = fwnode_property_read_u32(child, "reg", &source); | |
433 | if (ret) { | |
434 | dev_err(dev, "Missing reg property\n"); | |
435 | chip->num_leds--; | |
436 | continue; | |
437 | } | |
438 | ||
439 | if (source >= chip->cdef->channels) { | |
440 | dev_err(dev, "LED reg %u out of range (max %u)\n", | |
441 | source, chip->cdef->channels); | |
442 | chip->num_leds--; | |
443 | continue; | |
444 | } | |
445 | ||
446 | ret = fwnode_property_read_u32(child, "led-max-microamp", | |
447 | &imax); | |
448 | if (ret) { | |
449 | dev_info(&chip->client->dev, | |
450 | "DT property led-max-microamp is missing\n"); | |
451 | } else if (imax < current_min || imax > current_max) { | |
452 | dev_err(dev, "Invalid value %u for led-max-microamp\n", | |
453 | imax); | |
454 | chip->num_leds--; | |
455 | continue; | |
456 | } else { | |
457 | min_uA = min(min_uA, imax); | |
458 | } | |
459 | ||
460 | led = &chip->leds[i]; | |
461 | led->dim = -1; | |
462 | led->num = source; | |
463 | led->chip = chip; | |
464 | led->cdev.brightness_set_blocking = aw200xx_brightness_set; | |
150bca53 | 465 | led->cdev.max_brightness = AW200XX_FADE_MAX; |
36a87f37 MK |
466 | led->cdev.groups = dim_groups; |
467 | init_data.fwnode = child; | |
468 | ||
469 | ret = devm_led_classdev_register_ext(dev, &led->cdev, | |
470 | &init_data); | |
471 | if (ret) { | |
472 | fwnode_handle_put(child); | |
473 | break; | |
474 | } | |
475 | ||
476 | i++; | |
477 | } | |
478 | ||
479 | if (!chip->num_leds) | |
480 | return -EINVAL; | |
481 | ||
482 | if (min_uA == UINT_MAX) { | |
483 | min_uA = aw200xx_imax_from_global(chip, | |
484 | AW200XX_IMAX_DEFAULT_uA); | |
485 | } | |
486 | ||
487 | return aw200xx_set_imax(chip, min_uA); | |
488 | } | |
489 | ||
490 | static const struct regmap_range_cfg aw200xx_ranges[] = { | |
491 | { | |
492 | .name = "aw200xx", | |
493 | .range_min = 0, | |
494 | .range_max = AW200XX_REG_MAX, | |
495 | .selector_reg = AW200XX_REG_PAGE, | |
496 | .selector_mask = AW200XX_PAGE_MASK, | |
497 | .selector_shift = AW200XX_PAGE_SHIFT, | |
498 | .window_start = 0, | |
499 | .window_len = AW200XX_PAGE_SIZE, | |
500 | }, | |
501 | }; | |
502 | ||
503 | static const struct regmap_range aw200xx_writeonly_ranges[] = { | |
504 | regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX), | |
505 | }; | |
506 | ||
507 | static const struct regmap_access_table aw200xx_readable_table = { | |
508 | .no_ranges = aw200xx_writeonly_ranges, | |
509 | .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges), | |
510 | }; | |
511 | ||
512 | static const struct regmap_range aw200xx_readonly_ranges[] = { | |
513 | regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR), | |
514 | }; | |
515 | ||
516 | static const struct regmap_access_table aw200xx_writeable_table = { | |
517 | .no_ranges = aw200xx_readonly_ranges, | |
518 | .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges), | |
519 | }; | |
520 | ||
521 | static const struct regmap_config aw200xx_regmap_config = { | |
522 | .reg_bits = 8, | |
523 | .val_bits = 8, | |
524 | .max_register = AW200XX_REG_MAX, | |
525 | .ranges = aw200xx_ranges, | |
526 | .num_ranges = ARRAY_SIZE(aw200xx_ranges), | |
527 | .rd_table = &aw200xx_readable_table, | |
528 | .wr_table = &aw200xx_writeable_table, | |
65e9b513 | 529 | .cache_type = REGCACHE_MAPLE, |
96b43a10 | 530 | .disable_locking = true, |
36a87f37 MK |
531 | }; |
532 | ||
a59d8824 GS |
533 | static void aw200xx_chip_reset_action(void *data) |
534 | { | |
535 | aw200xx_chip_reset(data); | |
536 | } | |
537 | ||
538 | static void aw200xx_disable_action(void *data) | |
539 | { | |
540 | aw200xx_disable(data); | |
541 | } | |
542 | ||
36a87f37 MK |
543 | static int aw200xx_probe(struct i2c_client *client) |
544 | { | |
545 | const struct aw200xx_chipdef *cdef; | |
546 | struct aw200xx *chip; | |
547 | int count; | |
548 | int ret; | |
549 | ||
550 | cdef = device_get_match_data(&client->dev); | |
551 | if (!cdef) | |
552 | return -ENODEV; | |
553 | ||
554 | count = device_get_child_node_count(&client->dev); | |
555 | if (!count || count > cdef->channels) | |
556 | return dev_err_probe(&client->dev, -EINVAL, | |
557 | "Incorrect number of leds (%d)", count); | |
558 | ||
559 | chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), | |
560 | GFP_KERNEL); | |
561 | if (!chip) | |
562 | return -ENOMEM; | |
563 | ||
564 | chip->cdef = cdef; | |
565 | chip->num_leds = count; | |
566 | chip->client = client; | |
567 | i2c_set_clientdata(client, chip); | |
568 | ||
569 | chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config); | |
570 | if (IS_ERR(chip->regmap)) | |
571 | return PTR_ERR(chip->regmap); | |
572 | ||
d882762f DR |
573 | chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", |
574 | GPIOD_OUT_HIGH); | |
575 | if (IS_ERR(chip->hwen)) | |
576 | return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), | |
577 | "Cannot get enable GPIO"); | |
578 | ||
579 | aw200xx_enable(chip); | |
580 | ||
a59d8824 GS |
581 | ret = devm_add_action(&client->dev, aw200xx_disable_action, chip); |
582 | if (ret) | |
583 | return ret; | |
584 | ||
36a87f37 MK |
585 | ret = aw200xx_chip_check(chip); |
586 | if (ret) | |
587 | return ret; | |
588 | ||
a59d8824 GS |
589 | ret = devm_mutex_init(&client->dev, &chip->mutex); |
590 | if (ret) | |
591 | return ret; | |
36a87f37 MK |
592 | |
593 | /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */ | |
594 | mutex_lock(&chip->mutex); | |
595 | ||
596 | ret = aw200xx_chip_reset(chip); | |
597 | if (ret) | |
598 | goto out_unlock; | |
599 | ||
a59d8824 GS |
600 | ret = devm_add_action(&client->dev, aw200xx_chip_reset_action, chip); |
601 | if (ret) | |
602 | goto out_unlock; | |
603 | ||
36a87f37 MK |
604 | ret = aw200xx_probe_fw(&client->dev, chip); |
605 | if (ret) | |
606 | goto out_unlock; | |
607 | ||
608 | ret = aw200xx_chip_init(chip); | |
609 | ||
610 | out_unlock: | |
d882762f DR |
611 | if (ret) |
612 | aw200xx_disable(chip); | |
613 | ||
36a87f37 MK |
614 | mutex_unlock(&chip->mutex); |
615 | return ret; | |
616 | } | |
617 | ||
36a87f37 MK |
618 | static const struct aw200xx_chipdef aw20036_cdef = { |
619 | .channels = 36, | |
620 | .display_size_rows_max = 3, | |
621 | .display_size_columns = 12, | |
622 | }; | |
623 | ||
624 | static const struct aw200xx_chipdef aw20054_cdef = { | |
625 | .channels = 54, | |
626 | .display_size_rows_max = 6, | |
627 | .display_size_columns = 9, | |
628 | }; | |
629 | ||
630 | static const struct aw200xx_chipdef aw20072_cdef = { | |
631 | .channels = 72, | |
632 | .display_size_rows_max = 6, | |
633 | .display_size_columns = 12, | |
634 | }; | |
635 | ||
634fea79 GS |
636 | static const struct aw200xx_chipdef aw20108_cdef = { |
637 | .channels = 108, | |
638 | .display_size_rows_max = 9, | |
639 | .display_size_columns = 12, | |
640 | }; | |
641 | ||
36a87f37 MK |
642 | static const struct i2c_device_id aw200xx_id[] = { |
643 | { "aw20036" }, | |
644 | { "aw20054" }, | |
645 | { "aw20072" }, | |
634fea79 | 646 | { "aw20108" }, |
36a87f37 MK |
647 | {} |
648 | }; | |
649 | MODULE_DEVICE_TABLE(i2c, aw200xx_id); | |
650 | ||
651 | static const struct of_device_id aw200xx_match_table[] = { | |
652 | { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, | |
653 | { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, | |
654 | { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, | |
634fea79 | 655 | { .compatible = "awinic,aw20108", .data = &aw20108_cdef, }, |
36a87f37 MK |
656 | {} |
657 | }; | |
658 | MODULE_DEVICE_TABLE(of, aw200xx_match_table); | |
659 | ||
660 | static struct i2c_driver aw200xx_driver = { | |
661 | .driver = { | |
662 | .name = "aw200xx", | |
663 | .of_match_table = aw200xx_match_table, | |
664 | }, | |
07a476e0 | 665 | .probe = aw200xx_probe, |
36a87f37 MK |
666 | .id_table = aw200xx_id, |
667 | }; | |
668 | module_i2c_driver(aw200xx_driver); | |
669 | ||
670 | MODULE_AUTHOR("Martin Kurbanov <[email protected]>"); | |
671 | MODULE_DESCRIPTION("AW200XX LED driver"); | |
672 | MODULE_LICENSE("GPL"); |