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