1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Driver for MPS MP3309C White LED driver with I2C interface 4 * 5 * This driver support both analog (by I2C commands) and PWM dimming control 6 * modes. 7 * 8 * Copyright (C) 2023 ASEM Srl 9 * Author: Flavio Suligoi <f.suligoi@asem.it> 10 * 11 * Based on pwm_bl.c 12 */ 13 14 #include <linux/backlight.h> 15 #include <linux/delay.h> 16 #include <linux/gpio/consumer.h> 17 #include <linux/i2c.h> 18 #include <linux/mod_devicetable.h> 19 #include <linux/property.h> 20 #include <linux/pwm.h> 21 #include <linux/regmap.h> 22 23 #define REG_I2C_0 0x00 24 #define REG_I2C_1 0x01 25 26 #define REG_I2C_0_EN 0x80 27 #define REG_I2C_0_D0 0x40 28 #define REG_I2C_0_D1 0x20 29 #define REG_I2C_0_D2 0x10 30 #define REG_I2C_0_D3 0x08 31 #define REG_I2C_0_D4 0x04 32 #define REG_I2C_0_RSRV1 0x02 33 #define REG_I2C_0_RSRV2 0x01 34 35 #define REG_I2C_1_RSRV1 0x80 36 #define REG_I2C_1_DIMS 0x40 37 #define REG_I2C_1_SYNC 0x20 38 #define REG_I2C_1_OVP0 0x10 39 #define REG_I2C_1_OVP1 0x08 40 #define REG_I2C_1_VOS 0x04 41 #define REG_I2C_1_LEDO 0x02 42 #define REG_I2C_1_OTP 0x01 43 44 #define ANALOG_I2C_NUM_LEVELS 32 /* 0..31 */ 45 #define ANALOG_I2C_REG_MASK 0x7c 46 47 #define MP3309C_PWM_DEFAULT_NUM_LEVELS 256 /* 0..255 */ 48 49 enum mp3309c_status_value { 50 FIRST_POWER_ON, 51 BACKLIGHT_OFF, 52 BACKLIGHT_ON, 53 }; 54 55 enum mp3309c_dimming_mode_value { 56 DIMMING_PWM, 57 DIMMING_ANALOG_I2C, 58 }; 59 60 struct mp3309c_platform_data { 61 unsigned int max_brightness; 62 unsigned int default_brightness; 63 unsigned int *levels; 64 u8 dimming_mode; 65 u8 over_voltage_protection; 66 bool sync_mode; 67 u8 status; 68 }; 69 70 struct mp3309c_chip { 71 struct device *dev; 72 struct mp3309c_platform_data *pdata; 73 struct backlight_device *bl; 74 struct gpio_desc *enable_gpio; 75 struct regmap *regmap; 76 struct pwm_device *pwmd; 77 }; 78 79 static const struct regmap_config mp3309c_regmap = { 80 .name = "mp3309c_regmap", 81 .reg_bits = 8, 82 .reg_stride = 1, 83 .val_bits = 8, 84 .max_register = REG_I2C_1, 85 }; 86 87 static int mp3309c_enable_device(struct mp3309c_chip *chip) 88 { 89 u8 reg_val; 90 int ret; 91 92 /* I2C register #0 - Device enable */ 93 ret = regmap_update_bits(chip->regmap, REG_I2C_0, REG_I2C_0_EN, 94 REG_I2C_0_EN); 95 if (ret) 96 return ret; 97 98 /* 99 * I2C register #1 - Set working mode: 100 * - set one of the two dimming mode: 101 * - PWM dimming using an external PWM dimming signal 102 * - analog dimming using I2C commands 103 * - enable/disable synchronous mode 104 * - set overvoltage protection (OVP) 105 */ 106 reg_val = 0x00; 107 if (chip->pdata->dimming_mode == DIMMING_PWM) 108 reg_val |= REG_I2C_1_DIMS; 109 if (chip->pdata->sync_mode) 110 reg_val |= REG_I2C_1_SYNC; 111 reg_val |= chip->pdata->over_voltage_protection; 112 ret = regmap_write(chip->regmap, REG_I2C_1, reg_val); 113 if (ret) 114 return ret; 115 116 return 0; 117 } 118 119 static int mp3309c_bl_update_status(struct backlight_device *bl) 120 { 121 struct mp3309c_chip *chip = bl_get_data(bl); 122 int brightness = backlight_get_brightness(bl); 123 struct pwm_state pwmstate; 124 unsigned int analog_val, bits_val; 125 int i, ret; 126 127 if (chip->pdata->dimming_mode == DIMMING_PWM) { 128 /* 129 * PWM control mode 130 */ 131 pwm_get_state(chip->pwmd, &pwmstate); 132 pwm_set_relative_duty_cycle(&pwmstate, 133 chip->pdata->levels[brightness], 134 chip->pdata->levels[chip->pdata->max_brightness]); 135 pwmstate.enabled = true; 136 ret = pwm_apply_might_sleep(chip->pwmd, &pwmstate); 137 if (ret) 138 return ret; 139 140 switch (chip->pdata->status) { 141 case FIRST_POWER_ON: 142 case BACKLIGHT_OFF: 143 /* 144 * After 20ms of low pwm signal level, the chip turns 145 * off automatically. In this case, before enabling the 146 * chip again, we must wait about 10ms for pwm signal to 147 * stabilize. 148 */ 149 if (brightness > 0) { 150 msleep(10); 151 mp3309c_enable_device(chip); 152 chip->pdata->status = BACKLIGHT_ON; 153 } else { 154 chip->pdata->status = BACKLIGHT_OFF; 155 } 156 break; 157 case BACKLIGHT_ON: 158 if (brightness == 0) 159 chip->pdata->status = BACKLIGHT_OFF; 160 break; 161 } 162 } else { 163 /* 164 * Analog (by I2C command) control mode 165 * 166 * The first time, before setting brightness, we must enable the 167 * device 168 */ 169 if (chip->pdata->status == FIRST_POWER_ON) 170 mp3309c_enable_device(chip); 171 172 /* 173 * Dimming mode I2C command (fixed dimming range 0..31) 174 * 175 * The 5 bits of the dimming analog value D4..D0 is allocated 176 * in the I2C register #0, in the following way: 177 * 178 * +--+--+--+--+--+--+--+--+ 179 * |EN|D0|D1|D2|D3|D4|XX|XX| 180 * +--+--+--+--+--+--+--+--+ 181 */ 182 analog_val = brightness; 183 bits_val = 0; 184 for (i = 0; i <= 5; i++) 185 bits_val += ((analog_val >> i) & 0x01) << (6 - i); 186 ret = regmap_update_bits(chip->regmap, REG_I2C_0, 187 ANALOG_I2C_REG_MASK, bits_val); 188 if (ret) 189 return ret; 190 191 if (brightness > 0) 192 chip->pdata->status = BACKLIGHT_ON; 193 else 194 chip->pdata->status = BACKLIGHT_OFF; 195 } 196 197 return 0; 198 } 199 200 static const struct backlight_ops mp3309c_bl_ops = { 201 .update_status = mp3309c_bl_update_status, 202 }; 203 204 static int mp3309c_parse_fwnode(struct mp3309c_chip *chip, 205 struct mp3309c_platform_data *pdata) 206 { 207 int ret, i; 208 unsigned int num_levels, tmp_value; 209 struct device *dev = chip->dev; 210 211 if (!dev_fwnode(dev)) 212 return dev_err_probe(dev, -ENODEV, "failed to get firmware node\n"); 213 214 /* 215 * Dimming mode: the MP3309C provides two dimming control mode: 216 * 217 * - PWM mode 218 * - Analog by I2C control mode (default) 219 * 220 * I2C control mode is assumed as default but, if the pwms property is 221 * found in the backlight node, the mode switches to PWM mode. 222 */ 223 pdata->dimming_mode = DIMMING_ANALOG_I2C; 224 if (device_property_present(dev, "pwms")) { 225 chip->pwmd = devm_pwm_get(dev, NULL); 226 if (IS_ERR(chip->pwmd)) 227 return dev_err_probe(dev, PTR_ERR(chip->pwmd), "error getting pwm data\n"); 228 pdata->dimming_mode = DIMMING_PWM; 229 pwm_apply_args(chip->pwmd); 230 } 231 232 /* 233 * In I2C control mode the dimming levels (0..31) are fixed by the 234 * hardware, while in PWM control mode they can be chosen by the user, 235 * to allow nonlinear mappings. 236 */ 237 if (pdata->dimming_mode == DIMMING_ANALOG_I2C) { 238 /* 239 * Analog (by I2C commands) control mode: fixed 0..31 brightness 240 * levels 241 */ 242 num_levels = ANALOG_I2C_NUM_LEVELS; 243 244 /* Enable GPIO used in I2C dimming mode only */ 245 chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); 246 if (IS_ERR(chip->enable_gpio)) 247 return dev_err_probe(dev, PTR_ERR(chip->enable_gpio), 248 "error getting enable gpio\n"); 249 } else { 250 /* 251 * PWM control mode: check for brightness level in DT 252 */ 253 if (device_property_present(dev, "brightness-levels")) { 254 /* Read brightness levels from DT */ 255 num_levels = device_property_count_u32(dev, "brightness-levels"); 256 if (num_levels < 2) 257 return -EINVAL; 258 } else { 259 /* Use default brightness levels */ 260 num_levels = MP3309C_PWM_DEFAULT_NUM_LEVELS; 261 } 262 } 263 264 /* Fill brightness levels array */ 265 pdata->levels = devm_kcalloc(dev, num_levels, sizeof(*pdata->levels), GFP_KERNEL); 266 if (!pdata->levels) 267 return -ENOMEM; 268 if (device_property_present(dev, "brightness-levels")) { 269 ret = device_property_read_u32_array(dev, "brightness-levels", 270 pdata->levels, num_levels); 271 if (ret < 0) 272 return ret; 273 } else { 274 for (i = 0; i < num_levels; i++) 275 pdata->levels[i] = i; 276 } 277 278 pdata->max_brightness = num_levels - 1; 279 280 ret = device_property_read_u32(dev, "default-brightness", &pdata->default_brightness); 281 if (ret) 282 pdata->default_brightness = pdata->max_brightness; 283 if (pdata->default_brightness > pdata->max_brightness) { 284 dev_err_probe(dev, -ERANGE, "default brightness exceeds max brightness\n"); 285 pdata->default_brightness = pdata->max_brightness; 286 } 287 288 /* 289 * Over-voltage protection (OVP) 290 * 291 * This (optional) property values are: 292 * 293 * - 13.5V 294 * - 24V 295 * - 35.5V (hardware default setting) 296 * 297 * If missing, the default value for OVP is 35.5V 298 */ 299 pdata->over_voltage_protection = REG_I2C_1_OVP1; 300 ret = device_property_read_u32(dev, "mps,overvoltage-protection-microvolt", &tmp_value); 301 if (!ret) { 302 switch (tmp_value) { 303 case 13500000: 304 pdata->over_voltage_protection = 0x00; 305 break; 306 case 24000000: 307 pdata->over_voltage_protection = REG_I2C_1_OVP0; 308 break; 309 case 35500000: 310 pdata->over_voltage_protection = REG_I2C_1_OVP1; 311 break; 312 default: 313 return -EINVAL; 314 } 315 } 316 317 /* Synchronous (default) and non-synchronous mode */ 318 pdata->sync_mode = !device_property_read_bool(dev, "mps,no-sync-mode"); 319 320 return 0; 321 } 322 323 static int mp3309c_probe(struct i2c_client *client) 324 { 325 struct device *dev = &client->dev; 326 struct mp3309c_platform_data *pdata = dev_get_platdata(dev); 327 struct mp3309c_chip *chip; 328 struct backlight_properties props; 329 struct pwm_state pwmstate; 330 int ret; 331 332 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 333 return dev_err_probe(dev, -EOPNOTSUPP, "failed to check i2c functionality\n"); 334 335 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 336 if (!chip) 337 return -ENOMEM; 338 339 chip->dev = dev; 340 341 chip->regmap = devm_regmap_init_i2c(client, &mp3309c_regmap); 342 if (IS_ERR(chip->regmap)) 343 return dev_err_probe(dev, PTR_ERR(chip->regmap), 344 "failed to allocate register map\n"); 345 346 i2c_set_clientdata(client, chip); 347 348 if (!pdata) { 349 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 350 if (!pdata) 351 return -ENOMEM; 352 353 ret = mp3309c_parse_fwnode(chip, pdata); 354 if (ret) 355 return ret; 356 } 357 chip->pdata = pdata; 358 359 /* Backlight properties */ 360 memset(&props, 0, sizeof(struct backlight_properties)); 361 props.brightness = pdata->default_brightness; 362 props.max_brightness = pdata->max_brightness; 363 props.scale = BACKLIGHT_SCALE_LINEAR; 364 props.type = BACKLIGHT_RAW; 365 props.power = FB_BLANK_UNBLANK; 366 props.fb_blank = FB_BLANK_UNBLANK; 367 chip->bl = devm_backlight_device_register(dev, "mp3309c", dev, chip, 368 &mp3309c_bl_ops, &props); 369 if (IS_ERR(chip->bl)) 370 return dev_err_probe(dev, PTR_ERR(chip->bl), 371 "error registering backlight device\n"); 372 373 /* In PWM dimming mode, enable pwm device */ 374 if (chip->pdata->dimming_mode == DIMMING_PWM) { 375 pwm_init_state(chip->pwmd, &pwmstate); 376 pwm_set_relative_duty_cycle(&pwmstate, 377 chip->pdata->default_brightness, 378 chip->pdata->max_brightness); 379 pwmstate.enabled = true; 380 ret = pwm_apply_might_sleep(chip->pwmd, &pwmstate); 381 if (ret) 382 return dev_err_probe(dev, ret, "error setting pwm device\n"); 383 } 384 385 chip->pdata->status = FIRST_POWER_ON; 386 backlight_update_status(chip->bl); 387 388 return 0; 389 } 390 391 static void mp3309c_remove(struct i2c_client *client) 392 { 393 struct mp3309c_chip *chip = i2c_get_clientdata(client); 394 struct backlight_device *bl = chip->bl; 395 396 bl->props.power = FB_BLANK_POWERDOWN; 397 bl->props.brightness = 0; 398 backlight_update_status(chip->bl); 399 } 400 401 static const struct of_device_id mp3309c_match_table[] = { 402 { .compatible = "mps,mp3309c", }, 403 { }, 404 }; 405 MODULE_DEVICE_TABLE(of, mp3309c_match_table); 406 407 static const struct i2c_device_id mp3309c_id[] = { 408 { "mp3309c", 0 }, 409 { } 410 }; 411 MODULE_DEVICE_TABLE(i2c, mp3309c_id); 412 413 static struct i2c_driver mp3309c_i2c_driver = { 414 .driver = { 415 .name = KBUILD_MODNAME, 416 .of_match_table = mp3309c_match_table, 417 }, 418 .probe = mp3309c_probe, 419 .remove = mp3309c_remove, 420 .id_table = mp3309c_id, 421 }; 422 423 module_i2c_driver(mp3309c_i2c_driver); 424 425 MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C"); 426 MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>"); 427 MODULE_LICENSE("GPL"); 428