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