1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * aw99706 - Backlight driver for the AWINIC AW99706
4 *
5 * Copyright (C) 2025 Junjie Cao <caojunjie650@gmail.com>
6 * Copyright (C) 2025 Pengyu Luo <mitltlatltl@gmail.com>
7 *
8 * Based on vendor driver:
9 * Copyright (c) 2023 AWINIC Technology CO., LTD
10 */
11
12 #include <linux/backlight.h>
13 #include <linux/bitfield.h>
14 #include <linux/delay.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/i2c.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/regmap.h>
20
21 #define AW99706_MAX_BRT_LVL 4095
22 #define AW99706_REG_MAX 0x1F
23 #define AW99706_ID 0x07
24
25 /* registers list */
26 #define AW99706_CFG0_REG 0x00
27 #define AW99706_DIM_MODE_MASK GENMASK(1, 0)
28
29 #define AW99706_CFG1_REG 0x01
30 #define AW99706_SW_FREQ_MASK GENMASK(3, 0)
31 #define AW99706_SW_ILMT_MASK GENMASK(5, 4)
32
33 #define AW99706_CFG2_REG 0x02
34 #define AW99706_ILED_MAX_MASK GENMASK(6, 0)
35 #define AW99706_UVLOSEL_MASK BIT(7)
36
37 #define AW99706_CFG3_REG 0x03
38 #define AW99706_CFG4_REG 0x04
39 #define AW99706_BRT_MSB_MASK GENMASK(3, 0)
40
41 #define AW99706_CFG5_REG 0x05
42 #define AW99706_BRT_LSB_MASK GENMASK(7, 0)
43
44 #define AW99706_CFG6_REG 0x06
45 #define AW99706_RAMP_CTL_MASK GENMASK(7, 6)
46
47 #define AW99706_CFG7_REG 0x07
48 #define AW99706_CFG8_REG 0x08
49 #define AW99706_CFG9_REG 0x09
50 #define AW99706_CFGA_REG 0x0A
51 #define AW99706_CFGB_REG 0x0B
52 #define AW99706_CFGC_REG 0x0C
53 #define AW99706_CFGD_REG 0x0D
54 #define AW99706_FLAG_REG 0x10
55 #define AW99706_BACKLIGHT_EN_MASK BIT(7)
56
57 #define AW99706_CHIPID_REG 0x11
58 #define AW99706_LED_OPEN_FLAG_REG 0x12
59 #define AW99706_LED_SHORT_FLAG_REG 0x13
60 #define AW99706_MTPLDOSEL_REG 0x1E
61 #define AW99706_MTPRUN_REG 0x1F
62
63 #define RESV 0
64
65 /* Boost switching frequency table, in Hz */
66 static const u32 aw99706_sw_freq_tbl[] = {
67 RESV, RESV, RESV, RESV, 300000, 400000, 500000, 600000,
68 660000, 750000, 850000, 1000000, 1200000, 1330000, 1500000, 1700000
69 };
70
71 /* Switching current limitation table, in uA */
72 static const u32 aw99706_sw_ilmt_tbl[] = {
73 1500000, 2000000, 2500000, 3000000
74 };
75
76 /* ULVO threshold table, in uV */
77 static const u32 aw99706_ulvo_thres_tbl[] = {
78 2200000, 5000000
79 };
80
81 struct aw99706_dt_prop {
82 const char * const name;
83 int (*lookup)(const struct aw99706_dt_prop *prop, u32 dt_val, u8 *val);
84 const u32 * const lookup_tbl;
85 u8 tbl_size;
86 u8 reg;
87 u8 mask;
88 u32 def_val;
89 };
90
aw99706_dt_property_lookup(const struct aw99706_dt_prop * prop,u32 dt_val,u8 * val)91 static int aw99706_dt_property_lookup(const struct aw99706_dt_prop *prop,
92 u32 dt_val, u8 *val)
93 {
94 int i;
95
96 if (!prop->lookup_tbl) {
97 *val = dt_val;
98 return 0;
99 }
100
101 for (i = 0; i < prop->tbl_size; i++)
102 if (prop->lookup_tbl[i] == dt_val)
103 break;
104
105 *val = i;
106
107 return i == prop->tbl_size ? -1 : 0;
108 }
109
110 #define MIN_ILED_MAX 5000
111 #define MAX_ILED_MAX 50000
112 #define STEP_ILED_MAX 500
113
114 static int
aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop * prop,u32 dt_val,u8 * val)115 aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop *prop,
116 u32 dt_val, u8 *val)
117 {
118 if (dt_val > MAX_ILED_MAX || dt_val < MIN_ILED_MAX)
119 return -1;
120
121 *val = (dt_val - MIN_ILED_MAX) / STEP_ILED_MAX;
122
123 return (dt_val - MIN_ILED_MAX) % STEP_ILED_MAX;
124 }
125
126 static const struct aw99706_dt_prop aw99706_dt_props[] = {
127 {
128 "awinic,dim-mode", aw99706_dt_property_lookup,
129 NULL, 0,
130 AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
131 },
132 {
133 "awinic,sw-freq", aw99706_dt_property_lookup,
134 aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
135 AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
136 },
137 {
138 "awinic,sw-ilmt", aw99706_dt_property_lookup,
139 aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
140 AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
141 },
142 {
143 "awinic,iled-max", aw99706_dt_property_iled_max_convert,
144 NULL, 0,
145 AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
146
147 },
148 {
149 "awinic,uvlo-thres", aw99706_dt_property_lookup,
150 aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
151 AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
152 },
153 {
154 "awinic,ramp-ctl", aw99706_dt_property_lookup,
155 NULL, 0,
156 AW99706_CFG6_REG, AW99706_RAMP_CTL_MASK, 2,
157 }
158 };
159
160 struct reg_init_data {
161 u8 reg;
162 u8 mask;
163 u8 val;
164 };
165
166 struct aw99706_device {
167 struct i2c_client *client;
168 struct device *dev;
169 struct regmap *regmap;
170 struct backlight_device *bl_dev;
171 struct gpio_desc *hwen_gpio;
172 struct reg_init_data init_tbl[ARRAY_SIZE(aw99706_dt_props)];
173 bool bl_enable;
174 };
175
176 enum reg_access {
177 REG_NONE_ACCESS = 0,
178 REG_RD_ACCESS = 1,
179 REG_WR_ACCESS = 2,
180 };
181
182 static const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
183 [AW99706_CFG0_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
184 [AW99706_CFG1_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
185 [AW99706_CFG2_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
186 [AW99706_CFG3_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
187 [AW99706_CFG4_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
188 [AW99706_CFG5_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
189 [AW99706_CFG6_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
190 [AW99706_CFG7_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
191 [AW99706_CFG8_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
192 [AW99706_CFG9_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
193 [AW99706_CFGA_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
194 [AW99706_CFGB_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
195 [AW99706_CFGC_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
196 [AW99706_CFGD_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
197 [AW99706_FLAG_REG] = REG_RD_ACCESS,
198 [AW99706_CHIPID_REG] = REG_RD_ACCESS,
199 [AW99706_LED_OPEN_FLAG_REG] = REG_RD_ACCESS,
200 [AW99706_LED_SHORT_FLAG_REG] = REG_RD_ACCESS,
201
202 /*
203 * Write bit is dropped here, writing BIT(0) to MTPLDOSEL will unlock
204 * Multi-time Programmable (MTP).
205 */
206 [AW99706_MTPLDOSEL_REG] = REG_RD_ACCESS,
207 [AW99706_MTPRUN_REG] = REG_NONE_ACCESS,
208 };
209
aw99706_readable_reg(struct device * dev,unsigned int reg)210 static bool aw99706_readable_reg(struct device *dev, unsigned int reg)
211 {
212 return aw99706_regs[reg] & REG_RD_ACCESS;
213 }
214
aw99706_writeable_reg(struct device * dev,unsigned int reg)215 static bool aw99706_writeable_reg(struct device *dev, unsigned int reg)
216 {
217 return aw99706_regs[reg] & REG_WR_ACCESS;
218 }
219
aw99706_i2c_read(struct aw99706_device * aw,u8 reg,unsigned int * val)220 static inline int aw99706_i2c_read(struct aw99706_device *aw, u8 reg,
221 unsigned int *val)
222 {
223 return regmap_read(aw->regmap, reg, val);
224 }
225
aw99706_i2c_write(struct aw99706_device * aw,u8 reg,u8 val)226 static inline int aw99706_i2c_write(struct aw99706_device *aw, u8 reg, u8 val)
227 {
228 return regmap_write(aw->regmap, reg, val);
229 }
230
aw99706_i2c_update_bits(struct aw99706_device * aw,u8 reg,u8 mask,u8 val)231 static inline int aw99706_i2c_update_bits(struct aw99706_device *aw, u8 reg,
232 u8 mask, u8 val)
233 {
234 return regmap_update_bits(aw->regmap, reg, mask, val);
235 }
236
aw99706_dt_parse(struct aw99706_device * aw,struct backlight_properties * bl_props)237 static void aw99706_dt_parse(struct aw99706_device *aw,
238 struct backlight_properties *bl_props)
239 {
240 const struct aw99706_dt_prop *prop;
241 u32 dt_val;
242 int ret, i;
243 u8 val;
244
245 for (i = 0; i < ARRAY_SIZE(aw99706_dt_props); i++) {
246 prop = &aw99706_dt_props[i];
247 ret = device_property_read_u32(aw->dev, prop->name, &dt_val);
248 if (ret < 0)
249 dt_val = prop->def_val;
250
251 if (prop->lookup(prop, dt_val, &val)) {
252 dev_warn(aw->dev, "invalid value %d for property %s, using default value %d\n",
253 dt_val, prop->name, prop->def_val);
254
255 prop->lookup(prop, prop->def_val, &val);
256 }
257
258 aw->init_tbl[i].reg = prop->reg;
259 aw->init_tbl[i].mask = prop->mask;
260 aw->init_tbl[i].val = val << __ffs(prop->mask);
261 }
262
263 bl_props->brightness = AW99706_MAX_BRT_LVL >> 1;
264 bl_props->max_brightness = AW99706_MAX_BRT_LVL;
265 device_property_read_u32(aw->dev, "default-brightness",
266 &bl_props->brightness);
267 device_property_read_u32(aw->dev, "max-brightness",
268 &bl_props->max_brightness);
269
270 if (bl_props->max_brightness > AW99706_MAX_BRT_LVL)
271 bl_props->max_brightness = AW99706_MAX_BRT_LVL;
272
273 if (bl_props->brightness > bl_props->max_brightness)
274 bl_props->brightness = bl_props->max_brightness;
275 }
276
aw99706_hw_init(struct aw99706_device * aw)277 static int aw99706_hw_init(struct aw99706_device *aw)
278 {
279 int ret, i;
280
281 gpiod_set_value_cansleep(aw->hwen_gpio, 1);
282
283 for (i = 0; i < ARRAY_SIZE(aw->init_tbl); i++) {
284 ret = aw99706_i2c_update_bits(aw, aw->init_tbl[i].reg,
285 aw->init_tbl[i].mask,
286 aw->init_tbl[i].val);
287 if (ret < 0) {
288 dev_err(aw->dev, "Failed to write init data %d\n", ret);
289 return ret;
290 }
291 }
292
293 return 0;
294 }
295
aw99706_bl_enable(struct aw99706_device * aw,bool en)296 static int aw99706_bl_enable(struct aw99706_device *aw, bool en)
297 {
298 int ret;
299 u8 val;
300
301 val = FIELD_PREP(AW99706_BACKLIGHT_EN_MASK, en);
302 ret = aw99706_i2c_update_bits(aw, AW99706_CFGD_REG,
303 AW99706_BACKLIGHT_EN_MASK, val);
304 if (ret)
305 dev_err(aw->dev, "Failed to enable backlight!\n");
306
307 return ret;
308 }
309
aw99706_update_brightness(struct aw99706_device * aw,u32 brt_lvl)310 static int aw99706_update_brightness(struct aw99706_device *aw, u32 brt_lvl)
311 {
312 bool bl_enable_now = !!brt_lvl;
313 int ret;
314
315 ret = aw99706_i2c_write(aw, AW99706_CFG4_REG,
316 (brt_lvl >> 8) & AW99706_BRT_MSB_MASK);
317 if (ret < 0)
318 return ret;
319
320 ret = aw99706_i2c_write(aw, AW99706_CFG5_REG,
321 brt_lvl & AW99706_BRT_LSB_MASK);
322 if (ret < 0)
323 return ret;
324
325 if (aw->bl_enable != bl_enable_now) {
326 ret = aw99706_bl_enable(aw, bl_enable_now);
327 if (!ret)
328 aw->bl_enable = bl_enable_now;
329 }
330
331 return ret;
332 }
333
aw99706_bl_update_status(struct backlight_device * bl)334 static int aw99706_bl_update_status(struct backlight_device *bl)
335 {
336 struct aw99706_device *aw = bl_get_data(bl);
337
338 return aw99706_update_brightness(aw, bl->props.brightness);
339 }
340
341 static const struct backlight_ops aw99706_bl_ops = {
342 .options = BL_CORE_SUSPENDRESUME,
343 .update_status = aw99706_bl_update_status,
344 };
345
346 static const struct regmap_config aw99706_regmap_config = {
347 .reg_bits = 8,
348 .val_bits = 8,
349 .max_register = AW99706_REG_MAX,
350 .writeable_reg = aw99706_writeable_reg,
351 .readable_reg = aw99706_readable_reg,
352 };
353
aw99706_chip_id_read(struct aw99706_device * aw)354 static int aw99706_chip_id_read(struct aw99706_device *aw)
355 {
356 int ret;
357 unsigned int val;
358
359 ret = aw99706_i2c_read(aw, AW99706_CHIPID_REG, &val);
360 if (ret < 0)
361 return ret;
362
363 return val;
364 }
365
aw99706_probe(struct i2c_client * client)366 static int aw99706_probe(struct i2c_client *client)
367 {
368 struct device *dev = &client->dev;
369 struct aw99706_device *aw;
370 struct backlight_device *bl_dev;
371 struct backlight_properties props = {};
372 int ret = 0;
373
374 aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
375 if (!aw)
376 return -ENOMEM;
377
378 aw->client = client;
379 aw->dev = dev;
380 i2c_set_clientdata(client, aw);
381
382 aw->regmap = devm_regmap_init_i2c(client, &aw99706_regmap_config);
383 if (IS_ERR(aw->regmap))
384 return dev_err_probe(dev, PTR_ERR(aw->regmap),
385 "Failed to init regmap\n");
386
387 ret = aw99706_chip_id_read(aw);
388 if (ret != AW99706_ID)
389 return dev_err_probe(dev, -ENODEV,
390 "Unknown chip id 0x%02x\n", ret);
391
392 aw99706_dt_parse(aw, &props);
393
394 aw->hwen_gpio = devm_gpiod_get(aw->dev, "enable", GPIOD_OUT_LOW);
395 if (IS_ERR(aw->hwen_gpio))
396 return dev_err_probe(dev, PTR_ERR(aw->hwen_gpio),
397 "Failed to get enable gpio\n");
398
399 ret = aw99706_hw_init(aw);
400 if (ret < 0)
401 return dev_err_probe(dev, ret,
402 "Failed to initialize the chip\n");
403
404 props.type = BACKLIGHT_RAW;
405 props.scale = BACKLIGHT_SCALE_LINEAR;
406
407 bl_dev = devm_backlight_device_register(dev, "aw99706-backlight", dev,
408 aw, &aw99706_bl_ops, &props);
409 if (IS_ERR(bl_dev))
410 return dev_err_probe(dev, PTR_ERR(bl_dev),
411 "Failed to register backlight!\n");
412
413 aw->bl_dev = bl_dev;
414
415 return 0;
416 }
417
aw99706_remove(struct i2c_client * client)418 static void aw99706_remove(struct i2c_client *client)
419 {
420 struct aw99706_device *aw = i2c_get_clientdata(client);
421
422 aw99706_update_brightness(aw, 0);
423
424 msleep(50);
425
426 gpiod_set_value_cansleep(aw->hwen_gpio, 0);
427 }
428
aw99706_suspend(struct device * dev)429 static int aw99706_suspend(struct device *dev)
430 {
431 struct aw99706_device *aw = dev_get_drvdata(dev);
432
433 return aw99706_update_brightness(aw, 0);
434 }
435
aw99706_resume(struct device * dev)436 static int aw99706_resume(struct device *dev)
437 {
438 struct aw99706_device *aw = dev_get_drvdata(dev);
439
440 return aw99706_hw_init(aw);
441 }
442
443 static DEFINE_SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume);
444
445 static const struct i2c_device_id aw99706_ids[] = {
446 { "aw99706" },
447 { }
448 };
449 MODULE_DEVICE_TABLE(i2c, aw99706_ids);
450
451 static const struct of_device_id aw99706_match_table[] = {
452 { .compatible = "awinic,aw99706", },
453 { }
454 };
455 MODULE_DEVICE_TABLE(of, aw99706_match_table);
456
457 static struct i2c_driver aw99706_i2c_driver = {
458 .probe = aw99706_probe,
459 .remove = aw99706_remove,
460 .id_table = aw99706_ids,
461 .driver = {
462 .name = "aw99706",
463 .of_match_table = aw99706_match_table,
464 .pm = pm_ptr(&aw99706_pm_ops),
465 },
466 };
467
468 module_i2c_driver(aw99706_i2c_driver);
469
470 MODULE_LICENSE("GPL v2");
471 MODULE_DESCRIPTION("BackLight driver for aw99706");
472