Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: flash: mt6370: Add MediaTek MT6370 flashlight support

The MediaTek MT6370 is a highly-integrated smart power management IC,
which includes a single cell Li-Ion/Li-Polymer switching battery
charger, a USB Type-C & Power Delivery (PD) controller, dual Flash
LED current sources, a RGB LED driver, a backlight WLED driver,
a display bias driver and a general LDO for portable devices.

Add support for the MT6370 Flash LED driver. Flash LED in MT6370
has 2 channels and support torch/strobe mode.

Co-developed-by: Alice Chen <alice_chen@richtek.com>
Signed-off-by: Alice Chen <alice_chen@richtek.com>
Signed-off-by: ChiYuan Huang <cy_huang@richtek.com>
Signed-off-by: ChiaEn Wu <chiaen_wu@richtek.com>
Acked-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
Signed-off-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/52480420a160e5a4c71715fbbf105e684a16e7c2.1678430444.git.chiaen_wu@richtek.com

authored by

ChiYuan Huang and committed by
Lee Jones
fa31e422 5c38376e

+587
+13
drivers/leds/flash/Kconfig
··· 61 61 Independent current sources supply for each flash LED support torch 62 62 and strobe mode. 63 63 64 + config LEDS_MT6370_FLASH 65 + tristate "Flash LED Support for MediaTek MT6370 PMIC" 66 + depends on LEDS_CLASS 67 + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS 68 + depends on MFD_MT6370 69 + help 70 + Support 2 channels and torch/strobe mode. 71 + Say Y here to enable support for 72 + MT6370_FLASH_LED device. 73 + 74 + This driver can also be built as a module. If so, the module 75 + will be called "leds-mt6370-flash". 76 + 64 77 config LEDS_QCOM_FLASH 65 78 tristate "LED support for flash module inside Qualcomm Technologies, Inc. PMIC" 66 79 depends on MFD_SPMI_PMIC || COMPILE_TEST
+1
drivers/leds/flash/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 obj-$(CONFIG_LEDS_MT6360) += leds-mt6360.o 4 + obj-$(CONFIG_LEDS_MT6370_FLASH) += leds-mt6370-flash.o 4 5 obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o 5 6 obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o 6 7 obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
+573
drivers/leds/flash/leds-mt6370-flash.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2023 Richtek Technology Corp. 4 + * 5 + * Authors: 6 + * Alice Chen <alice_chen@richtek.com> 7 + * ChiYuan Huang <cy_huang@richtek.com> 8 + */ 9 + 10 + #include <linux/bitops.h> 11 + #include <linux/delay.h> 12 + #include <linux/init.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/kernel.h> 15 + #include <linux/led-class-flash.h> 16 + #include <linux/module.h> 17 + #include <linux/mutex.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/property.h> 20 + #include <linux/regmap.h> 21 + 22 + #include <media/v4l2-flash-led-class.h> 23 + 24 + enum { 25 + MT6370_LED_FLASH1 = 0, 26 + MT6370_LED_FLASH2, 27 + MT6370_MAX_LEDS 28 + }; 29 + 30 + /* Virtual definition for multicolor */ 31 + 32 + #define MT6370_REG_FLEDEN 0x17E 33 + #define MT6370_REG_STRBTO 0x173 34 + #define MT6370_REG_CHGSTAT2 0x1D1 35 + #define MT6370_REG_FLEDSTAT1 0x1D9 36 + #define MT6370_REG_FLEDISTRB(_id) (0x174 + 4 * (_id)) 37 + #define MT6370_REG_FLEDITOR(_id) (0x175 + 4 * (_id)) 38 + #define MT6370_ITORCH_MASK GENMASK(4, 0) 39 + #define MT6370_ISTROBE_MASK GENMASK(6, 0) 40 + #define MT6370_STRBTO_MASK GENMASK(6, 0) 41 + #define MT6370_TORCHEN_MASK BIT(3) 42 + #define MT6370_STROBEN_MASK BIT(2) 43 + #define MT6370_FLCSEN_MASK(_id) BIT(MT6370_LED_FLASH2 - (_id)) 44 + #define MT6370_FLCSEN_MASK_ALL GENMASK(1, 0) 45 + #define MT6370_FLEDCHGVINOVP_MASK BIT(3) 46 + #define MT6370_FLED1STRBTO_MASK BIT(11) 47 + #define MT6370_FLED2STRBTO_MASK BIT(10) 48 + #define MT6370_FLED1STRB_MASK BIT(9) 49 + #define MT6370_FLED2STRB_MASK BIT(8) 50 + #define MT6370_FLED1SHORT_MASK BIT(7) 51 + #define MT6370_FLED2SHORT_MASK BIT(6) 52 + #define MT6370_FLEDLVF_MASK BIT(3) 53 + 54 + #define MT6370_LED_JOINT 2 55 + #define MT6370_RANGE_FLED_REG 4 56 + #define MT6370_ITORCH_MIN_uA 25000 57 + #define MT6370_ITORCH_STEP_uA 12500 58 + #define MT6370_ITORCH_MAX_uA 400000 59 + #define MT6370_ITORCH_DOUBLE_MAX_uA 800000 60 + #define MT6370_ISTRB_MIN_uA 50000 61 + #define MT6370_ISTRB_STEP_uA 12500 62 + #define MT6370_ISTRB_MAX_uA 1500000 63 + #define MT6370_ISTRB_DOUBLE_MAX_uA 3000000 64 + #define MT6370_STRBTO_MIN_US 64000 65 + #define MT6370_STRBTO_STEP_US 32000 66 + #define MT6370_STRBTO_MAX_US 2432000 67 + 68 + #define to_mt6370_led(ptr, member) container_of(ptr, struct mt6370_led, member) 69 + 70 + struct mt6370_led { 71 + struct led_classdev_flash flash; 72 + struct v4l2_flash *v4l2_flash; 73 + struct mt6370_priv *priv; 74 + u8 led_no; 75 + }; 76 + 77 + struct mt6370_priv { 78 + struct regmap *regmap; 79 + struct mutex lock; 80 + unsigned int fled_strobe_used; 81 + unsigned int fled_torch_used; 82 + unsigned int leds_active; 83 + unsigned int leds_count; 84 + struct mt6370_led leds[]; 85 + }; 86 + 87 + static int mt6370_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level) 88 + { 89 + struct mt6370_led *led = to_mt6370_led(lcdev, flash.led_cdev); 90 + struct mt6370_priv *priv = led->priv; 91 + u32 led_enable_mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 92 + MT6370_FLCSEN_MASK(led->led_no); 93 + u32 enable_mask = MT6370_TORCHEN_MASK | led_enable_mask; 94 + u32 val = level ? led_enable_mask : 0; 95 + u32 curr; 96 + int ret, i; 97 + 98 + mutex_lock(&priv->lock); 99 + 100 + /* 101 + * There is only one set of flash control logic, and this flag is used to check if 'strobe' 102 + * is currently being used. 103 + */ 104 + if (priv->fled_strobe_used) { 105 + dev_warn(lcdev->dev, "Please disable strobe first [%d]\n", priv->fled_strobe_used); 106 + ret = -EBUSY; 107 + goto unlock; 108 + } 109 + 110 + if (level) 111 + curr = priv->fled_torch_used | BIT(led->led_no); 112 + else 113 + curr = priv->fled_torch_used & ~BIT(led->led_no); 114 + 115 + if (curr) 116 + val |= MT6370_TORCHEN_MASK; 117 + 118 + if (level) { 119 + level -= 1; 120 + if (led->led_no == MT6370_LED_JOINT) { 121 + u32 flevel[MT6370_MAX_LEDS]; 122 + 123 + /* 124 + * There're two flash channels in MT6370. If joint flash output is used, 125 + * torch current will be averaged output from both channels. 126 + */ 127 + flevel[0] = level / 2; 128 + flevel[1] = level - flevel[0]; 129 + for (i = 0; i < MT6370_MAX_LEDS; i++) { 130 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDITOR(i), 131 + MT6370_ITORCH_MASK, flevel[i]); 132 + if (ret) 133 + goto unlock; 134 + } 135 + } else { 136 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDITOR(led->led_no), 137 + MT6370_ITORCH_MASK, level); 138 + if (ret) 139 + goto unlock; 140 + } 141 + } 142 + 143 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, enable_mask, val); 144 + if (ret) 145 + goto unlock; 146 + 147 + priv->fled_torch_used = curr; 148 + 149 + unlock: 150 + mutex_unlock(&priv->lock); 151 + return ret; 152 + } 153 + 154 + static int mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness) 155 + { 156 + /* 157 + * Because of the current spikes when turning on the flash, the brightness should be kept 158 + * by the LED framework. This empty function is used to prevent checking failure when 159 + * led_classdev_flash registers ops. 160 + */ 161 + return 0; 162 + } 163 + 164 + static int _mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness) 165 + { 166 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 167 + struct mt6370_priv *priv = led->priv; 168 + struct led_flash_setting *setting = &fl_cdev->brightness; 169 + u32 val = (brightness - setting->min) / setting->step; 170 + int ret, i; 171 + 172 + if (led->led_no == MT6370_LED_JOINT) { 173 + u32 flevel[MT6370_MAX_LEDS]; 174 + 175 + /* 176 + * There're two flash channels in MT6370. If joint flash output is used, storbe 177 + * current will be averaged output from both channels. 178 + */ 179 + flevel[0] = val / 2; 180 + flevel[1] = val - flevel[0]; 181 + for (i = 0; i < MT6370_MAX_LEDS; i++) { 182 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDISTRB(i), 183 + MT6370_ISTROBE_MASK, flevel[i]); 184 + if (ret) 185 + break; 186 + } 187 + } else { 188 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDISTRB(led->led_no), 189 + MT6370_ISTROBE_MASK, val); 190 + } 191 + 192 + return ret; 193 + } 194 + 195 + static int mt6370_strobe_set(struct led_classdev_flash *fl_cdev, bool state) 196 + { 197 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 198 + struct mt6370_priv *priv = led->priv; 199 + struct led_classdev *lcdev = &fl_cdev->led_cdev; 200 + struct led_flash_setting *s = &fl_cdev->brightness; 201 + u32 led_enable_mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 202 + MT6370_FLCSEN_MASK(led->led_no); 203 + u32 enable_mask = MT6370_STROBEN_MASK | led_enable_mask; 204 + u32 val = state ? led_enable_mask : 0; 205 + u32 curr; 206 + int ret; 207 + 208 + mutex_lock(&priv->lock); 209 + 210 + /* 211 + * There is only one set of flash control logic, and this flag is used to check if 'torch' 212 + * is currently being used. 213 + */ 214 + if (priv->fled_torch_used) { 215 + dev_warn(lcdev->dev, "Please disable torch first [0x%x]\n", priv->fled_torch_used); 216 + ret = -EBUSY; 217 + goto unlock; 218 + } 219 + 220 + if (state) 221 + curr = priv->fled_strobe_used | BIT(led->led_no); 222 + else 223 + curr = priv->fled_strobe_used & ~BIT(led->led_no); 224 + 225 + if (curr) 226 + val |= MT6370_STROBEN_MASK; 227 + 228 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, enable_mask, val); 229 + if (ret) { 230 + dev_err(lcdev->dev, "[%d] control current source %d fail\n", led->led_no, state); 231 + goto unlock; 232 + } 233 + 234 + /* 235 + * If the flash needs to turn on, configure the flash current to ramp up to the setting 236 + * value. Otherwise, always revert to the minimum one. 237 + */ 238 + ret = _mt6370_flash_brightness_set(fl_cdev, state ? s->val : s->min); 239 + if (ret) { 240 + dev_err(lcdev->dev, "[%d] Failed to set brightness\n", led->led_no); 241 + goto unlock; 242 + } 243 + 244 + /* 245 + * For the flash to turn on/off, we must wait for HW ramping up/down time 5ms/500us to 246 + * prevent the unexpected problem. 247 + */ 248 + if (!priv->fled_strobe_used && curr) 249 + usleep_range(5000, 6000); 250 + else if (priv->fled_strobe_used && !curr) 251 + usleep_range(500, 600); 252 + 253 + priv->fled_strobe_used = curr; 254 + 255 + unlock: 256 + mutex_unlock(&priv->lock); 257 + return ret; 258 + } 259 + 260 + static int mt6370_strobe_get(struct led_classdev_flash *fl_cdev, bool *state) 261 + { 262 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 263 + struct mt6370_priv *priv = led->priv; 264 + 265 + mutex_lock(&priv->lock); 266 + *state = !!(priv->fled_strobe_used & BIT(led->led_no)); 267 + mutex_unlock(&priv->lock); 268 + 269 + return 0; 270 + } 271 + 272 + static int mt6370_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout) 273 + { 274 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 275 + struct mt6370_priv *priv = led->priv; 276 + struct led_flash_setting *s = &fl_cdev->timeout; 277 + u32 val = (timeout - s->min) / s->step; 278 + 279 + return regmap_update_bits(priv->regmap, MT6370_REG_STRBTO, MT6370_STRBTO_MASK, val); 280 + } 281 + 282 + static int mt6370_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault) 283 + { 284 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 285 + struct mt6370_priv *priv = led->priv; 286 + u16 fled_stat; 287 + unsigned int chg_stat, strobe_timeout_mask, fled_short_mask; 288 + u32 rfault = 0; 289 + int ret; 290 + 291 + ret = regmap_read(priv->regmap, MT6370_REG_CHGSTAT2, &chg_stat); 292 + if (ret) 293 + return ret; 294 + 295 + ret = regmap_raw_read(priv->regmap, MT6370_REG_FLEDSTAT1, &fled_stat, sizeof(fled_stat)); 296 + if (ret) 297 + return ret; 298 + 299 + switch (led->led_no) { 300 + case MT6370_LED_FLASH1: 301 + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK; 302 + fled_short_mask = MT6370_FLED1SHORT_MASK; 303 + break; 304 + 305 + case MT6370_LED_FLASH2: 306 + strobe_timeout_mask = MT6370_FLED2STRBTO_MASK; 307 + fled_short_mask = MT6370_FLED2SHORT_MASK; 308 + break; 309 + 310 + case MT6370_LED_JOINT: 311 + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK | MT6370_FLED2STRBTO_MASK; 312 + fled_short_mask = MT6370_FLED1SHORT_MASK | MT6370_FLED2SHORT_MASK; 313 + break; 314 + default: 315 + return -EINVAL; 316 + } 317 + 318 + if (chg_stat & MT6370_FLEDCHGVINOVP_MASK) 319 + rfault |= LED_FAULT_INPUT_VOLTAGE; 320 + 321 + if (fled_stat & strobe_timeout_mask) 322 + rfault |= LED_FAULT_TIMEOUT; 323 + 324 + if (fled_stat & fled_short_mask) 325 + rfault |= LED_FAULT_SHORT_CIRCUIT; 326 + 327 + if (fled_stat & MT6370_FLEDLVF_MASK) 328 + rfault |= LED_FAULT_UNDER_VOLTAGE; 329 + 330 + *fault = rfault; 331 + return ret; 332 + } 333 + 334 + static const struct led_flash_ops mt6370_flash_ops = { 335 + .flash_brightness_set = mt6370_flash_brightness_set, 336 + .strobe_set = mt6370_strobe_set, 337 + .strobe_get = mt6370_strobe_get, 338 + .timeout_set = mt6370_timeout_set, 339 + .fault_get = mt6370_fault_get, 340 + }; 341 + 342 + #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) 343 + static int mt6370_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, 344 + bool enable) 345 + { 346 + struct led_classdev_flash *flash = v4l2_flash->fled_cdev; 347 + struct mt6370_led *led = to_mt6370_led(flash, flash); 348 + struct mt6370_priv *priv = led->priv; 349 + u32 mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 350 + MT6370_FLCSEN_MASK(led->led_no); 351 + u32 val = enable ? mask : 0; 352 + int ret; 353 + 354 + mutex_lock(&priv->lock); 355 + 356 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, mask, val); 357 + if (ret) 358 + goto unlock; 359 + 360 + if (enable) 361 + priv->fled_strobe_used |= BIT(led->led_no); 362 + else 363 + priv->fled_strobe_used &= ~BIT(led->led_no); 364 + 365 + unlock: 366 + mutex_unlock(&priv->lock); 367 + return ret; 368 + } 369 + 370 + static const struct v4l2_flash_ops v4l2_flash_ops = { 371 + .external_strobe_set = mt6370_flash_external_strobe_set, 372 + }; 373 + 374 + static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, struct v4l2_flash_config *cfg) 375 + { 376 + struct led_classdev *lcdev; 377 + struct led_flash_setting *s = &cfg->intensity; 378 + 379 + lcdev = &led->flash.led_cdev; 380 + 381 + s->min = MT6370_ITORCH_MIN_uA; 382 + s->step = MT6370_ITORCH_STEP_uA; 383 + s->val = s->max = s->min + (lcdev->max_brightness - 1) * s->step; 384 + 385 + cfg->has_external_strobe = 1; 386 + strscpy(cfg->dev_name, dev_name(lcdev->dev), sizeof(cfg->dev_name)); 387 + 388 + cfg->flash_faults = LED_FAULT_SHORT_CIRCUIT | LED_FAULT_TIMEOUT | 389 + LED_FAULT_INPUT_VOLTAGE | LED_FAULT_UNDER_VOLTAGE; 390 + } 391 + #else 392 + static const struct v4l2_flash_ops v4l2_flash_ops; 393 + static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, struct v4l2_flash_config *cfg) 394 + { 395 + } 396 + #endif 397 + 398 + static void mt6370_v4l2_flash_release(void *v4l2_flash) 399 + { 400 + v4l2_flash_release(v4l2_flash); 401 + } 402 + 403 + static int mt6370_led_register(struct device *parent, struct mt6370_led *led, 404 + struct fwnode_handle *fwnode) 405 + { 406 + struct led_init_data init_data = { .fwnode = fwnode }; 407 + struct v4l2_flash_config v4l2_config = {}; 408 + int ret; 409 + 410 + ret = devm_led_classdev_flash_register_ext(parent, &led->flash, &init_data); 411 + if (ret) 412 + return dev_err_probe(parent, ret, "Couldn't register flash %d\n", led->led_no); 413 + 414 + mt6370_init_v4l2_flash_config(led, &v4l2_config); 415 + led->v4l2_flash = v4l2_flash_init(parent, fwnode, &led->flash, &v4l2_flash_ops, 416 + &v4l2_config); 417 + if (IS_ERR(led->v4l2_flash)) 418 + return dev_err_probe(parent, PTR_ERR(led->v4l2_flash), 419 + "Failed to register %d v4l2 sd\n", led->led_no); 420 + 421 + return devm_add_action_or_reset(parent, mt6370_v4l2_flash_release, led->v4l2_flash); 422 + } 423 + 424 + static u32 mt6370_clamp(u32 val, u32 min, u32 max, u32 step) 425 + { 426 + u32 retval; 427 + 428 + retval = clamp_val(val, min, max); 429 + if (step > 1) 430 + retval = rounddown(retval - min, step) + min; 431 + 432 + return retval; 433 + } 434 + 435 + static int mt6370_init_flash_properties(struct device *dev, struct mt6370_led *led, 436 + struct fwnode_handle *fwnode) 437 + { 438 + struct led_classdev_flash *flash = &led->flash; 439 + struct led_classdev *lcdev = &flash->led_cdev; 440 + struct mt6370_priv *priv = led->priv; 441 + struct led_flash_setting *s; 442 + u32 sources[MT6370_MAX_LEDS]; 443 + u32 max_ua, val; 444 + int i, ret, num; 445 + 446 + num = fwnode_property_count_u32(fwnode, "led-sources"); 447 + if (num < 1) 448 + return dev_err_probe(dev, -EINVAL, 449 + "Not specified or wrong number of led-sources\n"); 450 + 451 + ret = fwnode_property_read_u32_array(fwnode, "led-sources", sources, num); 452 + if (ret) 453 + return ret; 454 + 455 + for (i = 0; i < num; i++) { 456 + if (sources[i] >= MT6370_MAX_LEDS) 457 + return -EINVAL; 458 + if (priv->leds_active & BIT(sources[i])) 459 + return -EINVAL; 460 + priv->leds_active |= BIT(sources[i]); 461 + } 462 + 463 + /* If both channels are specified in 'led-sources', joint flash output mode is used */ 464 + led->led_no = num == 2 ? MT6370_LED_JOINT : sources[0]; 465 + 466 + max_ua = num == 2 ? MT6370_ITORCH_DOUBLE_MAX_uA : MT6370_ITORCH_MAX_uA; 467 + val = MT6370_ITORCH_MIN_uA; 468 + ret = fwnode_property_read_u32(fwnode, "led-max-microamp", &val); 469 + if (!ret) 470 + val = mt6370_clamp(val, MT6370_ITORCH_MIN_uA, max_ua, MT6370_ITORCH_STEP_uA); 471 + 472 + lcdev->max_brightness = (val - MT6370_ITORCH_MIN_uA) / MT6370_ITORCH_STEP_uA + 1; 473 + lcdev->brightness_set_blocking = mt6370_torch_brightness_set; 474 + lcdev->flags |= LED_DEV_CAP_FLASH; 475 + 476 + max_ua = num == 2 ? MT6370_ISTRB_DOUBLE_MAX_uA : MT6370_ISTRB_MAX_uA; 477 + val = MT6370_ISTRB_MIN_uA; 478 + ret = fwnode_property_read_u32(fwnode, "flash-max-microamp", &val); 479 + if (!ret) 480 + val = mt6370_clamp(val, MT6370_ISTRB_MIN_uA, max_ua, MT6370_ISTRB_STEP_uA); 481 + 482 + s = &flash->brightness; 483 + s->min = MT6370_ISTRB_MIN_uA; 484 + s->step = MT6370_ISTRB_STEP_uA; 485 + s->val = s->max = val; 486 + 487 + /* Always configure to the minimum level when off to prevent flash current spikes. */ 488 + ret = _mt6370_flash_brightness_set(flash, s->min); 489 + if (ret) 490 + return ret; 491 + 492 + val = MT6370_STRBTO_MIN_US; 493 + ret = fwnode_property_read_u32(fwnode, "flash-max-timeout-us", &val); 494 + if (!ret) 495 + val = mt6370_clamp(val, MT6370_STRBTO_MIN_US, MT6370_STRBTO_MAX_US, 496 + MT6370_STRBTO_STEP_US); 497 + 498 + s = &flash->timeout; 499 + s->min = MT6370_STRBTO_MIN_US; 500 + s->step = MT6370_STRBTO_STEP_US; 501 + s->val = s->max = val; 502 + 503 + flash->ops = &mt6370_flash_ops; 504 + 505 + return 0; 506 + } 507 + 508 + static int mt6370_led_probe(struct platform_device *pdev) 509 + { 510 + struct device *dev = &pdev->dev; 511 + struct mt6370_priv *priv; 512 + struct fwnode_handle *child; 513 + size_t count; 514 + int i = 0, ret; 515 + 516 + count = device_get_child_node_count(dev); 517 + if (!count || count > MT6370_MAX_LEDS) 518 + return dev_err_probe(dev, -EINVAL, 519 + "No child node or node count over max led number %zu\n", count); 520 + 521 + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); 522 + if (!priv) 523 + return -ENOMEM; 524 + 525 + priv->leds_count = count; 526 + mutex_init(&priv->lock); 527 + 528 + priv->regmap = dev_get_regmap(dev->parent, NULL); 529 + if (!priv->regmap) 530 + return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n"); 531 + 532 + device_for_each_child_node(dev, child) { 533 + struct mt6370_led *led = priv->leds + i; 534 + 535 + led->priv = priv; 536 + 537 + ret = mt6370_init_flash_properties(dev, led, child); 538 + if (ret) { 539 + fwnode_handle_put(child); 540 + return ret; 541 + } 542 + 543 + ret = mt6370_led_register(dev, led, child); 544 + if (ret) { 545 + fwnode_handle_put(child); 546 + return ret; 547 + } 548 + 549 + i++; 550 + } 551 + 552 + return 0; 553 + } 554 + 555 + static const struct of_device_id mt6370_led_of_id[] = { 556 + { .compatible = "mediatek,mt6370-flashlight" }, 557 + {} 558 + }; 559 + MODULE_DEVICE_TABLE(of, mt6370_led_of_id); 560 + 561 + static struct platform_driver mt6370_led_driver = { 562 + .driver = { 563 + .name = "mt6370-flashlight", 564 + .of_match_table = mt6370_led_of_id, 565 + }, 566 + .probe = mt6370_led_probe, 567 + }; 568 + module_platform_driver(mt6370_led_driver); 569 + 570 + MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>"); 571 + MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 572 + MODULE_DESCRIPTION("MT6370 FLASH LED Driver"); 573 + MODULE_LICENSE("GPL");