Commit | Line | Data |
---|---|---|
36a87f37 MK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Awinic AW20036/AW20054/AW20072 LED driver | |
4 | * | |
5 | * Copyright (c) 2023, SberDevices. All Rights Reserved. | |
6 | * | |
7 | * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru> | |
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) | |
90 | ||
91 | /* | |
92 | * Duty ratio of display scan (see p.15 of datasheet for formula): | |
93 | * duty = (592us / 600.5us) * (1 / (display_rows + 1)) | |
94 | * | |
95 | * Multiply to 1000 (MILLI) to improve the accuracy of calculations. | |
96 | */ | |
97 | #define AW200XX_DUTY_RATIO(rows) \ | |
98 | (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI) | |
99 | ||
100 | struct aw200xx_chipdef { | |
101 | u32 channels; | |
102 | u32 display_size_rows_max; | |
103 | u32 display_size_columns; | |
104 | }; | |
105 | ||
106 | struct aw200xx_led { | |
107 | struct led_classdev cdev; | |
108 | struct aw200xx *chip; | |
109 | int dim; | |
110 | u32 num; | |
111 | }; | |
112 | ||
113 | struct aw200xx { | |
114 | const struct aw200xx_chipdef *cdef; | |
115 | struct i2c_client *client; | |
116 | struct regmap *regmap; | |
117 | struct mutex mutex; | |
118 | u32 num_leds; | |
119 | u32 display_rows; | |
d882762f | 120 | struct gpio_desc *hwen; |
ff861ca9 | 121 | struct aw200xx_led leds[] __counted_by(num_leds); |
36a87f37 MK |
122 | }; |
123 | ||
124 | static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, | |
125 | char *buf) | |
126 | { | |
127 | struct led_classdev *cdev = dev_get_drvdata(dev); | |
128 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
129 | int dim = led->dim; | |
130 | ||
131 | if (dim < 0) | |
132 | return sysfs_emit(buf, "auto\n"); | |
133 | ||
134 | return sysfs_emit(buf, "%d\n", dim); | |
135 | } | |
136 | ||
137 | static ssize_t dim_store(struct device *dev, struct device_attribute *devattr, | |
138 | const char *buf, size_t count) | |
139 | { | |
140 | struct led_classdev *cdev = dev_get_drvdata(dev); | |
141 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
142 | struct aw200xx *chip = led->chip; | |
143 | u32 columns = chip->cdef->display_size_columns; | |
144 | int dim; | |
145 | ssize_t ret; | |
146 | ||
147 | if (sysfs_streq(buf, "auto")) { | |
148 | dim = -1; | |
149 | } else { | |
150 | ret = kstrtoint(buf, 0, &dim); | |
151 | if (ret) | |
152 | return ret; | |
153 | ||
154 | if (dim > AW200XX_DIM_MAX) | |
155 | return -EINVAL; | |
156 | } | |
157 | ||
158 | mutex_lock(&chip->mutex); | |
159 | ||
160 | if (dim >= 0) { | |
161 | ret = regmap_write(chip->regmap, | |
adfd4621 MK |
162 | AW200XX_REG_DIM_PAGE1(led->num, columns), |
163 | dim); | |
36a87f37 MK |
164 | if (ret) |
165 | goto out_unlock; | |
166 | } | |
167 | ||
168 | led->dim = dim; | |
169 | ret = count; | |
170 | ||
171 | out_unlock: | |
172 | mutex_unlock(&chip->mutex); | |
173 | return ret; | |
174 | } | |
175 | static DEVICE_ATTR_RW(dim); | |
176 | ||
177 | static struct attribute *dim_attrs[] = { | |
178 | &dev_attr_dim.attr, | |
179 | NULL | |
180 | }; | |
181 | ATTRIBUTE_GROUPS(dim); | |
182 | ||
183 | static int aw200xx_brightness_set(struct led_classdev *cdev, | |
184 | enum led_brightness brightness) | |
185 | { | |
186 | struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); | |
187 | struct aw200xx *chip = led->chip; | |
188 | int dim; | |
189 | u32 reg; | |
190 | int ret; | |
191 | ||
192 | mutex_lock(&chip->mutex); | |
193 | ||
194 | reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns); | |
195 | ||
196 | dim = led->dim; | |
197 | if (dim < 0) | |
198 | dim = max_t(int, | |
199 | brightness / (AW200XX_FADE_MAX / AW200XX_DIM_MAX), | |
200 | 1); | |
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); | |
285 | u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16}; | |
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 | ||
324 | regcache_mark_dirty(chip->regmap); | |
325 | return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); | |
326 | } | |
327 | ||
328 | static int aw200xx_chip_init(const struct aw200xx *const chip) | |
329 | { | |
330 | int ret; | |
331 | ||
332 | ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE, | |
333 | chip->display_rows - 1); | |
334 | if (ret) | |
335 | return ret; | |
336 | ||
337 | ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR, | |
338 | AW200XX_SLPCR_ACTIVE); | |
339 | if (ret) | |
340 | return ret; | |
341 | ||
342 | return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, | |
343 | AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON); | |
344 | } | |
345 | ||
346 | static int aw200xx_chip_check(const struct aw200xx *const chip) | |
347 | { | |
348 | struct device *dev = &chip->client->dev; | |
349 | u32 chipid; | |
350 | int ret; | |
351 | ||
352 | ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid); | |
353 | if (ret) | |
354 | return dev_err_probe(dev, ret, "Failed to read chip ID\n"); | |
355 | ||
356 | if (chipid != AW200XX_IDR_CHIPID) | |
357 | return dev_err_probe(dev, -ENODEV, | |
358 | "Chip reported wrong ID: %x\n", chipid); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
d882762f DR |
363 | static void aw200xx_enable(const struct aw200xx *const chip) |
364 | { | |
365 | gpiod_set_value_cansleep(chip->hwen, 1); | |
366 | ||
367 | /* | |
368 | * After HWEN pin set high the chip begins to load the OTP information, | |
369 | * which takes 200us to complete. About 200us wait time is needed for | |
370 | * internal oscillator startup and display SRAM initialization. After | |
371 | * display SRAM initialization, the registers in page1 to page5 can be | |
372 | * configured via i2c interface. | |
373 | */ | |
374 | fsleep(400); | |
375 | } | |
376 | ||
377 | static void aw200xx_disable(const struct aw200xx *const chip) | |
378 | { | |
379 | return gpiod_set_value_cansleep(chip->hwen, 0); | |
380 | } | |
381 | ||
36a87f37 MK |
382 | static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) |
383 | { | |
384 | struct fwnode_handle *child; | |
385 | u32 current_min, current_max, min_uA; | |
386 | int ret; | |
387 | int i; | |
388 | ||
389 | ret = device_property_read_u32(dev, "awinic,display-rows", | |
390 | &chip->display_rows); | |
391 | if (ret) | |
392 | return dev_err_probe(dev, ret, | |
393 | "Failed to read 'display-rows' property\n"); | |
394 | ||
395 | if (!chip->display_rows || | |
396 | chip->display_rows > chip->cdef->display_size_rows_max) { | |
ad5152b8 | 397 | return dev_err_probe(dev, -EINVAL, |
36a87f37 MK |
398 | "Invalid leds display size %u\n", |
399 | chip->display_rows); | |
400 | } | |
401 | ||
402 | current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); | |
403 | current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); | |
404 | min_uA = UINT_MAX; | |
405 | i = 0; | |
406 | ||
407 | device_for_each_child_node(dev, child) { | |
408 | struct led_init_data init_data = {}; | |
409 | struct aw200xx_led *led; | |
410 | u32 source, imax; | |
411 | ||
412 | ret = fwnode_property_read_u32(child, "reg", &source); | |
413 | if (ret) { | |
414 | dev_err(dev, "Missing reg property\n"); | |
415 | chip->num_leds--; | |
416 | continue; | |
417 | } | |
418 | ||
419 | if (source >= chip->cdef->channels) { | |
420 | dev_err(dev, "LED reg %u out of range (max %u)\n", | |
421 | source, chip->cdef->channels); | |
422 | chip->num_leds--; | |
423 | continue; | |
424 | } | |
425 | ||
426 | ret = fwnode_property_read_u32(child, "led-max-microamp", | |
427 | &imax); | |
428 | if (ret) { | |
429 | dev_info(&chip->client->dev, | |
430 | "DT property led-max-microamp is missing\n"); | |
431 | } else if (imax < current_min || imax > current_max) { | |
432 | dev_err(dev, "Invalid value %u for led-max-microamp\n", | |
433 | imax); | |
434 | chip->num_leds--; | |
435 | continue; | |
436 | } else { | |
437 | min_uA = min(min_uA, imax); | |
438 | } | |
439 | ||
440 | led = &chip->leds[i]; | |
441 | led->dim = -1; | |
442 | led->num = source; | |
443 | led->chip = chip; | |
444 | led->cdev.brightness_set_blocking = aw200xx_brightness_set; | |
445 | led->cdev.groups = dim_groups; | |
446 | init_data.fwnode = child; | |
447 | ||
448 | ret = devm_led_classdev_register_ext(dev, &led->cdev, | |
449 | &init_data); | |
450 | if (ret) { | |
451 | fwnode_handle_put(child); | |
452 | break; | |
453 | } | |
454 | ||
455 | i++; | |
456 | } | |
457 | ||
458 | if (!chip->num_leds) | |
459 | return -EINVAL; | |
460 | ||
461 | if (min_uA == UINT_MAX) { | |
462 | min_uA = aw200xx_imax_from_global(chip, | |
463 | AW200XX_IMAX_DEFAULT_uA); | |
464 | } | |
465 | ||
466 | return aw200xx_set_imax(chip, min_uA); | |
467 | } | |
468 | ||
469 | static const struct regmap_range_cfg aw200xx_ranges[] = { | |
470 | { | |
471 | .name = "aw200xx", | |
472 | .range_min = 0, | |
473 | .range_max = AW200XX_REG_MAX, | |
474 | .selector_reg = AW200XX_REG_PAGE, | |
475 | .selector_mask = AW200XX_PAGE_MASK, | |
476 | .selector_shift = AW200XX_PAGE_SHIFT, | |
477 | .window_start = 0, | |
478 | .window_len = AW200XX_PAGE_SIZE, | |
479 | }, | |
480 | }; | |
481 | ||
482 | static const struct regmap_range aw200xx_writeonly_ranges[] = { | |
483 | regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX), | |
484 | }; | |
485 | ||
486 | static const struct regmap_access_table aw200xx_readable_table = { | |
487 | .no_ranges = aw200xx_writeonly_ranges, | |
488 | .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges), | |
489 | }; | |
490 | ||
491 | static const struct regmap_range aw200xx_readonly_ranges[] = { | |
492 | regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR), | |
493 | }; | |
494 | ||
495 | static const struct regmap_access_table aw200xx_writeable_table = { | |
496 | .no_ranges = aw200xx_readonly_ranges, | |
497 | .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges), | |
498 | }; | |
499 | ||
500 | static const struct regmap_config aw200xx_regmap_config = { | |
501 | .reg_bits = 8, | |
502 | .val_bits = 8, | |
503 | .max_register = AW200XX_REG_MAX, | |
504 | .ranges = aw200xx_ranges, | |
505 | .num_ranges = ARRAY_SIZE(aw200xx_ranges), | |
506 | .rd_table = &aw200xx_readable_table, | |
507 | .wr_table = &aw200xx_writeable_table, | |
65e9b513 | 508 | .cache_type = REGCACHE_MAPLE, |
36a87f37 MK |
509 | }; |
510 | ||
511 | static int aw200xx_probe(struct i2c_client *client) | |
512 | { | |
513 | const struct aw200xx_chipdef *cdef; | |
514 | struct aw200xx *chip; | |
515 | int count; | |
516 | int ret; | |
517 | ||
518 | cdef = device_get_match_data(&client->dev); | |
519 | if (!cdef) | |
520 | return -ENODEV; | |
521 | ||
522 | count = device_get_child_node_count(&client->dev); | |
523 | if (!count || count > cdef->channels) | |
524 | return dev_err_probe(&client->dev, -EINVAL, | |
525 | "Incorrect number of leds (%d)", count); | |
526 | ||
527 | chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), | |
528 | GFP_KERNEL); | |
529 | if (!chip) | |
530 | return -ENOMEM; | |
531 | ||
532 | chip->cdef = cdef; | |
533 | chip->num_leds = count; | |
534 | chip->client = client; | |
535 | i2c_set_clientdata(client, chip); | |
536 | ||
537 | chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config); | |
538 | if (IS_ERR(chip->regmap)) | |
539 | return PTR_ERR(chip->regmap); | |
540 | ||
d882762f DR |
541 | chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", |
542 | GPIOD_OUT_HIGH); | |
543 | if (IS_ERR(chip->hwen)) | |
544 | return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), | |
545 | "Cannot get enable GPIO"); | |
546 | ||
547 | aw200xx_enable(chip); | |
548 | ||
36a87f37 MK |
549 | ret = aw200xx_chip_check(chip); |
550 | if (ret) | |
551 | return ret; | |
552 | ||
553 | mutex_init(&chip->mutex); | |
554 | ||
555 | /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */ | |
556 | mutex_lock(&chip->mutex); | |
557 | ||
558 | ret = aw200xx_chip_reset(chip); | |
559 | if (ret) | |
560 | goto out_unlock; | |
561 | ||
562 | ret = aw200xx_probe_fw(&client->dev, chip); | |
563 | if (ret) | |
564 | goto out_unlock; | |
565 | ||
566 | ret = aw200xx_chip_init(chip); | |
567 | ||
568 | out_unlock: | |
d882762f DR |
569 | if (ret) |
570 | aw200xx_disable(chip); | |
571 | ||
36a87f37 MK |
572 | mutex_unlock(&chip->mutex); |
573 | return ret; | |
574 | } | |
575 | ||
576 | static void aw200xx_remove(struct i2c_client *client) | |
577 | { | |
578 | struct aw200xx *chip = i2c_get_clientdata(client); | |
579 | ||
580 | aw200xx_chip_reset(chip); | |
d882762f | 581 | aw200xx_disable(chip); |
36a87f37 MK |
582 | mutex_destroy(&chip->mutex); |
583 | } | |
584 | ||
585 | static const struct aw200xx_chipdef aw20036_cdef = { | |
586 | .channels = 36, | |
587 | .display_size_rows_max = 3, | |
588 | .display_size_columns = 12, | |
589 | }; | |
590 | ||
591 | static const struct aw200xx_chipdef aw20054_cdef = { | |
592 | .channels = 54, | |
593 | .display_size_rows_max = 6, | |
594 | .display_size_columns = 9, | |
595 | }; | |
596 | ||
597 | static const struct aw200xx_chipdef aw20072_cdef = { | |
598 | .channels = 72, | |
599 | .display_size_rows_max = 6, | |
600 | .display_size_columns = 12, | |
601 | }; | |
602 | ||
603 | static const struct i2c_device_id aw200xx_id[] = { | |
604 | { "aw20036" }, | |
605 | { "aw20054" }, | |
606 | { "aw20072" }, | |
607 | {} | |
608 | }; | |
609 | MODULE_DEVICE_TABLE(i2c, aw200xx_id); | |
610 | ||
611 | static const struct of_device_id aw200xx_match_table[] = { | |
612 | { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, | |
613 | { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, | |
614 | { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, | |
615 | {} | |
616 | }; | |
617 | MODULE_DEVICE_TABLE(of, aw200xx_match_table); | |
618 | ||
619 | static struct i2c_driver aw200xx_driver = { | |
620 | .driver = { | |
621 | .name = "aw200xx", | |
622 | .of_match_table = aw200xx_match_table, | |
623 | }, | |
07a476e0 | 624 | .probe = aw200xx_probe, |
36a87f37 MK |
625 | .remove = aw200xx_remove, |
626 | .id_table = aw200xx_id, | |
627 | }; | |
628 | module_i2c_driver(aw200xx_driver); | |
629 | ||
630 | MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>"); | |
631 | MODULE_DESCRIPTION("AW200XX LED driver"); | |
632 | MODULE_LICENSE("GPL"); |