1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Awinic AW20036/AW20054/AW20072/AW20108 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> |
13 | #include <linux/gpio/consumer.h> |
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 | |
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 | |
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 | #define AW200XX_REG_FADE2DIM(fade) \ |
91 | DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX) |
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; |
122 | struct gpio_desc *hwen; |
123 | struct aw200xx_led leds[] __counted_by(num_leds); |
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, fmt: "auto\n" ); |
135 | |
136 | return sysfs_emit(buf, fmt: "%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(s1: buf, s2: "auto" )) { |
150 | dim = -1; |
151 | } else { |
152 | ret = kstrtoint(s: buf, base: 0, res: &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(map: chip->regmap, |
164 | AW200XX_REG_DIM_PAGE1(led->num, columns), |
165 | val: dim); |
166 | if (ret) |
167 | goto out_unlock; |
168 | } |
169 | |
170 | led->dim = dim; |
171 | ret = count; |
172 | |
173 | out_unlock: |
174 | mutex_unlock(lock: &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) |
200 | dim = AW200XX_REG_FADE2DIM(brightness); |
201 | |
202 | ret = regmap_write(map: chip->regmap, reg, val: dim); |
203 | if (ret) |
204 | goto out_unlock; |
205 | |
206 | ret = regmap_write(map: chip->regmap, |
207 | AW200XX_REG_DIM2FADE(reg), val: brightness); |
208 | |
209 | out_unlock: |
210 | mutex_unlock(lock: &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 | static const 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(map: 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(map: chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | /* According to the datasheet software reset takes at least 1ms */ |
325 | fsleep(usecs: 1000); |
326 | |
327 | regcache_mark_dirty(map: chip->regmap); |
328 | return regmap_write(map: 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(map: chip->regmap, AW200XX_REG_DSIZE, |
336 | val: chip->display_rows - 1); |
337 | if (ret) |
338 | return ret; |
339 | |
340 | ret = regmap_write(map: chip->regmap, AW200XX_REG_SLPCR, |
341 | AW200XX_SLPCR_ACTIVE); |
342 | if (ret) |
343 | return ret; |
344 | |
345 | return regmap_update_bits(map: 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(map: chip->regmap, AW200XX_REG_IDR, val: &chipid); |
356 | if (ret) |
357 | return dev_err_probe(dev, err: ret, fmt: "Failed to read chip ID\n" ); |
358 | |
359 | if (chipid != AW200XX_IDR_CHIPID) |
360 | return dev_err_probe(dev, err: -ENODEV, |
361 | fmt: "Chip reported wrong ID: %x\n" , chipid); |
362 | |
363 | return 0; |
364 | } |
365 | |
366 | static void aw200xx_enable(const struct aw200xx *const chip) |
367 | { |
368 | gpiod_set_value_cansleep(desc: chip->hwen, value: 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(usecs: 400); |
378 | } |
379 | |
380 | static void aw200xx_disable(const struct aw200xx *const chip) |
381 | { |
382 | return gpiod_set_value_cansleep(desc: chip->hwen, value: 0); |
383 | } |
384 | |
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(fwnode: child, propname: "reg" , val: &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 | |
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 | |
417 | ret = aw200xx_probe_get_display_rows(dev, chip); |
418 | if (ret) |
419 | return dev_err_probe(dev, err: ret, |
420 | fmt: "No valid led definitions found\n" ); |
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(fwnode: child, propname: "reg" , val: &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(fwnode: child, propname: "led-max-microamp" , |
447 | val: &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; |
465 | led->cdev.max_brightness = AW200XX_FADE_MAX; |
466 | led->cdev.groups = dim_groups; |
467 | init_data.fwnode = child; |
468 | |
469 | ret = devm_led_classdev_register_ext(parent: dev, led_cdev: &led->cdev, |
470 | init_data: &init_data); |
471 | if (ret) { |
472 | fwnode_handle_put(fwnode: 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, led_imax_uA: 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, |
529 | .cache_type = REGCACHE_MAPLE, |
530 | .disable_locking = true, |
531 | }; |
532 | |
533 | static int aw200xx_probe(struct i2c_client *client) |
534 | { |
535 | const struct aw200xx_chipdef *cdef; |
536 | struct aw200xx *chip; |
537 | int count; |
538 | int ret; |
539 | |
540 | cdef = device_get_match_data(dev: &client->dev); |
541 | if (!cdef) |
542 | return -ENODEV; |
543 | |
544 | count = device_get_child_node_count(dev: &client->dev); |
545 | if (!count || count > cdef->channels) |
546 | return dev_err_probe(dev: &client->dev, err: -EINVAL, |
547 | fmt: "Incorrect number of leds (%d)" , count); |
548 | |
549 | chip = devm_kzalloc(dev: &client->dev, struct_size(chip, leds, count), |
550 | GFP_KERNEL); |
551 | if (!chip) |
552 | return -ENOMEM; |
553 | |
554 | chip->cdef = cdef; |
555 | chip->num_leds = count; |
556 | chip->client = client; |
557 | i2c_set_clientdata(client, data: chip); |
558 | |
559 | chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config); |
560 | if (IS_ERR(ptr: chip->regmap)) |
561 | return PTR_ERR(ptr: chip->regmap); |
562 | |
563 | chip->hwen = devm_gpiod_get_optional(dev: &client->dev, con_id: "enable" , |
564 | flags: GPIOD_OUT_HIGH); |
565 | if (IS_ERR(ptr: chip->hwen)) |
566 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: chip->hwen), |
567 | fmt: "Cannot get enable GPIO" ); |
568 | |
569 | aw200xx_enable(chip); |
570 | |
571 | ret = aw200xx_chip_check(chip); |
572 | if (ret) |
573 | return ret; |
574 | |
575 | mutex_init(&chip->mutex); |
576 | |
577 | /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */ |
578 | mutex_lock(&chip->mutex); |
579 | |
580 | ret = aw200xx_chip_reset(chip); |
581 | if (ret) |
582 | goto out_unlock; |
583 | |
584 | ret = aw200xx_probe_fw(dev: &client->dev, chip); |
585 | if (ret) |
586 | goto out_unlock; |
587 | |
588 | ret = aw200xx_chip_init(chip); |
589 | |
590 | out_unlock: |
591 | if (ret) |
592 | aw200xx_disable(chip); |
593 | |
594 | mutex_unlock(lock: &chip->mutex); |
595 | return ret; |
596 | } |
597 | |
598 | static void aw200xx_remove(struct i2c_client *client) |
599 | { |
600 | struct aw200xx *chip = i2c_get_clientdata(client); |
601 | |
602 | aw200xx_chip_reset(chip); |
603 | aw200xx_disable(chip); |
604 | mutex_destroy(lock: &chip->mutex); |
605 | } |
606 | |
607 | static const struct aw200xx_chipdef aw20036_cdef = { |
608 | .channels = 36, |
609 | .display_size_rows_max = 3, |
610 | .display_size_columns = 12, |
611 | }; |
612 | |
613 | static const struct aw200xx_chipdef aw20054_cdef = { |
614 | .channels = 54, |
615 | .display_size_rows_max = 6, |
616 | .display_size_columns = 9, |
617 | }; |
618 | |
619 | static const struct aw200xx_chipdef aw20072_cdef = { |
620 | .channels = 72, |
621 | .display_size_rows_max = 6, |
622 | .display_size_columns = 12, |
623 | }; |
624 | |
625 | static const struct aw200xx_chipdef aw20108_cdef = { |
626 | .channels = 108, |
627 | .display_size_rows_max = 9, |
628 | .display_size_columns = 12, |
629 | }; |
630 | |
631 | static const struct i2c_device_id aw200xx_id[] = { |
632 | { "aw20036" }, |
633 | { "aw20054" }, |
634 | { "aw20072" }, |
635 | { "aw20108" }, |
636 | {} |
637 | }; |
638 | MODULE_DEVICE_TABLE(i2c, aw200xx_id); |
639 | |
640 | static const struct of_device_id aw200xx_match_table[] = { |
641 | { .compatible = "awinic,aw20036" , .data = &aw20036_cdef, }, |
642 | { .compatible = "awinic,aw20054" , .data = &aw20054_cdef, }, |
643 | { .compatible = "awinic,aw20072" , .data = &aw20072_cdef, }, |
644 | { .compatible = "awinic,aw20108" , .data = &aw20108_cdef, }, |
645 | {} |
646 | }; |
647 | MODULE_DEVICE_TABLE(of, aw200xx_match_table); |
648 | |
649 | static struct i2c_driver aw200xx_driver = { |
650 | .driver = { |
651 | .name = "aw200xx" , |
652 | .of_match_table = aw200xx_match_table, |
653 | }, |
654 | .probe = aw200xx_probe, |
655 | .remove = aw200xx_remove, |
656 | .id_table = aw200xx_id, |
657 | }; |
658 | module_i2c_driver(aw200xx_driver); |
659 | |
660 | MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>" ); |
661 | MODULE_DESCRIPTION("AW200XX LED driver" ); |
662 | MODULE_LICENSE("GPL" ); |
663 | |