540 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Silergy SY7802 flash LED driver with an I2C interface4 *5 * Copyright 2024 André Apitzsch <git@apitzsch.eu>6 */7 8#include <linux/gpio/consumer.h>9#include <linux/i2c.h>10#include <linux/kernel.h>11#include <linux/led-class-flash.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/regmap.h>15#include <linux/regulator/consumer.h>16 17#define SY7802_MAX_LEDS 218#define SY7802_LED_JOINT 219 20#define SY7802_REG_ENABLE 0x1021#define SY7802_REG_TORCH_BRIGHTNESS 0xa022#define SY7802_REG_FLASH_BRIGHTNESS 0xb023#define SY7802_REG_FLASH_DURATION 0xc024#define SY7802_REG_FLAGS 0xd025#define SY7802_REG_CONFIG_1 0xe026#define SY7802_REG_CONFIG_2 0xf027#define SY7802_REG_VIN_MONITOR 0x8028#define SY7802_REG_LAST_FLASH 0x8129#define SY7802_REG_VLED_MONITOR 0x3030#define SY7802_REG_ADC_DELAY 0x3131#define SY7802_REG_DEV_ID 0xff32 33#define SY7802_MODE_OFF 034#define SY7802_MODE_TORCH 235#define SY7802_MODE_FLASH 336#define SY7802_MODE_MASK GENMASK(1, 0)37 38#define SY7802_LEDS_SHIFT 339#define SY7802_LEDS_MASK(_id) (BIT(_id) << SY7802_LEDS_SHIFT)40#define SY7802_LEDS_MASK_ALL (SY7802_LEDS_MASK(0) | SY7802_LEDS_MASK(1))41 42#define SY7802_TORCH_CURRENT_SHIFT 343#define SY7802_TORCH_CURRENT_MASK(_id) \44 (GENMASK(2, 0) << (SY7802_TORCH_CURRENT_SHIFT * (_id)))45#define SY7802_TORCH_CURRENT_MASK_ALL \46 (SY7802_TORCH_CURRENT_MASK(0) | SY7802_TORCH_CURRENT_MASK(1))47 48#define SY7802_FLASH_CURRENT_SHIFT 449#define SY7802_FLASH_CURRENT_MASK(_id) \50 (GENMASK(3, 0) << (SY7802_FLASH_CURRENT_SHIFT * (_id)))51#define SY7802_FLASH_CURRENT_MASK_ALL \52 (SY7802_FLASH_CURRENT_MASK(0) | SY7802_FLASH_CURRENT_MASK(1))53 54#define SY7802_TIMEOUT_DEFAULT_US 512000U55#define SY7802_TIMEOUT_MIN_US 32000U56#define SY7802_TIMEOUT_MAX_US 1024000U57#define SY7802_TIMEOUT_STEPSIZE_US 32000U58 59#define SY7802_TORCH_BRIGHTNESS_MAX 860 61#define SY7802_FLASH_BRIGHTNESS_DEFAULT 1462#define SY7802_FLASH_BRIGHTNESS_MIN 063#define SY7802_FLASH_BRIGHTNESS_MAX 1564#define SY7802_FLASH_BRIGHTNESS_STEP 165 66#define SY7802_FLAG_TIMEOUT BIT(0)67#define SY7802_FLAG_THERMAL_SHUTDOWN BIT(1)68#define SY7802_FLAG_LED_FAULT BIT(2)69#define SY7802_FLAG_TX1_INTERRUPT BIT(3)70#define SY7802_FLAG_TX2_INTERRUPT BIT(4)71#define SY7802_FLAG_LED_THERMAL_FAULT BIT(5)72#define SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW BIT(6)73#define SY7802_FLAG_INPUT_VOLTAGE_LOW BIT(7)74 75#define SY7802_CHIP_ID 0x5176 77static const struct reg_default sy7802_regmap_defs[] = {78 { SY7802_REG_ENABLE, SY7802_LEDS_MASK_ALL },79 { SY7802_REG_TORCH_BRIGHTNESS, 0x92 },80 { SY7802_REG_FLASH_BRIGHTNESS, SY7802_FLASH_BRIGHTNESS_DEFAULT |81 SY7802_FLASH_BRIGHTNESS_DEFAULT << SY7802_FLASH_CURRENT_SHIFT },82 { SY7802_REG_FLASH_DURATION, 0x6f },83 { SY7802_REG_FLAGS, 0x0 },84 { SY7802_REG_CONFIG_1, 0x68 },85 { SY7802_REG_CONFIG_2, 0xf0 },86};87 88struct sy7802_led {89 struct led_classdev_flash flash;90 struct sy7802 *chip;91 u8 led_id;92};93 94struct sy7802 {95 struct device *dev;96 struct regmap *regmap;97 struct mutex mutex;98 99 struct gpio_desc *enable_gpio;100 struct regulator *vin_regulator;101 102 unsigned int fled_strobe_used;103 unsigned int fled_torch_used;104 unsigned int leds_active;105 int num_leds;106 struct sy7802_led leds[] __counted_by(num_leds);107};108 109static int sy7802_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness brightness)110{111 struct sy7802_led *led = container_of(lcdev, struct sy7802_led, flash.led_cdev);112 struct sy7802 *chip = led->chip;113 u32 fled_torch_used_tmp;114 u32 led_enable_mask;115 u32 enable_mask;116 u32 torch_mask;117 u32 val;118 int ret;119 120 mutex_lock(&chip->mutex);121 122 if (chip->fled_strobe_used) {123 dev_warn(chip->dev, "Cannot set torch brightness whilst strobe is enabled\n");124 ret = -EBUSY;125 goto unlock;126 }127 128 if (brightness)129 fled_torch_used_tmp = chip->fled_torch_used | BIT(led->led_id);130 else131 fled_torch_used_tmp = chip->fled_torch_used & ~BIT(led->led_id);132 133 led_enable_mask = led->led_id == SY7802_LED_JOINT ?134 SY7802_LEDS_MASK_ALL :135 SY7802_LEDS_MASK(led->led_id);136 137 val = brightness ? led_enable_mask : SY7802_MODE_OFF;138 if (fled_torch_used_tmp)139 val |= SY7802_MODE_TORCH;140 141 /* Disable torch to apply brightness */142 ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_MASK,143 SY7802_MODE_OFF);144 if (ret)145 goto unlock;146 147 torch_mask = led->led_id == SY7802_LED_JOINT ?148 SY7802_TORCH_CURRENT_MASK_ALL :149 SY7802_TORCH_CURRENT_MASK(led->led_id);150 151 /* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */152 if (brightness)153 brightness -= 1;154 155 brightness |= (brightness << SY7802_TORCH_CURRENT_SHIFT);156 157 ret = regmap_update_bits(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, torch_mask, brightness);158 if (ret)159 goto unlock;160 161 enable_mask = SY7802_MODE_MASK | led_enable_mask;162 ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);163 if (ret)164 goto unlock;165 166 chip->fled_torch_used = fled_torch_used_tmp;167 168unlock:169 mutex_unlock(&chip->mutex);170 return ret;171}172 173static int sy7802_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness)174{175 struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);176 struct led_flash_setting *s = &fl_cdev->brightness;177 u32 val = (brightness - s->min) / s->step;178 struct sy7802 *chip = led->chip;179 u32 flash_mask;180 int ret;181 182 val |= (val << SY7802_FLASH_CURRENT_SHIFT);183 flash_mask = led->led_id == SY7802_LED_JOINT ?184 SY7802_FLASH_CURRENT_MASK_ALL :185 SY7802_FLASH_CURRENT_MASK(led->led_id);186 187 mutex_lock(&chip->mutex);188 ret = regmap_update_bits(chip->regmap, SY7802_REG_FLASH_BRIGHTNESS, flash_mask, val);189 mutex_unlock(&chip->mutex);190 191 return ret;192}193 194static int sy7802_strobe_set(struct led_classdev_flash *fl_cdev, bool state)195{196 struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);197 struct sy7802 *chip = led->chip;198 u32 fled_strobe_used_tmp;199 u32 led_enable_mask;200 u32 enable_mask;201 u32 val;202 int ret;203 204 mutex_lock(&chip->mutex);205 206 if (chip->fled_torch_used) {207 dev_warn(chip->dev, "Cannot set strobe brightness whilst torch is enabled\n");208 ret = -EBUSY;209 goto unlock;210 }211 212 if (state)213 fled_strobe_used_tmp = chip->fled_strobe_used | BIT(led->led_id);214 else215 fled_strobe_used_tmp = chip->fled_strobe_used & ~BIT(led->led_id);216 217 led_enable_mask = led->led_id == SY7802_LED_JOINT ?218 SY7802_LEDS_MASK_ALL :219 SY7802_LEDS_MASK(led->led_id);220 221 val = state ? led_enable_mask : SY7802_MODE_OFF;222 if (fled_strobe_used_tmp)223 val |= SY7802_MODE_FLASH;224 225 enable_mask = SY7802_MODE_MASK | led_enable_mask;226 ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);227 228 if (ret)229 goto unlock;230 231 chip->fled_strobe_used = fled_strobe_used_tmp;232 233unlock:234 mutex_unlock(&chip->mutex);235 return ret;236}237 238static int sy7802_strobe_get(struct led_classdev_flash *fl_cdev, bool *state)239{240 struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);241 struct sy7802 *chip = led->chip;242 243 mutex_lock(&chip->mutex);244 *state = !!(chip->fled_strobe_used & BIT(led->led_id));245 mutex_unlock(&chip->mutex);246 247 return 0;248}249 250static int sy7802_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout)251{252 struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);253 struct led_flash_setting *s = &fl_cdev->timeout;254 u32 val = (timeout - s->min) / s->step;255 struct sy7802 *chip = led->chip;256 257 return regmap_write(chip->regmap, SY7802_REG_FLASH_DURATION, val);258}259 260static int sy7802_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault)261{262 struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);263 struct sy7802 *chip = led->chip;264 u32 val, led_faults = 0;265 int ret;266 267 /* NOTE: reading register clears fault status */268 ret = regmap_read(chip->regmap, SY7802_REG_FLAGS, &val);269 if (ret)270 return ret;271 272 if (val & (SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW | SY7802_FLAG_INPUT_VOLTAGE_LOW))273 led_faults |= LED_FAULT_INPUT_VOLTAGE;274 275 if (val & SY7802_FLAG_THERMAL_SHUTDOWN)276 led_faults |= LED_FAULT_OVER_TEMPERATURE;277 278 if (val & SY7802_FLAG_TIMEOUT)279 led_faults |= LED_FAULT_TIMEOUT;280 281 *fault = led_faults;282 return 0;283}284 285static const struct led_flash_ops sy7802_flash_ops = {286 .flash_brightness_set = sy7802_flash_brightness_set,287 .strobe_set = sy7802_strobe_set,288 .strobe_get = sy7802_strobe_get,289 .timeout_set = sy7802_timeout_set,290 .fault_get = sy7802_fault_get,291};292 293static void sy7802_init_flash_brightness(struct led_classdev_flash *fl_cdev)294{295 struct led_flash_setting *s;296 297 /* Init flash brightness setting */298 s = &fl_cdev->brightness;299 s->min = SY7802_FLASH_BRIGHTNESS_MIN;300 s->max = SY7802_FLASH_BRIGHTNESS_MAX;301 s->step = SY7802_FLASH_BRIGHTNESS_STEP;302 s->val = SY7802_FLASH_BRIGHTNESS_DEFAULT;303}304 305static void sy7802_init_flash_timeout(struct led_classdev_flash *fl_cdev)306{307 struct led_flash_setting *s;308 309 /* Init flash timeout setting */310 s = &fl_cdev->timeout;311 s->min = SY7802_TIMEOUT_MIN_US;312 s->max = SY7802_TIMEOUT_MAX_US;313 s->step = SY7802_TIMEOUT_STEPSIZE_US;314 s->val = SY7802_TIMEOUT_DEFAULT_US;315}316 317static int sy7802_led_register(struct device *dev, struct sy7802_led *led,318 struct device_node *np)319{320 struct led_init_data init_data = {};321 int ret;322 323 init_data.fwnode = of_fwnode_handle(np);324 325 ret = devm_led_classdev_flash_register_ext(dev, &led->flash, &init_data);326 if (ret) {327 dev_err(dev, "Couldn't register flash %d\n", led->led_id);328 return ret;329 }330 331 return 0;332}333 334static int sy7802_init_flash_properties(struct device *dev, struct sy7802_led *led,335 struct device_node *np)336{337 struct led_classdev_flash *flash = &led->flash;338 struct led_classdev *lcdev = &flash->led_cdev;339 u32 sources[SY7802_MAX_LEDS];340 int i, num, ret;341 342 num = of_property_count_u32_elems(np, "led-sources");343 if (num < 1) {344 dev_err(dev, "Not specified or wrong number of led-sources\n");345 return -EINVAL;346 }347 348 ret = of_property_read_u32_array(np, "led-sources", sources, num);349 if (ret)350 return ret;351 352 for (i = 0; i < num; i++) {353 if (sources[i] >= SY7802_MAX_LEDS)354 return -EINVAL;355 if (led->chip->leds_active & BIT(sources[i]))356 return -EINVAL;357 led->chip->leds_active |= BIT(sources[i]);358 }359 360 /* If both channels are specified in 'led-sources', joint flash output mode is used */361 led->led_id = num == 2 ? SY7802_LED_JOINT : sources[0];362 363 lcdev->max_brightness = SY7802_TORCH_BRIGHTNESS_MAX;364 lcdev->brightness_set_blocking = sy7802_torch_brightness_set;365 lcdev->flags |= LED_DEV_CAP_FLASH;366 367 flash->ops = &sy7802_flash_ops;368 369 sy7802_init_flash_brightness(flash);370 sy7802_init_flash_timeout(flash);371 372 return 0;373}374 375static int sy7802_chip_check(struct sy7802 *chip)376{377 struct device *dev = chip->dev;378 u32 chipid;379 int ret;380 381 ret = regmap_read(chip->regmap, SY7802_REG_DEV_ID, &chipid);382 if (ret)383 return dev_err_probe(dev, ret, "Failed to read chip ID\n");384 385 if (chipid != SY7802_CHIP_ID)386 return dev_err_probe(dev, -ENODEV, "Unsupported chip detected: %x\n", chipid);387 388 return 0;389}390 391static void sy7802_enable(struct sy7802 *chip)392{393 gpiod_set_value_cansleep(chip->enable_gpio, 1);394 usleep_range(200, 300);395}396 397static void sy7802_disable(struct sy7802 *chip)398{399 gpiod_set_value_cansleep(chip->enable_gpio, 0);400}401 402static int sy7802_probe_dt(struct sy7802 *chip)403{404 struct device_node *np = dev_of_node(chip->dev);405 int child_num;406 int ret;407 408 regmap_write(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_OFF);409 regmap_write(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, LED_OFF);410 411 child_num = 0;412 for_each_available_child_of_node_scoped(np, child) {413 struct sy7802_led *led = chip->leds + child_num;414 415 led->chip = chip;416 led->led_id = child_num;417 418 ret = sy7802_init_flash_properties(chip->dev, led, child);419 if (ret)420 return ret;421 422 ret = sy7802_led_register(chip->dev, led, child);423 if (ret)424 return ret;425 426 child_num++;427 }428 return 0;429}430 431static void sy7802_chip_disable_action(void *data)432{433 struct sy7802 *chip = data;434 435 sy7802_disable(chip);436}437 438static void sy7802_regulator_disable_action(void *data)439{440 struct sy7802 *chip = data;441 442 regulator_disable(chip->vin_regulator);443}444 445static const struct regmap_config sy7802_regmap_config = {446 .reg_bits = 8,447 .val_bits = 8,448 .max_register = 0xff,449 .cache_type = REGCACHE_MAPLE,450 .reg_defaults = sy7802_regmap_defs,451 .num_reg_defaults = ARRAY_SIZE(sy7802_regmap_defs),452};453 454static int sy7802_probe(struct i2c_client *client)455{456 struct device *dev = &client->dev;457 struct sy7802 *chip;458 size_t count;459 int ret;460 461 count = device_get_child_node_count(dev);462 if (!count || count > SY7802_MAX_LEDS)463 return dev_err_probe(dev, -EINVAL, "Invalid amount of LED nodes %zu\n", count);464 465 chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);466 if (!chip)467 return -ENOMEM;468 469 chip->num_leds = count;470 471 chip->dev = dev;472 i2c_set_clientdata(client, chip);473 474 chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);475 ret = PTR_ERR_OR_ZERO(chip->enable_gpio);476 if (ret)477 return dev_err_probe(dev, ret, "Failed to request enable gpio\n");478 479 chip->vin_regulator = devm_regulator_get(dev, "vin");480 ret = PTR_ERR_OR_ZERO(chip->vin_regulator);481 if (ret)482 return dev_err_probe(dev, ret, "Failed to request regulator\n");483 484 ret = regulator_enable(chip->vin_regulator);485 if (ret)486 return dev_err_probe(dev, ret, "Failed to enable regulator\n");487 488 ret = devm_add_action_or_reset(dev, sy7802_regulator_disable_action, chip);489 if (ret)490 return ret;491 492 ret = devm_mutex_init(dev, &chip->mutex);493 if (ret)494 return ret;495 496 mutex_lock(&chip->mutex);497 498 chip->regmap = devm_regmap_init_i2c(client, &sy7802_regmap_config);499 if (IS_ERR(chip->regmap)) {500 ret = PTR_ERR(chip->regmap);501 dev_err_probe(dev, ret, "Failed to allocate register map\n");502 goto error;503 }504 505 ret = sy7802_probe_dt(chip);506 if (ret < 0)507 goto error;508 509 sy7802_enable(chip);510 511 ret = devm_add_action_or_reset(dev, sy7802_chip_disable_action, chip);512 if (ret)513 goto error;514 515 ret = sy7802_chip_check(chip);516 517error:518 mutex_unlock(&chip->mutex);519 return ret;520}521 522static const struct of_device_id __maybe_unused sy7802_leds_match[] = {523 { .compatible = "silergy,sy7802", },524 {}525};526MODULE_DEVICE_TABLE(of, sy7802_leds_match);527 528static struct i2c_driver sy7802_driver = {529 .driver = {530 .name = "sy7802",531 .of_match_table = of_match_ptr(sy7802_leds_match),532 },533 .probe = sy7802_probe,534};535module_i2c_driver(sy7802_driver);536 537MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");538MODULE_DESCRIPTION("Silergy SY7802 flash LED driver");539MODULE_LICENSE("GPL");540