xref: /linux/drivers/video/backlight/aw99706.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
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	U32_MAX
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 
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 		if (dt_val > (prop->mask >> __ffs(prop->mask)))
98 			return -EINVAL;
99 		*val = dt_val;
100 		return 0;
101 	}
102 
103 	for (i = 0; i < prop->tbl_size; i++)
104 		if (prop->lookup_tbl[i] != RESV && prop->lookup_tbl[i] == dt_val)
105 			break;
106 
107 	*val = i;
108 
109 	return i == prop->tbl_size ? -EINVAL : 0;
110 }
111 
112 #define MIN_ILED_MAX	5000
113 #define MAX_ILED_MAX	50000
114 #define STEP_ILED_MAX	500
115 
116 static int
117 aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop *prop,
118 				     u32 dt_val, u8 *val)
119 {
120 	if (dt_val > MAX_ILED_MAX || dt_val < MIN_ILED_MAX)
121 		return -EINVAL;
122 
123 	if ((dt_val - MIN_ILED_MAX) % STEP_ILED_MAX)
124 		return -EINVAL;
125 
126 	*val = (dt_val - MIN_ILED_MAX) / STEP_ILED_MAX;
127 
128 	return 0;
129 }
130 
131 static const struct aw99706_dt_prop aw99706_dt_props[] = {
132 	{
133 		"awinic,dim-mode", aw99706_dt_property_lookup,
134 		NULL, 0,
135 		AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
136 	},
137 	{
138 		"awinic,sw-freq-hz", aw99706_dt_property_lookup,
139 		aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
140 		AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
141 	},
142 	{
143 		"awinic,sw-ilmt-microamp", aw99706_dt_property_lookup,
144 		aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
145 		AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
146 	},
147 	{
148 		"awinic,iled-max-microamp", aw99706_dt_property_iled_max_convert,
149 		NULL, 0,
150 		AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
151 
152 	},
153 	{
154 		"awinic,uvlo-thres-microvolt", aw99706_dt_property_lookup,
155 		aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
156 		AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
157 	},
158 	{
159 		"awinic,ramp-ctl", aw99706_dt_property_lookup,
160 		NULL, 0,
161 		AW99706_CFG6_REG, AW99706_RAMP_CTL_MASK, 2,
162 	}
163 };
164 
165 struct reg_init_data {
166 	u8 reg;
167 	u8 mask;
168 	u8 val;
169 };
170 
171 struct aw99706_device {
172 	struct i2c_client *client;
173 	struct device *dev;
174 	struct regmap *regmap;
175 	struct backlight_device *bl_dev;
176 	struct gpio_desc *hwen_gpio;
177 	struct reg_init_data init_tbl[ARRAY_SIZE(aw99706_dt_props)];
178 	bool bl_enable;
179 };
180 
181 enum reg_access {
182 	REG_NONE_ACCESS	= 0,
183 	REG_RD_ACCESS	= 1,
184 	REG_WR_ACCESS	= 2,
185 };
186 
187 static const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
188 	[AW99706_CFG0_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
189 	[AW99706_CFG1_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
190 	[AW99706_CFG2_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
191 	[AW99706_CFG3_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
192 	[AW99706_CFG4_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
193 	[AW99706_CFG5_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
194 	[AW99706_CFG6_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
195 	[AW99706_CFG7_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
196 	[AW99706_CFG8_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
197 	[AW99706_CFG9_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
198 	[AW99706_CFGA_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
199 	[AW99706_CFGB_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
200 	[AW99706_CFGC_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
201 	[AW99706_CFGD_REG]		= REG_RD_ACCESS | REG_WR_ACCESS,
202 	[AW99706_FLAG_REG]		= REG_RD_ACCESS,
203 	[AW99706_CHIPID_REG]		= REG_RD_ACCESS,
204 	[AW99706_LED_OPEN_FLAG_REG]	= REG_RD_ACCESS,
205 	[AW99706_LED_SHORT_FLAG_REG]	= REG_RD_ACCESS,
206 
207 	/*
208 	 * Write bit is dropped here, writing BIT(0) to MTPLDOSEL will unlock
209 	 * Multi-time Programmable (MTP).
210 	 */
211 	[AW99706_MTPLDOSEL_REG]		= REG_RD_ACCESS,
212 	[AW99706_MTPRUN_REG]		= REG_NONE_ACCESS,
213 };
214 
215 static bool aw99706_readable_reg(struct device *dev, unsigned int reg)
216 {
217 	return aw99706_regs[reg] & REG_RD_ACCESS;
218 }
219 
220 static bool aw99706_writeable_reg(struct device *dev, unsigned int reg)
221 {
222 	return aw99706_regs[reg] & REG_WR_ACCESS;
223 }
224 
225 static inline int aw99706_i2c_read(struct aw99706_device *aw, u8 reg,
226 				   unsigned int *val)
227 {
228 	return regmap_read(aw->regmap, reg, val);
229 }
230 
231 static inline int aw99706_i2c_write(struct aw99706_device *aw, u8 reg, u8 val)
232 {
233 	return regmap_write(aw->regmap, reg, val);
234 }
235 
236 static inline int aw99706_i2c_update_bits(struct aw99706_device *aw, u8 reg,
237 					  u8 mask, u8 val)
238 {
239 	return regmap_update_bits(aw->regmap, reg, mask, val);
240 }
241 
242 static void aw99706_dt_parse(struct aw99706_device *aw,
243 			     struct backlight_properties *bl_props)
244 {
245 	const struct aw99706_dt_prop *prop;
246 	u32 dt_val;
247 	int ret, i;
248 	u8 val;
249 
250 	for (i = 0; i < ARRAY_SIZE(aw99706_dt_props); i++) {
251 		prop = &aw99706_dt_props[i];
252 		ret = device_property_read_u32(aw->dev, prop->name, &dt_val);
253 		if (ret < 0)
254 			dt_val = prop->def_val;
255 
256 		if (prop->lookup(prop, dt_val, &val)) {
257 			dev_warn(aw->dev, "invalid value %d for property %s, using default value %d\n",
258 				 dt_val, prop->name, prop->def_val);
259 
260 			prop->lookup(prop, prop->def_val, &val);
261 		}
262 
263 		aw->init_tbl[i].reg = prop->reg;
264 		aw->init_tbl[i].mask = prop->mask;
265 		aw->init_tbl[i].val = val << __ffs(prop->mask);
266 	}
267 
268 	bl_props->brightness = AW99706_MAX_BRT_LVL >> 1;
269 	bl_props->max_brightness = AW99706_MAX_BRT_LVL;
270 	device_property_read_u32(aw->dev, "default-brightness",
271 				 &bl_props->brightness);
272 	device_property_read_u32(aw->dev, "max-brightness",
273 				 &bl_props->max_brightness);
274 
275 	if (bl_props->max_brightness > AW99706_MAX_BRT_LVL)
276 		bl_props->max_brightness = AW99706_MAX_BRT_LVL;
277 
278 	if (bl_props->brightness > bl_props->max_brightness)
279 		bl_props->brightness = bl_props->max_brightness;
280 }
281 
282 static int aw99706_hw_init(struct aw99706_device *aw)
283 {
284 	int ret, i;
285 
286 	gpiod_set_value_cansleep(aw->hwen_gpio, 1);
287 
288 	for (i = 0; i < ARRAY_SIZE(aw->init_tbl); i++) {
289 		ret = aw99706_i2c_update_bits(aw, aw->init_tbl[i].reg,
290 					      aw->init_tbl[i].mask,
291 					      aw->init_tbl[i].val);
292 		if (ret < 0) {
293 			dev_err(aw->dev, "Failed to write init data %d\n", ret);
294 			return ret;
295 		}
296 	}
297 
298 	return 0;
299 }
300 
301 static int aw99706_bl_enable(struct aw99706_device *aw, bool en)
302 {
303 	int ret;
304 	u8 val;
305 
306 	val = FIELD_PREP(AW99706_BACKLIGHT_EN_MASK, en);
307 	ret = aw99706_i2c_update_bits(aw, AW99706_CFGD_REG,
308 				      AW99706_BACKLIGHT_EN_MASK, val);
309 	if (ret)
310 		dev_err(aw->dev, "Failed to enable backlight!\n");
311 
312 	return ret;
313 }
314 
315 static int aw99706_update_brightness(struct aw99706_device *aw, u32 brt_lvl)
316 {
317 	bool bl_enable_now = !!brt_lvl;
318 	int ret;
319 
320 	ret = aw99706_i2c_write(aw, AW99706_CFG4_REG,
321 				(brt_lvl >> 8) & AW99706_BRT_MSB_MASK);
322 	if (ret < 0)
323 		return ret;
324 
325 	ret = aw99706_i2c_write(aw, AW99706_CFG5_REG,
326 				brt_lvl & AW99706_BRT_LSB_MASK);
327 	if (ret < 0)
328 		return ret;
329 
330 	if (aw->bl_enable != bl_enable_now) {
331 		ret = aw99706_bl_enable(aw, bl_enable_now);
332 		if (!ret)
333 			aw->bl_enable = bl_enable_now;
334 	}
335 
336 	return ret;
337 }
338 
339 static int aw99706_bl_update_status(struct backlight_device *bl)
340 {
341 	struct aw99706_device *aw = bl_get_data(bl);
342 
343 	return aw99706_update_brightness(aw, backlight_get_brightness(bl));
344 }
345 
346 static const struct backlight_ops aw99706_bl_ops = {
347 	.options = BL_CORE_SUSPENDRESUME,
348 	.update_status = aw99706_bl_update_status,
349 };
350 
351 static const struct regmap_config aw99706_regmap_config = {
352 	.reg_bits = 8,
353 	.val_bits = 8,
354 	.max_register = AW99706_REG_MAX,
355 	.writeable_reg = aw99706_writeable_reg,
356 	.readable_reg = aw99706_readable_reg,
357 };
358 
359 static int aw99706_chip_id_read(struct aw99706_device *aw)
360 {
361 	int ret;
362 	unsigned int val;
363 
364 	ret = aw99706_i2c_read(aw, AW99706_CHIPID_REG, &val);
365 	if (ret < 0)
366 		return ret;
367 
368 	return val;
369 }
370 
371 static int aw99706_probe(struct i2c_client *client)
372 {
373 	struct device *dev = &client->dev;
374 	struct aw99706_device *aw;
375 	struct backlight_device *bl_dev;
376 	struct backlight_properties props = {};
377 	int ret = 0;
378 
379 	aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
380 	if (!aw)
381 		return -ENOMEM;
382 
383 	aw->client = client;
384 	aw->dev = dev;
385 	i2c_set_clientdata(client, aw);
386 
387 	aw->regmap = devm_regmap_init_i2c(client, &aw99706_regmap_config);
388 	if (IS_ERR(aw->regmap))
389 		return dev_err_probe(dev, PTR_ERR(aw->regmap),
390 				     "Failed to init regmap\n");
391 
392 	ret = aw99706_chip_id_read(aw);
393 	if (ret != AW99706_ID)
394 		return dev_err_probe(dev, -ENODEV,
395 				     "Unknown chip id 0x%02x\n", ret);
396 
397 	aw99706_dt_parse(aw, &props);
398 
399 	aw->hwen_gpio = devm_gpiod_get(aw->dev, "enable", GPIOD_OUT_LOW);
400 	if (IS_ERR(aw->hwen_gpio))
401 		return dev_err_probe(dev, PTR_ERR(aw->hwen_gpio),
402 				     "Failed to get enable gpio\n");
403 
404 	ret = aw99706_hw_init(aw);
405 	if (ret < 0)
406 		return dev_err_probe(dev, ret,
407 				     "Failed to initialize the chip\n");
408 
409 	props.type = BACKLIGHT_RAW;
410 	props.scale = BACKLIGHT_SCALE_LINEAR;
411 
412 	bl_dev = devm_backlight_device_register(dev, "aw99706-backlight", dev,
413 						aw, &aw99706_bl_ops, &props);
414 	if (IS_ERR(bl_dev))
415 		return dev_err_probe(dev, PTR_ERR(bl_dev),
416 				     "Failed to register backlight!\n");
417 
418 	aw->bl_dev = bl_dev;
419 
420 	return 0;
421 }
422 
423 static void aw99706_remove(struct i2c_client *client)
424 {
425 	struct aw99706_device *aw = i2c_get_clientdata(client);
426 
427 	aw99706_update_brightness(aw, 0);
428 
429 	msleep(50);
430 
431 	gpiod_set_value_cansleep(aw->hwen_gpio, 0);
432 }
433 
434 static int aw99706_suspend(struct device *dev)
435 {
436 	struct aw99706_device *aw = dev_get_drvdata(dev);
437 
438 	return aw99706_update_brightness(aw, 0);
439 }
440 
441 static int aw99706_resume(struct device *dev)
442 {
443 	struct aw99706_device *aw = dev_get_drvdata(dev);
444 
445 	return aw99706_hw_init(aw);
446 }
447 
448 static DEFINE_SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume);
449 
450 static const struct i2c_device_id aw99706_ids[] = {
451 	{ .name = "aw99706" },
452 	{ }
453 };
454 MODULE_DEVICE_TABLE(i2c, aw99706_ids);
455 
456 static const struct of_device_id aw99706_match_table[] = {
457 	{ .compatible = "awinic,aw99706", },
458 	{ }
459 };
460 MODULE_DEVICE_TABLE(of, aw99706_match_table);
461 
462 static struct i2c_driver aw99706_i2c_driver = {
463 	.probe = aw99706_probe,
464 	.remove = aw99706_remove,
465 	.id_table = aw99706_ids,
466 	.driver = {
467 		.name = "aw99706",
468 		.of_match_table = aw99706_match_table,
469 		.pm = pm_ptr(&aw99706_pm_ops),
470 	},
471 };
472 
473 module_i2c_driver(aw99706_i2c_driver);
474 
475 MODULE_LICENSE("GPL v2");
476 MODULE_DESCRIPTION("BackLight driver for aw99706");
477