1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * LED driver for Mediatek MT6323 PMIC 4 * 5 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 6 */ 7 #include <linux/kernel.h> 8 #include <linux/leds.h> 9 #include <linux/mfd/mt6323/registers.h> 10 #include <linux/mfd/mt6397/core.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/regmap.h> 15 16 /* 17 * Register field for TOP_CKPDN0 to enable 18 * 32K clock common for LED device. 19 */ 20 #define RG_DRV_32K_CK_PDN BIT(11) 21 #define RG_DRV_32K_CK_PDN_MASK BIT(11) 22 23 /* 32K/1M/6M clock common for WLED device */ 24 #define RG_VWLED_1M_CK_PDN BIT(0) 25 #define RG_VWLED_32K_CK_PDN BIT(12) 26 #define RG_VWLED_6M_CK_PDN BIT(13) 27 28 /* 29 * Register field for TOP_CKPDN2 to enable 30 * individual clock for LED device. 31 */ 32 #define RG_ISINK_CK_PDN(i) BIT(i) 33 #define RG_ISINK_CK_PDN_MASK(i) BIT(i) 34 35 /* 36 * Register field for TOP_CKCON1 to select 37 * clock source. 38 */ 39 #define RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i)) 40 41 #define ISINK_CON(r, i) (r + 0x8 * (i)) 42 43 /* ISINK_CON0: Register to setup the duty cycle of the blink. */ 44 #define ISINK_DIM_DUTY_MASK (0x1f << 8) 45 #define ISINK_DIM_DUTY(i) (((i) << 8) & ISINK_DIM_DUTY_MASK) 46 47 /* ISINK_CON1: Register to setup the period of the blink. */ 48 #define ISINK_DIM_FSEL_MASK (0xffff) 49 #define ISINK_DIM_FSEL(i) ((i) & ISINK_DIM_FSEL_MASK) 50 51 /* ISINK_CON2: Register to control the brightness. */ 52 #define ISINK_CH_STEP_SHIFT 12 53 #define ISINK_CH_STEP_MASK (0x7 << 12) 54 #define ISINK_CH_STEP(i) (((i) << 12) & ISINK_CH_STEP_MASK) 55 #define ISINK_SFSTR0_TC_MASK (0x3 << 1) 56 #define ISINK_SFSTR0_TC(i) (((i) << 1) & ISINK_SFSTR0_TC_MASK) 57 #define ISINK_SFSTR0_EN_MASK BIT(0) 58 #define ISINK_SFSTR0_EN BIT(0) 59 60 /* Register to LED channel enablement. */ 61 #define ISINK_CH_EN_MASK(i) BIT(i) 62 #define ISINK_CH_EN(i) BIT(i) 63 64 #define MAX_SUPPORTED_LEDS 8 65 66 struct mt6323_leds; 67 68 /** 69 * struct mt6323_led - state container for the LED device 70 * @id: the identifier in MT6323 LED device 71 * @parent: the pointer to MT6323 LED controller 72 * @cdev: LED class device for this LED device 73 * @current_brightness: current state of the LED device 74 */ 75 struct mt6323_led { 76 int id; 77 struct mt6323_leds *parent; 78 struct led_classdev cdev; 79 enum led_brightness current_brightness; 80 }; 81 82 /** 83 * struct mt6323_regs - register spec for the LED device 84 * @top_ckpdn: Offset to ISINK_CKPDN[0..x] registers 85 * @num_top_ckpdn: Number of ISINK_CKPDN registers 86 * @top_ckcon: Offset to ISINK_CKCON[0..x] registers 87 * @num_top_ckcon: Number of ISINK_CKCON registers 88 * @isink_con: Offset to ISINKx_CON[0..x] registers 89 * @num_isink_con: Number of ISINKx_CON registers 90 * @isink_max_regs: Number of ISINK[0..x] registers 91 * @isink_en_ctrl: Offset to ISINK_EN_CTRL register 92 * @iwled_en_ctrl: Offset to IWLED_EN_CTRL register 93 */ 94 struct mt6323_regs { 95 const u16 *top_ckpdn; 96 u8 num_top_ckpdn; 97 const u16 *top_ckcon; 98 u8 num_top_ckcon; 99 const u16 *isink_con; 100 u8 num_isink_con; 101 u8 isink_max_regs; 102 u16 isink_en_ctrl; 103 u16 iwled_en_ctrl; 104 }; 105 106 /** 107 * struct mt6323_hwspec - hardware specific parameters 108 * @max_period: Maximum period for all LEDs 109 * @max_leds: Maximum number of supported LEDs 110 * @max_wleds: Maximum number of WLEDs 111 * @max_brightness: Maximum brightness for all LEDs 112 * @unit_duty: Steps of duty per period 113 */ 114 struct mt6323_hwspec { 115 u16 max_period; 116 u8 max_leds; 117 u8 max_wleds; 118 u16 max_brightness; 119 u16 unit_duty; 120 }; 121 122 /** 123 * struct mt6323_data - device specific data 124 * @regs: Register spec for this device 125 * @spec: Hardware specific parameters 126 */ 127 struct mt6323_data { 128 const struct mt6323_regs *regs; 129 const struct mt6323_hwspec *spec; 130 }; 131 132 /** 133 * struct mt6323_leds - state container for holding LED controller 134 * of the driver 135 * @dev: the device pointer 136 * @hw: the underlying hardware providing shared 137 * bus for the register operations 138 * @pdata: device specific data 139 * @lock: the lock among process context 140 * @led: the array that contains the state of individual 141 * LED device 142 */ 143 struct mt6323_leds { 144 struct device *dev; 145 struct mt6397_chip *hw; 146 const struct mt6323_data *pdata; 147 /* protect among process context */ 148 struct mutex lock; 149 struct mt6323_led *led[MAX_SUPPORTED_LEDS]; 150 }; 151 152 static int mt6323_led_hw_brightness(struct led_classdev *cdev, 153 enum led_brightness brightness) 154 { 155 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 156 struct mt6323_leds *leds = led->parent; 157 const struct mt6323_regs *regs = leds->pdata->regs; 158 struct regmap *regmap = leds->hw->regmap; 159 u32 con2_mask = 0, con2_val = 0; 160 int ret; 161 162 /* 163 * Setup current output for the corresponding 164 * brightness level. 165 */ 166 con2_mask |= ISINK_CH_STEP_MASK | 167 ISINK_SFSTR0_TC_MASK | 168 ISINK_SFSTR0_EN_MASK; 169 con2_val |= ISINK_CH_STEP(brightness - 1) | 170 ISINK_SFSTR0_TC(2) | 171 ISINK_SFSTR0_EN; 172 173 ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[2], led->id), 174 con2_mask, con2_val); 175 return ret; 176 } 177 178 static int mt6323_led_hw_off(struct led_classdev *cdev) 179 { 180 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 181 struct mt6323_leds *leds = led->parent; 182 const struct mt6323_regs *regs = leds->pdata->regs; 183 struct regmap *regmap = leds->hw->regmap; 184 unsigned int status; 185 int ret; 186 187 status = ISINK_CH_EN(led->id); 188 ret = regmap_update_bits(regmap, regs->isink_en_ctrl, 189 ISINK_CH_EN_MASK(led->id), ~status); 190 if (ret < 0) 191 return ret; 192 193 usleep_range(100, 300); 194 ret = regmap_update_bits(regmap, regs->top_ckpdn[2], 195 RG_ISINK_CK_PDN_MASK(led->id), 196 RG_ISINK_CK_PDN(led->id)); 197 if (ret < 0) 198 return ret; 199 200 return 0; 201 } 202 203 static enum led_brightness 204 mt6323_get_led_hw_brightness(struct led_classdev *cdev) 205 { 206 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 207 struct mt6323_leds *leds = led->parent; 208 const struct mt6323_regs *regs = leds->pdata->regs; 209 struct regmap *regmap = leds->hw->regmap; 210 unsigned int status; 211 int ret; 212 213 ret = regmap_read(regmap, regs->top_ckpdn[2], &status); 214 if (ret < 0) 215 return ret; 216 217 if (status & RG_ISINK_CK_PDN_MASK(led->id)) 218 return 0; 219 220 ret = regmap_read(regmap, regs->isink_en_ctrl, &status); 221 if (ret < 0) 222 return ret; 223 224 if (!(status & ISINK_CH_EN(led->id))) 225 return 0; 226 227 ret = regmap_read(regmap, ISINK_CON(regs->isink_con[2], led->id), &status); 228 if (ret < 0) 229 return ret; 230 231 return ((status & ISINK_CH_STEP_MASK) 232 >> ISINK_CH_STEP_SHIFT) + 1; 233 } 234 235 static int mt6323_led_hw_on(struct led_classdev *cdev, 236 enum led_brightness brightness) 237 { 238 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 239 struct mt6323_leds *leds = led->parent; 240 const struct mt6323_regs *regs = leds->pdata->regs; 241 struct regmap *regmap = leds->hw->regmap; 242 unsigned int status; 243 int ret; 244 245 /* 246 * Setup required clock source, enable the corresponding 247 * clock and channel and let work with continuous blink as 248 * the default. 249 */ 250 ret = regmap_update_bits(regmap, regs->top_ckcon[1], 251 RG_ISINK_CK_SEL_MASK(led->id), 0); 252 if (ret < 0) 253 return ret; 254 255 status = RG_ISINK_CK_PDN(led->id); 256 ret = regmap_update_bits(regmap, regs->top_ckpdn[2], 257 RG_ISINK_CK_PDN_MASK(led->id), 258 ~status); 259 if (ret < 0) 260 return ret; 261 262 usleep_range(100, 300); 263 264 ret = regmap_update_bits(regmap, regs->isink_en_ctrl, 265 ISINK_CH_EN_MASK(led->id), 266 ISINK_CH_EN(led->id)); 267 if (ret < 0) 268 return ret; 269 270 ret = mt6323_led_hw_brightness(cdev, brightness); 271 if (ret < 0) 272 return ret; 273 274 ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[0], led->id), 275 ISINK_DIM_DUTY_MASK, 276 ISINK_DIM_DUTY(31)); 277 if (ret < 0) 278 return ret; 279 280 ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[1], led->id), 281 ISINK_DIM_FSEL_MASK, 282 ISINK_DIM_FSEL(1000)); 283 if (ret < 0) 284 return ret; 285 286 return 0; 287 } 288 289 static int mt6323_led_set_blink(struct led_classdev *cdev, 290 unsigned long *delay_on, 291 unsigned long *delay_off) 292 { 293 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 294 struct mt6323_leds *leds = led->parent; 295 const struct mt6323_regs *regs = leds->pdata->regs; 296 const struct mt6323_hwspec *spec = leds->pdata->spec; 297 struct regmap *regmap = leds->hw->regmap; 298 unsigned long period; 299 u8 duty_hw; 300 int ret; 301 302 /* 303 * LED subsystem requires a default user 304 * friendly blink pattern for the LED so using 305 * 1Hz duty cycle 50% here if without specific 306 * value delay_on and delay off being assigned. 307 */ 308 if (!*delay_on && !*delay_off) { 309 *delay_on = 500; 310 *delay_off = 500; 311 } 312 313 /* 314 * Units are in ms, if over the hardware able 315 * to support, fallback into software blink 316 */ 317 period = *delay_on + *delay_off; 318 319 if (period > spec->max_period) 320 return -EINVAL; 321 322 /* 323 * Calculate duty_hw based on the percentage of period during 324 * which the led is ON. 325 */ 326 duty_hw = DIV_ROUND_CLOSEST(*delay_on * 100000ul, period * spec->unit_duty); 327 328 /* hardware doesn't support zero duty cycle. */ 329 if (!duty_hw) 330 return -EINVAL; 331 332 mutex_lock(&leds->lock); 333 /* 334 * Set max_brightness as the software blink behavior 335 * when no blink brightness. 336 */ 337 if (!led->current_brightness) { 338 ret = mt6323_led_hw_on(cdev, cdev->max_brightness); 339 if (ret < 0) 340 goto out; 341 led->current_brightness = cdev->max_brightness; 342 } 343 344 ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[0], led->id), 345 ISINK_DIM_DUTY_MASK, 346 ISINK_DIM_DUTY(duty_hw - 1)); 347 if (ret < 0) 348 goto out; 349 350 ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[1], led->id), 351 ISINK_DIM_FSEL_MASK, 352 ISINK_DIM_FSEL(period - 1)); 353 out: 354 mutex_unlock(&leds->lock); 355 356 return ret; 357 } 358 359 static int mt6323_led_set_brightness(struct led_classdev *cdev, 360 enum led_brightness brightness) 361 { 362 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 363 struct mt6323_leds *leds = led->parent; 364 int ret; 365 366 mutex_lock(&leds->lock); 367 368 if (!led->current_brightness && brightness) { 369 ret = mt6323_led_hw_on(cdev, brightness); 370 if (ret < 0) 371 goto out; 372 } else if (brightness) { 373 ret = mt6323_led_hw_brightness(cdev, brightness); 374 if (ret < 0) 375 goto out; 376 } else { 377 ret = mt6323_led_hw_off(cdev); 378 if (ret < 0) 379 goto out; 380 } 381 382 led->current_brightness = brightness; 383 out: 384 mutex_unlock(&leds->lock); 385 386 return ret; 387 } 388 389 static int mtk_wled_hw_on(struct led_classdev *cdev) 390 { 391 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 392 struct mt6323_leds *leds = led->parent; 393 const struct mt6323_regs *regs = leds->pdata->regs; 394 struct regmap *regmap = leds->hw->regmap; 395 int ret; 396 397 ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_32K_CK_PDN); 398 if (ret) 399 return ret; 400 401 ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_6M_CK_PDN); 402 if (ret) 403 return ret; 404 405 ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_1M_CK_PDN); 406 if (ret) 407 return ret; 408 409 usleep_range(5000, 6000); 410 411 /* Enable WLED channel pair */ 412 ret = regmap_set_bits(regmap, regs->iwled_en_ctrl, BIT(led->id)); 413 if (ret) 414 return ret; 415 416 ret = regmap_set_bits(regmap, regs->iwled_en_ctrl, BIT(led->id + 1)); 417 if (ret) 418 return ret; 419 420 return 0; 421 } 422 423 static int mtk_wled_hw_off(struct led_classdev *cdev) 424 { 425 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 426 struct mt6323_leds *leds = led->parent; 427 const struct mt6323_regs *regs = leds->pdata->regs; 428 struct regmap *regmap = leds->hw->regmap; 429 int ret; 430 431 ret = regmap_clear_bits(regmap, regs->iwled_en_ctrl, BIT(led->id + 1)); 432 if (ret) 433 return ret; 434 435 ret = regmap_clear_bits(regmap, regs->iwled_en_ctrl, BIT(led->id)); 436 if (ret) 437 return ret; 438 439 ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_32K_CK_PDN); 440 if (ret) 441 return ret; 442 443 ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_6M_CK_PDN); 444 if (ret) 445 return ret; 446 447 ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_1M_CK_PDN); 448 if (ret) 449 return ret; 450 451 return 0; 452 } 453 454 static enum led_brightness mt6323_get_wled_brightness(struct led_classdev *cdev) 455 { 456 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 457 struct mt6323_leds *leds = led->parent; 458 const struct mt6323_regs *regs = leds->pdata->regs; 459 struct regmap *regmap = leds->hw->regmap; 460 unsigned int status; 461 int ret; 462 463 ret = regmap_read(regmap, regs->iwled_en_ctrl, &status); 464 if (ret) 465 return 0; 466 467 /* Always two channels per WLED */ 468 status &= BIT(led->id) | BIT(led->id + 1); 469 470 return status ? led->current_brightness : 0; 471 } 472 473 static int mt6323_wled_set_brightness(struct led_classdev *cdev, 474 enum led_brightness brightness) 475 { 476 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 477 struct mt6323_leds *leds = led->parent; 478 int ret = 0; 479 480 mutex_lock(&leds->lock); 481 482 if (brightness) { 483 if (!led->current_brightness) 484 ret = mtk_wled_hw_on(cdev); 485 if (ret) 486 goto out; 487 } else { 488 ret = mtk_wled_hw_off(cdev); 489 if (ret) 490 goto out; 491 } 492 493 led->current_brightness = brightness; 494 out: 495 mutex_unlock(&leds->lock); 496 497 return ret; 498 } 499 500 static int mt6323_led_set_dt_default(struct led_classdev *cdev, 501 struct device_node *np) 502 { 503 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 504 enum led_default_state state; 505 int ret = 0; 506 507 state = led_init_default_state_get(of_fwnode_handle(np)); 508 switch (state) { 509 case LEDS_DEFSTATE_ON: 510 ret = mt6323_led_set_brightness(cdev, cdev->max_brightness); 511 break; 512 case LEDS_DEFSTATE_KEEP: 513 ret = mt6323_get_led_hw_brightness(cdev); 514 if (ret < 0) 515 return ret; 516 led->current_brightness = ret; 517 ret = 0; 518 break; 519 default: 520 ret = mt6323_led_set_brightness(cdev, LED_OFF); 521 } 522 523 return ret; 524 } 525 526 static int mt6323_led_probe(struct platform_device *pdev) 527 { 528 struct device *dev = &pdev->dev; 529 struct device_node *np = dev_of_node(dev); 530 struct mt6397_chip *hw = dev_get_drvdata(dev->parent); 531 struct mt6323_leds *leds; 532 struct mt6323_led *led; 533 const struct mt6323_regs *regs; 534 const struct mt6323_hwspec *spec; 535 int ret; 536 unsigned int status; 537 u32 reg; 538 u8 max_leds; 539 540 leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); 541 if (!leds) 542 return -ENOMEM; 543 544 platform_set_drvdata(pdev, leds); 545 leds->dev = dev; 546 leds->pdata = device_get_match_data(dev); 547 regs = leds->pdata->regs; 548 spec = leds->pdata->spec; 549 max_leds = spec->max_leds + spec->max_wleds; 550 551 /* 552 * leds->hw points to the underlying bus for the register 553 * controlled. 554 */ 555 leds->hw = hw; 556 mutex_init(&leds->lock); 557 558 status = RG_DRV_32K_CK_PDN; 559 ret = regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0], 560 RG_DRV_32K_CK_PDN_MASK, ~status); 561 if (ret < 0) { 562 dev_err(leds->dev, 563 "Failed to update TOP_CKPDN0 Register\n"); 564 return ret; 565 } 566 567 for_each_available_child_of_node_scoped(np, child) { 568 struct led_init_data init_data = {}; 569 bool is_wled; 570 571 ret = of_property_read_u32(child, "reg", ®); 572 if (ret) { 573 dev_err(dev, "Failed to read led 'reg' property\n"); 574 return ret; 575 } 576 577 if (reg >= max_leds || reg >= MAX_SUPPORTED_LEDS || 578 leds->led[reg]) { 579 dev_err(dev, "Invalid led reg %u\n", reg); 580 return -EINVAL; 581 } 582 583 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 584 if (!led) 585 return -ENOMEM; 586 587 is_wled = of_property_read_bool(child, "mediatek,is-wled"); 588 589 leds->led[reg] = led; 590 leds->led[reg]->id = reg; 591 leds->led[reg]->cdev.max_brightness = spec->max_brightness; 592 593 if (is_wled) { 594 leds->led[reg]->cdev.brightness_set_blocking = 595 mt6323_wled_set_brightness; 596 leds->led[reg]->cdev.brightness_get = 597 mt6323_get_wled_brightness; 598 } else { 599 leds->led[reg]->cdev.brightness_set_blocking = 600 mt6323_led_set_brightness; 601 leds->led[reg]->cdev.blink_set = mt6323_led_set_blink; 602 leds->led[reg]->cdev.brightness_get = 603 mt6323_get_led_hw_brightness; 604 } 605 leds->led[reg]->parent = leds; 606 607 ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child); 608 if (ret < 0) { 609 dev_err(leds->dev, 610 "Failed to LED set default from devicetree\n"); 611 return ret; 612 } 613 614 init_data.fwnode = of_fwnode_handle(child); 615 616 ret = devm_led_classdev_register_ext(dev, &leds->led[reg]->cdev, 617 &init_data); 618 if (ret) { 619 dev_err(dev, "Failed to register LED: %d\n", ret); 620 return ret; 621 } 622 } 623 624 return 0; 625 } 626 627 static void mt6323_led_remove(struct platform_device *pdev) 628 { 629 struct mt6323_leds *leds = platform_get_drvdata(pdev); 630 const struct mt6323_regs *regs = leds->pdata->regs; 631 int i; 632 633 /* Turn the LEDs off on driver removal. */ 634 for (i = 0 ; leds->led[i] ; i++) 635 mt6323_led_hw_off(&leds->led[i]->cdev); 636 637 regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0], 638 RG_DRV_32K_CK_PDN_MASK, 639 RG_DRV_32K_CK_PDN); 640 641 mutex_destroy(&leds->lock); 642 } 643 644 static const struct mt6323_regs mt6323_registers = { 645 .top_ckpdn = (const u16[]){ 0x102, 0x106, 0x10e }, 646 .num_top_ckpdn = 3, 647 .top_ckcon = (const u16[]){ 0x120, 0x126 }, 648 .num_top_ckcon = 2, 649 .isink_con = (const u16[]){ 0x330, 0x332, 0x334 }, 650 .num_isink_con = 3, 651 .isink_max_regs = 4, /* ISINK[0..3] */ 652 .isink_en_ctrl = 0x356, 653 }; 654 655 static const struct mt6323_regs mt6331_registers = { 656 .top_ckpdn = (const u16[]){ 0x138, 0x13e, 0x144 }, 657 .num_top_ckpdn = 3, 658 .top_ckcon = (const u16[]){ 0x14c, 0x14a }, 659 .num_top_ckcon = 2, 660 .isink_con = (const u16[]){ 0x40c, 0x40e, 0x410, 0x412, 0x414 }, 661 .num_isink_con = 5, 662 .isink_max_regs = 4, /* ISINK[0..3] */ 663 .isink_en_ctrl = 0x43a, 664 }; 665 666 static const struct mt6323_regs mt6332_registers = { 667 .top_ckpdn = (const u16[]){ 0x8094, 0x809a, 0x80a0 }, 668 .num_top_ckpdn = 3, 669 .top_ckcon = (const u16[]){ 0x80a6, 0x80ac }, 670 .num_top_ckcon = 2, 671 .isink_con = (const u16[]){ 0x8cd4 }, 672 .num_isink_con = 1, 673 .isink_max_regs = 12, /* IWLED[0..2, 3..9] */ 674 .iwled_en_ctrl = 0x8cda, 675 }; 676 677 static const struct mt6323_hwspec mt6323_spec = { 678 .max_period = 10000, 679 .max_leds = 4, 680 .max_brightness = 6, 681 .unit_duty = 3125, 682 }; 683 684 static const struct mt6323_hwspec mt6332_spec = { 685 /* There are no LEDs in MT6332. Only WLEDs are present. */ 686 .max_leds = 0, 687 .max_wleds = 1, 688 .max_brightness = 1024, 689 }; 690 691 static const struct mt6323_data mt6323_pdata = { 692 .regs = &mt6323_registers, 693 .spec = &mt6323_spec, 694 }; 695 696 static const struct mt6323_data mt6331_pdata = { 697 .regs = &mt6331_registers, 698 .spec = &mt6323_spec, 699 }; 700 701 static const struct mt6323_data mt6332_pdata = { 702 .regs = &mt6332_registers, 703 .spec = &mt6332_spec, 704 }; 705 706 static const struct of_device_id mt6323_led_dt_match[] = { 707 { .compatible = "mediatek,mt6323-led", .data = &mt6323_pdata}, 708 { .compatible = "mediatek,mt6331-led", .data = &mt6331_pdata }, 709 { .compatible = "mediatek,mt6332-led", .data = &mt6332_pdata }, 710 {}, 711 }; 712 MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); 713 714 static struct platform_driver mt6323_led_driver = { 715 .probe = mt6323_led_probe, 716 .remove_new = mt6323_led_remove, 717 .driver = { 718 .name = "mt6323-led", 719 .of_match_table = mt6323_led_dt_match, 720 }, 721 }; 722 723 module_platform_driver(mt6323_led_driver); 724 725 MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC"); 726 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 727 MODULE_LICENSE("GPL"); 728