linux/drivers/video/backlight/qcom-wled.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (c) 2015, Sony Mobile Communications, AB.
   3 */
   4
   5#include <linux/delay.h>
   6#include <linux/interrupt.h>
   7#include <linux/ktime.h>
   8#include <linux/kernel.h>
   9#include <linux/backlight.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_device.h>
  13#include <linux/of_address.h>
  14#include <linux/regmap.h>
  15
  16/* From DT binding */
  17#define WLED_MAX_STRINGS                                4
  18
  19#define WLED_DEFAULT_BRIGHTNESS                         2048
  20#define WLED_SOFT_START_DLY_US                          10000
  21#define WLED3_SINK_REG_BRIGHT_MAX                       0xFFF
  22
  23/* WLED3/WLED4 control registers */
  24#define WLED3_CTRL_REG_FAULT_STATUS                     0x08
  25#define  WLED3_CTRL_REG_ILIM_FAULT_BIT                  BIT(0)
  26#define  WLED3_CTRL_REG_OVP_FAULT_BIT                   BIT(1)
  27#define  WLED4_CTRL_REG_SC_FAULT_BIT                    BIT(2)
  28
  29#define WLED3_CTRL_REG_INT_RT_STS                       0x10
  30#define  WLED3_CTRL_REG_OVP_FAULT_STATUS                BIT(1)
  31
  32#define WLED3_CTRL_REG_MOD_EN                           0x46
  33#define  WLED3_CTRL_REG_MOD_EN_MASK                     BIT(7)
  34#define  WLED3_CTRL_REG_MOD_EN_SHIFT                    7
  35
  36#define WLED3_CTRL_REG_FEEDBACK_CONTROL                 0x48
  37
  38#define WLED3_CTRL_REG_FREQ                             0x4c
  39#define  WLED3_CTRL_REG_FREQ_MASK                       GENMASK(3, 0)
  40
  41#define WLED3_CTRL_REG_OVP                              0x4d
  42#define  WLED3_CTRL_REG_OVP_MASK                        GENMASK(1, 0)
  43
  44#define WLED3_CTRL_REG_ILIMIT                           0x4e
  45#define  WLED3_CTRL_REG_ILIMIT_MASK                     GENMASK(2, 0)
  46
  47/* WLED3/WLED4 sink registers */
  48#define WLED3_SINK_REG_SYNC                             0x47
  49#define  WLED3_SINK_REG_SYNC_CLEAR                      0x00
  50
  51#define WLED3_SINK_REG_CURR_SINK                        0x4f
  52#define  WLED3_SINK_REG_CURR_SINK_MASK                  GENMASK(7, 5)
  53#define  WLED3_SINK_REG_CURR_SINK_SHFT                  5
  54
  55/* WLED3 specific per-'string' registers below */
  56#define WLED3_SINK_REG_BRIGHT(n)                        (0x40 + n)
  57
  58#define WLED3_SINK_REG_STR_MOD_EN(n)                    (0x60 + (n * 0x10))
  59#define  WLED3_SINK_REG_STR_MOD_MASK                    BIT(7)
  60
  61#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)           (0x62 + (n * 0x10))
  62#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK        GENMASK(4, 0)
  63
  64#define WLED3_SINK_REG_STR_MOD_SRC(n)                   (0x63 + (n * 0x10))
  65#define  WLED3_SINK_REG_STR_MOD_SRC_MASK                BIT(0)
  66#define  WLED3_SINK_REG_STR_MOD_SRC_INT                 0x00
  67#define  WLED3_SINK_REG_STR_MOD_SRC_EXT                 0x01
  68
  69#define WLED3_SINK_REG_STR_CABC(n)                      (0x66 + (n * 0x10))
  70#define  WLED3_SINK_REG_STR_CABC_MASK                   BIT(7)
  71
  72/* WLED4 specific control registers */
  73#define WLED4_CTRL_REG_SHORT_PROTECT                    0x5e
  74#define  WLED4_CTRL_REG_SHORT_EN_MASK                   BIT(7)
  75
  76#define WLED4_CTRL_REG_SEC_ACCESS                       0xd0
  77#define  WLED4_CTRL_REG_SEC_UNLOCK                      0xa5
  78
  79#define WLED4_CTRL_REG_TEST1                            0xe2
  80#define  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2            0x09
  81
  82/* WLED4 specific sink registers */
  83#define WLED4_SINK_REG_CURR_SINK                        0x46
  84#define  WLED4_SINK_REG_CURR_SINK_MASK                  GENMASK(7, 4)
  85#define  WLED4_SINK_REG_CURR_SINK_SHFT                  4
  86
  87/* WLED4 specific per-'string' registers below */
  88#define WLED4_SINK_REG_STR_MOD_EN(n)                    (0x50 + (n * 0x10))
  89#define  WLED4_SINK_REG_STR_MOD_MASK                    BIT(7)
  90
  91#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n)           (0x52 + (n * 0x10))
  92#define  WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK        GENMASK(3, 0)
  93
  94#define WLED4_SINK_REG_STR_MOD_SRC(n)                   (0x53 + (n * 0x10))
  95#define  WLED4_SINK_REG_STR_MOD_SRC_MASK                BIT(0)
  96#define  WLED4_SINK_REG_STR_MOD_SRC_INT                 0x00
  97#define  WLED4_SINK_REG_STR_MOD_SRC_EXT                 0x01
  98
  99#define WLED4_SINK_REG_STR_CABC(n)                      (0x56 + (n * 0x10))
 100#define  WLED4_SINK_REG_STR_CABC_MASK                   BIT(7)
 101
 102#define WLED4_SINK_REG_BRIGHT(n)                        (0x57 + (n * 0x10))
 103
 104struct wled_var_cfg {
 105        const u32 *values;
 106        u32 (*fn)(u32);
 107        int size;
 108};
 109
 110struct wled_u32_opts {
 111        const char *name;
 112        u32 *val_ptr;
 113        const struct wled_var_cfg *cfg;
 114};
 115
 116struct wled_bool_opts {
 117        const char *name;
 118        bool *val_ptr;
 119};
 120
 121struct wled_config {
 122        u32 boost_i_limit;
 123        u32 ovp;
 124        u32 switch_freq;
 125        u32 num_strings;
 126        u32 string_i_limit;
 127        u32 enabled_strings[WLED_MAX_STRINGS];
 128        bool cs_out_en;
 129        bool ext_gen;
 130        bool cabc;
 131        bool external_pfet;
 132        bool auto_detection_enabled;
 133};
 134
 135struct wled {
 136        const char *name;
 137        struct device *dev;
 138        struct regmap *regmap;
 139        struct mutex lock;      /* Lock to avoid race from thread irq handler */
 140        ktime_t last_short_event;
 141        ktime_t start_ovp_fault_time;
 142        u16 ctrl_addr;
 143        u16 sink_addr;
 144        u16 max_string_count;
 145        u16 auto_detection_ovp_count;
 146        u32 brightness;
 147        u32 max_brightness;
 148        u32 short_count;
 149        u32 auto_detect_count;
 150        bool disabled_by_short;
 151        bool has_short_detect;
 152        int short_irq;
 153        int ovp_irq;
 154
 155        struct wled_config cfg;
 156        struct delayed_work ovp_work;
 157        int (*wled_set_brightness)(struct wled *wled, u16 brightness);
 158};
 159
 160static int wled3_set_brightness(struct wled *wled, u16 brightness)
 161{
 162        int rc, i;
 163        u8 v[2];
 164
 165        v[0] = brightness & 0xff;
 166        v[1] = (brightness >> 8) & 0xf;
 167
 168        for (i = 0;  i < wled->cfg.num_strings; ++i) {
 169                rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
 170                                       WLED3_SINK_REG_BRIGHT(i), v, 2);
 171                if (rc < 0)
 172                        return rc;
 173        }
 174
 175        return 0;
 176}
 177
 178static int wled4_set_brightness(struct wled *wled, u16 brightness)
 179{
 180        int rc, i;
 181        u16 low_limit = wled->max_brightness * 4 / 1000;
 182        u8 v[2];
 183
 184        /* WLED4's lower limit of operation is 0.4% */
 185        if (brightness > 0 && brightness < low_limit)
 186                brightness = low_limit;
 187
 188        v[0] = brightness & 0xff;
 189        v[1] = (brightness >> 8) & 0xf;
 190
 191        for (i = 0;  i < wled->cfg.num_strings; ++i) {
 192                rc = regmap_bulk_write(wled->regmap, wled->sink_addr +
 193                                       WLED4_SINK_REG_BRIGHT(i), v, 2);
 194                if (rc < 0)
 195                        return rc;
 196        }
 197
 198        return 0;
 199}
 200
 201static void wled_ovp_work(struct work_struct *work)
 202{
 203        struct wled *wled = container_of(work,
 204                                         struct wled, ovp_work.work);
 205        enable_irq(wled->ovp_irq);
 206}
 207
 208static int wled_module_enable(struct wled *wled, int val)
 209{
 210        int rc;
 211
 212        if (wled->disabled_by_short)
 213                return -ENXIO;
 214
 215        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 216                                WLED3_CTRL_REG_MOD_EN,
 217                                WLED3_CTRL_REG_MOD_EN_MASK,
 218                                val << WLED3_CTRL_REG_MOD_EN_SHIFT);
 219        if (rc < 0)
 220                return rc;
 221
 222        if (wled->ovp_irq > 0) {
 223                if (val) {
 224                        /*
 225                         * The hardware generates a storm of spurious OVP
 226                         * interrupts during soft start operations. So defer
 227                         * enabling the IRQ for 10ms to ensure that the
 228                         * soft start is complete.
 229                         */
 230                        schedule_delayed_work(&wled->ovp_work, HZ / 100);
 231                } else {
 232                        if (!cancel_delayed_work_sync(&wled->ovp_work))
 233                                disable_irq(wled->ovp_irq);
 234                }
 235        }
 236
 237        return 0;
 238}
 239
 240static int wled_sync_toggle(struct wled *wled)
 241{
 242        int rc;
 243        unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
 244
 245        rc = regmap_update_bits(wled->regmap,
 246                                wled->ctrl_addr + WLED3_SINK_REG_SYNC,
 247                                mask, mask);
 248        if (rc < 0)
 249                return rc;
 250
 251        rc = regmap_update_bits(wled->regmap,
 252                                wled->ctrl_addr + WLED3_SINK_REG_SYNC,
 253                                mask, WLED3_SINK_REG_SYNC_CLEAR);
 254
 255        return rc;
 256}
 257
 258static int wled_update_status(struct backlight_device *bl)
 259{
 260        struct wled *wled = bl_get_data(bl);
 261        u16 brightness = bl->props.brightness;
 262        int rc = 0;
 263
 264        if (bl->props.power != FB_BLANK_UNBLANK ||
 265            bl->props.fb_blank != FB_BLANK_UNBLANK ||
 266            bl->props.state & BL_CORE_FBBLANK)
 267                brightness = 0;
 268
 269        mutex_lock(&wled->lock);
 270        if (brightness) {
 271                rc = wled->wled_set_brightness(wled, brightness);
 272                if (rc < 0) {
 273                        dev_err(wled->dev, "wled failed to set brightness rc:%d\n",
 274                                rc);
 275                        goto unlock_mutex;
 276                }
 277
 278                rc = wled_sync_toggle(wled);
 279                if (rc < 0) {
 280                        dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
 281                        goto unlock_mutex;
 282                }
 283        }
 284
 285        if (!!brightness != !!wled->brightness) {
 286                rc = wled_module_enable(wled, !!brightness);
 287                if (rc < 0) {
 288                        dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
 289                        goto unlock_mutex;
 290                }
 291        }
 292
 293        wled->brightness = brightness;
 294
 295unlock_mutex:
 296        mutex_unlock(&wled->lock);
 297
 298        return rc;
 299}
 300
 301#define WLED_SHORT_DLY_MS                       20
 302#define WLED_SHORT_CNT_MAX                      5
 303#define WLED_SHORT_RESET_CNT_DLY_US             USEC_PER_SEC
 304
 305static irqreturn_t wled_short_irq_handler(int irq, void *_wled)
 306{
 307        struct wled *wled = _wled;
 308        int rc;
 309        s64 elapsed_time;
 310
 311        wled->short_count++;
 312        mutex_lock(&wled->lock);
 313        rc = wled_module_enable(wled, false);
 314        if (rc < 0) {
 315                dev_err(wled->dev, "wled disable failed rc:%d\n", rc);
 316                goto unlock_mutex;
 317        }
 318
 319        elapsed_time = ktime_us_delta(ktime_get(),
 320                                      wled->last_short_event);
 321        if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US)
 322                wled->short_count = 1;
 323
 324        if (wled->short_count > WLED_SHORT_CNT_MAX) {
 325                dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n",
 326                        wled->short_count);
 327                wled->disabled_by_short = true;
 328                goto unlock_mutex;
 329        }
 330
 331        wled->last_short_event = ktime_get();
 332
 333        msleep(WLED_SHORT_DLY_MS);
 334        rc = wled_module_enable(wled, true);
 335        if (rc < 0)
 336                dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
 337
 338unlock_mutex:
 339        mutex_unlock(&wled->lock);
 340
 341        return IRQ_HANDLED;
 342}
 343
 344#define AUTO_DETECT_BRIGHTNESS          200
 345
 346static void wled_auto_string_detection(struct wled *wled)
 347{
 348        int rc = 0, i;
 349        u32 sink_config = 0, int_sts;
 350        u8 sink_test = 0, sink_valid = 0, val;
 351
 352        /* Read configured sink configuration */
 353        rc = regmap_read(wled->regmap, wled->sink_addr +
 354                         WLED4_SINK_REG_CURR_SINK, &sink_config);
 355        if (rc < 0) {
 356                dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n",
 357                        rc);
 358                goto failed_detect;
 359        }
 360
 361        /* Disable the module before starting detection */
 362        rc = regmap_update_bits(wled->regmap,
 363                                wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 364                                WLED3_CTRL_REG_MOD_EN_MASK, 0);
 365        if (rc < 0) {
 366                dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc);
 367                goto failed_detect;
 368        }
 369
 370        /* Set low brightness across all sinks */
 371        rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS);
 372        if (rc < 0) {
 373                dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n",
 374                        rc);
 375                goto failed_detect;
 376        }
 377
 378        if (wled->cfg.cabc) {
 379                for (i = 0; i < wled->cfg.num_strings; i++) {
 380                        rc = regmap_update_bits(wled->regmap, wled->sink_addr +
 381                                                WLED4_SINK_REG_STR_CABC(i),
 382                                                WLED4_SINK_REG_STR_CABC_MASK,
 383                                                0);
 384                        if (rc < 0)
 385                                goto failed_detect;
 386                }
 387        }
 388
 389        /* Disable all sinks */
 390        rc = regmap_write(wled->regmap,
 391                          wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0);
 392        if (rc < 0) {
 393                dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc);
 394                goto failed_detect;
 395        }
 396
 397        /* Iterate through the strings one by one */
 398        for (i = 0; i < wled->cfg.num_strings; i++) {
 399                sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + i));
 400
 401                /* Enable feedback control */
 402                rc = regmap_write(wled->regmap, wled->ctrl_addr +
 403                                  WLED3_CTRL_REG_FEEDBACK_CONTROL, i + 1);
 404                if (rc < 0) {
 405                        dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n",
 406                                i + 1, rc);
 407                        goto failed_detect;
 408                }
 409
 410                /* Enable the sink */
 411                rc = regmap_write(wled->regmap, wled->sink_addr +
 412                                  WLED4_SINK_REG_CURR_SINK, sink_test);
 413                if (rc < 0) {
 414                        dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n",
 415                                i + 1, rc);
 416                        goto failed_detect;
 417                }
 418
 419                /* Enable the module */
 420                rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 421                                        WLED3_CTRL_REG_MOD_EN,
 422                                        WLED3_CTRL_REG_MOD_EN_MASK,
 423                                        WLED3_CTRL_REG_MOD_EN_MASK);
 424                if (rc < 0) {
 425                        dev_err(wled->dev, "Failed to enable WLED module rc=%d\n",
 426                                rc);
 427                        goto failed_detect;
 428                }
 429
 430                usleep_range(WLED_SOFT_START_DLY_US,
 431                             WLED_SOFT_START_DLY_US + 1000);
 432
 433                rc = regmap_read(wled->regmap, wled->ctrl_addr +
 434                                 WLED3_CTRL_REG_INT_RT_STS, &int_sts);
 435                if (rc < 0) {
 436                        dev_err(wled->dev, "Error in reading WLED3_CTRL_INT_RT_STS rc=%d\n",
 437                                rc);
 438                        goto failed_detect;
 439                }
 440
 441                if (int_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
 442                        dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n",
 443                                i + 1);
 444                else
 445                        sink_valid |= sink_test;
 446
 447                /* Disable the module */
 448                rc = regmap_update_bits(wled->regmap,
 449                                        wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 450                                        WLED3_CTRL_REG_MOD_EN_MASK, 0);
 451                if (rc < 0) {
 452                        dev_err(wled->dev, "Failed to disable WLED module rc=%d\n",
 453                                rc);
 454                        goto failed_detect;
 455                }
 456        }
 457
 458        if (!sink_valid) {
 459                dev_err(wled->dev, "No valid WLED sinks found\n");
 460                wled->disabled_by_short = true;
 461                goto failed_detect;
 462        }
 463
 464        if (sink_valid != sink_config) {
 465                dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n",
 466                         sink_config, sink_valid);
 467                sink_config = sink_valid;
 468        }
 469
 470        /* Write the new sink configuration */
 471        rc = regmap_write(wled->regmap,
 472                          wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
 473                          sink_config);
 474        if (rc < 0) {
 475                dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n",
 476                        rc);
 477                goto failed_detect;
 478        }
 479
 480        /* Enable valid sinks */
 481        for (i = 0; i < wled->cfg.num_strings; i++) {
 482                if (wled->cfg.cabc) {
 483                        rc = regmap_update_bits(wled->regmap, wled->sink_addr +
 484                                                WLED4_SINK_REG_STR_CABC(i),
 485                                                WLED4_SINK_REG_STR_CABC_MASK,
 486                                                WLED4_SINK_REG_STR_CABC_MASK);
 487                        if (rc < 0)
 488                                goto failed_detect;
 489                }
 490
 491                if (sink_config & BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i))
 492                        val = WLED4_SINK_REG_STR_MOD_MASK;
 493                else
 494                        val = 0x0; /* Disable modulator_en for unused sink */
 495
 496                rc = regmap_write(wled->regmap, wled->sink_addr +
 497                                  WLED4_SINK_REG_STR_MOD_EN(i), val);
 498                if (rc < 0) {
 499                        dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n",
 500                                rc);
 501                        goto failed_detect;
 502                }
 503        }
 504
 505        /* Restore the feedback setting */
 506        rc = regmap_write(wled->regmap,
 507                          wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0);
 508        if (rc < 0) {
 509                dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n",
 510                        rc);
 511                goto failed_detect;
 512        }
 513
 514        /* Restore brightness */
 515        rc = wled4_set_brightness(wled, wled->brightness);
 516        if (rc < 0) {
 517                dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n",
 518                        rc);
 519                goto failed_detect;
 520        }
 521
 522        rc = regmap_update_bits(wled->regmap,
 523                                wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 524                                WLED3_CTRL_REG_MOD_EN_MASK,
 525                                WLED3_CTRL_REG_MOD_EN_MASK);
 526        if (rc < 0) {
 527                dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc);
 528                goto failed_detect;
 529        }
 530
 531failed_detect:
 532        return;
 533}
 534
 535#define WLED_AUTO_DETECT_OVP_COUNT              5
 536#define WLED_AUTO_DETECT_CNT_DLY_US             USEC_PER_SEC
 537static bool wled_auto_detection_required(struct wled *wled)
 538{
 539        s64 elapsed_time_us;
 540
 541        if (!wled->cfg.auto_detection_enabled)
 542                return false;
 543
 544        /*
 545         * Check if the OVP fault was an occasional one
 546         * or if it's firing continuously, the latter qualifies
 547         * for an auto-detection check.
 548         */
 549        if (!wled->auto_detection_ovp_count) {
 550                wled->start_ovp_fault_time = ktime_get();
 551                wled->auto_detection_ovp_count++;
 552        } else {
 553                elapsed_time_us = ktime_us_delta(ktime_get(),
 554                                                 wled->start_ovp_fault_time);
 555                if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US)
 556                        wled->auto_detection_ovp_count = 0;
 557                else
 558                        wled->auto_detection_ovp_count++;
 559
 560                if (wled->auto_detection_ovp_count >=
 561                                WLED_AUTO_DETECT_OVP_COUNT) {
 562                        wled->auto_detection_ovp_count = 0;
 563                        return true;
 564                }
 565        }
 566
 567        return false;
 568}
 569
 570static int wled_auto_detection_at_init(struct wled *wled)
 571{
 572        int rc;
 573        u32 fault_status, rt_status;
 574
 575        if (!wled->cfg.auto_detection_enabled)
 576                return 0;
 577
 578        rc = regmap_read(wled->regmap,
 579                         wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
 580                         &rt_status);
 581        if (rc < 0) {
 582                dev_err(wled->dev, "Failed to read RT status rc=%d\n", rc);
 583                return rc;
 584        }
 585
 586        rc = regmap_read(wled->regmap,
 587                         wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
 588                         &fault_status);
 589        if (rc < 0) {
 590                dev_err(wled->dev, "Failed to read fault status rc=%d\n", rc);
 591                return rc;
 592        }
 593
 594        if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) ||
 595            (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) {
 596                mutex_lock(&wled->lock);
 597                wled_auto_string_detection(wled);
 598                mutex_unlock(&wled->lock);
 599        }
 600
 601        return rc;
 602}
 603
 604static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
 605{
 606        struct wled *wled = _wled;
 607        int rc;
 608        u32 int_sts, fault_sts;
 609
 610        rc = regmap_read(wled->regmap,
 611                         wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts);
 612        if (rc < 0) {
 613                dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n",
 614                        rc);
 615                return IRQ_HANDLED;
 616        }
 617
 618        rc = regmap_read(wled->regmap, wled->ctrl_addr +
 619                         WLED3_CTRL_REG_FAULT_STATUS, &fault_sts);
 620        if (rc < 0) {
 621                dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n",
 622                        rc);
 623                return IRQ_HANDLED;
 624        }
 625
 626        if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
 627                WLED3_CTRL_REG_ILIM_FAULT_BIT))
 628                dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
 629                        int_sts, fault_sts);
 630
 631        if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) {
 632                if (wled_auto_detection_required(wled)) {
 633                        mutex_lock(&wled->lock);
 634                        wled_auto_string_detection(wled);
 635                        mutex_unlock(&wled->lock);
 636                }
 637        }
 638
 639        return IRQ_HANDLED;
 640}
 641
 642static int wled3_setup(struct wled *wled)
 643{
 644        u16 addr;
 645        u8 sink_en = 0;
 646        int rc, i, j;
 647
 648        rc = regmap_update_bits(wled->regmap,
 649                                wled->ctrl_addr + WLED3_CTRL_REG_OVP,
 650                                WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
 651        if (rc)
 652                return rc;
 653
 654        rc = regmap_update_bits(wled->regmap,
 655                                wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
 656                                WLED3_CTRL_REG_ILIMIT_MASK,
 657                                wled->cfg.boost_i_limit);
 658        if (rc)
 659                return rc;
 660
 661        rc = regmap_update_bits(wled->regmap,
 662                                wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
 663                                WLED3_CTRL_REG_FREQ_MASK,
 664                                wled->cfg.switch_freq);
 665        if (rc)
 666                return rc;
 667
 668        for (i = 0; i < wled->cfg.num_strings; ++i) {
 669                j = wled->cfg.enabled_strings[i];
 670                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j);
 671                rc = regmap_update_bits(wled->regmap, addr,
 672                                        WLED3_SINK_REG_STR_MOD_MASK,
 673                                        WLED3_SINK_REG_STR_MOD_MASK);
 674                if (rc)
 675                        return rc;
 676
 677                if (wled->cfg.ext_gen) {
 678                        addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j);
 679                        rc = regmap_update_bits(wled->regmap, addr,
 680                                                WLED3_SINK_REG_STR_MOD_SRC_MASK,
 681                                                WLED3_SINK_REG_STR_MOD_SRC_EXT);
 682                        if (rc)
 683                                return rc;
 684                }
 685
 686                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j);
 687                rc = regmap_update_bits(wled->regmap, addr,
 688                                        WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
 689                                        wled->cfg.string_i_limit);
 690                if (rc)
 691                        return rc;
 692
 693                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j);
 694                rc = regmap_update_bits(wled->regmap, addr,
 695                                        WLED3_SINK_REG_STR_CABC_MASK,
 696                                        wled->cfg.cabc ?
 697                                        WLED3_SINK_REG_STR_CABC_MASK : 0);
 698                if (rc)
 699                        return rc;
 700
 701                sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
 702        }
 703
 704        rc = regmap_update_bits(wled->regmap,
 705                                wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK,
 706                                WLED3_SINK_REG_CURR_SINK_MASK, sink_en);
 707        if (rc)
 708                return rc;
 709
 710        return 0;
 711}
 712
 713static const struct wled_config wled3_config_defaults = {
 714        .boost_i_limit = 3,
 715        .string_i_limit = 20,
 716        .ovp = 2,
 717        .num_strings = 3,
 718        .switch_freq = 5,
 719        .cs_out_en = false,
 720        .ext_gen = false,
 721        .cabc = false,
 722        .enabled_strings = {0, 1, 2, 3},
 723};
 724
 725static int wled4_setup(struct wled *wled)
 726{
 727        int rc, temp, i, j;
 728        u16 addr;
 729        u8 sink_en = 0;
 730        u32 sink_cfg;
 731
 732        rc = regmap_update_bits(wled->regmap,
 733                                wled->ctrl_addr + WLED3_CTRL_REG_OVP,
 734                                WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
 735        if (rc < 0)
 736                return rc;
 737
 738        rc = regmap_update_bits(wled->regmap,
 739                                wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
 740                                WLED3_CTRL_REG_ILIMIT_MASK,
 741                                wled->cfg.boost_i_limit);
 742        if (rc < 0)
 743                return rc;
 744
 745        rc = regmap_update_bits(wled->regmap,
 746                                wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
 747                                WLED3_CTRL_REG_FREQ_MASK,
 748                                wled->cfg.switch_freq);
 749        if (rc < 0)
 750                return rc;
 751
 752        if (wled->cfg.external_pfet) {
 753                /* Unlock the secure register access */
 754                rc = regmap_write(wled->regmap, wled->ctrl_addr +
 755                                  WLED4_CTRL_REG_SEC_ACCESS,
 756                                  WLED4_CTRL_REG_SEC_UNLOCK);
 757                if (rc < 0)
 758                        return rc;
 759
 760                rc = regmap_write(wled->regmap,
 761                                  wled->ctrl_addr + WLED4_CTRL_REG_TEST1,
 762                                  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2);
 763                if (rc < 0)
 764                        return rc;
 765        }
 766
 767        rc = regmap_read(wled->regmap, wled->sink_addr +
 768                         WLED4_SINK_REG_CURR_SINK, &sink_cfg);
 769        if (rc < 0)
 770                return rc;
 771
 772        for (i = 0; i < wled->cfg.num_strings; i++) {
 773                j = wled->cfg.enabled_strings[i];
 774                temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
 775                sink_en |= 1 << temp;
 776        }
 777
 778        if (sink_cfg == sink_en) {
 779                rc = wled_auto_detection_at_init(wled);
 780                return rc;
 781        }
 782
 783        rc = regmap_update_bits(wled->regmap,
 784                                wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
 785                                WLED4_SINK_REG_CURR_SINK_MASK, 0);
 786        if (rc < 0)
 787                return rc;
 788
 789        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 790                                WLED3_CTRL_REG_MOD_EN,
 791                                WLED3_CTRL_REG_MOD_EN_MASK, 0);
 792        if (rc < 0)
 793                return rc;
 794
 795        /* Per sink/string configuration */
 796        for (i = 0; i < wled->cfg.num_strings; i++) {
 797                j = wled->cfg.enabled_strings[i];
 798
 799                addr = wled->sink_addr +
 800                                WLED4_SINK_REG_STR_MOD_EN(j);
 801                rc = regmap_update_bits(wled->regmap, addr,
 802                                        WLED4_SINK_REG_STR_MOD_MASK,
 803                                        WLED4_SINK_REG_STR_MOD_MASK);
 804                if (rc < 0)
 805                        return rc;
 806
 807                addr = wled->sink_addr +
 808                                WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
 809                rc = regmap_update_bits(wled->regmap, addr,
 810                                        WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
 811                                        wled->cfg.string_i_limit);
 812                if (rc < 0)
 813                        return rc;
 814
 815                addr = wled->sink_addr +
 816                                WLED4_SINK_REG_STR_CABC(j);
 817                rc = regmap_update_bits(wled->regmap, addr,
 818                                        WLED4_SINK_REG_STR_CABC_MASK,
 819                                        wled->cfg.cabc ?
 820                                        WLED4_SINK_REG_STR_CABC_MASK : 0);
 821                if (rc < 0)
 822                        return rc;
 823        }
 824
 825        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 826                                WLED3_CTRL_REG_MOD_EN,
 827                                WLED3_CTRL_REG_MOD_EN_MASK,
 828                                WLED3_CTRL_REG_MOD_EN_MASK);
 829        if (rc < 0)
 830                return rc;
 831
 832        rc = regmap_update_bits(wled->regmap,
 833                                wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
 834                                WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
 835        if (rc < 0)
 836                return rc;
 837
 838        rc = wled_sync_toggle(wled);
 839        if (rc < 0) {
 840                dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc);
 841                return rc;
 842        }
 843
 844        rc = wled_auto_detection_at_init(wled);
 845
 846        return rc;
 847}
 848
 849static const struct wled_config wled4_config_defaults = {
 850        .boost_i_limit = 4,
 851        .string_i_limit = 10,
 852        .ovp = 1,
 853        .num_strings = 4,
 854        .switch_freq = 11,
 855        .cabc = false,
 856        .external_pfet = false,
 857        .auto_detection_enabled = false,
 858};
 859
 860static const u32 wled3_boost_i_limit_values[] = {
 861        105, 385, 525, 805, 980, 1260, 1400, 1680,
 862};
 863
 864static const struct wled_var_cfg wled3_boost_i_limit_cfg = {
 865        .values = wled3_boost_i_limit_values,
 866        .size = ARRAY_SIZE(wled3_boost_i_limit_values),
 867};
 868
 869static const u32 wled4_boost_i_limit_values[] = {
 870        105, 280, 450, 620, 970, 1150, 1300, 1500,
 871};
 872
 873static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
 874        .values = wled4_boost_i_limit_values,
 875        .size = ARRAY_SIZE(wled4_boost_i_limit_values),
 876};
 877
 878static const u32 wled3_ovp_values[] = {
 879        35, 32, 29, 27,
 880};
 881
 882static const struct wled_var_cfg wled3_ovp_cfg = {
 883        .values = wled3_ovp_values,
 884        .size = ARRAY_SIZE(wled3_ovp_values),
 885};
 886
 887static const u32 wled4_ovp_values[] = {
 888        31100, 29600, 19600, 18100,
 889};
 890
 891static const struct wled_var_cfg wled4_ovp_cfg = {
 892        .values = wled4_ovp_values,
 893        .size = ARRAY_SIZE(wled4_ovp_values),
 894};
 895
 896static u32 wled3_num_strings_values_fn(u32 idx)
 897{
 898        return idx + 1;
 899}
 900
 901static const struct wled_var_cfg wled3_num_strings_cfg = {
 902        .fn = wled3_num_strings_values_fn,
 903        .size = 3,
 904};
 905
 906static const struct wled_var_cfg wled4_num_strings_cfg = {
 907        .fn = wled3_num_strings_values_fn,
 908        .size = 4,
 909};
 910
 911static u32 wled3_switch_freq_values_fn(u32 idx)
 912{
 913        return 19200 / (2 * (1 + idx));
 914}
 915
 916static const struct wled_var_cfg wled3_switch_freq_cfg = {
 917        .fn = wled3_switch_freq_values_fn,
 918        .size = 16,
 919};
 920
 921static const struct wled_var_cfg wled3_string_i_limit_cfg = {
 922        .size = 26,
 923};
 924
 925static const u32 wled4_string_i_limit_values[] = {
 926        0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000,
 927        22500, 25000, 27500, 30000,
 928};
 929
 930static const struct wled_var_cfg wled4_string_i_limit_cfg = {
 931        .values = wled4_string_i_limit_values,
 932        .size = ARRAY_SIZE(wled4_string_i_limit_values),
 933};
 934
 935static const struct wled_var_cfg wled3_string_cfg = {
 936        .size = 8,
 937};
 938
 939static const struct wled_var_cfg wled4_string_cfg = {
 940        .size = 16,
 941};
 942
 943static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
 944{
 945        if (idx >= cfg->size)
 946                return UINT_MAX;
 947        if (cfg->fn)
 948                return cfg->fn(idx);
 949        if (cfg->values)
 950                return cfg->values[idx];
 951        return idx;
 952}
 953
 954static int wled_configure(struct wled *wled, int version)
 955{
 956        struct wled_config *cfg = &wled->cfg;
 957        struct device *dev = wled->dev;
 958        const __be32 *prop_addr;
 959        u32 size, val, c;
 960        int rc, i, j, string_len;
 961
 962        const struct wled_u32_opts *u32_opts = NULL;
 963        const struct wled_u32_opts wled3_opts[] = {
 964                {
 965                        .name = "qcom,current-boost-limit",
 966                        .val_ptr = &cfg->boost_i_limit,
 967                        .cfg = &wled3_boost_i_limit_cfg,
 968                },
 969                {
 970                        .name = "qcom,current-limit",
 971                        .val_ptr = &cfg->string_i_limit,
 972                        .cfg = &wled3_string_i_limit_cfg,
 973                },
 974                {
 975                        .name = "qcom,ovp",
 976                        .val_ptr = &cfg->ovp,
 977                        .cfg = &wled3_ovp_cfg,
 978                },
 979                {
 980                        .name = "qcom,switching-freq",
 981                        .val_ptr = &cfg->switch_freq,
 982                        .cfg = &wled3_switch_freq_cfg,
 983                },
 984                {
 985                        .name = "qcom,num-strings",
 986                        .val_ptr = &cfg->num_strings,
 987                        .cfg = &wled3_num_strings_cfg,
 988                },
 989        };
 990
 991        const struct wled_u32_opts wled4_opts[] = {
 992                {
 993                        .name = "qcom,current-boost-limit",
 994                        .val_ptr = &cfg->boost_i_limit,
 995                        .cfg = &wled4_boost_i_limit_cfg,
 996                },
 997                {
 998                        .name = "qcom,current-limit-microamp",
 999                        .val_ptr = &cfg->string_i_limit,
1000                        .cfg = &wled4_string_i_limit_cfg,
1001                },
1002                {
1003                        .name = "qcom,ovp-millivolt",
1004                        .val_ptr = &cfg->ovp,
1005                        .cfg = &wled4_ovp_cfg,
1006                },
1007                {
1008                        .name = "qcom,switching-freq",
1009                        .val_ptr = &cfg->switch_freq,
1010                        .cfg = &wled3_switch_freq_cfg,
1011                },
1012                {
1013                        .name = "qcom,num-strings",
1014                        .val_ptr = &cfg->num_strings,
1015                        .cfg = &wled4_num_strings_cfg,
1016                },
1017        };
1018
1019        const struct wled_bool_opts bool_opts[] = {
1020                { "qcom,cs-out", &cfg->cs_out_en, },
1021                { "qcom,ext-gen", &cfg->ext_gen, },
1022                { "qcom,cabc", &cfg->cabc, },
1023                { "qcom,external-pfet", &cfg->external_pfet, },
1024                { "qcom,auto-string-detection", &cfg->auto_detection_enabled, },
1025        };
1026
1027        prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
1028        if (!prop_addr) {
1029                dev_err(wled->dev, "invalid IO resources\n");
1030                return -EINVAL;
1031        }
1032        wled->ctrl_addr = be32_to_cpu(*prop_addr);
1033
1034        rc = of_property_read_string(dev->of_node, "label", &wled->name);
1035        if (rc)
1036                wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
1037
1038        switch (version) {
1039        case 3:
1040                u32_opts = wled3_opts;
1041                size = ARRAY_SIZE(wled3_opts);
1042                *cfg = wled3_config_defaults;
1043                wled->wled_set_brightness = wled3_set_brightness;
1044                wled->max_string_count = 3;
1045                wled->sink_addr = wled->ctrl_addr;
1046                break;
1047
1048        case 4:
1049                u32_opts = wled4_opts;
1050                size = ARRAY_SIZE(wled4_opts);
1051                *cfg = wled4_config_defaults;
1052                wled->wled_set_brightness = wled4_set_brightness;
1053                wled->max_string_count = 4;
1054
1055                prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
1056                if (!prop_addr) {
1057                        dev_err(wled->dev, "invalid IO resources\n");
1058                        return -EINVAL;
1059                }
1060                wled->sink_addr = be32_to_cpu(*prop_addr);
1061                break;
1062
1063        default:
1064                dev_err(wled->dev, "Invalid WLED version\n");
1065                return -EINVAL;
1066        }
1067
1068        for (i = 0; i < size; ++i) {
1069                rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
1070                if (rc == -EINVAL) {
1071                        continue;
1072                } else if (rc) {
1073                        dev_err(dev, "error reading '%s'\n", u32_opts[i].name);
1074                        return rc;
1075                }
1076
1077                c = UINT_MAX;
1078                for (j = 0; c != val; j++) {
1079                        c = wled_values(u32_opts[i].cfg, j);
1080                        if (c == UINT_MAX) {
1081                                dev_err(dev, "invalid value for '%s'\n",
1082                                        u32_opts[i].name);
1083                                return -EINVAL;
1084                        }
1085
1086                        if (c == val)
1087                                break;
1088                }
1089
1090                dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
1091                *u32_opts[i].val_ptr = j;
1092        }
1093
1094        for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
1095                if (of_property_read_bool(dev->of_node, bool_opts[i].name))
1096                        *bool_opts[i].val_ptr = true;
1097        }
1098
1099        cfg->num_strings = cfg->num_strings + 1;
1100
1101        string_len = of_property_count_elems_of_size(dev->of_node,
1102                                                     "qcom,enabled-strings",
1103                                                     sizeof(u32));
1104        if (string_len > 0)
1105                of_property_read_u32_array(dev->of_node,
1106                                                "qcom,enabled-strings",
1107                                                wled->cfg.enabled_strings,
1108                                                sizeof(u32));
1109
1110        return 0;
1111}
1112
1113static int wled_configure_short_irq(struct wled *wled,
1114                                    struct platform_device *pdev)
1115{
1116        int rc;
1117
1118        if (!wled->has_short_detect)
1119                return 0;
1120
1121        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
1122                                WLED4_CTRL_REG_SHORT_PROTECT,
1123                                WLED4_CTRL_REG_SHORT_EN_MASK,
1124                                WLED4_CTRL_REG_SHORT_EN_MASK);
1125        if (rc < 0)
1126                return rc;
1127
1128        wled->short_irq = platform_get_irq_byname(pdev, "short");
1129        if (wled->short_irq < 0) {
1130                dev_dbg(&pdev->dev, "short irq is not used\n");
1131                return 0;
1132        }
1133
1134        rc = devm_request_threaded_irq(wled->dev, wled->short_irq,
1135                                       NULL, wled_short_irq_handler,
1136                                       IRQF_ONESHOT,
1137                                       "wled_short_irq", wled);
1138        if (rc < 0)
1139                dev_err(wled->dev, "Unable to request short_irq (err:%d)\n",
1140                        rc);
1141
1142        return rc;
1143}
1144
1145static int wled_configure_ovp_irq(struct wled *wled,
1146                                  struct platform_device *pdev)
1147{
1148        int rc;
1149        u32 val;
1150
1151        wled->ovp_irq = platform_get_irq_byname(pdev, "ovp");
1152        if (wled->ovp_irq < 0) {
1153                dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n");
1154                return 0;
1155        }
1156
1157        rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL,
1158                                       wled_ovp_irq_handler, IRQF_ONESHOT,
1159                                       "wled_ovp_irq", wled);
1160        if (rc < 0) {
1161                dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n",
1162                        rc);
1163                wled->ovp_irq = 0;
1164                return 0;
1165        }
1166
1167        rc = regmap_read(wled->regmap, wled->ctrl_addr +
1168                         WLED3_CTRL_REG_MOD_EN, &val);
1169        if (rc < 0)
1170                return rc;
1171
1172        /* Keep OVP irq disabled until module is enabled */
1173        if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
1174                disable_irq(wled->ovp_irq);
1175
1176        return 0;
1177}
1178
1179static const struct backlight_ops wled_ops = {
1180        .update_status = wled_update_status,
1181};
1182
1183static int wled_probe(struct platform_device *pdev)
1184{
1185        struct backlight_properties props;
1186        struct backlight_device *bl;
1187        struct wled *wled;
1188        struct regmap *regmap;
1189        int version;
1190        u32 val;
1191        int rc;
1192
1193        regmap = dev_get_regmap(pdev->dev.parent, NULL);
1194        if (!regmap) {
1195                dev_err(&pdev->dev, "Unable to get regmap\n");
1196                return -EINVAL;
1197        }
1198
1199        wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
1200        if (!wled)
1201                return -ENOMEM;
1202
1203        wled->regmap = regmap;
1204        wled->dev = &pdev->dev;
1205
1206        version = (uintptr_t)of_device_get_match_data(&pdev->dev);
1207        if (!version) {
1208                dev_err(&pdev->dev, "Unknown device version\n");
1209                return -ENODEV;
1210        }
1211
1212        mutex_init(&wled->lock);
1213        rc = wled_configure(wled, version);
1214        if (rc)
1215                return rc;
1216
1217        switch (version) {
1218        case 3:
1219                wled->cfg.auto_detection_enabled = false;
1220                rc = wled3_setup(wled);
1221                if (rc) {
1222                        dev_err(&pdev->dev, "wled3_setup failed\n");
1223                        return rc;
1224                }
1225                break;
1226
1227        case 4:
1228                wled->has_short_detect = true;
1229                rc = wled4_setup(wled);
1230                if (rc) {
1231                        dev_err(&pdev->dev, "wled4_setup failed\n");
1232                        return rc;
1233                }
1234                break;
1235
1236        default:
1237                dev_err(wled->dev, "Invalid WLED version\n");
1238                break;
1239        }
1240
1241        INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work);
1242
1243        rc = wled_configure_short_irq(wled, pdev);
1244        if (rc < 0)
1245                return rc;
1246
1247        rc = wled_configure_ovp_irq(wled, pdev);
1248        if (rc < 0)
1249                return rc;
1250
1251        val = WLED_DEFAULT_BRIGHTNESS;
1252        of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
1253
1254        memset(&props, 0, sizeof(struct backlight_properties));
1255        props.type = BACKLIGHT_RAW;
1256        props.brightness = val;
1257        props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
1258        bl = devm_backlight_device_register(&pdev->dev, wled->name,
1259                                            &pdev->dev, wled,
1260                                            &wled_ops, &props);
1261        return PTR_ERR_OR_ZERO(bl);
1262};
1263
1264static int wled_remove(struct platform_device *pdev)
1265{
1266        struct wled *wled = dev_get_drvdata(&pdev->dev);
1267
1268        mutex_destroy(&wled->lock);
1269        cancel_delayed_work_sync(&wled->ovp_work);
1270        disable_irq(wled->short_irq);
1271        disable_irq(wled->ovp_irq);
1272
1273        return 0;
1274}
1275
1276static const struct of_device_id wled_match_table[] = {
1277        { .compatible = "qcom,pm8941-wled", .data = (void *)3 },
1278        { .compatible = "qcom,pmi8998-wled", .data = (void *)4 },
1279        { .compatible = "qcom,pm660l-wled", .data = (void *)4 },
1280        {}
1281};
1282MODULE_DEVICE_TABLE(of, wled_match_table);
1283
1284static struct platform_driver wled_driver = {
1285        .probe = wled_probe,
1286        .remove = wled_remove,
1287        .driver = {
1288                .name = "qcom,wled",
1289                .of_match_table = wled_match_table,
1290        },
1291};
1292
1293module_platform_driver(wled_driver);
1294
1295MODULE_DESCRIPTION("Qualcomm WLED driver");
1296MODULE_LICENSE("GPL v2");
1297