brintos

brintos / linux-shallow public Read only

0
0
Text · 22.4 KiB · 4c74f1c Raw
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), &regval);402	if (ret)403		return ret;404	level = regval & MT6360_ISNK_MASK;405 406	ret = regmap_read(priv->regmap, MT6360_REG_RGBEN, &regval);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			  &regval);438	if (ret)439		return ret;440	level = regval & MT6360_ITORCH_MASK;441 442	ret = regmap_read(priv->regmap, MT6360_REG_FLEDEN, &regval);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", &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", &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