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.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 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 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 210 static bool aw99706_readable_reg(struct device *dev, unsigned int reg) 211 { 212 return aw99706_regs[reg] & REG_RD_ACCESS; 213 } 214 215 static bool aw99706_writeable_reg(struct device *dev, unsigned int reg) 216 { 217 return aw99706_regs[reg] & REG_WR_ACCESS; 218 } 219 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 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 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 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 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 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 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 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 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 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 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 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 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