1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <linux/backlight.h> 3 #include <linux/delay.h> 4 #include <linux/gpio/consumer.h> 5 #include <linux/i2c.h> 6 #include <linux/module.h> 7 #include <linux/regmap.h> 8 9 #define LM3509_NAME "lm3509_bl" 10 11 #define LM3509_SINK_MAIN 0 12 #define LM3509_SINK_SUB 1 13 #define LM3509_NUM_SINKS 2 14 15 #define LM3509_DEF_BRIGHTNESS 0x12 16 #define LM3509_MAX_BRIGHTNESS 0x1F 17 18 #define REG_GP 0x10 19 #define REG_BMAIN 0xA0 20 #define REG_BSUB 0xB0 21 #define REG_MAX 0xFF 22 23 enum { 24 REG_GP_ENM_BIT = 0, 25 REG_GP_ENS_BIT, 26 REG_GP_UNI_BIT, 27 REG_GP_RMP0_BIT, 28 REG_GP_RMP1_BIT, 29 REG_GP_OLED_BIT, 30 }; 31 32 struct lm3509_bl { 33 struct regmap *regmap; 34 struct backlight_device *bl_main; 35 struct backlight_device *bl_sub; 36 struct gpio_desc *reset_gpio; 37 }; 38 39 struct lm3509_bl_led_data { 40 const char *label; 41 int led_sources; 42 u32 brightness; 43 u32 max_brightness; 44 }; 45 46 static void lm3509_reset(struct lm3509_bl *data) 47 { 48 if (data->reset_gpio) { 49 gpiod_set_value(data->reset_gpio, 1); 50 udelay(1); 51 gpiod_set_value(data->reset_gpio, 0); 52 udelay(10); 53 } 54 } 55 56 static int lm3509_update_status(struct backlight_device *bl, 57 unsigned int en_mask, unsigned int br_reg) 58 { 59 struct lm3509_bl *data = bl_get_data(bl); 60 int ret; 61 bool en; 62 63 ret = regmap_write(data->regmap, br_reg, backlight_get_brightness(bl)); 64 if (ret < 0) 65 return ret; 66 67 en = !backlight_is_blank(bl); 68 return regmap_update_bits(data->regmap, REG_GP, en_mask, 69 en ? en_mask : 0); 70 } 71 72 static int lm3509_main_update_status(struct backlight_device *bl) 73 { 74 return lm3509_update_status(bl, BIT(REG_GP_ENM_BIT), REG_BMAIN); 75 } 76 77 static const struct backlight_ops lm3509_main_ops = { 78 .options = BL_CORE_SUSPENDRESUME, 79 .update_status = lm3509_main_update_status, 80 }; 81 82 static int lm3509_sub_update_status(struct backlight_device *bl) 83 { 84 return lm3509_update_status(bl, BIT(REG_GP_ENS_BIT), REG_BSUB); 85 } 86 87 static const struct backlight_ops lm3509_sub_ops = { 88 .options = BL_CORE_SUSPENDRESUME, 89 .update_status = lm3509_sub_update_status, 90 }; 91 92 static struct backlight_device * 93 lm3509_backlight_register(struct device *dev, const char *name_suffix, 94 struct lm3509_bl *data, 95 const struct backlight_ops *ops, 96 const struct lm3509_bl_led_data *led_data) 97 98 { 99 struct backlight_device *bd; 100 struct backlight_properties props; 101 const char *label = led_data->label; 102 char name[64]; 103 104 memset(&props, 0, sizeof(props)); 105 props.type = BACKLIGHT_RAW; 106 props.brightness = led_data->brightness; 107 props.max_brightness = led_data->max_brightness; 108 props.scale = BACKLIGHT_SCALE_NON_LINEAR; 109 110 if (!label) { 111 snprintf(name, sizeof(name), "lm3509-%s-%s", dev_name(dev), 112 name_suffix); 113 label = name; 114 } 115 116 bd = devm_backlight_device_register(dev, label, dev, data, ops, &props); 117 if (IS_ERR(bd)) 118 return bd; 119 120 backlight_update_status(bd); 121 return bd; 122 } 123 124 static const struct regmap_config lm3509_regmap = { 125 .reg_bits = 8, 126 .val_bits = 8, 127 .max_register = REG_MAX, 128 }; 129 130 static int lm3509_parse_led_sources(struct device_node *node, 131 int default_led_sources) 132 { 133 u32 sources[LM3509_NUM_SINKS]; 134 int ret, num_sources, i; 135 136 num_sources = of_property_count_u32_elems(node, "led-sources"); 137 if (num_sources < 0) 138 return default_led_sources; 139 else if (num_sources > ARRAY_SIZE(sources)) 140 return -EINVAL; 141 142 ret = of_property_read_u32_array(node, "led-sources", sources, 143 num_sources); 144 if (ret) 145 return ret; 146 147 for (i = 0; i < num_sources; i++) { 148 if (sources[i] >= LM3509_NUM_SINKS) 149 return -EINVAL; 150 151 ret |= BIT(sources[i]); 152 } 153 154 return ret; 155 } 156 157 static int lm3509_parse_dt_node(struct device *dev, 158 struct lm3509_bl_led_data *led_data) 159 { 160 int seen_led_sources = 0; 161 162 for_each_child_of_node_scoped(dev->of_node, child) { 163 struct lm3509_bl_led_data *ld; 164 int ret; 165 u32 reg; 166 int valid_led_sources; 167 168 ret = of_property_read_u32(child, "reg", ®); 169 if (ret < 0) 170 return ret; 171 if (reg >= LM3509_NUM_SINKS) 172 return -EINVAL; 173 ld = &led_data[reg]; 174 175 ld->led_sources = lm3509_parse_led_sources(child, BIT(reg)); 176 if (ld->led_sources < 0) 177 return ld->led_sources; 178 179 if (reg == 0) 180 valid_led_sources = BIT(LM3509_SINK_MAIN) | 181 BIT(LM3509_SINK_SUB); 182 else 183 valid_led_sources = BIT(LM3509_SINK_SUB); 184 185 if (ld->led_sources != (ld->led_sources & valid_led_sources)) 186 return -EINVAL; 187 188 if (seen_led_sources & ld->led_sources) 189 return -EINVAL; 190 191 seen_led_sources |= ld->led_sources; 192 193 ld->label = NULL; 194 of_property_read_string(child, "label", &ld->label); 195 196 ld->max_brightness = LM3509_MAX_BRIGHTNESS; 197 of_property_read_u32(child, "max-brightness", 198 &ld->max_brightness); 199 ld->max_brightness = 200 min_t(u32, ld->max_brightness, LM3509_MAX_BRIGHTNESS); 201 202 ld->brightness = LM3509_DEF_BRIGHTNESS; 203 of_property_read_u32(child, "default-brightness", 204 &ld->brightness); 205 ld->brightness = min_t(u32, ld->brightness, ld->max_brightness); 206 } 207 208 return 0; 209 } 210 211 static int lm3509_probe(struct i2c_client *client) 212 { 213 struct lm3509_bl *data; 214 struct device *dev = &client->dev; 215 int ret; 216 bool oled_mode = false; 217 unsigned int reg_gp_val = 0; 218 struct lm3509_bl_led_data led_data[LM3509_NUM_SINKS]; 219 u32 rate_of_change = 0; 220 221 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 222 dev_err(dev, "i2c functionality check failed\n"); 223 return -EOPNOTSUPP; 224 } 225 226 data = devm_kzalloc(dev, sizeof(struct lm3509_bl), GFP_KERNEL); 227 if (!data) 228 return -ENOMEM; 229 230 data->regmap = devm_regmap_init_i2c(client, &lm3509_regmap); 231 if (IS_ERR(data->regmap)) 232 return PTR_ERR(data->regmap); 233 i2c_set_clientdata(client, data); 234 235 data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 236 if (IS_ERR(data->reset_gpio)) 237 return dev_err_probe(dev, PTR_ERR(data->reset_gpio), 238 "Failed to get 'reset' gpio\n"); 239 240 lm3509_reset(data); 241 242 memset(led_data, 0, sizeof(led_data)); 243 ret = lm3509_parse_dt_node(dev, led_data); 244 if (ret) 245 return ret; 246 247 oled_mode = of_property_read_bool(dev->of_node, "ti,oled-mode"); 248 249 if (!of_property_read_u32(dev->of_node, 250 "ti,brightness-rate-of-change-us", 251 &rate_of_change)) { 252 switch (rate_of_change) { 253 case 51: 254 reg_gp_val = 0; 255 break; 256 case 13000: 257 reg_gp_val = BIT(REG_GP_RMP1_BIT); 258 break; 259 case 26000: 260 reg_gp_val = BIT(REG_GP_RMP0_BIT); 261 break; 262 case 52000: 263 reg_gp_val = BIT(REG_GP_RMP0_BIT) | 264 BIT(REG_GP_RMP1_BIT); 265 break; 266 default: 267 dev_warn(dev, "invalid rate of change %u\n", 268 rate_of_change); 269 break; 270 } 271 } 272 273 if (led_data[0].led_sources == 274 (BIT(LM3509_SINK_MAIN) | BIT(LM3509_SINK_SUB))) 275 reg_gp_val |= BIT(REG_GP_UNI_BIT); 276 if (oled_mode) 277 reg_gp_val |= BIT(REG_GP_OLED_BIT); 278 279 ret = regmap_write(data->regmap, REG_GP, reg_gp_val); 280 if (ret < 0) 281 return dev_err_probe(dev, ret, "failed to write register\n"); 282 283 if (led_data[0].led_sources) { 284 data->bl_main = lm3509_backlight_register( 285 dev, "main", data, &lm3509_main_ops, &led_data[0]); 286 if (IS_ERR(data->bl_main)) { 287 return dev_err_probe( 288 dev, PTR_ERR(data->bl_main), 289 "failed to register main backlight\n"); 290 } 291 } 292 293 if (led_data[1].led_sources) { 294 data->bl_sub = lm3509_backlight_register( 295 dev, "sub", data, &lm3509_sub_ops, &led_data[1]); 296 if (IS_ERR(data->bl_sub)) { 297 return dev_err_probe( 298 dev, PTR_ERR(data->bl_sub), 299 "failed to register secondary backlight\n"); 300 } 301 } 302 303 return 0; 304 } 305 306 static void lm3509_remove(struct i2c_client *client) 307 { 308 struct lm3509_bl *data = i2c_get_clientdata(client); 309 310 regmap_write(data->regmap, REG_GP, 0x00); 311 } 312 313 static const struct i2c_device_id lm3509_id[] = { 314 { LM3509_NAME }, 315 {} 316 }; 317 318 MODULE_DEVICE_TABLE(i2c, lm3509_id); 319 320 static const struct of_device_id lm3509_match_table[] = { 321 { 322 .compatible = "ti,lm3509", 323 }, 324 {}, 325 }; 326 327 MODULE_DEVICE_TABLE(of, lm3509_match_table); 328 329 static struct i2c_driver lm3509_i2c_driver = { 330 .driver = { 331 .name = LM3509_NAME, 332 .of_match_table = lm3509_match_table, 333 }, 334 .probe = lm3509_probe, 335 .remove = lm3509_remove, 336 .id_table = lm3509_id, 337 }; 338 339 module_i2c_driver(lm3509_i2c_driver); 340 341 MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3509"); 342 MODULE_AUTHOR("Patrick Gansterer <paroga@paroga.com>"); 343 MODULE_LICENSE("GPL"); 344