xref: /linux/drivers/leds/rgb/leds-mt6370-rgb.c (revision 594ce0b8a998aa4d05827cd7c0d0dcec9a1e3ae2)
15c38376eSChiYuan Huang // SPDX-License-Identifier: GPL-2.0-only
25c38376eSChiYuan Huang /*
35c38376eSChiYuan Huang  * Copyright (C) 2023 Richtek Technology Corp.
45c38376eSChiYuan Huang  *
55c38376eSChiYuan Huang  * Authors:
65c38376eSChiYuan Huang  *   ChiYuan Huang <cy_huang@richtek.com>
75c38376eSChiYuan Huang  *   Alice Chen <alice_chen@richtek.com>
85c38376eSChiYuan Huang  */
95c38376eSChiYuan Huang 
104793b19eSChiYuan Huang #include <linux/bitfield.h>
115c38376eSChiYuan Huang #include <linux/bitops.h>
125c38376eSChiYuan Huang #include <linux/kernel.h>
135c38376eSChiYuan Huang #include <linux/leds.h>
145c38376eSChiYuan Huang #include <linux/led-class-multicolor.h>
155c38376eSChiYuan Huang #include <linux/linear_range.h>
165c38376eSChiYuan Huang #include <linux/mod_devicetable.h>
175c38376eSChiYuan Huang #include <linux/module.h>
185c38376eSChiYuan Huang #include <linux/mutex.h>
195c38376eSChiYuan Huang #include <linux/platform_device.h>
205c38376eSChiYuan Huang #include <linux/property.h>
215c38376eSChiYuan Huang #include <linux/regmap.h>
225c38376eSChiYuan Huang #include <linux/util_macros.h>
235c38376eSChiYuan Huang 
245c38376eSChiYuan Huang #include <asm/unaligned.h>
255c38376eSChiYuan Huang 
265c38376eSChiYuan Huang enum {
275c38376eSChiYuan Huang 	MT6370_LED_ISNK1 = 0,
285c38376eSChiYuan Huang 	MT6370_LED_ISNK2,
295c38376eSChiYuan Huang 	MT6370_LED_ISNK3,
305c38376eSChiYuan Huang 	MT6370_LED_ISNK4,
315c38376eSChiYuan Huang 	MT6370_MAX_LEDS
325c38376eSChiYuan Huang };
335c38376eSChiYuan Huang 
345c38376eSChiYuan Huang enum mt6370_led_mode {
355c38376eSChiYuan Huang 	MT6370_LED_PWM_MODE = 0,
365c38376eSChiYuan Huang 	MT6370_LED_BREATH_MODE,
375c38376eSChiYuan Huang 	MT6370_LED_REG_MODE,
385c38376eSChiYuan Huang 	MT6370_LED_MAX_MODE
395c38376eSChiYuan Huang };
405c38376eSChiYuan Huang 
415c38376eSChiYuan Huang enum mt6370_led_field {
425c38376eSChiYuan Huang 	F_RGB_EN = 0,
435c38376eSChiYuan Huang 	F_CHGIND_EN,
445c38376eSChiYuan Huang 	F_LED1_CURR,
455c38376eSChiYuan Huang 	F_LED2_CURR,
465c38376eSChiYuan Huang 	F_LED3_CURR,
475c38376eSChiYuan Huang 	F_LED4_CURR,
485c38376eSChiYuan Huang 	F_LED1_MODE,
495c38376eSChiYuan Huang 	F_LED2_MODE,
505c38376eSChiYuan Huang 	F_LED3_MODE,
515c38376eSChiYuan Huang 	F_LED4_MODE,
525c38376eSChiYuan Huang 	F_LED1_DUTY,
535c38376eSChiYuan Huang 	F_LED2_DUTY,
545c38376eSChiYuan Huang 	F_LED3_DUTY,
555c38376eSChiYuan Huang 	F_LED4_DUTY,
565c38376eSChiYuan Huang 	F_LED1_FREQ,
575c38376eSChiYuan Huang 	F_LED2_FREQ,
585c38376eSChiYuan Huang 	F_LED3_FREQ,
595c38376eSChiYuan Huang 	F_LED4_FREQ,
605c38376eSChiYuan Huang 	F_MAX_FIELDS
615c38376eSChiYuan Huang };
625c38376eSChiYuan Huang 
635c38376eSChiYuan Huang enum mt6370_led_ranges {
645c38376eSChiYuan Huang 	R_LED123_CURR = 0,
655c38376eSChiYuan Huang 	R_LED4_CURR,
665c38376eSChiYuan Huang 	R_LED_TRFON,
675c38376eSChiYuan Huang 	R_LED_TOFF,
685c38376eSChiYuan Huang 	R_MAX_RANGES
695c38376eSChiYuan Huang };
705c38376eSChiYuan Huang 
715c38376eSChiYuan Huang enum mt6370_pattern {
725c38376eSChiYuan Huang 	P_LED_TR1 = 0,
735c38376eSChiYuan Huang 	P_LED_TR2,
745c38376eSChiYuan Huang 	P_LED_TF1,
755c38376eSChiYuan Huang 	P_LED_TF2,
765c38376eSChiYuan Huang 	P_LED_TON,
775c38376eSChiYuan Huang 	P_LED_TOFF,
785c38376eSChiYuan Huang 	P_MAX_PATTERNS
795c38376eSChiYuan Huang };
805c38376eSChiYuan Huang 
815c38376eSChiYuan Huang #define MT6370_REG_DEV_INFO			0x100
825c38376eSChiYuan Huang #define MT6370_REG_RGB1_DIM			0x182
835c38376eSChiYuan Huang #define MT6370_REG_RGB2_DIM			0x183
845c38376eSChiYuan Huang #define MT6370_REG_RGB3_DIM			0x184
855c38376eSChiYuan Huang #define MT6370_REG_RGB_EN			0x185
865c38376eSChiYuan Huang #define MT6370_REG_RGB1_ISNK			0x186
875c38376eSChiYuan Huang #define MT6370_REG_RGB2_ISNK			0x187
885c38376eSChiYuan Huang #define MT6370_REG_RGB3_ISNK			0x188
895c38376eSChiYuan Huang #define MT6370_REG_RGB1_TR			0x189
905c38376eSChiYuan Huang #define MT6370_REG_RGB_CHRIND_DIM		0x192
915c38376eSChiYuan Huang #define MT6370_REG_RGB_CHRIND_CTRL		0x193
925c38376eSChiYuan Huang #define MT6370_REG_RGB_CHRIND_TR		0x194
935c38376eSChiYuan Huang 
945c38376eSChiYuan Huang #define MT6372_REG_RGB_EN			0x182
955c38376eSChiYuan Huang #define MT6372_REG_RGB1_ISNK			0x183
965c38376eSChiYuan Huang #define MT6372_REG_RGB2_ISNK			0x184
975c38376eSChiYuan Huang #define MT6372_REG_RGB3_ISNK			0x185
985c38376eSChiYuan Huang #define MT6372_REG_RGB4_ISNK			0x186
995c38376eSChiYuan Huang #define MT6372_REG_RGB1_DIM			0x187
1005c38376eSChiYuan Huang #define MT6372_REG_RGB2_DIM			0x188
1015c38376eSChiYuan Huang #define MT6372_REG_RGB3_DIM			0x189
1025c38376eSChiYuan Huang #define MT6372_REG_RGB4_DIM			0x18A
1035c38376eSChiYuan Huang #define MT6372_REG_RGB12_FREQ			0x18B
1045c38376eSChiYuan Huang #define MT6372_REG_RGB34_FREQ			0x18C
1055c38376eSChiYuan Huang #define MT6372_REG_RGB1_TR			0x18D
1065c38376eSChiYuan Huang 
1075c38376eSChiYuan Huang #define MT6370_VENDOR_ID_MASK			GENMASK(7, 4)
1085c38376eSChiYuan Huang #define MT6372_VENDOR_ID			0x9
1095c38376eSChiYuan Huang #define MT6372C_VENDOR_ID			0xb
1105c38376eSChiYuan Huang #define MT6370_CHEN_BIT(id)			BIT(MT6370_LED_ISNK4 - id)
1115c38376eSChiYuan Huang #define MT6370_VIRTUAL_MULTICOLOR		5
1125c38376eSChiYuan Huang #define MC_CHANNEL_NUM				3
1135c38376eSChiYuan Huang #define MT6370_PWM_DUTY				(BIT(5) - 1)
1145c38376eSChiYuan Huang #define MT6372_PWM_DUTY				(BIT(8) - 1)
1155c38376eSChiYuan Huang 
1165c38376eSChiYuan Huang struct mt6370_led {
1175c38376eSChiYuan Huang 	/*
1185c38376eSChiYuan Huang 	 * If the color of the LED in DT is set to
1195c38376eSChiYuan Huang 	 *   - 'LED_COLOR_ID_RGB'
1205c38376eSChiYuan Huang 	 *   - 'LED_COLOR_ID_MULTI'
1215c38376eSChiYuan Huang 	 * The member 'index' of this struct will be set to
1225c38376eSChiYuan Huang 	 * 'MT6370_VIRTUAL_MULTICOLOR'.
1235c38376eSChiYuan Huang 	 * If so, this LED will choose 'struct led_classdev_mc mc' to use.
1245c38376eSChiYuan Huang 	 * Instead, if the member 'index' of this struct is set to
1255c38376eSChiYuan Huang 	 * 'MT6370_LED_ISNK1' ~ 'MT6370_LED_ISNK4', then this LED will choose
1265c38376eSChiYuan Huang 	 * 'struct led_classdev isink' to use.
1275c38376eSChiYuan Huang 	 */
1285c38376eSChiYuan Huang 	union {
1295c38376eSChiYuan Huang 		struct led_classdev isink;
1305c38376eSChiYuan Huang 		struct led_classdev_mc mc;
1315c38376eSChiYuan Huang 	};
1325c38376eSChiYuan Huang 	struct mt6370_priv *priv;
1335c38376eSChiYuan Huang 	enum led_default_state default_state;
1345c38376eSChiYuan Huang 	u32 index;
1355c38376eSChiYuan Huang };
1365c38376eSChiYuan Huang 
1375c38376eSChiYuan Huang struct mt6370_pdata {
1385c38376eSChiYuan Huang 	const unsigned int *tfreq;
1395c38376eSChiYuan Huang 	unsigned int tfreq_len;
1405c38376eSChiYuan Huang 	u16 reg_rgb1_tr;
1415c38376eSChiYuan Huang 	s16 reg_rgb_chrind_tr;
1425c38376eSChiYuan Huang 	u8 pwm_duty;
1435c38376eSChiYuan Huang };
1445c38376eSChiYuan Huang 
1455c38376eSChiYuan Huang struct mt6370_priv {
1465c38376eSChiYuan Huang 	/* Per LED access lock */
1475c38376eSChiYuan Huang 	struct mutex lock;
1485c38376eSChiYuan Huang 	struct regmap *regmap;
1495c38376eSChiYuan Huang 	struct regmap_field *fields[F_MAX_FIELDS];
1505c38376eSChiYuan Huang 	const struct reg_field *reg_fields;
1515c38376eSChiYuan Huang 	const struct linear_range *ranges;
1525c38376eSChiYuan Huang 	const struct mt6370_pdata *pdata;
1535c38376eSChiYuan Huang 	unsigned int leds_count;
1545c38376eSChiYuan Huang 	unsigned int leds_active;
155*e3c9d952SKees Cook 	struct mt6370_led leds[] __counted_by(leds_count);
1565c38376eSChiYuan Huang };
1575c38376eSChiYuan Huang 
1585c38376eSChiYuan Huang static const struct reg_field common_reg_fields[F_MAX_FIELDS] = {
1595c38376eSChiYuan Huang 	[F_RGB_EN]	= REG_FIELD(MT6370_REG_RGB_EN, 4, 7),
1605c38376eSChiYuan Huang 	[F_CHGIND_EN]	= REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 7, 7),
1615c38376eSChiYuan Huang 	[F_LED1_CURR]	= REG_FIELD(MT6370_REG_RGB1_ISNK, 0, 2),
1625c38376eSChiYuan Huang 	[F_LED2_CURR]	= REG_FIELD(MT6370_REG_RGB2_ISNK, 0, 2),
1635c38376eSChiYuan Huang 	[F_LED3_CURR]	= REG_FIELD(MT6370_REG_RGB3_ISNK, 0, 2),
1645c38376eSChiYuan Huang 	[F_LED4_CURR]	= REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 0, 1),
1655c38376eSChiYuan Huang 	[F_LED1_MODE]	= REG_FIELD(MT6370_REG_RGB1_DIM, 5, 6),
1665c38376eSChiYuan Huang 	[F_LED2_MODE]	= REG_FIELD(MT6370_REG_RGB2_DIM, 5, 6),
1675c38376eSChiYuan Huang 	[F_LED3_MODE]	= REG_FIELD(MT6370_REG_RGB3_DIM, 5, 6),
1685c38376eSChiYuan Huang 	[F_LED4_MODE]	= REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 5, 6),
1695c38376eSChiYuan Huang 	[F_LED1_DUTY]	= REG_FIELD(MT6370_REG_RGB1_DIM, 0, 4),
1705c38376eSChiYuan Huang 	[F_LED2_DUTY]	= REG_FIELD(MT6370_REG_RGB2_DIM, 0, 4),
1715c38376eSChiYuan Huang 	[F_LED3_DUTY]	= REG_FIELD(MT6370_REG_RGB3_DIM, 0, 4),
1725c38376eSChiYuan Huang 	[F_LED4_DUTY]	= REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 0, 4),
1735c38376eSChiYuan Huang 	[F_LED1_FREQ]	= REG_FIELD(MT6370_REG_RGB1_ISNK, 3, 5),
1745c38376eSChiYuan Huang 	[F_LED2_FREQ]	= REG_FIELD(MT6370_REG_RGB2_ISNK, 3, 5),
1755c38376eSChiYuan Huang 	[F_LED3_FREQ]	= REG_FIELD(MT6370_REG_RGB3_ISNK, 3, 5),
1765c38376eSChiYuan Huang 	[F_LED4_FREQ]	= REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 2, 4),
1775c38376eSChiYuan Huang };
1785c38376eSChiYuan Huang 
1795c38376eSChiYuan Huang static const struct reg_field mt6372_reg_fields[F_MAX_FIELDS] = {
1805c38376eSChiYuan Huang 	[F_RGB_EN]	= REG_FIELD(MT6372_REG_RGB_EN, 4, 7),
1815c38376eSChiYuan Huang 	[F_CHGIND_EN]	= REG_FIELD(MT6372_REG_RGB_EN, 3, 3),
1825c38376eSChiYuan Huang 	[F_LED1_CURR]	= REG_FIELD(MT6372_REG_RGB1_ISNK, 0, 3),
1835c38376eSChiYuan Huang 	[F_LED2_CURR]	= REG_FIELD(MT6372_REG_RGB2_ISNK, 0, 3),
1845c38376eSChiYuan Huang 	[F_LED3_CURR]	= REG_FIELD(MT6372_REG_RGB3_ISNK, 0, 3),
1855c38376eSChiYuan Huang 	[F_LED4_CURR]	= REG_FIELD(MT6372_REG_RGB4_ISNK, 0, 3),
1865c38376eSChiYuan Huang 	[F_LED1_MODE]	= REG_FIELD(MT6372_REG_RGB1_ISNK, 6, 7),
1875c38376eSChiYuan Huang 	[F_LED2_MODE]	= REG_FIELD(MT6372_REG_RGB2_ISNK, 6, 7),
1885c38376eSChiYuan Huang 	[F_LED3_MODE]	= REG_FIELD(MT6372_REG_RGB3_ISNK, 6, 7),
1895c38376eSChiYuan Huang 	[F_LED4_MODE]	= REG_FIELD(MT6372_REG_RGB4_ISNK, 6, 7),
1905c38376eSChiYuan Huang 	[F_LED1_DUTY]	= REG_FIELD(MT6372_REG_RGB1_DIM, 0, 7),
1915c38376eSChiYuan Huang 	[F_LED2_DUTY]	= REG_FIELD(MT6372_REG_RGB2_DIM, 0, 7),
1925c38376eSChiYuan Huang 	[F_LED3_DUTY]	= REG_FIELD(MT6372_REG_RGB3_DIM, 0, 7),
1935c38376eSChiYuan Huang 	[F_LED4_DUTY]	= REG_FIELD(MT6372_REG_RGB4_DIM, 0, 7),
1945c38376eSChiYuan Huang 	[F_LED1_FREQ]	= REG_FIELD(MT6372_REG_RGB12_FREQ, 5, 7),
1955c38376eSChiYuan Huang 	[F_LED2_FREQ]	= REG_FIELD(MT6372_REG_RGB12_FREQ, 2, 4),
1965c38376eSChiYuan Huang 	[F_LED3_FREQ]	= REG_FIELD(MT6372_REG_RGB34_FREQ, 5, 7),
1975c38376eSChiYuan Huang 	[F_LED4_FREQ]	= REG_FIELD(MT6372_REG_RGB34_FREQ, 2, 4),
1985c38376eSChiYuan Huang };
1995c38376eSChiYuan Huang 
2005c38376eSChiYuan Huang /* Current unit: microamp, time unit: millisecond */
2015c38376eSChiYuan Huang static const struct linear_range common_led_ranges[R_MAX_RANGES] = {
2025c38376eSChiYuan Huang 	[R_LED123_CURR]	= { 4000, 1, 6, 4000 },
2035c38376eSChiYuan Huang 	[R_LED4_CURR]	= { 2000, 1, 3, 2000 },
2045c38376eSChiYuan Huang 	[R_LED_TRFON]	= { 125, 0, 15, 200 },
2055c38376eSChiYuan Huang 	[R_LED_TOFF]	= { 250, 0, 15, 400 },
2065c38376eSChiYuan Huang };
2075c38376eSChiYuan Huang 
2085c38376eSChiYuan Huang static const struct linear_range mt6372_led_ranges[R_MAX_RANGES] = {
2095c38376eSChiYuan Huang 	[R_LED123_CURR]	= { 2000, 1, 14, 2000 },
2105c38376eSChiYuan Huang 	[R_LED4_CURR]	= { 2000, 1, 14, 2000 },
2115c38376eSChiYuan Huang 	[R_LED_TRFON]	= { 125, 0, 15, 250 },
2125c38376eSChiYuan Huang 	[R_LED_TOFF]	= { 250, 0, 15, 500 },
2135c38376eSChiYuan Huang };
2145c38376eSChiYuan Huang 
2155c38376eSChiYuan Huang static const unsigned int common_tfreqs[] = {
2165c38376eSChiYuan Huang 	10000, 5000, 2000, 1000, 500, 200, 5, 1,
2175c38376eSChiYuan Huang };
2185c38376eSChiYuan Huang 
2195c38376eSChiYuan Huang static const unsigned int mt6372_tfreqs[] = {
2205c38376eSChiYuan Huang 	8000, 4000, 2000, 1000, 500, 250, 8, 4,
2215c38376eSChiYuan Huang };
2225c38376eSChiYuan Huang 
2235c38376eSChiYuan Huang static const struct mt6370_pdata common_pdata = {
2245c38376eSChiYuan Huang 	.tfreq = common_tfreqs,
2255c38376eSChiYuan Huang 	.tfreq_len = ARRAY_SIZE(common_tfreqs),
2265c38376eSChiYuan Huang 	.pwm_duty = MT6370_PWM_DUTY,
2275c38376eSChiYuan Huang 	.reg_rgb1_tr = MT6370_REG_RGB1_TR,
2285c38376eSChiYuan Huang 	.reg_rgb_chrind_tr = MT6370_REG_RGB_CHRIND_TR,
2295c38376eSChiYuan Huang };
2305c38376eSChiYuan Huang 
2315c38376eSChiYuan Huang static const struct mt6370_pdata mt6372_pdata = {
2325c38376eSChiYuan Huang 	.tfreq = mt6372_tfreqs,
2335c38376eSChiYuan Huang 	.tfreq_len = ARRAY_SIZE(mt6372_tfreqs),
2345c38376eSChiYuan Huang 	.pwm_duty = MT6372_PWM_DUTY,
2355c38376eSChiYuan Huang 	.reg_rgb1_tr = MT6372_REG_RGB1_TR,
2365c38376eSChiYuan Huang 	.reg_rgb_chrind_tr = -1,
2375c38376eSChiYuan Huang };
2385c38376eSChiYuan Huang 
2395c38376eSChiYuan Huang static enum mt6370_led_field mt6370_get_led_current_field(unsigned int led_no)
2405c38376eSChiYuan Huang {
2415c38376eSChiYuan Huang 	switch (led_no) {
2425c38376eSChiYuan Huang 	case MT6370_LED_ISNK1:
2435c38376eSChiYuan Huang 		return F_LED1_CURR;
2445c38376eSChiYuan Huang 	case MT6370_LED_ISNK2:
2455c38376eSChiYuan Huang 		return F_LED2_CURR;
2465c38376eSChiYuan Huang 	case MT6370_LED_ISNK3:
2475c38376eSChiYuan Huang 		return F_LED3_CURR;
2485c38376eSChiYuan Huang 	default:
2495c38376eSChiYuan Huang 		return F_LED4_CURR;
2505c38376eSChiYuan Huang 	}
2515c38376eSChiYuan Huang }
2525c38376eSChiYuan Huang 
2535c38376eSChiYuan Huang static int mt6370_set_led_brightness(struct mt6370_priv *priv, unsigned int led_no,
2545c38376eSChiYuan Huang 				     unsigned int level)
2555c38376eSChiYuan Huang {
2565c38376eSChiYuan Huang 	enum mt6370_led_field sel_field;
2575c38376eSChiYuan Huang 
2585c38376eSChiYuan Huang 	sel_field = mt6370_get_led_current_field(led_no);
2595c38376eSChiYuan Huang 
2605c38376eSChiYuan Huang 	return regmap_field_write(priv->fields[sel_field], level);
2615c38376eSChiYuan Huang }
2625c38376eSChiYuan Huang 
2635c38376eSChiYuan Huang static int mt6370_get_led_brightness(struct mt6370_priv *priv, unsigned int led_no,
2645c38376eSChiYuan Huang 				     unsigned int *level)
2655c38376eSChiYuan Huang {
2665c38376eSChiYuan Huang 	enum mt6370_led_field sel_field;
2675c38376eSChiYuan Huang 
2685c38376eSChiYuan Huang 	sel_field = mt6370_get_led_current_field(led_no);
2695c38376eSChiYuan Huang 
2705c38376eSChiYuan Huang 	return regmap_field_read(priv->fields[sel_field], level);
2715c38376eSChiYuan Huang }
2725c38376eSChiYuan Huang 
2735c38376eSChiYuan Huang static int mt6370_set_led_duty(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton,
2745c38376eSChiYuan Huang 			       unsigned int toff)
2755c38376eSChiYuan Huang {
2765c38376eSChiYuan Huang 	const struct mt6370_pdata *pdata = priv->pdata;
2775c38376eSChiYuan Huang 	enum mt6370_led_field sel_field;
2785c38376eSChiYuan Huang 	unsigned int divisor, ratio;
2795c38376eSChiYuan Huang 
2805c38376eSChiYuan Huang 	divisor = pdata->pwm_duty;
2815c38376eSChiYuan Huang 	ratio = ton * divisor / (ton + toff);
2825c38376eSChiYuan Huang 
2835c38376eSChiYuan Huang 	switch (led_no) {
2845c38376eSChiYuan Huang 	case MT6370_LED_ISNK1:
2855c38376eSChiYuan Huang 		sel_field = F_LED1_DUTY;
2865c38376eSChiYuan Huang 		break;
2875c38376eSChiYuan Huang 	case MT6370_LED_ISNK2:
2885c38376eSChiYuan Huang 		sel_field = F_LED2_DUTY;
2895c38376eSChiYuan Huang 		break;
2905c38376eSChiYuan Huang 	case MT6370_LED_ISNK3:
2915c38376eSChiYuan Huang 		sel_field = F_LED3_DUTY;
2925c38376eSChiYuan Huang 		break;
2935c38376eSChiYuan Huang 	default:
2945c38376eSChiYuan Huang 		sel_field = F_LED4_DUTY;
2955c38376eSChiYuan Huang 		break;
2965c38376eSChiYuan Huang 	}
2975c38376eSChiYuan Huang 
2985c38376eSChiYuan Huang 	return regmap_field_write(priv->fields[sel_field], ratio);
2995c38376eSChiYuan Huang }
3005c38376eSChiYuan Huang 
3015c38376eSChiYuan Huang static int mt6370_set_led_freq(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton,
3025c38376eSChiYuan Huang 			       unsigned int toff)
3035c38376eSChiYuan Huang {
3045c38376eSChiYuan Huang 	const struct mt6370_pdata *pdata = priv->pdata;
3055c38376eSChiYuan Huang 	enum mt6370_led_field sel_field;
3065c38376eSChiYuan Huang 	unsigned int tfreq_len = pdata->tfreq_len;
3075c38376eSChiYuan Huang 	unsigned int tsum, sel;
3085c38376eSChiYuan Huang 
3095c38376eSChiYuan Huang 	tsum = ton + toff;
3105c38376eSChiYuan Huang 
3115c38376eSChiYuan Huang 	if (tsum > pdata->tfreq[0] || tsum < pdata->tfreq[tfreq_len - 1])
3125c38376eSChiYuan Huang 		return -EOPNOTSUPP;
3135c38376eSChiYuan Huang 
3145c38376eSChiYuan Huang 	sel = find_closest_descending(tsum, pdata->tfreq, tfreq_len);
3155c38376eSChiYuan Huang 
3165c38376eSChiYuan Huang 	switch (led_no) {
3175c38376eSChiYuan Huang 	case MT6370_LED_ISNK1:
3185c38376eSChiYuan Huang 		sel_field = F_LED1_FREQ;
3195c38376eSChiYuan Huang 		break;
3205c38376eSChiYuan Huang 	case MT6370_LED_ISNK2:
3215c38376eSChiYuan Huang 		sel_field = F_LED2_FREQ;
3225c38376eSChiYuan Huang 		break;
3235c38376eSChiYuan Huang 	case MT6370_LED_ISNK3:
3245c38376eSChiYuan Huang 		sel_field = F_LED3_FREQ;
3255c38376eSChiYuan Huang 		break;
3265c38376eSChiYuan Huang 	default:
3275c38376eSChiYuan Huang 		sel_field = F_LED4_FREQ;
3285c38376eSChiYuan Huang 		break;
3295c38376eSChiYuan Huang 	}
3305c38376eSChiYuan Huang 
3315c38376eSChiYuan Huang 	return regmap_field_write(priv->fields[sel_field], sel);
3325c38376eSChiYuan Huang }
3335c38376eSChiYuan Huang 
3345c38376eSChiYuan Huang static void mt6370_get_breath_reg_base(struct mt6370_priv *priv, unsigned int led_no,
3355c38376eSChiYuan Huang 				       unsigned int *base)
3365c38376eSChiYuan Huang {
3375c38376eSChiYuan Huang 	const struct mt6370_pdata *pdata = priv->pdata;
3385c38376eSChiYuan Huang 
3395c38376eSChiYuan Huang 	if (pdata->reg_rgb_chrind_tr < 0) {
3405c38376eSChiYuan Huang 		*base = pdata->reg_rgb1_tr + led_no * 3;
3415c38376eSChiYuan Huang 		return;
3425c38376eSChiYuan Huang 	}
3435c38376eSChiYuan Huang 
3445c38376eSChiYuan Huang 	switch (led_no) {
3455c38376eSChiYuan Huang 	case MT6370_LED_ISNK1:
3465c38376eSChiYuan Huang 	case MT6370_LED_ISNK2:
3475c38376eSChiYuan Huang 	case MT6370_LED_ISNK3:
3485c38376eSChiYuan Huang 		*base = pdata->reg_rgb1_tr + led_no * 3;
3495c38376eSChiYuan Huang 		break;
3505c38376eSChiYuan Huang 	default:
3515c38376eSChiYuan Huang 		*base = pdata->reg_rgb_chrind_tr;
3525c38376eSChiYuan Huang 		break;
3535c38376eSChiYuan Huang 	}
3545c38376eSChiYuan Huang }
3555c38376eSChiYuan Huang 
3565c38376eSChiYuan Huang static int mt6370_gen_breath_pattern(struct mt6370_priv *priv, struct led_pattern *pattern, u32 len,
3575c38376eSChiYuan Huang 				     u8 *pattern_val, u32 val_len)
3585c38376eSChiYuan Huang {
3595c38376eSChiYuan Huang 	enum mt6370_led_ranges sel_range;
3605c38376eSChiYuan Huang 	struct led_pattern *curr;
3615c38376eSChiYuan Huang 	unsigned int sel;
3625c38376eSChiYuan Huang 	u32 val = 0;
3635c38376eSChiYuan Huang 	int i;
3645c38376eSChiYuan Huang 
3655c38376eSChiYuan Huang 	if (len < P_MAX_PATTERNS && val_len < P_MAX_PATTERNS / 2)
3665c38376eSChiYuan Huang 		return -EINVAL;
3675c38376eSChiYuan Huang 
3685c38376eSChiYuan Huang 	/*
3695c38376eSChiYuan Huang 	 * Pattern list
3705c38376eSChiYuan Huang 	 * tr1:	 byte 0, b'[7:4]
3715c38376eSChiYuan Huang 	 * tr2:	 byte 0, b'[3:0]
3725c38376eSChiYuan Huang 	 * tf1:	 byte 1, b'[7:4]
3735c38376eSChiYuan Huang 	 * tf2:	 byte 1, b'[3:0]
3745c38376eSChiYuan Huang 	 * ton:	 byte 2, b'[7:4]
3755c38376eSChiYuan Huang 	 * toff: byte 2, b'[3:0]
3765c38376eSChiYuan Huang 	 */
3775c38376eSChiYuan Huang 	for (i = 0; i < P_MAX_PATTERNS; i++) {
3785c38376eSChiYuan Huang 		curr = pattern + i;
3795c38376eSChiYuan Huang 
3805c38376eSChiYuan Huang 		sel_range = i == P_LED_TOFF ? R_LED_TOFF : R_LED_TRFON;
3815c38376eSChiYuan Huang 
3825c38376eSChiYuan Huang 		linear_range_get_selector_within(priv->ranges + sel_range, curr->delta_t, &sel);
3835c38376eSChiYuan Huang 
3845c38376eSChiYuan Huang 		if (i % 2) {
3855c38376eSChiYuan Huang 			val |= sel;
3865c38376eSChiYuan Huang 		} else {
3875c38376eSChiYuan Huang 			val <<= 8;
3885c38376eSChiYuan Huang 			val |= sel << 4;
3895c38376eSChiYuan Huang 		}
3905c38376eSChiYuan Huang 	}
3915c38376eSChiYuan Huang 
3925c38376eSChiYuan Huang 	put_unaligned_be24(val, pattern_val);
3935c38376eSChiYuan Huang 
3945c38376eSChiYuan Huang 	return 0;
3955c38376eSChiYuan Huang }
3965c38376eSChiYuan Huang 
3975c38376eSChiYuan Huang static int mt6370_set_led_mode(struct mt6370_priv *priv, unsigned int led_no,
3985c38376eSChiYuan Huang 			       enum mt6370_led_mode mode)
3995c38376eSChiYuan Huang {
4005c38376eSChiYuan Huang 	enum mt6370_led_field sel_field;
4015c38376eSChiYuan Huang 
4025c38376eSChiYuan Huang 	switch (led_no) {
4035c38376eSChiYuan Huang 	case MT6370_LED_ISNK1:
4045c38376eSChiYuan Huang 		sel_field = F_LED1_MODE;
4055c38376eSChiYuan Huang 		break;
4065c38376eSChiYuan Huang 	case MT6370_LED_ISNK2:
4075c38376eSChiYuan Huang 		sel_field = F_LED2_MODE;
4085c38376eSChiYuan Huang 		break;
4095c38376eSChiYuan Huang 	case MT6370_LED_ISNK3:
4105c38376eSChiYuan Huang 		sel_field = F_LED3_MODE;
4115c38376eSChiYuan Huang 		break;
4125c38376eSChiYuan Huang 	default:
4135c38376eSChiYuan Huang 		sel_field = F_LED4_MODE;
4145c38376eSChiYuan Huang 		break;
4155c38376eSChiYuan Huang 	}
4165c38376eSChiYuan Huang 
4175c38376eSChiYuan Huang 	return regmap_field_write(priv->fields[sel_field], mode);
4185c38376eSChiYuan Huang }
4195c38376eSChiYuan Huang 
4205c38376eSChiYuan Huang static int mt6370_mc_brightness_set(struct led_classdev *lcdev, enum led_brightness level)
4215c38376eSChiYuan Huang {
4225c38376eSChiYuan Huang 	struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
4235c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);
4245c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
4255c38376eSChiYuan Huang 	struct mc_subled *subled;
4265c38376eSChiYuan Huang 	unsigned int enable, disable;
4275c38376eSChiYuan Huang 	int i, ret;
4285c38376eSChiYuan Huang 
4295c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
4305c38376eSChiYuan Huang 
4315c38376eSChiYuan Huang 	led_mc_calc_color_components(mccdev, level);
4325c38376eSChiYuan Huang 
4335c38376eSChiYuan Huang 	ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);
4345c38376eSChiYuan Huang 	if (ret)
4355c38376eSChiYuan Huang 		goto out_unlock;
4365c38376eSChiYuan Huang 
4375c38376eSChiYuan Huang 	disable = enable;
4385c38376eSChiYuan Huang 
4395c38376eSChiYuan Huang 	for (i = 0; i < mccdev->num_colors; i++) {
4405c38376eSChiYuan Huang 		u32 brightness;
4415c38376eSChiYuan Huang 
4425c38376eSChiYuan Huang 		subled = mccdev->subled_info + i;
4435c38376eSChiYuan Huang 		brightness = min(subled->brightness, lcdev->max_brightness);
4445c38376eSChiYuan Huang 		disable &= ~MT6370_CHEN_BIT(subled->channel);
4455c38376eSChiYuan Huang 
4465c38376eSChiYuan Huang 		if (level == 0) {
4475c38376eSChiYuan Huang 			enable &= ~MT6370_CHEN_BIT(subled->channel);
4485c38376eSChiYuan Huang 
4495c38376eSChiYuan Huang 			ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE);
4505c38376eSChiYuan Huang 			if (ret)
4515c38376eSChiYuan Huang 				goto out_unlock;
4525c38376eSChiYuan Huang 
4535c38376eSChiYuan Huang 			continue;
4545c38376eSChiYuan Huang 		}
4555c38376eSChiYuan Huang 
4565c38376eSChiYuan Huang 		if (brightness == 0) {
4575c38376eSChiYuan Huang 			enable &= ~MT6370_CHEN_BIT(subled->channel);
4585c38376eSChiYuan Huang 			continue;
4595c38376eSChiYuan Huang 		}
4605c38376eSChiYuan Huang 
4615c38376eSChiYuan Huang 		enable |= MT6370_CHEN_BIT(subled->channel);
4625c38376eSChiYuan Huang 
4635c38376eSChiYuan Huang 		ret = mt6370_set_led_brightness(priv, subled->channel, brightness);
4645c38376eSChiYuan Huang 		if (ret)
4655c38376eSChiYuan Huang 			goto out_unlock;
4665c38376eSChiYuan Huang 	}
4675c38376eSChiYuan Huang 
4685c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], disable);
4695c38376eSChiYuan Huang 	if (ret)
4705c38376eSChiYuan Huang 		goto out_unlock;
4715c38376eSChiYuan Huang 
4725c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], enable);
4735c38376eSChiYuan Huang 
4745c38376eSChiYuan Huang out_unlock:
4755c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
4765c38376eSChiYuan Huang 
4775c38376eSChiYuan Huang 	return ret;
4785c38376eSChiYuan Huang }
4795c38376eSChiYuan Huang 
4805c38376eSChiYuan Huang static int mt6370_mc_blink_set(struct led_classdev *lcdev,
4815c38376eSChiYuan Huang 			       unsigned long *delay_on,
4825c38376eSChiYuan Huang 			       unsigned long *delay_off)
4835c38376eSChiYuan Huang {
4845c38376eSChiYuan Huang 	struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
4855c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);
4865c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
4875c38376eSChiYuan Huang 	struct mc_subled *subled;
4885c38376eSChiYuan Huang 	unsigned int enable, disable;
4895c38376eSChiYuan Huang 	int i, ret;
4905c38376eSChiYuan Huang 
4915c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
4925c38376eSChiYuan Huang 
4935c38376eSChiYuan Huang 	if (!*delay_on && !*delay_off)
4945c38376eSChiYuan Huang 		*delay_on = *delay_off = 500;
4955c38376eSChiYuan Huang 
4965c38376eSChiYuan Huang 	ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);
4975c38376eSChiYuan Huang 	if (ret)
4985c38376eSChiYuan Huang 		goto out_unlock;
4995c38376eSChiYuan Huang 
5005c38376eSChiYuan Huang 	disable = enable;
5015c38376eSChiYuan Huang 
5025c38376eSChiYuan Huang 	for (i = 0; i < mccdev->num_colors; i++) {
5035c38376eSChiYuan Huang 		subled = mccdev->subled_info + i;
5045c38376eSChiYuan Huang 
5055c38376eSChiYuan Huang 		disable &= ~MT6370_CHEN_BIT(subled->channel);
5065c38376eSChiYuan Huang 
5075c38376eSChiYuan Huang 		ret = mt6370_set_led_duty(priv, subled->channel, *delay_on, *delay_off);
5085c38376eSChiYuan Huang 		if (ret)
5095c38376eSChiYuan Huang 			goto out_unlock;
5105c38376eSChiYuan Huang 
5115c38376eSChiYuan Huang 		ret = mt6370_set_led_freq(priv, subled->channel, *delay_on, *delay_off);
5125c38376eSChiYuan Huang 		if (ret)
5135c38376eSChiYuan Huang 			goto out_unlock;
5145c38376eSChiYuan Huang 
5155c38376eSChiYuan Huang 		ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_PWM_MODE);
5165c38376eSChiYuan Huang 		if (ret)
5175c38376eSChiYuan Huang 			goto out_unlock;
5185c38376eSChiYuan Huang 	}
5195c38376eSChiYuan Huang 
5205c38376eSChiYuan Huang 	/* Toggle to make pattern timing the same */
5215c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], disable);
5225c38376eSChiYuan Huang 	if (ret)
5235c38376eSChiYuan Huang 		goto out_unlock;
5245c38376eSChiYuan Huang 
5255c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], enable);
5265c38376eSChiYuan Huang 
5275c38376eSChiYuan Huang out_unlock:
5285c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
5295c38376eSChiYuan Huang 
5305c38376eSChiYuan Huang 	return ret;
5315c38376eSChiYuan Huang }
5325c38376eSChiYuan Huang 
5335c38376eSChiYuan Huang static int mt6370_mc_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len,
5345c38376eSChiYuan Huang 				 int repeat)
5355c38376eSChiYuan Huang {
5365c38376eSChiYuan Huang 	struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
5375c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);
5385c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
5395c38376eSChiYuan Huang 	struct mc_subled *subled;
5405c38376eSChiYuan Huang 	unsigned int reg_base, enable, disable;
5415c38376eSChiYuan Huang 	u8 params[P_MAX_PATTERNS / 2];
5425c38376eSChiYuan Huang 	int i, ret;
5435c38376eSChiYuan Huang 
5445c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
5455c38376eSChiYuan Huang 
5465c38376eSChiYuan Huang 	ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params));
5475c38376eSChiYuan Huang 	if (ret)
5485c38376eSChiYuan Huang 		goto out_unlock;
5495c38376eSChiYuan Huang 
5505c38376eSChiYuan Huang 	ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);
5515c38376eSChiYuan Huang 	if (ret)
5525c38376eSChiYuan Huang 		goto out_unlock;
5535c38376eSChiYuan Huang 
5545c38376eSChiYuan Huang 	disable = enable;
5555c38376eSChiYuan Huang 
5565c38376eSChiYuan Huang 	for (i = 0; i < mccdev->num_colors; i++) {
5575c38376eSChiYuan Huang 		subled = mccdev->subled_info + i;
5585c38376eSChiYuan Huang 
5595c38376eSChiYuan Huang 		mt6370_get_breath_reg_base(priv, subled->channel, &reg_base);
5605c38376eSChiYuan Huang 		disable &= ~MT6370_CHEN_BIT(subled->channel);
5615c38376eSChiYuan Huang 
5625c38376eSChiYuan Huang 		ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params));
5635c38376eSChiYuan Huang 		if (ret)
5645c38376eSChiYuan Huang 			goto out_unlock;
5655c38376eSChiYuan Huang 
5665c38376eSChiYuan Huang 		ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_BREATH_MODE);
5675c38376eSChiYuan Huang 		if (ret)
5685c38376eSChiYuan Huang 			goto out_unlock;
5695c38376eSChiYuan Huang 	}
5705c38376eSChiYuan Huang 
5715c38376eSChiYuan Huang 	/* Toggle to make pattern timing be the same */
5725c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], disable);
5735c38376eSChiYuan Huang 	if (ret)
5745c38376eSChiYuan Huang 		goto out_unlock;
5755c38376eSChiYuan Huang 
5765c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], enable);
5775c38376eSChiYuan Huang 
5785c38376eSChiYuan Huang out_unlock:
5795c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
5805c38376eSChiYuan Huang 
5815c38376eSChiYuan Huang 	return ret;
5825c38376eSChiYuan Huang }
5835c38376eSChiYuan Huang 
5845c38376eSChiYuan Huang static inline int mt6370_mc_pattern_clear(struct led_classdev *lcdev)
5855c38376eSChiYuan Huang {
5865c38376eSChiYuan Huang 	struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
5875c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);
5885c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
5895c38376eSChiYuan Huang 	struct mc_subled *subled;
5905c38376eSChiYuan Huang 	int i, ret;
5915c38376eSChiYuan Huang 
5925c38376eSChiYuan Huang 	mutex_lock(&led->priv->lock);
5935c38376eSChiYuan Huang 
5945c38376eSChiYuan Huang 	for (i = 0; i < mccdev->num_colors; i++) {
5955c38376eSChiYuan Huang 		subled = mccdev->subled_info + i;
5965c38376eSChiYuan Huang 
5975c38376eSChiYuan Huang 		ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE);
5985c38376eSChiYuan Huang 		if (ret)
5995c38376eSChiYuan Huang 			break;
6005c38376eSChiYuan Huang 	}
6015c38376eSChiYuan Huang 
6025c38376eSChiYuan Huang 	mutex_unlock(&led->priv->lock);
6035c38376eSChiYuan Huang 
6045c38376eSChiYuan Huang 	return ret;
6055c38376eSChiYuan Huang }
6065c38376eSChiYuan Huang 
6075c38376eSChiYuan Huang static int mt6370_isnk_brightness_set(struct led_classdev *lcdev,
6085c38376eSChiYuan Huang 				      enum led_brightness level)
6095c38376eSChiYuan Huang {
6105c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);
6115c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
6125c38376eSChiYuan Huang 	unsigned int enable;
6135c38376eSChiYuan Huang 	int ret;
6145c38376eSChiYuan Huang 
6155c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
6165c38376eSChiYuan Huang 
6175c38376eSChiYuan Huang 	ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);
6185c38376eSChiYuan Huang 	if (ret)
6195c38376eSChiYuan Huang 		goto out_unlock;
6205c38376eSChiYuan Huang 
6215c38376eSChiYuan Huang 	if (level == 0) {
6225c38376eSChiYuan Huang 		enable &= ~MT6370_CHEN_BIT(led->index);
6235c38376eSChiYuan Huang 
6245c38376eSChiYuan Huang 		ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE);
6255c38376eSChiYuan Huang 		if (ret)
6265c38376eSChiYuan Huang 			goto out_unlock;
6275c38376eSChiYuan Huang 	} else {
6285c38376eSChiYuan Huang 		enable |= MT6370_CHEN_BIT(led->index);
6295c38376eSChiYuan Huang 
6305c38376eSChiYuan Huang 		ret = mt6370_set_led_brightness(priv, led->index, level);
6315c38376eSChiYuan Huang 		if (ret)
6325c38376eSChiYuan Huang 			goto out_unlock;
6335c38376eSChiYuan Huang 	}
6345c38376eSChiYuan Huang 
6355c38376eSChiYuan Huang 	ret = regmap_field_write(priv->fields[F_RGB_EN], enable);
6365c38376eSChiYuan Huang 
6375c38376eSChiYuan Huang out_unlock:
6385c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
6395c38376eSChiYuan Huang 
6405c38376eSChiYuan Huang 	return ret;
6415c38376eSChiYuan Huang }
6425c38376eSChiYuan Huang 
6435c38376eSChiYuan Huang static int mt6370_isnk_blink_set(struct led_classdev *lcdev, unsigned long *delay_on,
6445c38376eSChiYuan Huang 				 unsigned long *delay_off)
6455c38376eSChiYuan Huang {
6465c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);
6475c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
6485c38376eSChiYuan Huang 	int ret;
6495c38376eSChiYuan Huang 
6505c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
6515c38376eSChiYuan Huang 
6525c38376eSChiYuan Huang 	if (!*delay_on && !*delay_off)
6535c38376eSChiYuan Huang 		*delay_on = *delay_off = 500;
6545c38376eSChiYuan Huang 
6555c38376eSChiYuan Huang 	ret = mt6370_set_led_duty(priv, led->index, *delay_on, *delay_off);
6565c38376eSChiYuan Huang 	if (ret)
6575c38376eSChiYuan Huang 		goto out_unlock;
6585c38376eSChiYuan Huang 
6595c38376eSChiYuan Huang 	ret = mt6370_set_led_freq(priv, led->index, *delay_on, *delay_off);
6605c38376eSChiYuan Huang 	if (ret)
6615c38376eSChiYuan Huang 		goto out_unlock;
6625c38376eSChiYuan Huang 
6635c38376eSChiYuan Huang 	ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_PWM_MODE);
6645c38376eSChiYuan Huang 
6655c38376eSChiYuan Huang out_unlock:
6665c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
6675c38376eSChiYuan Huang 
6685c38376eSChiYuan Huang 	return ret;
6695c38376eSChiYuan Huang }
6705c38376eSChiYuan Huang 
6715c38376eSChiYuan Huang static int mt6370_isnk_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len,
6725c38376eSChiYuan Huang 				   int repeat)
6735c38376eSChiYuan Huang {
6745c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);
6755c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
6765c38376eSChiYuan Huang 	unsigned int reg_base;
6775c38376eSChiYuan Huang 	u8 params[P_MAX_PATTERNS / 2];
6785c38376eSChiYuan Huang 	int ret;
6795c38376eSChiYuan Huang 
6805c38376eSChiYuan Huang 	mutex_lock(&priv->lock);
6815c38376eSChiYuan Huang 
6825c38376eSChiYuan Huang 	ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params));
6835c38376eSChiYuan Huang 	if (ret)
6845c38376eSChiYuan Huang 		goto out_unlock;
6855c38376eSChiYuan Huang 
6865c38376eSChiYuan Huang 	mt6370_get_breath_reg_base(priv, led->index, &reg_base);
6875c38376eSChiYuan Huang 
6885c38376eSChiYuan Huang 	ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params));
6895c38376eSChiYuan Huang 	if (ret)
6905c38376eSChiYuan Huang 		goto out_unlock;
6915c38376eSChiYuan Huang 
6925c38376eSChiYuan Huang 	ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_BREATH_MODE);
6935c38376eSChiYuan Huang 
6945c38376eSChiYuan Huang out_unlock:
6955c38376eSChiYuan Huang 	mutex_unlock(&priv->lock);
6965c38376eSChiYuan Huang 
6975c38376eSChiYuan Huang 	return ret;
6985c38376eSChiYuan Huang }
6995c38376eSChiYuan Huang 
7005c38376eSChiYuan Huang static inline int mt6370_isnk_pattern_clear(struct led_classdev *lcdev)
7015c38376eSChiYuan Huang {
7025c38376eSChiYuan Huang 	struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);
7035c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
7045c38376eSChiYuan Huang 	int ret;
7055c38376eSChiYuan Huang 
7065c38376eSChiYuan Huang 	mutex_lock(&led->priv->lock);
7075c38376eSChiYuan Huang 	ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE);
7085c38376eSChiYuan Huang 	mutex_unlock(&led->priv->lock);
7095c38376eSChiYuan Huang 
7105c38376eSChiYuan Huang 	return ret;
7115c38376eSChiYuan Huang }
7125c38376eSChiYuan Huang 
7135c38376eSChiYuan Huang static int mt6370_assign_multicolor_info(struct device *dev, struct mt6370_led *led,
7145c38376eSChiYuan Huang 					 struct fwnode_handle *fwnode)
7155c38376eSChiYuan Huang {
7165c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
7175c38376eSChiYuan Huang 	struct fwnode_handle *child;
7185c38376eSChiYuan Huang 	struct mc_subled *sub_led;
7195c38376eSChiYuan Huang 	u32 num_color = 0;
7205c38376eSChiYuan Huang 	int ret;
7215c38376eSChiYuan Huang 
7225c38376eSChiYuan Huang 	sub_led = devm_kcalloc(dev, MC_CHANNEL_NUM, sizeof(*sub_led), GFP_KERNEL);
7235c38376eSChiYuan Huang 	if (!sub_led)
7245c38376eSChiYuan Huang 		return -ENOMEM;
7255c38376eSChiYuan Huang 
7265c38376eSChiYuan Huang 	fwnode_for_each_child_node(fwnode, child) {
7275c38376eSChiYuan Huang 		u32 reg, color;
7285c38376eSChiYuan Huang 
7295c38376eSChiYuan Huang 		ret = fwnode_property_read_u32(child, "reg", &reg);
7305c38376eSChiYuan Huang 		if (ret || reg > MT6370_LED_ISNK3 || priv->leds_active & BIT(reg)) {
7315c38376eSChiYuan Huang 			fwnode_handle_put(child);
7325c38376eSChiYuan Huang 			return -EINVAL;
7335c38376eSChiYuan Huang 		}
7345c38376eSChiYuan Huang 
7355c38376eSChiYuan Huang 		ret = fwnode_property_read_u32(child, "color", &color);
7365c38376eSChiYuan Huang 		if (ret) {
7375c38376eSChiYuan Huang 			fwnode_handle_put(child);
7385c38376eSChiYuan Huang 			return dev_err_probe(dev, ret, "LED %d, no color specified\n", led->index);
7395c38376eSChiYuan Huang 		}
7405c38376eSChiYuan Huang 
7415c38376eSChiYuan Huang 		priv->leds_active |= BIT(reg);
7425c38376eSChiYuan Huang 		sub_led[num_color].color_index = color;
7435c38376eSChiYuan Huang 		sub_led[num_color].channel = reg;
7445c38376eSChiYuan Huang 		sub_led[num_color].intensity = 0;
7455c38376eSChiYuan Huang 		num_color++;
7465c38376eSChiYuan Huang 	}
7475c38376eSChiYuan Huang 
7485c38376eSChiYuan Huang 	if (num_color < 2)
7495c38376eSChiYuan Huang 		return dev_err_probe(dev, -EINVAL,
7505c38376eSChiYuan Huang 				     "Multicolor must include 2 or more LED channels\n");
7515c38376eSChiYuan Huang 
7525c38376eSChiYuan Huang 	led->mc.num_colors = num_color;
7535c38376eSChiYuan Huang 	led->mc.subled_info = sub_led;
7545c38376eSChiYuan Huang 
7555c38376eSChiYuan Huang 	return 0;
7565c38376eSChiYuan Huang }
7575c38376eSChiYuan Huang 
7585c38376eSChiYuan Huang static int mt6370_init_led_properties(struct device *dev, struct mt6370_led *led,
7595c38376eSChiYuan Huang 				      struct led_init_data *init_data)
7605c38376eSChiYuan Huang {
7615c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
7625c38376eSChiYuan Huang 	struct led_classdev *lcdev;
7635c38376eSChiYuan Huang 	enum mt6370_led_ranges sel_range;
7645c38376eSChiYuan Huang 	u32 max_uA, max_level;
7655c38376eSChiYuan Huang 	int ret;
7665c38376eSChiYuan Huang 
7675c38376eSChiYuan Huang 	if (led->index == MT6370_VIRTUAL_MULTICOLOR) {
7685c38376eSChiYuan Huang 		ret = mt6370_assign_multicolor_info(dev, led, init_data->fwnode);
7695c38376eSChiYuan Huang 		if (ret)
7705c38376eSChiYuan Huang 			return ret;
7715c38376eSChiYuan Huang 
7725c38376eSChiYuan Huang 		lcdev = &led->mc.led_cdev;
7735c38376eSChiYuan Huang 		lcdev->brightness_set_blocking = mt6370_mc_brightness_set;
7745c38376eSChiYuan Huang 		lcdev->blink_set = mt6370_mc_blink_set;
7755c38376eSChiYuan Huang 		lcdev->pattern_set = mt6370_mc_pattern_set;
7765c38376eSChiYuan Huang 		lcdev->pattern_clear = mt6370_mc_pattern_clear;
7775c38376eSChiYuan Huang 	} else {
7785c38376eSChiYuan Huang 		lcdev = &led->isink;
7795c38376eSChiYuan Huang 		lcdev->brightness_set_blocking = mt6370_isnk_brightness_set;
7805c38376eSChiYuan Huang 		lcdev->blink_set = mt6370_isnk_blink_set;
7815c38376eSChiYuan Huang 		lcdev->pattern_set = mt6370_isnk_pattern_set;
7825c38376eSChiYuan Huang 		lcdev->pattern_clear = mt6370_isnk_pattern_clear;
7835c38376eSChiYuan Huang 	}
7845c38376eSChiYuan Huang 
7855c38376eSChiYuan Huang 	ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp", &max_uA);
7865c38376eSChiYuan Huang 	if (ret) {
7875c38376eSChiYuan Huang 		dev_warn(dev, "Not specified led-max-microamp, config to the minimum\n");
7885c38376eSChiYuan Huang 		max_uA = 0;
7895c38376eSChiYuan Huang 	}
7905c38376eSChiYuan Huang 
7915c38376eSChiYuan Huang 	if (led->index == MT6370_LED_ISNK4)
7925c38376eSChiYuan Huang 		sel_range = R_LED4_CURR;
7935c38376eSChiYuan Huang 	else
7945c38376eSChiYuan Huang 		sel_range = R_LED123_CURR;
7955c38376eSChiYuan Huang 
7965c38376eSChiYuan Huang 	linear_range_get_selector_within(priv->ranges + sel_range, max_uA, &max_level);
7975c38376eSChiYuan Huang 
7985c38376eSChiYuan Huang 	lcdev->max_brightness = max_level;
7995c38376eSChiYuan Huang 
8005c38376eSChiYuan Huang 	led->default_state = led_init_default_state_get(init_data->fwnode);
8015c38376eSChiYuan Huang 
8025c38376eSChiYuan Huang 	return 0;
8035c38376eSChiYuan Huang }
8045c38376eSChiYuan Huang 
8055c38376eSChiYuan Huang static int mt6370_isnk_init_default_state(struct mt6370_led *led)
8065c38376eSChiYuan Huang {
8075c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
8085c38376eSChiYuan Huang 	unsigned int enable, level;
8095c38376eSChiYuan Huang 	int ret;
8105c38376eSChiYuan Huang 
8115c38376eSChiYuan Huang 	ret = mt6370_get_led_brightness(priv, led->index, &level);
8125c38376eSChiYuan Huang 	if (ret)
8135c38376eSChiYuan Huang 		return ret;
8145c38376eSChiYuan Huang 
8155c38376eSChiYuan Huang 	ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);
8165c38376eSChiYuan Huang 	if (ret)
8175c38376eSChiYuan Huang 		return ret;
8185c38376eSChiYuan Huang 
8195c38376eSChiYuan Huang 	if (!(enable & MT6370_CHEN_BIT(led->index)))
8205c38376eSChiYuan Huang 		level = 0;
8215c38376eSChiYuan Huang 
8225c38376eSChiYuan Huang 	switch (led->default_state) {
8235c38376eSChiYuan Huang 	case LEDS_DEFSTATE_ON:
8245c38376eSChiYuan Huang 		led->isink.brightness = led->isink.max_brightness;
8255c38376eSChiYuan Huang 		break;
8265c38376eSChiYuan Huang 	case LEDS_DEFSTATE_KEEP:
8275c38376eSChiYuan Huang 		led->isink.brightness = min(level, led->isink.max_brightness);
8285c38376eSChiYuan Huang 		break;
8295c38376eSChiYuan Huang 	default:
8305c38376eSChiYuan Huang 		led->isink.brightness = 0;
8315c38376eSChiYuan Huang 		break;
8325c38376eSChiYuan Huang 	}
8335c38376eSChiYuan Huang 
8345c38376eSChiYuan Huang 	return mt6370_isnk_brightness_set(&led->isink, led->isink.brightness);
8355c38376eSChiYuan Huang }
8365c38376eSChiYuan Huang 
8375c38376eSChiYuan Huang static int mt6370_multicolor_led_register(struct device *dev, struct mt6370_led *led,
8385c38376eSChiYuan Huang 					  struct led_init_data *init_data)
8395c38376eSChiYuan Huang {
8405c38376eSChiYuan Huang 	int ret;
8415c38376eSChiYuan Huang 
8425c38376eSChiYuan Huang 	ret = mt6370_mc_brightness_set(&led->mc.led_cdev, 0);
8435c38376eSChiYuan Huang 	if (ret)
8445c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Couldn't set multicolor brightness\n");
8455c38376eSChiYuan Huang 
8465c38376eSChiYuan Huang 	ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc, init_data);
8475c38376eSChiYuan Huang 	if (ret)
8485c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Couldn't register multicolor\n");
8495c38376eSChiYuan Huang 
8505c38376eSChiYuan Huang 	return 0;
8515c38376eSChiYuan Huang }
8525c38376eSChiYuan Huang 
8535c38376eSChiYuan Huang static int mt6370_led_register(struct device *dev, struct mt6370_led *led,
8545c38376eSChiYuan Huang 			       struct led_init_data *init_data)
8555c38376eSChiYuan Huang {
8565c38376eSChiYuan Huang 	struct mt6370_priv *priv = led->priv;
8575c38376eSChiYuan Huang 	int ret;
8585c38376eSChiYuan Huang 
8595c38376eSChiYuan Huang 	if (led->index == MT6370_VIRTUAL_MULTICOLOR)
8605c38376eSChiYuan Huang 		return mt6370_multicolor_led_register(dev, led, init_data);
8615c38376eSChiYuan Huang 
8625c38376eSChiYuan Huang 	/* If ISNK4 is declared, change its mode from HW auto to SW control */
8635c38376eSChiYuan Huang 	if (led->index == MT6370_LED_ISNK4) {
8645c38376eSChiYuan Huang 		ret = regmap_field_write(priv->fields[F_CHGIND_EN], 1);
8655c38376eSChiYuan Huang 		if (ret)
8665c38376eSChiYuan Huang 			return dev_err_probe(dev, ret, "Failed to set CHRIND to SW\n");
8675c38376eSChiYuan Huang 	}
8685c38376eSChiYuan Huang 
8695c38376eSChiYuan Huang 	ret = mt6370_isnk_init_default_state(led);
8705c38376eSChiYuan Huang 	if (ret)
8715c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Failed to init %d isnk state\n", led->index);
8725c38376eSChiYuan Huang 
8735c38376eSChiYuan Huang 	ret = devm_led_classdev_register_ext(dev, &led->isink, init_data);
8745c38376eSChiYuan Huang 	if (ret)
8755c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Couldn't register isink %d\n", led->index);
8765c38376eSChiYuan Huang 
8775c38376eSChiYuan Huang 	return 0;
8785c38376eSChiYuan Huang }
8795c38376eSChiYuan Huang 
8805c38376eSChiYuan Huang static int mt6370_check_vendor_info(struct mt6370_priv *priv)
8815c38376eSChiYuan Huang {
8825c38376eSChiYuan Huang 	unsigned int devinfo, vid;
8835c38376eSChiYuan Huang 	int ret;
8845c38376eSChiYuan Huang 
8855c38376eSChiYuan Huang 	ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &devinfo);
8865c38376eSChiYuan Huang 	if (ret)
8875c38376eSChiYuan Huang 		return ret;
8885c38376eSChiYuan Huang 
8895c38376eSChiYuan Huang 	vid = FIELD_GET(MT6370_VENDOR_ID_MASK, devinfo);
8905c38376eSChiYuan Huang 	if (vid == MT6372_VENDOR_ID || vid == MT6372C_VENDOR_ID) {
8915c38376eSChiYuan Huang 		priv->reg_fields = mt6372_reg_fields;
8925c38376eSChiYuan Huang 		priv->ranges = mt6372_led_ranges;
8935c38376eSChiYuan Huang 		priv->pdata = &mt6372_pdata;
8945c38376eSChiYuan Huang 	} else {
8955c38376eSChiYuan Huang 		/* Common for MT6370/71 */
8965c38376eSChiYuan Huang 		priv->reg_fields = common_reg_fields;
8975c38376eSChiYuan Huang 		priv->ranges = common_led_ranges;
8985c38376eSChiYuan Huang 		priv->pdata = &common_pdata;
8995c38376eSChiYuan Huang 	}
9005c38376eSChiYuan Huang 
9015c38376eSChiYuan Huang 	return 0;
9025c38376eSChiYuan Huang }
9035c38376eSChiYuan Huang 
9045c38376eSChiYuan Huang static int mt6370_leds_probe(struct platform_device *pdev)
9055c38376eSChiYuan Huang {
9065c38376eSChiYuan Huang 	struct device *dev = &pdev->dev;
9075c38376eSChiYuan Huang 	struct mt6370_priv *priv;
9085c38376eSChiYuan Huang 	struct fwnode_handle *child;
9095c38376eSChiYuan Huang 	size_t count;
9105c38376eSChiYuan Huang 	unsigned int i = 0;
9115c38376eSChiYuan Huang 	int ret;
9125c38376eSChiYuan Huang 
9135c38376eSChiYuan Huang 	count = device_get_child_node_count(dev);
9145c38376eSChiYuan Huang 	if (!count || count > MT6370_MAX_LEDS)
9155c38376eSChiYuan Huang 		return dev_err_probe(dev, -EINVAL,
9165c38376eSChiYuan Huang 				     "No child node or node count over max LED number %zu\n",
9175c38376eSChiYuan Huang 				      count);
9185c38376eSChiYuan Huang 
9195c38376eSChiYuan Huang 	priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);
9205c38376eSChiYuan Huang 	if (!priv)
9215c38376eSChiYuan Huang 		return -ENOMEM;
9225c38376eSChiYuan Huang 
9235c38376eSChiYuan Huang 	priv->leds_count = count;
9245c38376eSChiYuan Huang 	mutex_init(&priv->lock);
9255c38376eSChiYuan Huang 
9265c38376eSChiYuan Huang 	priv->regmap = dev_get_regmap(dev->parent, NULL);
9275c38376eSChiYuan Huang 	if (!priv->regmap)
9285c38376eSChiYuan Huang 		return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n");
9295c38376eSChiYuan Huang 
9305c38376eSChiYuan Huang 	ret = mt6370_check_vendor_info(priv);
9315c38376eSChiYuan Huang 	if (ret)
9325c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Failed to check vendor info\n");
9335c38376eSChiYuan Huang 
9345c38376eSChiYuan Huang 	ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->fields, priv->reg_fields,
9355c38376eSChiYuan Huang 					   F_MAX_FIELDS);
9365c38376eSChiYuan Huang 	if (ret)
9375c38376eSChiYuan Huang 		return dev_err_probe(dev, ret, "Failed to allocate regmap field\n");
9385c38376eSChiYuan Huang 
9395c38376eSChiYuan Huang 	device_for_each_child_node(dev, child) {
9405c38376eSChiYuan Huang 		struct mt6370_led *led = priv->leds + i++;
9415c38376eSChiYuan Huang 		struct led_init_data init_data = { .fwnode = child };
9425c38376eSChiYuan Huang 		u32 reg, color;
9435c38376eSChiYuan Huang 
9445c38376eSChiYuan Huang 		ret = fwnode_property_read_u32(child, "reg", &reg);
9455c38376eSChiYuan Huang 		if (ret) {
9465c38376eSChiYuan Huang 			dev_err(dev, "Failed to parse reg property\n");
9475c38376eSChiYuan Huang 			goto fwnode_release;
9485c38376eSChiYuan Huang 		}
9495c38376eSChiYuan Huang 
9505c38376eSChiYuan Huang 		if (reg >= MT6370_MAX_LEDS) {
9515c38376eSChiYuan Huang 			ret = -EINVAL;
9525c38376eSChiYuan Huang 			dev_err(dev, "Error reg property number\n");
9535c38376eSChiYuan Huang 			goto fwnode_release;
9545c38376eSChiYuan Huang 		}
9555c38376eSChiYuan Huang 
9565c38376eSChiYuan Huang 		ret = fwnode_property_read_u32(child, "color", &color);
9575c38376eSChiYuan Huang 		if (ret) {
9585c38376eSChiYuan Huang 			dev_err(dev, "Failed to parse color property\n");
9595c38376eSChiYuan Huang 			goto fwnode_release;
9605c38376eSChiYuan Huang 		}
9615c38376eSChiYuan Huang 
9625c38376eSChiYuan Huang 		if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)
9635c38376eSChiYuan Huang 			reg = MT6370_VIRTUAL_MULTICOLOR;
9645c38376eSChiYuan Huang 
9655c38376eSChiYuan Huang 		if (priv->leds_active & BIT(reg)) {
9665c38376eSChiYuan Huang 			ret = -EINVAL;
9675c38376eSChiYuan Huang 			dev_err(dev, "Duplicate reg property\n");
9685c38376eSChiYuan Huang 			goto fwnode_release;
9695c38376eSChiYuan Huang 		}
9705c38376eSChiYuan Huang 
9715c38376eSChiYuan Huang 		priv->leds_active |= BIT(reg);
9725c38376eSChiYuan Huang 
9735c38376eSChiYuan Huang 		led->index = reg;
9745c38376eSChiYuan Huang 		led->priv = priv;
9755c38376eSChiYuan Huang 
9765c38376eSChiYuan Huang 		ret = mt6370_init_led_properties(dev, led, &init_data);
9775c38376eSChiYuan Huang 		if (ret)
9785c38376eSChiYuan Huang 			goto fwnode_release;
9795c38376eSChiYuan Huang 
9805c38376eSChiYuan Huang 		ret = mt6370_led_register(dev, led, &init_data);
9815c38376eSChiYuan Huang 		if (ret)
9825c38376eSChiYuan Huang 			goto fwnode_release;
9835c38376eSChiYuan Huang 	}
9845c38376eSChiYuan Huang 
9855c38376eSChiYuan Huang 	return 0;
9865c38376eSChiYuan Huang 
9875c38376eSChiYuan Huang fwnode_release:
9885c38376eSChiYuan Huang 	fwnode_handle_put(child);
9895c38376eSChiYuan Huang 	return ret;
9905c38376eSChiYuan Huang }
9915c38376eSChiYuan Huang 
9925c38376eSChiYuan Huang static const struct of_device_id mt6370_rgbled_device_table[] = {
9935c38376eSChiYuan Huang 	{ .compatible = "mediatek,mt6370-indicator" },
9945c38376eSChiYuan Huang 	{}
9955c38376eSChiYuan Huang };
9965c38376eSChiYuan Huang MODULE_DEVICE_TABLE(of, mt6370_rgbled_device_table);
9975c38376eSChiYuan Huang 
9985c38376eSChiYuan Huang static struct platform_driver mt6370_rgbled_driver = {
9995c38376eSChiYuan Huang 	.driver = {
10005c38376eSChiYuan Huang 		.name = "mt6370-indicator",
10015c38376eSChiYuan Huang 		.of_match_table = mt6370_rgbled_device_table,
10025c38376eSChiYuan Huang 	},
10035c38376eSChiYuan Huang 	.probe = mt6370_leds_probe,
10045c38376eSChiYuan Huang };
10055c38376eSChiYuan Huang module_platform_driver(mt6370_rgbled_driver);
10065c38376eSChiYuan Huang 
10075c38376eSChiYuan Huang MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>");
10085c38376eSChiYuan Huang MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
10095c38376eSChiYuan Huang MODULE_DESCRIPTION("MediaTek MT6370 RGB LED Driver");
10105c38376eSChiYuan Huang MODULE_LICENSE("GPL");
1011