897 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3#include <linux/bitops.h>4#include <linux/delay.h>5#include <linux/init.h>6#include <linux/interrupt.h>7#include <linux/kernel.h>8#include <linux/led-class-flash.h>9#include <linux/led-class-multicolor.h>10#include <linux/module.h>11#include <linux/mutex.h>12#include <linux/platform_device.h>13#include <linux/property.h>14#include <linux/regmap.h>15#include <media/v4l2-flash-led-class.h>16 17enum {18 MT6360_LED_ISNK1 = 0,19 MT6360_LED_ISNK2,20 MT6360_LED_ISNK3,21 MT6360_LED_ISNKML,22 MT6360_LED_FLASH1,23 MT6360_LED_FLASH2,24 MT6360_MAX_LEDS25};26 27#define MT6360_REG_RGBEN 0x38028#define MT6360_REG_ISNK(_led_no) (0x381 + (_led_no))29#define MT6360_ISNK_ENMASK(_led_no) BIT(7 - (_led_no))30#define MT6360_ISNK_MASK GENMASK(4, 0)31#define MT6360_CHRINDSEL_MASK BIT(3)32 33/* Virtual definition for multicolor */34#define MT6360_VIRTUAL_MULTICOLOR (MT6360_MAX_LEDS + 1)35#define MULTICOLOR_NUM_CHANNELS 336 37#define MT6360_REG_FLEDEN 0x37E38#define MT6360_REG_STRBTO 0x37339#define MT6360_REG_FLEDBASE(_id) (0x372 + 4 * (_id - MT6360_LED_FLASH1))40#define MT6360_REG_FLEDISTRB(_id) (MT6360_REG_FLEDBASE(_id) + 2)41#define MT6360_REG_FLEDITOR(_id) (MT6360_REG_FLEDBASE(_id) + 3)42#define MT6360_REG_CHGSTAT2 0x3E143#define MT6360_REG_FLEDSTAT1 0x3E944#define MT6360_ITORCH_MASK GENMASK(4, 0)45#define MT6360_ISTROBE_MASK GENMASK(6, 0)46#define MT6360_STRBTO_MASK GENMASK(6, 0)47#define MT6360_TORCHEN_MASK BIT(3)48#define MT6360_STROBEN_MASK BIT(2)49#define MT6360_FLCSEN_MASK(_id) BIT(MT6360_LED_FLASH2 - _id)50#define MT6360_FLEDCHGVINOVP_MASK BIT(3)51#define MT6360_FLED1STRBTO_MASK BIT(11)52#define MT6360_FLED2STRBTO_MASK BIT(10)53#define MT6360_FLED1STRB_MASK BIT(9)54#define MT6360_FLED2STRB_MASK BIT(8)55#define MT6360_FLED1SHORT_MASK BIT(7)56#define MT6360_FLED2SHORT_MASK BIT(6)57#define MT6360_FLEDLVF_MASK BIT(3)58 59#define MT6360_ISNKRGB_STEPUA 200060#define MT6360_ISNKRGB_MAXUA 2400061#define MT6360_ISNKML_STEPUA 500062#define MT6360_ISNKML_MAXUA 15000063 64#define MT6360_ITORCH_MINUA 2500065#define MT6360_ITORCH_STEPUA 1250066#define MT6360_ITORCH_MAXUA 40000067#define MT6360_ISTRB_MINUA 5000068#define MT6360_ISTRB_STEPUA 1250069#define MT6360_ISTRB_MAXUA 150000070#define MT6360_STRBTO_MINUS 6400071#define MT6360_STRBTO_STEPUS 3200072#define MT6360_STRBTO_MAXUS 243200073 74struct mt6360_led {75 union {76 struct led_classdev isnk;77 struct led_classdev_mc mc;78 struct led_classdev_flash flash;79 };80 struct v4l2_flash *v4l2_flash;81 struct mt6360_priv *priv;82 u32 led_no;83 enum led_default_state default_state;84};85 86struct mt6360_priv {87 struct device *dev;88 struct regmap *regmap;89 struct mutex lock;90 unsigned int fled_strobe_used;91 unsigned int fled_torch_used;92 unsigned int leds_active;93 unsigned int leds_count;94 struct mt6360_led leds[] __counted_by(leds_count);95};96 97static int mt6360_mc_brightness_set(struct led_classdev *lcdev,98 enum led_brightness level)99{100 struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);101 struct mt6360_led *led = container_of(mccdev, struct mt6360_led, mc);102 struct mt6360_priv *priv = led->priv;103 u32 real_bright, enable_mask = 0, enable = 0;104 int i, ret;105 106 mutex_lock(&priv->lock);107 108 led_mc_calc_color_components(mccdev, level);109 110 for (i = 0; i < mccdev->num_colors; i++) {111 struct mc_subled *subled = mccdev->subled_info + i;112 113 real_bright = min(lcdev->max_brightness, subled->brightness);114 ret = regmap_update_bits(priv->regmap, MT6360_REG_ISNK(i),115 MT6360_ISNK_MASK, real_bright);116 if (ret)117 goto out;118 119 enable_mask |= MT6360_ISNK_ENMASK(subled->channel);120 if (real_bright)121 enable |= MT6360_ISNK_ENMASK(subled->channel);122 }123 124 ret = regmap_update_bits(priv->regmap, MT6360_REG_RGBEN, enable_mask,125 enable);126 127out:128 mutex_unlock(&priv->lock);129 return ret;130}131 132static int mt6360_isnk_brightness_set(struct led_classdev *lcdev,133 enum led_brightness level)134{135 struct mt6360_led *led = container_of(lcdev, struct mt6360_led, isnk);136 struct mt6360_priv *priv = led->priv;137 u32 enable_mask = MT6360_ISNK_ENMASK(led->led_no);138 u32 val = level ? MT6360_ISNK_ENMASK(led->led_no) : 0;139 int ret;140 141 mutex_lock(&priv->lock);142 143 ret = regmap_update_bits(priv->regmap, MT6360_REG_ISNK(led->led_no),144 MT6360_ISNK_MASK, level);145 if (ret)146 goto out;147 148 ret = regmap_update_bits(priv->regmap, MT6360_REG_RGBEN, enable_mask,149 val);150 151out:152 mutex_unlock(&priv->lock);153 return ret;154}155 156static int mt6360_torch_brightness_set(struct led_classdev *lcdev,157 enum led_brightness level)158{159 struct mt6360_led *led =160 container_of(lcdev, struct mt6360_led, flash.led_cdev);161 struct mt6360_priv *priv = led->priv;162 u32 enable_mask = MT6360_TORCHEN_MASK | MT6360_FLCSEN_MASK(led->led_no);163 u32 val = level ? MT6360_FLCSEN_MASK(led->led_no) : 0;164 u32 prev = priv->fled_torch_used, curr;165 int ret;166 167 mutex_lock(&priv->lock);168 169 /*170 * Only one set of flash control logic, use the flag to avoid strobe is171 * currently used.172 */173 if (priv->fled_strobe_used) {174 dev_warn(lcdev->dev, "Please disable strobe first [%d]\n",175 priv->fled_strobe_used);176 ret = -EBUSY;177 goto unlock;178 }179 180 if (level)181 curr = prev | BIT(led->led_no);182 else183 curr = prev & ~BIT(led->led_no);184 185 if (curr)186 val |= MT6360_TORCHEN_MASK;187 188 if (level) {189 ret = regmap_update_bits(priv->regmap,190 MT6360_REG_FLEDITOR(led->led_no),191 MT6360_ITORCH_MASK, level - 1);192 if (ret)193 goto unlock;194 }195 196 ret = regmap_update_bits(priv->regmap, MT6360_REG_FLEDEN, enable_mask,197 val);198 if (ret)199 goto unlock;200 201 priv->fled_torch_used = curr;202 203unlock:204 mutex_unlock(&priv->lock);205 return ret;206}207 208static int mt6360_flash_brightness_set(struct led_classdev_flash *fl_cdev,209 u32 brightness)210{211 /*212 * Due to the current spike when turning on flash, let brightness to be213 * kept by framework.214 * This empty function is used to prevent led_classdev_flash register215 * ops check failure.216 */217 return 0;218}219 220static int _mt6360_flash_brightness_set(struct led_classdev_flash *fl_cdev,221 u32 brightness)222{223 struct mt6360_led *led =224 container_of(fl_cdev, struct mt6360_led, flash);225 struct mt6360_priv *priv = led->priv;226 struct led_flash_setting *s = &fl_cdev->brightness;227 u32 val = (brightness - s->min) / s->step;228 229 return regmap_update_bits(priv->regmap,230 MT6360_REG_FLEDISTRB(led->led_no),231 MT6360_ISTROBE_MASK, val);232}233 234static int mt6360_strobe_set(struct led_classdev_flash *fl_cdev, bool state)235{236 struct mt6360_led *led =237 container_of(fl_cdev, struct mt6360_led, flash);238 struct mt6360_priv *priv = led->priv;239 struct led_classdev *lcdev = &fl_cdev->led_cdev;240 struct led_flash_setting *s = &fl_cdev->brightness;241 u32 enable_mask = MT6360_STROBEN_MASK | MT6360_FLCSEN_MASK(led->led_no);242 u32 val = state ? MT6360_FLCSEN_MASK(led->led_no) : 0;243 u32 prev = priv->fled_strobe_used, curr;244 int ret = 0;245 246 mutex_lock(&priv->lock);247 248 /*249 * If the state of the upcoming change is the same as the current LED250 * device state, then skip the subsequent code to avoid conflict251 * with the flow of turning on LED torch mode in V4L2.252 */253 if (state == !!(BIT(led->led_no) & prev)) {254 dev_info(lcdev->dev, "No change in strobe state [0x%x]\n", prev);255 goto unlock;256 }257 258 /*259 * Only one set of flash control logic, use the flag to avoid torch is260 * currently used261 */262 if (priv->fled_torch_used) {263 dev_warn(lcdev->dev, "Please disable torch first [0x%x]\n",264 priv->fled_torch_used);265 ret = -EBUSY;266 goto unlock;267 }268 269 if (state)270 curr = prev | BIT(led->led_no);271 else272 curr = prev & ~BIT(led->led_no);273 274 if (curr)275 val |= MT6360_STROBEN_MASK;276 277 ret = regmap_update_bits(priv->regmap, MT6360_REG_FLEDEN, enable_mask,278 val);279 if (ret) {280 dev_err(lcdev->dev, "[%d] control current source %d fail\n",281 led->led_no, state);282 goto unlock;283 }284 285 /*286 * If the flash need to be on, config the flash current ramping up to287 * the setting value.288 * Else, always recover back to the minimum one289 */290 ret = _mt6360_flash_brightness_set(fl_cdev, state ? s->val : s->min);291 if (ret)292 goto unlock;293 294 /*295 * For the flash turn on/off, HW rampping up/down time is 5ms/500us,296 * respectively.297 */298 if (!prev && curr)299 usleep_range(5000, 6000);300 else if (prev && !curr)301 udelay(500);302 303 priv->fled_strobe_used = curr;304 305unlock:306 mutex_unlock(&priv->lock);307 return ret;308}309 310static int mt6360_strobe_get(struct led_classdev_flash *fl_cdev, bool *state)311{312 struct mt6360_led *led =313 container_of(fl_cdev, struct mt6360_led, flash);314 struct mt6360_priv *priv = led->priv;315 316 mutex_lock(&priv->lock);317 *state = !!(priv->fled_strobe_used & BIT(led->led_no));318 mutex_unlock(&priv->lock);319 320 return 0;321}322 323static int mt6360_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout)324{325 struct mt6360_led *led =326 container_of(fl_cdev, struct mt6360_led, flash);327 struct mt6360_priv *priv = led->priv;328 struct led_flash_setting *s = &fl_cdev->timeout;329 u32 val = (timeout - s->min) / s->step;330 int ret;331 332 mutex_lock(&priv->lock);333 ret = regmap_update_bits(priv->regmap, MT6360_REG_STRBTO,334 MT6360_STRBTO_MASK, val);335 mutex_unlock(&priv->lock);336 337 return ret;338}339 340static int mt6360_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault)341{342 struct mt6360_led *led =343 container_of(fl_cdev, struct mt6360_led, flash);344 struct mt6360_priv *priv = led->priv;345 u16 fled_stat;346 unsigned int chg_stat, strobe_timeout_mask, fled_short_mask;347 u32 rfault = 0;348 int ret;349 350 mutex_lock(&priv->lock);351 ret = regmap_read(priv->regmap, MT6360_REG_CHGSTAT2, &chg_stat);352 if (ret)353 goto unlock;354 355 ret = regmap_raw_read(priv->regmap, MT6360_REG_FLEDSTAT1, &fled_stat,356 sizeof(fled_stat));357 if (ret)358 goto unlock;359 360 if (led->led_no == MT6360_LED_FLASH1) {361 strobe_timeout_mask = MT6360_FLED1STRBTO_MASK;362 fled_short_mask = MT6360_FLED1SHORT_MASK;363 } else {364 strobe_timeout_mask = MT6360_FLED2STRBTO_MASK;365 fled_short_mask = MT6360_FLED2SHORT_MASK;366 }367 368 if (chg_stat & MT6360_FLEDCHGVINOVP_MASK)369 rfault |= LED_FAULT_INPUT_VOLTAGE;370 371 if (fled_stat & strobe_timeout_mask)372 rfault |= LED_FAULT_TIMEOUT;373 374 if (fled_stat & fled_short_mask)375 rfault |= LED_FAULT_SHORT_CIRCUIT;376 377 if (fled_stat & MT6360_FLEDLVF_MASK)378 rfault |= LED_FAULT_UNDER_VOLTAGE;379 380 *fault = rfault;381unlock:382 mutex_unlock(&priv->lock);383 return ret;384}385 386static const struct led_flash_ops mt6360_flash_ops = {387 .flash_brightness_set = mt6360_flash_brightness_set,388 .strobe_set = mt6360_strobe_set,389 .strobe_get = mt6360_strobe_get,390 .timeout_set = mt6360_timeout_set,391 .fault_get = mt6360_fault_get,392};393 394static int mt6360_isnk_init_default_state(struct mt6360_led *led)395{396 struct mt6360_priv *priv = led->priv;397 unsigned int regval;398 u32 level;399 int ret;400 401 ret = regmap_read(priv->regmap, MT6360_REG_ISNK(led->led_no), ®val);402 if (ret)403 return ret;404 level = regval & MT6360_ISNK_MASK;405 406 ret = regmap_read(priv->regmap, MT6360_REG_RGBEN, ®val);407 if (ret)408 return ret;409 410 if (!(regval & MT6360_ISNK_ENMASK(led->led_no)))411 level = LED_OFF;412 413 switch (led->default_state) {414 case LEDS_DEFSTATE_ON:415 led->isnk.brightness = led->isnk.max_brightness;416 break;417 case LEDS_DEFSTATE_KEEP:418 led->isnk.brightness = min(level, led->isnk.max_brightness);419 break;420 default:421 led->isnk.brightness = LED_OFF;422 }423 424 return mt6360_isnk_brightness_set(&led->isnk, led->isnk.brightness);425}426 427static int mt6360_flash_init_default_state(struct mt6360_led *led)428{429 struct led_classdev_flash *flash = &led->flash;430 struct mt6360_priv *priv = led->priv;431 u32 enable_mask = MT6360_TORCHEN_MASK | MT6360_FLCSEN_MASK(led->led_no);432 u32 level;433 unsigned int regval;434 int ret;435 436 ret = regmap_read(priv->regmap, MT6360_REG_FLEDITOR(led->led_no),437 ®val);438 if (ret)439 return ret;440 level = regval & MT6360_ITORCH_MASK;441 442 ret = regmap_read(priv->regmap, MT6360_REG_FLEDEN, ®val);443 if (ret)444 return ret;445 446 if ((regval & enable_mask) == enable_mask)447 level += 1;448 else449 level = LED_OFF;450 451 switch (led->default_state) {452 case LEDS_DEFSTATE_ON:453 flash->led_cdev.brightness = flash->led_cdev.max_brightness;454 break;455 case LEDS_DEFSTATE_KEEP:456 flash->led_cdev.brightness =457 min(level, flash->led_cdev.max_brightness);458 break;459 default:460 flash->led_cdev.brightness = LED_OFF;461 }462 463 return mt6360_torch_brightness_set(&flash->led_cdev,464 flash->led_cdev.brightness);465}466 467#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)468static int mt6360_flash_external_strobe_set(struct v4l2_flash *v4l2_flash,469 bool enable)470{471 struct led_classdev_flash *flash = v4l2_flash->fled_cdev;472 struct mt6360_led *led = container_of(flash, struct mt6360_led, flash);473 struct mt6360_priv *priv = led->priv;474 u32 mask = MT6360_FLCSEN_MASK(led->led_no);475 u32 val = enable ? mask : 0;476 int ret;477 478 mutex_lock(&priv->lock);479 480 ret = regmap_update_bits(priv->regmap, MT6360_REG_FLEDEN, mask, val);481 if (ret)482 goto unlock;483 484 if (enable)485 priv->fled_strobe_used |= BIT(led->led_no);486 else487 priv->fled_strobe_used &= ~BIT(led->led_no);488 489unlock:490 mutex_unlock(&priv->lock);491 return ret;492}493 494static const struct v4l2_flash_ops v4l2_flash_ops = {495 .external_strobe_set = mt6360_flash_external_strobe_set,496};497 498static void mt6360_init_v4l2_flash_config(struct mt6360_led *led,499 struct v4l2_flash_config *config)500{501 struct led_classdev *lcdev;502 struct led_flash_setting *s = &config->intensity;503 504 lcdev = &led->flash.led_cdev;505 506 s->min = MT6360_ITORCH_MINUA;507 s->step = MT6360_ITORCH_STEPUA;508 s->val = s->max = s->min + (lcdev->max_brightness - 1) * s->step;509 510 config->has_external_strobe = 1;511 strscpy(config->dev_name, lcdev->dev->kobj.name,512 sizeof(config->dev_name));513 514 config->flash_faults = LED_FAULT_SHORT_CIRCUIT | LED_FAULT_TIMEOUT |515 LED_FAULT_INPUT_VOLTAGE |516 LED_FAULT_UNDER_VOLTAGE;517}518#else519static const struct v4l2_flash_ops v4l2_flash_ops;520static void mt6360_init_v4l2_flash_config(struct mt6360_led *led,521 struct v4l2_flash_config *config)522{523}524#endif525 526static int mt6360_led_register(struct device *parent, struct mt6360_led *led,527 struct led_init_data *init_data)528{529 struct mt6360_priv *priv = led->priv;530 struct v4l2_flash_config v4l2_config = {0};531 int ret;532 533 if ((led->led_no == MT6360_LED_ISNK1 ||534 led->led_no == MT6360_VIRTUAL_MULTICOLOR) &&535 (priv->leds_active & BIT(MT6360_LED_ISNK1))) {536 /*537 * Change isink1 to SW control mode, disconnect it with538 * charger state539 */540 ret = regmap_update_bits(priv->regmap, MT6360_REG_RGBEN,541 MT6360_CHRINDSEL_MASK,542 MT6360_CHRINDSEL_MASK);543 if (ret) {544 dev_err(parent, "Failed to config ISNK1 to SW mode\n");545 return ret;546 }547 }548 549 switch (led->led_no) {550 case MT6360_VIRTUAL_MULTICOLOR:551 ret = mt6360_mc_brightness_set(&led->mc.led_cdev, LED_OFF);552 if (ret) {553 dev_err(parent,554 "Failed to init multicolor brightness\n");555 return ret;556 }557 558 ret = devm_led_classdev_multicolor_register_ext(parent,559 &led->mc, init_data);560 if (ret) {561 dev_err(parent, "Couldn't register multicolor\n");562 return ret;563 }564 break;565 case MT6360_LED_ISNK1 ... MT6360_LED_ISNKML:566 ret = mt6360_isnk_init_default_state(led);567 if (ret) {568 dev_err(parent, "Failed to init %d isnk state\n",569 led->led_no);570 return ret;571 }572 573 ret = devm_led_classdev_register_ext(parent, &led->isnk,574 init_data);575 if (ret) {576 dev_err(parent, "Couldn't register isink %d\n",577 led->led_no);578 return ret;579 }580 break;581 default:582 ret = mt6360_flash_init_default_state(led);583 if (ret) {584 dev_err(parent, "Failed to init %d flash state\n",585 led->led_no);586 return ret;587 }588 589 ret = devm_led_classdev_flash_register_ext(parent, &led->flash,590 init_data);591 if (ret) {592 dev_err(parent, "Couldn't register flash %d\n",593 led->led_no);594 return ret;595 }596 597 mt6360_init_v4l2_flash_config(led, &v4l2_config);598 led->v4l2_flash = v4l2_flash_init(parent, init_data->fwnode,599 &led->flash,600 &v4l2_flash_ops,601 &v4l2_config);602 if (IS_ERR(led->v4l2_flash)) {603 dev_err(parent, "Failed to register %d v4l2 sd\n",604 led->led_no);605 return PTR_ERR(led->v4l2_flash);606 }607 }608 609 return 0;610}611 612static u32 clamp_align(u32 val, u32 min, u32 max, u32 step)613{614 u32 retval;615 616 retval = clamp_val(val, min, max);617 if (step > 1)618 retval = rounddown(retval - min, step) + min;619 620 return retval;621}622 623static int mt6360_init_isnk_properties(struct mt6360_led *led,624 struct led_init_data *init_data)625{626 struct led_classdev *lcdev;627 struct mt6360_priv *priv = led->priv;628 struct fwnode_handle *child;629 u32 step_uA = MT6360_ISNKRGB_STEPUA, max_uA = MT6360_ISNKRGB_MAXUA;630 u32 val;631 int num_color = 0, ret;632 633 if (led->led_no == MT6360_VIRTUAL_MULTICOLOR) {634 struct mc_subled *sub_led;635 636 sub_led = devm_kzalloc(priv->dev,637 sizeof(*sub_led) * MULTICOLOR_NUM_CHANNELS, GFP_KERNEL);638 if (!sub_led)639 return -ENOMEM;640 641 fwnode_for_each_child_node(init_data->fwnode, child) {642 u32 reg, color;643 644 ret = fwnode_property_read_u32(child, "reg", ®);645 if (ret || reg > MT6360_LED_ISNK3 ||646 priv->leds_active & BIT(reg)) {647 fwnode_handle_put(child);648 return -EINVAL;649 }650 651 ret = fwnode_property_read_u32(child, "color", &color);652 if (ret) {653 dev_err(priv->dev,654 "led %d, no color specified\n",655 led->led_no);656 fwnode_handle_put(child);657 return ret;658 }659 660 priv->leds_active |= BIT(reg);661 sub_led[num_color].color_index = color;662 sub_led[num_color].channel = reg;663 num_color++;664 }665 666 if (num_color < 2) {667 dev_err(priv->dev,668 "Multicolor must include 2 or more led channel\n");669 return -EINVAL;670 }671 672 led->mc.num_colors = num_color;673 led->mc.subled_info = sub_led;674 675 lcdev = &led->mc.led_cdev;676 lcdev->brightness_set_blocking = mt6360_mc_brightness_set;677 } else {678 if (led->led_no == MT6360_LED_ISNKML) {679 step_uA = MT6360_ISNKML_STEPUA;680 max_uA = MT6360_ISNKML_MAXUA;681 }682 683 lcdev = &led->isnk;684 lcdev->brightness_set_blocking = mt6360_isnk_brightness_set;685 }686 687 ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp",688 &val);689 if (ret) {690 dev_warn(priv->dev,691 "Not specified led-max-microamp, config to the minimum\n");692 val = step_uA;693 } else694 val = clamp_align(val, 0, max_uA, step_uA);695 696 lcdev->max_brightness = val / step_uA;697 698 fwnode_property_read_string(init_data->fwnode, "linux,default-trigger",699 &lcdev->default_trigger);700 701 return 0;702}703 704static int mt6360_init_flash_properties(struct mt6360_led *led,705 struct led_init_data *init_data)706{707 struct led_classdev_flash *flash = &led->flash;708 struct led_classdev *lcdev = &flash->led_cdev;709 struct mt6360_priv *priv = led->priv;710 struct led_flash_setting *s;711 u32 val;712 int ret;713 714 ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp",715 &val);716 if (ret) {717 dev_warn(priv->dev,718 "Not specified led-max-microamp, config to the minimum\n");719 val = MT6360_ITORCH_MINUA;720 } else721 val = clamp_align(val, MT6360_ITORCH_MINUA, MT6360_ITORCH_MAXUA,722 MT6360_ITORCH_STEPUA);723 724 lcdev->max_brightness =725 (val - MT6360_ITORCH_MINUA) / MT6360_ITORCH_STEPUA + 1;726 lcdev->brightness_set_blocking = mt6360_torch_brightness_set;727 lcdev->flags |= LED_DEV_CAP_FLASH;728 729 ret = fwnode_property_read_u32(init_data->fwnode, "flash-max-microamp",730 &val);731 if (ret) {732 dev_warn(priv->dev,733 "Not specified flash-max-microamp, config to the minimum\n");734 val = MT6360_ISTRB_MINUA;735 } else736 val = clamp_align(val, MT6360_ISTRB_MINUA, MT6360_ISTRB_MAXUA,737 MT6360_ISTRB_STEPUA);738 739 s = &flash->brightness;740 s->min = MT6360_ISTRB_MINUA;741 s->step = MT6360_ISTRB_STEPUA;742 s->val = s->max = val;743 744 /*745 * Always configure as min level when off to prevent flash current746 * spike.747 */748 ret = _mt6360_flash_brightness_set(flash, s->min);749 if (ret)750 return ret;751 752 ret = fwnode_property_read_u32(init_data->fwnode,753 "flash-max-timeout-us", &val);754 if (ret) {755 dev_warn(priv->dev,756 "Not specified flash-max-timeout-us, config to the minimum\n");757 val = MT6360_STRBTO_MINUS;758 } else759 val = clamp_align(val, MT6360_STRBTO_MINUS, MT6360_STRBTO_MAXUS,760 MT6360_STRBTO_STEPUS);761 762 s = &flash->timeout;763 s->min = MT6360_STRBTO_MINUS;764 s->step = MT6360_STRBTO_STEPUS;765 s->val = s->max = val;766 767 flash->ops = &mt6360_flash_ops;768 769 return 0;770}771 772static void mt6360_v4l2_flash_release(struct mt6360_priv *priv)773{774 int i;775 776 for (i = 0; i < priv->leds_count; i++) {777 struct mt6360_led *led = priv->leds + i;778 779 if (led->v4l2_flash)780 v4l2_flash_release(led->v4l2_flash);781 }782}783 784static int mt6360_led_probe(struct platform_device *pdev)785{786 struct mt6360_priv *priv;787 struct fwnode_handle *child;788 size_t count;789 int i = 0, ret;790 791 count = device_get_child_node_count(&pdev->dev);792 if (!count || count > MT6360_MAX_LEDS) {793 dev_err(&pdev->dev,794 "No child node or node count over max led number %zu\n",795 count);796 return -EINVAL;797 }798 799 priv = devm_kzalloc(&pdev->dev,800 struct_size(priv, leds, count), GFP_KERNEL);801 if (!priv)802 return -ENOMEM;803 804 priv->leds_count = count;805 priv->dev = &pdev->dev;806 mutex_init(&priv->lock);807 808 priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);809 if (!priv->regmap) {810 dev_err(&pdev->dev, "Failed to get parent regmap\n");811 return -ENODEV;812 }813 814 device_for_each_child_node(&pdev->dev, child) {815 struct mt6360_led *led = priv->leds + i;816 struct led_init_data init_data = { .fwnode = child, };817 u32 reg, led_color;818 819 ret = fwnode_property_read_u32(child, "color", &led_color);820 if (ret)821 goto out_flash_release;822 823 if (led_color == LED_COLOR_ID_RGB ||824 led_color == LED_COLOR_ID_MULTI)825 reg = MT6360_VIRTUAL_MULTICOLOR;826 else {827 ret = fwnode_property_read_u32(child, "reg", ®);828 if (ret)829 goto out_flash_release;830 831 if (reg >= MT6360_MAX_LEDS) {832 ret = -EINVAL;833 goto out_flash_release;834 }835 }836 837 if (priv->leds_active & BIT(reg)) {838 ret = -EINVAL;839 goto out_flash_release;840 }841 priv->leds_active |= BIT(reg);842 843 led->led_no = reg;844 led->priv = priv;845 led->default_state = led_init_default_state_get(child);846 847 if (reg == MT6360_VIRTUAL_MULTICOLOR ||848 reg <= MT6360_LED_ISNKML)849 ret = mt6360_init_isnk_properties(led, &init_data);850 else851 ret = mt6360_init_flash_properties(led, &init_data);852 853 if (ret)854 goto out_flash_release;855 856 ret = mt6360_led_register(&pdev->dev, led, &init_data);857 if (ret)858 goto out_flash_release;859 860 i++;861 }862 863 platform_set_drvdata(pdev, priv);864 return 0;865 866out_flash_release:867 mt6360_v4l2_flash_release(priv);868 return ret;869}870 871static void mt6360_led_remove(struct platform_device *pdev)872{873 struct mt6360_priv *priv = platform_get_drvdata(pdev);874 875 mt6360_v4l2_flash_release(priv);876}877 878static const struct of_device_id __maybe_unused mt6360_led_of_id[] = {879 { .compatible = "mediatek,mt6360-led", },880 {}881};882MODULE_DEVICE_TABLE(of, mt6360_led_of_id);883 884static struct platform_driver mt6360_led_driver = {885 .driver = {886 .name = "mt6360-led",887 .of_match_table = mt6360_led_of_id,888 },889 .probe = mt6360_led_probe,890 .remove_new = mt6360_led_remove,891};892module_platform_driver(mt6360_led_driver);893 894MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");895MODULE_DESCRIPTION("MT6360 LED Driver");896MODULE_LICENSE("GPL v2");897