1011 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2023 Richtek Technology Corp.4 *5 * Authors:6 * ChiYuan Huang <cy_huang@richtek.com>7 * Alice Chen <alice_chen@richtek.com>8 */9 10#include <linux/bitfield.h>11#include <linux/bitops.h>12#include <linux/kernel.h>13#include <linux/leds.h>14#include <linux/led-class-multicolor.h>15#include <linux/linear_range.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/mutex.h>19#include <linux/platform_device.h>20#include <linux/property.h>21#include <linux/regmap.h>22#include <linux/util_macros.h>23 24#include <linux/unaligned.h>25 26enum {27 MT6370_LED_ISNK1 = 0,28 MT6370_LED_ISNK2,29 MT6370_LED_ISNK3,30 MT6370_LED_ISNK4,31 MT6370_MAX_LEDS32};33 34enum mt6370_led_mode {35 MT6370_LED_PWM_MODE = 0,36 MT6370_LED_BREATH_MODE,37 MT6370_LED_REG_MODE,38 MT6370_LED_MAX_MODE39};40 41enum mt6370_led_field {42 F_RGB_EN = 0,43 F_CHGIND_EN,44 F_LED1_CURR,45 F_LED2_CURR,46 F_LED3_CURR,47 F_LED4_CURR,48 F_LED1_MODE,49 F_LED2_MODE,50 F_LED3_MODE,51 F_LED4_MODE,52 F_LED1_DUTY,53 F_LED2_DUTY,54 F_LED3_DUTY,55 F_LED4_DUTY,56 F_LED1_FREQ,57 F_LED2_FREQ,58 F_LED3_FREQ,59 F_LED4_FREQ,60 F_MAX_FIELDS61};62 63enum mt6370_led_ranges {64 R_LED123_CURR = 0,65 R_LED4_CURR,66 R_LED_TRFON,67 R_LED_TOFF,68 R_MAX_RANGES69};70 71enum mt6370_pattern {72 P_LED_TR1 = 0,73 P_LED_TR2,74 P_LED_TF1,75 P_LED_TF2,76 P_LED_TON,77 P_LED_TOFF,78 P_MAX_PATTERNS79};80 81#define MT6370_REG_DEV_INFO 0x10082#define MT6370_REG_RGB1_DIM 0x18283#define MT6370_REG_RGB2_DIM 0x18384#define MT6370_REG_RGB3_DIM 0x18485#define MT6370_REG_RGB_EN 0x18586#define MT6370_REG_RGB1_ISNK 0x18687#define MT6370_REG_RGB2_ISNK 0x18788#define MT6370_REG_RGB3_ISNK 0x18889#define MT6370_REG_RGB1_TR 0x18990#define MT6370_REG_RGB_CHRIND_DIM 0x19291#define MT6370_REG_RGB_CHRIND_CTRL 0x19392#define MT6370_REG_RGB_CHRIND_TR 0x19493 94#define MT6372_REG_RGB_EN 0x18295#define MT6372_REG_RGB1_ISNK 0x18396#define MT6372_REG_RGB2_ISNK 0x18497#define MT6372_REG_RGB3_ISNK 0x18598#define MT6372_REG_RGB4_ISNK 0x18699#define MT6372_REG_RGB1_DIM 0x187100#define MT6372_REG_RGB2_DIM 0x188101#define MT6372_REG_RGB3_DIM 0x189102#define MT6372_REG_RGB4_DIM 0x18A103#define MT6372_REG_RGB12_FREQ 0x18B104#define MT6372_REG_RGB34_FREQ 0x18C105#define MT6372_REG_RGB1_TR 0x18D106 107#define MT6370_VENDOR_ID_MASK GENMASK(7, 4)108#define MT6372_VENDOR_ID 0x9109#define MT6372C_VENDOR_ID 0xb110#define MT6370_CHEN_BIT(id) BIT(MT6370_LED_ISNK4 - id)111#define MT6370_VIRTUAL_MULTICOLOR 5112#define MC_CHANNEL_NUM 3113#define MT6370_PWM_DUTY (BIT(5) - 1)114#define MT6372_PWM_DUTY (BIT(8) - 1)115 116struct mt6370_led {117 /*118 * If the color of the LED in DT is set to119 * - 'LED_COLOR_ID_RGB'120 * - 'LED_COLOR_ID_MULTI'121 * The member 'index' of this struct will be set to122 * 'MT6370_VIRTUAL_MULTICOLOR'.123 * If so, this LED will choose 'struct led_classdev_mc mc' to use.124 * Instead, if the member 'index' of this struct is set to125 * 'MT6370_LED_ISNK1' ~ 'MT6370_LED_ISNK4', then this LED will choose126 * 'struct led_classdev isink' to use.127 */128 union {129 struct led_classdev isink;130 struct led_classdev_mc mc;131 };132 struct mt6370_priv *priv;133 enum led_default_state default_state;134 u32 index;135};136 137struct mt6370_pdata {138 const unsigned int *tfreq;139 unsigned int tfreq_len;140 u16 reg_rgb1_tr;141 s16 reg_rgb_chrind_tr;142 u8 pwm_duty;143};144 145struct mt6370_priv {146 /* Per LED access lock */147 struct mutex lock;148 struct regmap *regmap;149 struct regmap_field *fields[F_MAX_FIELDS];150 const struct reg_field *reg_fields;151 const struct linear_range *ranges;152 const struct mt6370_pdata *pdata;153 unsigned int leds_count;154 unsigned int leds_active;155 struct mt6370_led leds[] __counted_by(leds_count);156};157 158static const struct reg_field common_reg_fields[F_MAX_FIELDS] = {159 [F_RGB_EN] = REG_FIELD(MT6370_REG_RGB_EN, 4, 7),160 [F_CHGIND_EN] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 7, 7),161 [F_LED1_CURR] = REG_FIELD(MT6370_REG_RGB1_ISNK, 0, 2),162 [F_LED2_CURR] = REG_FIELD(MT6370_REG_RGB2_ISNK, 0, 2),163 [F_LED3_CURR] = REG_FIELD(MT6370_REG_RGB3_ISNK, 0, 2),164 [F_LED4_CURR] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 0, 1),165 [F_LED1_MODE] = REG_FIELD(MT6370_REG_RGB1_DIM, 5, 6),166 [F_LED2_MODE] = REG_FIELD(MT6370_REG_RGB2_DIM, 5, 6),167 [F_LED3_MODE] = REG_FIELD(MT6370_REG_RGB3_DIM, 5, 6),168 [F_LED4_MODE] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 5, 6),169 [F_LED1_DUTY] = REG_FIELD(MT6370_REG_RGB1_DIM, 0, 4),170 [F_LED2_DUTY] = REG_FIELD(MT6370_REG_RGB2_DIM, 0, 4),171 [F_LED3_DUTY] = REG_FIELD(MT6370_REG_RGB3_DIM, 0, 4),172 [F_LED4_DUTY] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 0, 4),173 [F_LED1_FREQ] = REG_FIELD(MT6370_REG_RGB1_ISNK, 3, 5),174 [F_LED2_FREQ] = REG_FIELD(MT6370_REG_RGB2_ISNK, 3, 5),175 [F_LED3_FREQ] = REG_FIELD(MT6370_REG_RGB3_ISNK, 3, 5),176 [F_LED4_FREQ] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 2, 4),177};178 179static const struct reg_field mt6372_reg_fields[F_MAX_FIELDS] = {180 [F_RGB_EN] = REG_FIELD(MT6372_REG_RGB_EN, 4, 7),181 [F_CHGIND_EN] = REG_FIELD(MT6372_REG_RGB_EN, 3, 3),182 [F_LED1_CURR] = REG_FIELD(MT6372_REG_RGB1_ISNK, 0, 3),183 [F_LED2_CURR] = REG_FIELD(MT6372_REG_RGB2_ISNK, 0, 3),184 [F_LED3_CURR] = REG_FIELD(MT6372_REG_RGB3_ISNK, 0, 3),185 [F_LED4_CURR] = REG_FIELD(MT6372_REG_RGB4_ISNK, 0, 3),186 [F_LED1_MODE] = REG_FIELD(MT6372_REG_RGB1_ISNK, 6, 7),187 [F_LED2_MODE] = REG_FIELD(MT6372_REG_RGB2_ISNK, 6, 7),188 [F_LED3_MODE] = REG_FIELD(MT6372_REG_RGB3_ISNK, 6, 7),189 [F_LED4_MODE] = REG_FIELD(MT6372_REG_RGB4_ISNK, 6, 7),190 [F_LED1_DUTY] = REG_FIELD(MT6372_REG_RGB1_DIM, 0, 7),191 [F_LED2_DUTY] = REG_FIELD(MT6372_REG_RGB2_DIM, 0, 7),192 [F_LED3_DUTY] = REG_FIELD(MT6372_REG_RGB3_DIM, 0, 7),193 [F_LED4_DUTY] = REG_FIELD(MT6372_REG_RGB4_DIM, 0, 7),194 [F_LED1_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 5, 7),195 [F_LED2_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 2, 4),196 [F_LED3_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 5, 7),197 [F_LED4_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 2, 4),198};199 200/* Current unit: microamp, time unit: millisecond */201static const struct linear_range common_led_ranges[R_MAX_RANGES] = {202 [R_LED123_CURR] = { 4000, 1, 6, 4000 },203 [R_LED4_CURR] = { 2000, 1, 3, 2000 },204 [R_LED_TRFON] = { 125, 0, 15, 200 },205 [R_LED_TOFF] = { 250, 0, 15, 400 },206};207 208static const struct linear_range mt6372_led_ranges[R_MAX_RANGES] = {209 [R_LED123_CURR] = { 2000, 1, 14, 2000 },210 [R_LED4_CURR] = { 2000, 1, 14, 2000 },211 [R_LED_TRFON] = { 125, 0, 15, 250 },212 [R_LED_TOFF] = { 250, 0, 15, 500 },213};214 215static const unsigned int common_tfreqs[] = {216 10000, 5000, 2000, 1000, 500, 200, 5, 1,217};218 219static const unsigned int mt6372_tfreqs[] = {220 8000, 4000, 2000, 1000, 500, 250, 8, 4,221};222 223static const struct mt6370_pdata common_pdata = {224 .tfreq = common_tfreqs,225 .tfreq_len = ARRAY_SIZE(common_tfreqs),226 .pwm_duty = MT6370_PWM_DUTY,227 .reg_rgb1_tr = MT6370_REG_RGB1_TR,228 .reg_rgb_chrind_tr = MT6370_REG_RGB_CHRIND_TR,229};230 231static const struct mt6370_pdata mt6372_pdata = {232 .tfreq = mt6372_tfreqs,233 .tfreq_len = ARRAY_SIZE(mt6372_tfreqs),234 .pwm_duty = MT6372_PWM_DUTY,235 .reg_rgb1_tr = MT6372_REG_RGB1_TR,236 .reg_rgb_chrind_tr = -1,237};238 239static enum mt6370_led_field mt6370_get_led_current_field(unsigned int led_no)240{241 switch (led_no) {242 case MT6370_LED_ISNK1:243 return F_LED1_CURR;244 case MT6370_LED_ISNK2:245 return F_LED2_CURR;246 case MT6370_LED_ISNK3:247 return F_LED3_CURR;248 default:249 return F_LED4_CURR;250 }251}252 253static int mt6370_set_led_brightness(struct mt6370_priv *priv, unsigned int led_no,254 unsigned int level)255{256 enum mt6370_led_field sel_field;257 258 sel_field = mt6370_get_led_current_field(led_no);259 260 return regmap_field_write(priv->fields[sel_field], level);261}262 263static int mt6370_get_led_brightness(struct mt6370_priv *priv, unsigned int led_no,264 unsigned int *level)265{266 enum mt6370_led_field sel_field;267 268 sel_field = mt6370_get_led_current_field(led_no);269 270 return regmap_field_read(priv->fields[sel_field], level);271}272 273static int mt6370_set_led_duty(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton,274 unsigned int toff)275{276 const struct mt6370_pdata *pdata = priv->pdata;277 enum mt6370_led_field sel_field;278 unsigned int divisor, ratio;279 280 divisor = pdata->pwm_duty;281 ratio = ton * divisor / (ton + toff);282 283 switch (led_no) {284 case MT6370_LED_ISNK1:285 sel_field = F_LED1_DUTY;286 break;287 case MT6370_LED_ISNK2:288 sel_field = F_LED2_DUTY;289 break;290 case MT6370_LED_ISNK3:291 sel_field = F_LED3_DUTY;292 break;293 default:294 sel_field = F_LED4_DUTY;295 break;296 }297 298 return regmap_field_write(priv->fields[sel_field], ratio);299}300 301static int mt6370_set_led_freq(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton,302 unsigned int toff)303{304 const struct mt6370_pdata *pdata = priv->pdata;305 enum mt6370_led_field sel_field;306 unsigned int tfreq_len = pdata->tfreq_len;307 unsigned int tsum, sel;308 309 tsum = ton + toff;310 311 if (tsum > pdata->tfreq[0] || tsum < pdata->tfreq[tfreq_len - 1])312 return -EOPNOTSUPP;313 314 sel = find_closest_descending(tsum, pdata->tfreq, tfreq_len);315 316 switch (led_no) {317 case MT6370_LED_ISNK1:318 sel_field = F_LED1_FREQ;319 break;320 case MT6370_LED_ISNK2:321 sel_field = F_LED2_FREQ;322 break;323 case MT6370_LED_ISNK3:324 sel_field = F_LED3_FREQ;325 break;326 default:327 sel_field = F_LED4_FREQ;328 break;329 }330 331 return regmap_field_write(priv->fields[sel_field], sel);332}333 334static void mt6370_get_breath_reg_base(struct mt6370_priv *priv, unsigned int led_no,335 unsigned int *base)336{337 const struct mt6370_pdata *pdata = priv->pdata;338 339 if (pdata->reg_rgb_chrind_tr < 0) {340 *base = pdata->reg_rgb1_tr + led_no * 3;341 return;342 }343 344 switch (led_no) {345 case MT6370_LED_ISNK1:346 case MT6370_LED_ISNK2:347 case MT6370_LED_ISNK3:348 *base = pdata->reg_rgb1_tr + led_no * 3;349 break;350 default:351 *base = pdata->reg_rgb_chrind_tr;352 break;353 }354}355 356static int mt6370_gen_breath_pattern(struct mt6370_priv *priv, struct led_pattern *pattern, u32 len,357 u8 *pattern_val, u32 val_len)358{359 enum mt6370_led_ranges sel_range;360 struct led_pattern *curr;361 unsigned int sel;362 u32 val = 0;363 int i;364 365 if (len < P_MAX_PATTERNS && val_len < P_MAX_PATTERNS / 2)366 return -EINVAL;367 368 /*369 * Pattern list370 * tr1: byte 0, b'[7:4]371 * tr2: byte 0, b'[3:0]372 * tf1: byte 1, b'[7:4]373 * tf2: byte 1, b'[3:0]374 * ton: byte 2, b'[7:4]375 * toff: byte 2, b'[3:0]376 */377 for (i = 0; i < P_MAX_PATTERNS; i++) {378 curr = pattern + i;379 380 sel_range = i == P_LED_TOFF ? R_LED_TOFF : R_LED_TRFON;381 382 linear_range_get_selector_within(priv->ranges + sel_range, curr->delta_t, &sel);383 384 if (i % 2) {385 val |= sel;386 } else {387 val <<= 8;388 val |= sel << 4;389 }390 }391 392 put_unaligned_be24(val, pattern_val);393 394 return 0;395}396 397static int mt6370_set_led_mode(struct mt6370_priv *priv, unsigned int led_no,398 enum mt6370_led_mode mode)399{400 enum mt6370_led_field sel_field;401 402 switch (led_no) {403 case MT6370_LED_ISNK1:404 sel_field = F_LED1_MODE;405 break;406 case MT6370_LED_ISNK2:407 sel_field = F_LED2_MODE;408 break;409 case MT6370_LED_ISNK3:410 sel_field = F_LED3_MODE;411 break;412 default:413 sel_field = F_LED4_MODE;414 break;415 }416 417 return regmap_field_write(priv->fields[sel_field], mode);418}419 420static int mt6370_mc_brightness_set(struct led_classdev *lcdev, enum led_brightness level)421{422 struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);423 struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);424 struct mt6370_priv *priv = led->priv;425 struct mc_subled *subled;426 unsigned int enable, disable;427 int i, ret;428 429 mutex_lock(&priv->lock);430 431 led_mc_calc_color_components(mccdev, level);432 433 ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);434 if (ret)435 goto out_unlock;436 437 disable = enable;438 439 for (i = 0; i < mccdev->num_colors; i++) {440 u32 brightness;441 442 subled = mccdev->subled_info + i;443 brightness = min(subled->brightness, lcdev->max_brightness);444 disable &= ~MT6370_CHEN_BIT(subled->channel);445 446 if (level == 0) {447 enable &= ~MT6370_CHEN_BIT(subled->channel);448 449 ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE);450 if (ret)451 goto out_unlock;452 453 continue;454 }455 456 if (brightness == 0) {457 enable &= ~MT6370_CHEN_BIT(subled->channel);458 continue;459 }460 461 enable |= MT6370_CHEN_BIT(subled->channel);462 463 ret = mt6370_set_led_brightness(priv, subled->channel, brightness);464 if (ret)465 goto out_unlock;466 }467 468 ret = regmap_field_write(priv->fields[F_RGB_EN], disable);469 if (ret)470 goto out_unlock;471 472 ret = regmap_field_write(priv->fields[F_RGB_EN], enable);473 474out_unlock:475 mutex_unlock(&priv->lock);476 477 return ret;478}479 480static int mt6370_mc_blink_set(struct led_classdev *lcdev,481 unsigned long *delay_on,482 unsigned long *delay_off)483{484 struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);485 struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);486 struct mt6370_priv *priv = led->priv;487 struct mc_subled *subled;488 unsigned int enable, disable;489 int i, ret;490 491 mutex_lock(&priv->lock);492 493 if (!*delay_on && !*delay_off)494 *delay_on = *delay_off = 500;495 496 ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);497 if (ret)498 goto out_unlock;499 500 disable = enable;501 502 for (i = 0; i < mccdev->num_colors; i++) {503 subled = mccdev->subled_info + i;504 505 disable &= ~MT6370_CHEN_BIT(subled->channel);506 507 ret = mt6370_set_led_duty(priv, subled->channel, *delay_on, *delay_off);508 if (ret)509 goto out_unlock;510 511 ret = mt6370_set_led_freq(priv, subled->channel, *delay_on, *delay_off);512 if (ret)513 goto out_unlock;514 515 ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_PWM_MODE);516 if (ret)517 goto out_unlock;518 }519 520 /* Toggle to make pattern timing the same */521 ret = regmap_field_write(priv->fields[F_RGB_EN], disable);522 if (ret)523 goto out_unlock;524 525 ret = regmap_field_write(priv->fields[F_RGB_EN], enable);526 527out_unlock:528 mutex_unlock(&priv->lock);529 530 return ret;531}532 533static int mt6370_mc_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len,534 int repeat)535{536 struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);537 struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);538 struct mt6370_priv *priv = led->priv;539 struct mc_subled *subled;540 unsigned int reg_base, enable, disable;541 u8 params[P_MAX_PATTERNS / 2];542 int i, ret;543 544 mutex_lock(&priv->lock);545 546 ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params));547 if (ret)548 goto out_unlock;549 550 ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);551 if (ret)552 goto out_unlock;553 554 disable = enable;555 556 for (i = 0; i < mccdev->num_colors; i++) {557 subled = mccdev->subled_info + i;558 559 mt6370_get_breath_reg_base(priv, subled->channel, ®_base);560 disable &= ~MT6370_CHEN_BIT(subled->channel);561 562 ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params));563 if (ret)564 goto out_unlock;565 566 ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_BREATH_MODE);567 if (ret)568 goto out_unlock;569 }570 571 /* Toggle to make pattern timing be the same */572 ret = regmap_field_write(priv->fields[F_RGB_EN], disable);573 if (ret)574 goto out_unlock;575 576 ret = regmap_field_write(priv->fields[F_RGB_EN], enable);577 578out_unlock:579 mutex_unlock(&priv->lock);580 581 return ret;582}583 584static inline int mt6370_mc_pattern_clear(struct led_classdev *lcdev)585{586 struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);587 struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc);588 struct mt6370_priv *priv = led->priv;589 struct mc_subled *subled;590 int i, ret;591 592 mutex_lock(&led->priv->lock);593 594 for (i = 0; i < mccdev->num_colors; i++) {595 subled = mccdev->subled_info + i;596 597 ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE);598 if (ret)599 break;600 }601 602 mutex_unlock(&led->priv->lock);603 604 return ret;605}606 607static int mt6370_isnk_brightness_set(struct led_classdev *lcdev,608 enum led_brightness level)609{610 struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);611 struct mt6370_priv *priv = led->priv;612 unsigned int enable;613 int ret;614 615 mutex_lock(&priv->lock);616 617 ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);618 if (ret)619 goto out_unlock;620 621 if (level == 0) {622 enable &= ~MT6370_CHEN_BIT(led->index);623 624 ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE);625 if (ret)626 goto out_unlock;627 } else {628 enable |= MT6370_CHEN_BIT(led->index);629 630 ret = mt6370_set_led_brightness(priv, led->index, level);631 if (ret)632 goto out_unlock;633 }634 635 ret = regmap_field_write(priv->fields[F_RGB_EN], enable);636 637out_unlock:638 mutex_unlock(&priv->lock);639 640 return ret;641}642 643static int mt6370_isnk_blink_set(struct led_classdev *lcdev, unsigned long *delay_on,644 unsigned long *delay_off)645{646 struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);647 struct mt6370_priv *priv = led->priv;648 int ret;649 650 mutex_lock(&priv->lock);651 652 if (!*delay_on && !*delay_off)653 *delay_on = *delay_off = 500;654 655 ret = mt6370_set_led_duty(priv, led->index, *delay_on, *delay_off);656 if (ret)657 goto out_unlock;658 659 ret = mt6370_set_led_freq(priv, led->index, *delay_on, *delay_off);660 if (ret)661 goto out_unlock;662 663 ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_PWM_MODE);664 665out_unlock:666 mutex_unlock(&priv->lock);667 668 return ret;669}670 671static int mt6370_isnk_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len,672 int repeat)673{674 struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);675 struct mt6370_priv *priv = led->priv;676 unsigned int reg_base;677 u8 params[P_MAX_PATTERNS / 2];678 int ret;679 680 mutex_lock(&priv->lock);681 682 ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params));683 if (ret)684 goto out_unlock;685 686 mt6370_get_breath_reg_base(priv, led->index, ®_base);687 688 ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params));689 if (ret)690 goto out_unlock;691 692 ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_BREATH_MODE);693 694out_unlock:695 mutex_unlock(&priv->lock);696 697 return ret;698}699 700static inline int mt6370_isnk_pattern_clear(struct led_classdev *lcdev)701{702 struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink);703 struct mt6370_priv *priv = led->priv;704 int ret;705 706 mutex_lock(&led->priv->lock);707 ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE);708 mutex_unlock(&led->priv->lock);709 710 return ret;711}712 713static int mt6370_assign_multicolor_info(struct device *dev, struct mt6370_led *led,714 struct fwnode_handle *fwnode)715{716 struct mt6370_priv *priv = led->priv;717 struct fwnode_handle *child;718 struct mc_subled *sub_led;719 u32 num_color = 0;720 int ret;721 722 sub_led = devm_kcalloc(dev, MC_CHANNEL_NUM, sizeof(*sub_led), GFP_KERNEL);723 if (!sub_led)724 return -ENOMEM;725 726 fwnode_for_each_child_node(fwnode, child) {727 u32 reg, color;728 729 ret = fwnode_property_read_u32(child, "reg", ®);730 if (ret || reg > MT6370_LED_ISNK3 || priv->leds_active & BIT(reg)) {731 fwnode_handle_put(child);732 return -EINVAL;733 }734 735 ret = fwnode_property_read_u32(child, "color", &color);736 if (ret) {737 fwnode_handle_put(child);738 return dev_err_probe(dev, ret, "LED %d, no color specified\n", led->index);739 }740 741 priv->leds_active |= BIT(reg);742 sub_led[num_color].color_index = color;743 sub_led[num_color].channel = reg;744 sub_led[num_color].intensity = 0;745 num_color++;746 }747 748 if (num_color < 2)749 return dev_err_probe(dev, -EINVAL,750 "Multicolor must include 2 or more LED channels\n");751 752 led->mc.num_colors = num_color;753 led->mc.subled_info = sub_led;754 755 return 0;756}757 758static int mt6370_init_led_properties(struct device *dev, struct mt6370_led *led,759 struct led_init_data *init_data)760{761 struct mt6370_priv *priv = led->priv;762 struct led_classdev *lcdev;763 enum mt6370_led_ranges sel_range;764 u32 max_uA, max_level;765 int ret;766 767 if (led->index == MT6370_VIRTUAL_MULTICOLOR) {768 ret = mt6370_assign_multicolor_info(dev, led, init_data->fwnode);769 if (ret)770 return ret;771 772 lcdev = &led->mc.led_cdev;773 lcdev->brightness_set_blocking = mt6370_mc_brightness_set;774 lcdev->blink_set = mt6370_mc_blink_set;775 lcdev->pattern_set = mt6370_mc_pattern_set;776 lcdev->pattern_clear = mt6370_mc_pattern_clear;777 } else {778 lcdev = &led->isink;779 lcdev->brightness_set_blocking = mt6370_isnk_brightness_set;780 lcdev->blink_set = mt6370_isnk_blink_set;781 lcdev->pattern_set = mt6370_isnk_pattern_set;782 lcdev->pattern_clear = mt6370_isnk_pattern_clear;783 }784 785 ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp", &max_uA);786 if (ret) {787 dev_warn(dev, "Not specified led-max-microamp, config to the minimum\n");788 max_uA = 0;789 }790 791 if (led->index == MT6370_LED_ISNK4)792 sel_range = R_LED4_CURR;793 else794 sel_range = R_LED123_CURR;795 796 linear_range_get_selector_within(priv->ranges + sel_range, max_uA, &max_level);797 798 lcdev->max_brightness = max_level;799 800 led->default_state = led_init_default_state_get(init_data->fwnode);801 802 return 0;803}804 805static int mt6370_isnk_init_default_state(struct mt6370_led *led)806{807 struct mt6370_priv *priv = led->priv;808 unsigned int enable, level;809 int ret;810 811 ret = mt6370_get_led_brightness(priv, led->index, &level);812 if (ret)813 return ret;814 815 ret = regmap_field_read(priv->fields[F_RGB_EN], &enable);816 if (ret)817 return ret;818 819 if (!(enable & MT6370_CHEN_BIT(led->index)))820 level = 0;821 822 switch (led->default_state) {823 case LEDS_DEFSTATE_ON:824 led->isink.brightness = led->isink.max_brightness;825 break;826 case LEDS_DEFSTATE_KEEP:827 led->isink.brightness = min(level, led->isink.max_brightness);828 break;829 default:830 led->isink.brightness = 0;831 break;832 }833 834 return mt6370_isnk_brightness_set(&led->isink, led->isink.brightness);835}836 837static int mt6370_multicolor_led_register(struct device *dev, struct mt6370_led *led,838 struct led_init_data *init_data)839{840 int ret;841 842 ret = mt6370_mc_brightness_set(&led->mc.led_cdev, 0);843 if (ret)844 return dev_err_probe(dev, ret, "Couldn't set multicolor brightness\n");845 846 ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc, init_data);847 if (ret)848 return dev_err_probe(dev, ret, "Couldn't register multicolor\n");849 850 return 0;851}852 853static int mt6370_led_register(struct device *dev, struct mt6370_led *led,854 struct led_init_data *init_data)855{856 struct mt6370_priv *priv = led->priv;857 int ret;858 859 if (led->index == MT6370_VIRTUAL_MULTICOLOR)860 return mt6370_multicolor_led_register(dev, led, init_data);861 862 /* If ISNK4 is declared, change its mode from HW auto to SW control */863 if (led->index == MT6370_LED_ISNK4) {864 ret = regmap_field_write(priv->fields[F_CHGIND_EN], 1);865 if (ret)866 return dev_err_probe(dev, ret, "Failed to set CHRIND to SW\n");867 }868 869 ret = mt6370_isnk_init_default_state(led);870 if (ret)871 return dev_err_probe(dev, ret, "Failed to init %d isnk state\n", led->index);872 873 ret = devm_led_classdev_register_ext(dev, &led->isink, init_data);874 if (ret)875 return dev_err_probe(dev, ret, "Couldn't register isink %d\n", led->index);876 877 return 0;878}879 880static int mt6370_check_vendor_info(struct mt6370_priv *priv)881{882 unsigned int devinfo, vid;883 int ret;884 885 ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &devinfo);886 if (ret)887 return ret;888 889 vid = FIELD_GET(MT6370_VENDOR_ID_MASK, devinfo);890 if (vid == MT6372_VENDOR_ID || vid == MT6372C_VENDOR_ID) {891 priv->reg_fields = mt6372_reg_fields;892 priv->ranges = mt6372_led_ranges;893 priv->pdata = &mt6372_pdata;894 } else {895 /* Common for MT6370/71 */896 priv->reg_fields = common_reg_fields;897 priv->ranges = common_led_ranges;898 priv->pdata = &common_pdata;899 }900 901 return 0;902}903 904static int mt6370_leds_probe(struct platform_device *pdev)905{906 struct device *dev = &pdev->dev;907 struct mt6370_priv *priv;908 struct fwnode_handle *child;909 size_t count;910 unsigned int i = 0;911 int ret;912 913 count = device_get_child_node_count(dev);914 if (!count || count > MT6370_MAX_LEDS)915 return dev_err_probe(dev, -EINVAL,916 "No child node or node count over max LED number %zu\n",917 count);918 919 priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);920 if (!priv)921 return -ENOMEM;922 923 priv->leds_count = count;924 mutex_init(&priv->lock);925 926 priv->regmap = dev_get_regmap(dev->parent, NULL);927 if (!priv->regmap)928 return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n");929 930 ret = mt6370_check_vendor_info(priv);931 if (ret)932 return dev_err_probe(dev, ret, "Failed to check vendor info\n");933 934 ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->fields, priv->reg_fields,935 F_MAX_FIELDS);936 if (ret)937 return dev_err_probe(dev, ret, "Failed to allocate regmap field\n");938 939 device_for_each_child_node(dev, child) {940 struct mt6370_led *led = priv->leds + i++;941 struct led_init_data init_data = { .fwnode = child };942 u32 reg, color;943 944 ret = fwnode_property_read_u32(child, "reg", ®);945 if (ret) {946 dev_err(dev, "Failed to parse reg property\n");947 goto fwnode_release;948 }949 950 if (reg >= MT6370_MAX_LEDS) {951 ret = -EINVAL;952 dev_err(dev, "Error reg property number\n");953 goto fwnode_release;954 }955 956 ret = fwnode_property_read_u32(child, "color", &color);957 if (ret) {958 dev_err(dev, "Failed to parse color property\n");959 goto fwnode_release;960 }961 962 if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)963 reg = MT6370_VIRTUAL_MULTICOLOR;964 965 if (priv->leds_active & BIT(reg)) {966 ret = -EINVAL;967 dev_err(dev, "Duplicate reg property\n");968 goto fwnode_release;969 }970 971 priv->leds_active |= BIT(reg);972 973 led->index = reg;974 led->priv = priv;975 976 ret = mt6370_init_led_properties(dev, led, &init_data);977 if (ret)978 goto fwnode_release;979 980 ret = mt6370_led_register(dev, led, &init_data);981 if (ret)982 goto fwnode_release;983 }984 985 return 0;986 987fwnode_release:988 fwnode_handle_put(child);989 return ret;990}991 992static const struct of_device_id mt6370_rgbled_device_table[] = {993 { .compatible = "mediatek,mt6370-indicator" },994 {}995};996MODULE_DEVICE_TABLE(of, mt6370_rgbled_device_table);997 998static struct platform_driver mt6370_rgbled_driver = {999 .driver = {1000 .name = "mt6370-indicator",1001 .of_match_table = mt6370_rgbled_device_table,1002 },1003 .probe = mt6370_leds_probe,1004};1005module_platform_driver(mt6370_rgbled_driver);1006 1007MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>");1008MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");1009MODULE_DESCRIPTION("MediaTek MT6370 RGB LED Driver");1010MODULE_LICENSE("GPL");1011