574 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 24enum {25 MT6370_LED_FLASH1 = 0,26 MT6370_LED_FLASH2,27 MT6370_MAX_LEDS28};29 30/* Virtual definition for multicolor */31 32#define MT6370_REG_FLEDEN 0x17E33#define MT6370_REG_STRBTO 0x17334#define MT6370_REG_CHGSTAT2 0x1D135#define MT6370_REG_FLEDSTAT1 0x1D936#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 255#define MT6370_RANGE_FLED_REG 456#define MT6370_ITORCH_MIN_uA 2500057#define MT6370_ITORCH_STEP_uA 1250058#define MT6370_ITORCH_MAX_uA 40000059#define MT6370_ITORCH_DOUBLE_MAX_uA 80000060#define MT6370_ISTRB_MIN_uA 5000061#define MT6370_ISTRB_STEP_uA 1250062#define MT6370_ISTRB_MAX_uA 150000063#define MT6370_ISTRB_DOUBLE_MAX_uA 300000064#define MT6370_STRBTO_MIN_US 6400065#define MT6370_STRBTO_STEP_US 3200066#define MT6370_STRBTO_MAX_US 243200067 68#define to_mt6370_led(ptr, member) container_of(ptr, struct mt6370_led, member)69 70struct 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 77struct 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[] __counted_by(leds_count);85};86 87static 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 else113 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 149unlock:150 mutex_unlock(&priv->lock);151 return ret;152}153 154static 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 kept158 * by the LED framework. This empty function is used to prevent checking failure when159 * led_classdev_flash registers ops.160 */161 return 0;162}163 164static 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, storbe177 * 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 195static 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 else223 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 setting236 * 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 to246 * 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 255unlock:256 mutex_unlock(&priv->lock);257 return ret;258}259 260static 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 272static 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 282static 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 334static 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)343static 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 else363 priv->fled_strobe_used &= ~BIT(led->led_no);364 365unlock:366 mutex_unlock(&priv->lock);367 return ret;368}369 370static const struct v4l2_flash_ops v4l2_flash_ops = {371 .external_strobe_set = mt6370_flash_external_strobe_set,372};373 374static 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#else392static const struct v4l2_flash_ops v4l2_flash_ops;393static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, struct v4l2_flash_config *cfg)394{395}396#endif397 398static void mt6370_v4l2_flash_release(void *v4l2_flash)399{400 v4l2_flash_release(v4l2_flash);401}402 403static 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 424static 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 435static 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 508static 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 555static const struct of_device_id mt6370_led_of_id[] = {556 { .compatible = "mediatek,mt6370-flashlight" },557 {}558};559MODULE_DEVICE_TABLE(of, mt6370_led_of_id);560 561static 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};568module_platform_driver(mt6370_led_driver);569 570MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>");571MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");572MODULE_DESCRIPTION("MT6370 FLASH LED Driver");573MODULE_LICENSE("GPL");574