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#define MOD_A                                           0
  19#define MOD_B                                           1
  20
  21#define WLED_DEFAULT_BRIGHTNESS                         2048
  22#define WLED_SOFT_START_DLY_US                          10000
  23#define WLED3_SINK_REG_BRIGHT_MAX                       0xFFF
  24#define WLED5_SINK_REG_BRIGHT_MAX_12B                   0xFFF
  25#define WLED5_SINK_REG_BRIGHT_MAX_15B                   0x7FFF
  26
  27/* WLED3/WLED4 control registers */
  28#define WLED3_CTRL_REG_FAULT_STATUS                     0x08
  29#define  WLED3_CTRL_REG_ILIM_FAULT_BIT                  BIT(0)
  30#define  WLED3_CTRL_REG_OVP_FAULT_BIT                   BIT(1)
  31#define  WLED4_CTRL_REG_SC_FAULT_BIT                    BIT(2)
  32#define  WLED5_CTRL_REG_OVP_PRE_ALARM_BIT               BIT(4)
  33
  34#define WLED3_CTRL_REG_INT_RT_STS                       0x10
  35#define  WLED3_CTRL_REG_OVP_FAULT_STATUS                BIT(1)
  36
  37#define WLED3_CTRL_REG_MOD_EN                           0x46
  38#define  WLED3_CTRL_REG_MOD_EN_MASK                     BIT(7)
  39#define  WLED3_CTRL_REG_MOD_EN_SHIFT                    7
  40
  41#define WLED3_CTRL_REG_FEEDBACK_CONTROL                 0x48
  42
  43#define WLED3_CTRL_REG_FREQ                             0x4c
  44#define  WLED3_CTRL_REG_FREQ_MASK                       GENMASK(3, 0)
  45
  46#define WLED3_CTRL_REG_OVP                              0x4d
  47#define  WLED3_CTRL_REG_OVP_MASK                        GENMASK(1, 0)
  48#define  WLED5_CTRL_REG_OVP_MASK                        GENMASK(3, 0)
  49
  50#define WLED3_CTRL_REG_ILIMIT                           0x4e
  51#define  WLED3_CTRL_REG_ILIMIT_MASK                     GENMASK(2, 0)
  52
  53/* WLED3/WLED4 sink registers */
  54#define WLED3_SINK_REG_SYNC                             0x47
  55#define  WLED3_SINK_REG_SYNC_CLEAR                      0x00
  56
  57#define WLED3_SINK_REG_CURR_SINK                        0x4f
  58#define  WLED3_SINK_REG_CURR_SINK_MASK                  GENMASK(7, 5)
  59#define  WLED3_SINK_REG_CURR_SINK_SHFT                  5
  60
  61/* WLED3 specific per-'string' registers below */
  62#define WLED3_SINK_REG_BRIGHT(n)                        (0x40 + n)
  63
  64#define WLED3_SINK_REG_STR_MOD_EN(n)                    (0x60 + (n * 0x10))
  65#define  WLED3_SINK_REG_STR_MOD_MASK                    BIT(7)
  66
  67#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)           (0x62 + (n * 0x10))
  68#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK        GENMASK(4, 0)
  69
  70#define WLED3_SINK_REG_STR_MOD_SRC(n)                   (0x63 + (n * 0x10))
  71#define  WLED3_SINK_REG_STR_MOD_SRC_MASK                BIT(0)
  72#define  WLED3_SINK_REG_STR_MOD_SRC_INT                 0x00
  73#define  WLED3_SINK_REG_STR_MOD_SRC_EXT                 0x01
  74
  75#define WLED3_SINK_REG_STR_CABC(n)                      (0x66 + (n * 0x10))
  76#define  WLED3_SINK_REG_STR_CABC_MASK                   BIT(7)
  77
  78/* WLED4 specific control registers */
  79#define WLED4_CTRL_REG_SHORT_PROTECT                    0x5e
  80#define  WLED4_CTRL_REG_SHORT_EN_MASK                   BIT(7)
  81
  82#define WLED4_CTRL_REG_SEC_ACCESS                       0xd0
  83#define  WLED4_CTRL_REG_SEC_UNLOCK                      0xa5
  84
  85#define WLED4_CTRL_REG_TEST1                            0xe2
  86#define  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2            0x09
  87
  88/* WLED4 specific sink registers */
  89#define WLED4_SINK_REG_CURR_SINK                        0x46
  90#define  WLED4_SINK_REG_CURR_SINK_MASK                  GENMASK(7, 4)
  91#define  WLED4_SINK_REG_CURR_SINK_SHFT                  4
  92
  93/* WLED4 specific per-'string' registers below */
  94#define WLED4_SINK_REG_STR_MOD_EN(n)                    (0x50 + (n * 0x10))
  95#define  WLED4_SINK_REG_STR_MOD_MASK                    BIT(7)
  96
  97#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n)           (0x52 + (n * 0x10))
  98#define  WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK        GENMASK(3, 0)
  99
 100#define WLED4_SINK_REG_STR_MOD_SRC(n)                   (0x53 + (n * 0x10))
 101#define  WLED4_SINK_REG_STR_MOD_SRC_MASK                BIT(0)
 102#define  WLED4_SINK_REG_STR_MOD_SRC_INT                 0x00
 103#define  WLED4_SINK_REG_STR_MOD_SRC_EXT                 0x01
 104
 105#define WLED4_SINK_REG_STR_CABC(n)                      (0x56 + (n * 0x10))
 106#define  WLED4_SINK_REG_STR_CABC_MASK                   BIT(7)
 107
 108#define WLED4_SINK_REG_BRIGHT(n)                        (0x57 + (n * 0x10))
 109
 110/* WLED5 specific control registers */
 111#define WLED5_CTRL_REG_OVP_INT_CTL                      0x5f
 112#define  WLED5_CTRL_REG_OVP_INT_TIMER_MASK              GENMASK(2, 0)
 113
 114/* WLED5 specific sink registers */
 115#define WLED5_SINK_REG_MOD_A_EN                         0x50
 116#define WLED5_SINK_REG_MOD_B_EN                         0x60
 117#define  WLED5_SINK_REG_MOD_EN_MASK                     BIT(7)
 118
 119#define WLED5_SINK_REG_MOD_A_SRC_SEL                    0x51
 120#define WLED5_SINK_REG_MOD_B_SRC_SEL                    0x61
 121#define  WLED5_SINK_REG_MOD_SRC_SEL_HIGH                0
 122#define  WLED5_SINK_REG_MOD_SRC_SEL_EXT                 0x03
 123#define  WLED5_SINK_REG_MOD_SRC_SEL_MASK                GENMASK(1, 0)
 124
 125#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL       0x52
 126#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL       0x62
 127#define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B            0
 128#define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B            1
 129
 130#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB             0x53
 131#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB             0x54
 132#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB             0x63
 133#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB             0x64
 134
 135#define WLED5_SINK_REG_MOD_SYNC_BIT                     0x65
 136#define  WLED5_SINK_REG_SYNC_MOD_A_BIT                  BIT(0)
 137#define  WLED5_SINK_REG_SYNC_MOD_B_BIT                  BIT(1)
 138#define  WLED5_SINK_REG_SYNC_MASK                       GENMASK(1, 0)
 139
 140/* WLED5 specific per-'string' registers below */
 141#define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n)           (0x72 + (n * 0x10))
 142
 143#define WLED5_SINK_REG_STR_SRC_SEL(n)                   (0x73 + (n * 0x10))
 144#define  WLED5_SINK_REG_SRC_SEL_MOD_A                   0
 145#define  WLED5_SINK_REG_SRC_SEL_MOD_B                   1
 146#define  WLED5_SINK_REG_SRC_SEL_MASK                    GENMASK(1, 0)
 147
 148struct wled_var_cfg {
 149        const u32 *values;
 150        u32 (*fn)(u32);
 151        int size;
 152};
 153
 154struct wled_u32_opts {
 155        const char *name;
 156        u32 *val_ptr;
 157        const struct wled_var_cfg *cfg;
 158};
 159
 160struct wled_bool_opts {
 161        const char *name;
 162        bool *val_ptr;
 163};
 164
 165struct wled_config {
 166        u32 boost_i_limit;
 167        u32 ovp;
 168        u32 switch_freq;
 169        u32 num_strings;
 170        u32 string_i_limit;
 171        u32 enabled_strings[WLED_MAX_STRINGS];
 172        u32 mod_sel;
 173        u32 cabc_sel;
 174        bool cs_out_en;
 175        bool ext_gen;
 176        bool cabc;
 177        bool external_pfet;
 178        bool auto_detection_enabled;
 179};
 180
 181struct wled {
 182        const char *name;
 183        struct device *dev;
 184        struct regmap *regmap;
 185        struct mutex lock;      /* Lock to avoid race from thread irq handler */
 186        ktime_t last_short_event;
 187        ktime_t start_ovp_fault_time;
 188        u16 ctrl_addr;
 189        u16 sink_addr;
 190        u16 max_string_count;
 191        u16 auto_detection_ovp_count;
 192        u32 brightness;
 193        u32 max_brightness;
 194        u32 short_count;
 195        u32 auto_detect_count;
 196        u32 version;
 197        bool disabled_by_short;
 198        bool has_short_detect;
 199        bool cabc_disabled;
 200        int short_irq;
 201        int ovp_irq;
 202
 203        struct wled_config cfg;
 204        struct delayed_work ovp_work;
 205
 206        /* Configures the brightness. Applicable for wled3, wled4 and wled5 */
 207        int (*wled_set_brightness)(struct wled *wled, u16 brightness);
 208
 209        /* Configures the cabc register. Applicable for wled4 and wled5 */
 210        int (*wled_cabc_config)(struct wled *wled, bool enable);
 211
 212        /*
 213         * Toggles the sync bit for the brightness update to take place.
 214         * Applicable for WLED3, WLED4 and WLED5.
 215         */
 216        int (*wled_sync_toggle)(struct wled *wled);
 217
 218        /*
 219         * Time to wait before checking the OVP status after wled module enable.
 220         * Applicable for WLED4 and WLED5.
 221         */
 222        int (*wled_ovp_delay)(struct wled *wled);
 223
 224        /*
 225         * Determines if the auto string detection is required.
 226         * Applicable for WLED4 and WLED5
 227         */
 228        bool (*wled_auto_detection_required)(struct wled *wled);
 229};
 230
 231static int wled3_set_brightness(struct wled *wled, u16 brightness)
 232{
 233        int rc, i;
 234        u8 v[2];
 235
 236        v[0] = brightness & 0xff;
 237        v[1] = (brightness >> 8) & 0xf;
 238
 239        for (i = 0;  i < wled->cfg.num_strings; ++i) {
 240                rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
 241                                       WLED3_SINK_REG_BRIGHT(i), v, 2);
 242                if (rc < 0)
 243                        return rc;
 244        }
 245
 246        return 0;
 247}
 248
 249static int wled4_set_brightness(struct wled *wled, u16 brightness)
 250{
 251        int rc, i;
 252        u16 low_limit = wled->max_brightness * 4 / 1000;
 253        u8 v[2];
 254
 255        /* WLED4's lower limit of operation is 0.4% */
 256        if (brightness > 0 && brightness < low_limit)
 257                brightness = low_limit;
 258
 259        v[0] = brightness & 0xff;
 260        v[1] = (brightness >> 8) & 0xf;
 261
 262        for (i = 0;  i < wled->cfg.num_strings; ++i) {
 263                rc = regmap_bulk_write(wled->regmap, wled->sink_addr +
 264                                       WLED4_SINK_REG_BRIGHT(i), v, 2);
 265                if (rc < 0)
 266                        return rc;
 267        }
 268
 269        return 0;
 270}
 271
 272static int wled5_set_brightness(struct wled *wled, u16 brightness)
 273{
 274        int rc, offset;
 275        u16 low_limit = wled->max_brightness * 1 / 1000;
 276        u8 v[2];
 277
 278        /* WLED5's lower limit is 0.1% */
 279        if (brightness < low_limit)
 280                brightness = low_limit;
 281
 282        v[0] = brightness & 0xff;
 283        v[1] = (brightness >> 8) & 0x7f;
 284
 285        offset = (wled->cfg.mod_sel == MOD_A) ?
 286                  WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
 287                  WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB;
 288
 289        rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
 290                               v, 2);
 291        return rc;
 292}
 293
 294static void wled_ovp_work(struct work_struct *work)
 295{
 296        struct wled *wled = container_of(work,
 297                                         struct wled, ovp_work.work);
 298        enable_irq(wled->ovp_irq);
 299}
 300
 301static int wled_module_enable(struct wled *wled, int val)
 302{
 303        int rc;
 304
 305        if (wled->disabled_by_short)
 306                return -ENXIO;
 307
 308        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 309                                WLED3_CTRL_REG_MOD_EN,
 310                                WLED3_CTRL_REG_MOD_EN_MASK,
 311                                val << WLED3_CTRL_REG_MOD_EN_SHIFT);
 312        if (rc < 0)
 313                return rc;
 314
 315        if (wled->ovp_irq > 0) {
 316                if (val) {
 317                        /*
 318                         * The hardware generates a storm of spurious OVP
 319                         * interrupts during soft start operations. So defer
 320                         * enabling the IRQ for 10ms to ensure that the
 321                         * soft start is complete.
 322                         */
 323                        schedule_delayed_work(&wled->ovp_work, HZ / 100);
 324                } else {
 325                        if (!cancel_delayed_work_sync(&wled->ovp_work))
 326                                disable_irq(wled->ovp_irq);
 327                }
 328        }
 329
 330        return 0;
 331}
 332
 333static int wled3_sync_toggle(struct wled *wled)
 334{
 335        int rc;
 336        unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
 337
 338        rc = regmap_update_bits(wled->regmap,
 339                                wled->sink_addr + WLED3_SINK_REG_SYNC,
 340                                mask, WLED3_SINK_REG_SYNC_CLEAR);
 341        if (rc < 0)
 342                return rc;
 343
 344        rc = regmap_update_bits(wled->regmap,
 345                                wled->sink_addr + WLED3_SINK_REG_SYNC,
 346                                mask, mask);
 347
 348        return rc;
 349}
 350
 351static int wled5_mod_sync_toggle(struct wled *wled)
 352{
 353        int rc;
 354        u8 val;
 355
 356        rc = regmap_update_bits(wled->regmap,
 357                                wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
 358                                WLED5_SINK_REG_SYNC_MASK, 0);
 359        if (rc < 0)
 360                return rc;
 361
 362        val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
 363                                             WLED5_SINK_REG_SYNC_MOD_B_BIT;
 364        return regmap_update_bits(wled->regmap,
 365                                  wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
 366                                  WLED5_SINK_REG_SYNC_MASK, val);
 367}
 368
 369static int wled_ovp_fault_status(struct wled *wled, bool *fault_set)
 370{
 371        int rc;
 372        u32 int_rt_sts, fault_sts;
 373
 374        *fault_set = false;
 375        rc = regmap_read(wled->regmap,
 376                        wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
 377                        &int_rt_sts);
 378        if (rc < 0) {
 379                dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
 380                return rc;
 381        }
 382
 383        rc = regmap_read(wled->regmap,
 384                        wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
 385                        &fault_sts);
 386        if (rc < 0) {
 387                dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
 388                return rc;
 389        }
 390
 391        if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
 392                *fault_set = true;
 393
 394        if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT))
 395                *fault_set = true;
 396
 397        if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
 398                                   WLED5_CTRL_REG_OVP_PRE_ALARM_BIT)))
 399                *fault_set = true;
 400
 401        if (*fault_set)
 402                dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
 403                        int_rt_sts, fault_sts);
 404
 405        return rc;
 406}
 407
 408static int wled4_ovp_delay(struct wled *wled)
 409{
 410        return WLED_SOFT_START_DLY_US;
 411}
 412
 413static int wled5_ovp_delay(struct wled *wled)
 414{
 415        int rc, delay_us;
 416        u32 val;
 417        u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24};
 418
 419        /* For WLED5, get the delay based on OVP timer */
 420        rc = regmap_read(wled->regmap, wled->ctrl_addr +
 421                         WLED5_CTRL_REG_OVP_INT_CTL, &val);
 422        if (rc < 0)
 423                delay_us =
 424                ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000;
 425        else
 426                delay_us = 2 * WLED_SOFT_START_DLY_US;
 427
 428        dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us);
 429
 430        return delay_us;
 431}
 432
 433static int wled_update_status(struct backlight_device *bl)
 434{
 435        struct wled *wled = bl_get_data(bl);
 436        u16 brightness = backlight_get_brightness(bl);
 437        int rc = 0;
 438
 439        mutex_lock(&wled->lock);
 440        if (brightness) {
 441                rc = wled->wled_set_brightness(wled, brightness);
 442                if (rc < 0) {
 443                        dev_err(wled->dev, "wled failed to set brightness rc:%d\n",
 444                                rc);
 445                        goto unlock_mutex;
 446                }
 447
 448                if (wled->version < 5) {
 449                        rc = wled->wled_sync_toggle(wled);
 450                        if (rc < 0) {
 451                                dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
 452                                goto unlock_mutex;
 453                        }
 454                } else {
 455                        /*
 456                         * For WLED5 toggling the MOD_SYNC_BIT updates the
 457                         * brightness
 458                         */
 459                        rc = wled5_mod_sync_toggle(wled);
 460                        if (rc < 0) {
 461                                dev_err(wled->dev, "wled mod sync failed rc:%d\n",
 462                                        rc);
 463                                goto unlock_mutex;
 464                        }
 465                }
 466        }
 467
 468        if (!!brightness != !!wled->brightness) {
 469                rc = wled_module_enable(wled, !!brightness);
 470                if (rc < 0) {
 471                        dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
 472                        goto unlock_mutex;
 473                }
 474        }
 475
 476        wled->brightness = brightness;
 477
 478unlock_mutex:
 479        mutex_unlock(&wled->lock);
 480
 481        return rc;
 482}
 483
 484static int wled4_cabc_config(struct wled *wled, bool enable)
 485{
 486        int i, j, rc;
 487        u8 val;
 488
 489        for (i = 0; i < wled->cfg.num_strings; i++) {
 490                j = wled->cfg.enabled_strings[i];
 491
 492                val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0;
 493                rc = regmap_update_bits(wled->regmap, wled->sink_addr +
 494                                        WLED4_SINK_REG_STR_CABC(j),
 495                                        WLED4_SINK_REG_STR_CABC_MASK, val);
 496                if (rc < 0)
 497                        return rc;
 498        }
 499
 500        return 0;
 501}
 502
 503static int wled5_cabc_config(struct wled *wled, bool enable)
 504{
 505        int rc, offset;
 506        u8 reg;
 507
 508        if (wled->cabc_disabled)
 509                return 0;
 510
 511        reg = enable ? wled->cfg.cabc_sel : 0;
 512        offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL :
 513                                                WLED5_SINK_REG_MOD_B_SRC_SEL;
 514
 515        rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset,
 516                                WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg);
 517        if (rc < 0) {
 518                pr_err("Error in configuring CABC rc=%d\n", rc);
 519                return rc;
 520        }
 521
 522        if (!wled->cfg.cabc_sel)
 523                wled->cabc_disabled = true;
 524
 525        return 0;
 526}
 527
 528#define WLED_SHORT_DLY_MS                       20
 529#define WLED_SHORT_CNT_MAX                      5
 530#define WLED_SHORT_RESET_CNT_DLY_US             USEC_PER_SEC
 531
 532static irqreturn_t wled_short_irq_handler(int irq, void *_wled)
 533{
 534        struct wled *wled = _wled;
 535        int rc;
 536        s64 elapsed_time;
 537
 538        wled->short_count++;
 539        mutex_lock(&wled->lock);
 540        rc = wled_module_enable(wled, false);
 541        if (rc < 0) {
 542                dev_err(wled->dev, "wled disable failed rc:%d\n", rc);
 543                goto unlock_mutex;
 544        }
 545
 546        elapsed_time = ktime_us_delta(ktime_get(),
 547                                      wled->last_short_event);
 548        if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US)
 549                wled->short_count = 1;
 550
 551        if (wled->short_count > WLED_SHORT_CNT_MAX) {
 552                dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n",
 553                        wled->short_count);
 554                wled->disabled_by_short = true;
 555                goto unlock_mutex;
 556        }
 557
 558        wled->last_short_event = ktime_get();
 559
 560        msleep(WLED_SHORT_DLY_MS);
 561        rc = wled_module_enable(wled, true);
 562        if (rc < 0)
 563                dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
 564
 565unlock_mutex:
 566        mutex_unlock(&wled->lock);
 567
 568        return IRQ_HANDLED;
 569}
 570
 571#define AUTO_DETECT_BRIGHTNESS          200
 572
 573static void wled_auto_string_detection(struct wled *wled)
 574{
 575        int rc = 0, i, delay_time_us;
 576        u32 sink_config = 0;
 577        u8 sink_test = 0, sink_valid = 0, val;
 578        bool fault_set;
 579
 580        /* Read configured sink configuration */
 581        rc = regmap_read(wled->regmap, wled->sink_addr +
 582                         WLED4_SINK_REG_CURR_SINK, &sink_config);
 583        if (rc < 0) {
 584                dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n",
 585                        rc);
 586                goto failed_detect;
 587        }
 588
 589        /* Disable the module before starting detection */
 590        rc = regmap_update_bits(wled->regmap,
 591                                wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 592                                WLED3_CTRL_REG_MOD_EN_MASK, 0);
 593        if (rc < 0) {
 594                dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc);
 595                goto failed_detect;
 596        }
 597
 598        /* Set low brightness across all sinks */
 599        rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS);
 600        if (rc < 0) {
 601                dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n",
 602                        rc);
 603                goto failed_detect;
 604        }
 605
 606        if (wled->cfg.cabc) {
 607                rc = wled->wled_cabc_config(wled, false);
 608                if (rc < 0)
 609                        goto failed_detect;
 610        }
 611
 612        /* Disable all sinks */
 613        rc = regmap_write(wled->regmap,
 614                          wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0);
 615        if (rc < 0) {
 616                dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc);
 617                goto failed_detect;
 618        }
 619
 620        /* Iterate through the strings one by one */
 621        for (i = 0; i < wled->cfg.num_strings; i++) {
 622                sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + i));
 623
 624                /* Enable feedback control */
 625                rc = regmap_write(wled->regmap, wled->ctrl_addr +
 626                                  WLED3_CTRL_REG_FEEDBACK_CONTROL, i + 1);
 627                if (rc < 0) {
 628                        dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n",
 629                                i + 1, rc);
 630                        goto failed_detect;
 631                }
 632
 633                /* Enable the sink */
 634                rc = regmap_write(wled->regmap, wled->sink_addr +
 635                                  WLED4_SINK_REG_CURR_SINK, sink_test);
 636                if (rc < 0) {
 637                        dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n",
 638                                i + 1, rc);
 639                        goto failed_detect;
 640                }
 641
 642                /* Enable the module */
 643                rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
 644                                        WLED3_CTRL_REG_MOD_EN,
 645                                        WLED3_CTRL_REG_MOD_EN_MASK,
 646                                        WLED3_CTRL_REG_MOD_EN_MASK);
 647                if (rc < 0) {
 648                        dev_err(wled->dev, "Failed to enable WLED module rc=%d\n",
 649                                rc);
 650                        goto failed_detect;
 651                }
 652
 653                delay_time_us = wled->wled_ovp_delay(wled);
 654                usleep_range(delay_time_us, delay_time_us + 1000);
 655
 656                rc = wled_ovp_fault_status(wled, &fault_set);
 657                if (rc < 0) {
 658                        dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
 659                                rc);
 660                        goto failed_detect;
 661                }
 662
 663                if (fault_set)
 664                        dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n",
 665                                i + 1);
 666                else
 667                        sink_valid |= sink_test;
 668
 669                /* Disable the module */
 670                rc = regmap_update_bits(wled->regmap,
 671                                        wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 672                                        WLED3_CTRL_REG_MOD_EN_MASK, 0);
 673                if (rc < 0) {
 674                        dev_err(wled->dev, "Failed to disable WLED module rc=%d\n",
 675                                rc);
 676                        goto failed_detect;
 677                }
 678        }
 679
 680        if (!sink_valid) {
 681                dev_err(wled->dev, "No valid WLED sinks found\n");
 682                wled->disabled_by_short = true;
 683                goto failed_detect;
 684        }
 685
 686        if (sink_valid != sink_config) {
 687                dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n",
 688                         sink_config, sink_valid);
 689                sink_config = sink_valid;
 690        }
 691
 692        /* Write the new sink configuration */
 693        rc = regmap_write(wled->regmap,
 694                          wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
 695                          sink_config);
 696        if (rc < 0) {
 697                dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n",
 698                        rc);
 699                goto failed_detect;
 700        }
 701
 702        /* Enable valid sinks */
 703        if (wled->version == 4) {
 704                for (i = 0; i < wled->cfg.num_strings; i++) {
 705                        if (sink_config &
 706                            BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i))
 707                                val = WLED4_SINK_REG_STR_MOD_MASK;
 708                        else
 709                                /* Disable modulator_en for unused sink */
 710                                val = 0;
 711
 712                        rc = regmap_write(wled->regmap, wled->sink_addr +
 713                                          WLED4_SINK_REG_STR_MOD_EN(i), val);
 714                        if (rc < 0) {
 715                                dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n",
 716                                        rc);
 717                                goto failed_detect;
 718                        }
 719                }
 720        }
 721
 722        /* Enable CABC */
 723        rc = wled->wled_cabc_config(wled, true);
 724        if (rc < 0)
 725                goto failed_detect;
 726
 727        /* Restore the feedback setting */
 728        rc = regmap_write(wled->regmap,
 729                          wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0);
 730        if (rc < 0) {
 731                dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n",
 732                        rc);
 733                goto failed_detect;
 734        }
 735
 736        /* Restore brightness */
 737        rc = wled4_set_brightness(wled, wled->brightness);
 738        if (rc < 0) {
 739                dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n",
 740                        rc);
 741                goto failed_detect;
 742        }
 743
 744        rc = regmap_update_bits(wled->regmap,
 745                                wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
 746                                WLED3_CTRL_REG_MOD_EN_MASK,
 747                                WLED3_CTRL_REG_MOD_EN_MASK);
 748        if (rc < 0) {
 749                dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc);
 750                goto failed_detect;
 751        }
 752
 753failed_detect:
 754        return;
 755}
 756
 757#define WLED_AUTO_DETECT_OVP_COUNT              5
 758#define WLED_AUTO_DETECT_CNT_DLY_US             USEC_PER_SEC
 759
 760static bool wled4_auto_detection_required(struct wled *wled)
 761{
 762        s64 elapsed_time_us;
 763
 764        if (!wled->cfg.auto_detection_enabled)
 765                return false;
 766
 767        /*
 768         * Check if the OVP fault was an occasional one
 769         * or if it's firing continuously, the latter qualifies
 770         * for an auto-detection check.
 771         */
 772        if (!wled->auto_detection_ovp_count) {
 773                wled->start_ovp_fault_time = ktime_get();
 774                wled->auto_detection_ovp_count++;
 775        } else {
 776                elapsed_time_us = ktime_us_delta(ktime_get(),
 777                                                 wled->start_ovp_fault_time);
 778                if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US)
 779                        wled->auto_detection_ovp_count = 0;
 780                else
 781                        wled->auto_detection_ovp_count++;
 782
 783                if (wled->auto_detection_ovp_count >=
 784                                WLED_AUTO_DETECT_OVP_COUNT) {
 785                        wled->auto_detection_ovp_count = 0;
 786                        return true;
 787                }
 788        }
 789
 790        return false;
 791}
 792
 793static bool wled5_auto_detection_required(struct wled *wled)
 794{
 795        if (!wled->cfg.auto_detection_enabled)
 796                return false;
 797
 798        /*
 799         * Unlike WLED4, WLED5 has OVP fault density interrupt configuration
 800         * i.e. to count the number of OVP alarms for a certain duration before
 801         * triggering OVP fault interrupt. By default, number of OVP fault
 802         * events counted before an interrupt is fired is 32 and the time
 803         * interval is 12 ms. If we see one OVP fault interrupt, then that
 804         * should qualify for a real OVP fault condition to run auto detection
 805         * algorithm.
 806         */
 807        return true;
 808}
 809
 810static int wled_auto_detection_at_init(struct wled *wled)
 811{
 812        int rc;
 813        bool fault_set;
 814
 815        if (!wled->cfg.auto_detection_enabled)
 816                return 0;
 817
 818        rc = wled_ovp_fault_status(wled, &fault_set);
 819        if (rc < 0) {
 820                dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
 821                        rc);
 822                return rc;
 823        }
 824
 825        if (fault_set) {
 826                mutex_lock(&wled->lock);
 827                wled_auto_string_detection(wled);
 828                mutex_unlock(&wled->lock);
 829        }
 830
 831        return rc;
 832}
 833
 834static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
 835{
 836        struct wled *wled = _wled;
 837        int rc;
 838        u32 int_sts, fault_sts;
 839
 840        rc = regmap_read(wled->regmap,
 841                         wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts);
 842        if (rc < 0) {
 843                dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n",
 844                        rc);
 845                return IRQ_HANDLED;
 846        }
 847
 848        rc = regmap_read(wled->regmap, wled->ctrl_addr +
 849                         WLED3_CTRL_REG_FAULT_STATUS, &fault_sts);
 850        if (rc < 0) {
 851                dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n",
 852                        rc);
 853                return IRQ_HANDLED;
 854        }
 855
 856        if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
 857                WLED3_CTRL_REG_ILIM_FAULT_BIT))
 858                dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
 859                        int_sts, fault_sts);
 860
 861        if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) {
 862                if (wled->wled_auto_detection_required(wled)) {
 863                        mutex_lock(&wled->lock);
 864                        wled_auto_string_detection(wled);
 865                        mutex_unlock(&wled->lock);
 866                }
 867        }
 868
 869        return IRQ_HANDLED;
 870}
 871
 872static int wled3_setup(struct wled *wled)
 873{
 874        u16 addr;
 875        u8 sink_en = 0;
 876        int rc, i, j;
 877
 878        rc = regmap_update_bits(wled->regmap,
 879                                wled->ctrl_addr + WLED3_CTRL_REG_OVP,
 880                                WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
 881        if (rc)
 882                return rc;
 883
 884        rc = regmap_update_bits(wled->regmap,
 885                                wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
 886                                WLED3_CTRL_REG_ILIMIT_MASK,
 887                                wled->cfg.boost_i_limit);
 888        if (rc)
 889                return rc;
 890
 891        rc = regmap_update_bits(wled->regmap,
 892                                wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
 893                                WLED3_CTRL_REG_FREQ_MASK,
 894                                wled->cfg.switch_freq);
 895        if (rc)
 896                return rc;
 897
 898        for (i = 0; i < wled->cfg.num_strings; ++i) {
 899                j = wled->cfg.enabled_strings[i];
 900                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j);
 901                rc = regmap_update_bits(wled->regmap, addr,
 902                                        WLED3_SINK_REG_STR_MOD_MASK,
 903                                        WLED3_SINK_REG_STR_MOD_MASK);
 904                if (rc)
 905                        return rc;
 906
 907                if (wled->cfg.ext_gen) {
 908                        addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j);
 909                        rc = regmap_update_bits(wled->regmap, addr,
 910                                                WLED3_SINK_REG_STR_MOD_SRC_MASK,
 911                                                WLED3_SINK_REG_STR_MOD_SRC_EXT);
 912                        if (rc)
 913                                return rc;
 914                }
 915
 916                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j);
 917                rc = regmap_update_bits(wled->regmap, addr,
 918                                        WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
 919                                        wled->cfg.string_i_limit);
 920                if (rc)
 921                        return rc;
 922
 923                addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j);
 924                rc = regmap_update_bits(wled->regmap, addr,
 925                                        WLED3_SINK_REG_STR_CABC_MASK,
 926                                        wled->cfg.cabc ?
 927                                        WLED3_SINK_REG_STR_CABC_MASK : 0);
 928                if (rc)
 929                        return rc;
 930
 931                sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
 932        }
 933
 934        rc = regmap_update_bits(wled->regmap,
 935                                wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK,
 936                                WLED3_SINK_REG_CURR_SINK_MASK, sink_en);
 937        if (rc)
 938                return rc;
 939
 940        return 0;
 941}
 942
 943static const struct wled_config wled3_config_defaults = {
 944        .boost_i_limit = 3,
 945        .string_i_limit = 20,
 946        .ovp = 2,
 947        .num_strings = 3,
 948        .switch_freq = 5,
 949        .cs_out_en = false,
 950        .ext_gen = false,
 951        .cabc = false,
 952        .enabled_strings = {0, 1, 2, 3},
 953};
 954
 955static int wled4_setup(struct wled *wled)
 956{
 957        int rc, temp, i, j;
 958        u16 addr;
 959        u8 sink_en = 0;
 960        u32 sink_cfg;
 961
 962        rc = regmap_update_bits(wled->regmap,
 963                                wled->ctrl_addr + WLED3_CTRL_REG_OVP,
 964                                WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
 965        if (rc < 0)
 966                return rc;
 967
 968        rc = regmap_update_bits(wled->regmap,
 969                                wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
 970                                WLED3_CTRL_REG_ILIMIT_MASK,
 971                                wled->cfg.boost_i_limit);
 972        if (rc < 0)
 973                return rc;
 974
 975        rc = regmap_update_bits(wled->regmap,
 976                                wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
 977                                WLED3_CTRL_REG_FREQ_MASK,
 978                                wled->cfg.switch_freq);
 979        if (rc < 0)
 980                return rc;
 981
 982        if (wled->cfg.external_pfet) {
 983                /* Unlock the secure register access */
 984                rc = regmap_write(wled->regmap, wled->ctrl_addr +
 985                                  WLED4_CTRL_REG_SEC_ACCESS,
 986                                  WLED4_CTRL_REG_SEC_UNLOCK);
 987                if (rc < 0)
 988                        return rc;
 989
 990                rc = regmap_write(wled->regmap,
 991                                  wled->ctrl_addr + WLED4_CTRL_REG_TEST1,
 992                                  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2);
 993                if (rc < 0)
 994                        return rc;
 995        }
 996
 997        rc = regmap_read(wled->regmap, wled->sink_addr +
 998                         WLED4_SINK_REG_CURR_SINK, &sink_cfg);
 999        if (rc < 0)
1000                return rc;
1001
1002        for (i = 0; i < wled->cfg.num_strings; i++) {
1003                j = wled->cfg.enabled_strings[i];
1004                temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
1005                sink_en |= 1 << temp;
1006        }
1007
1008        if (sink_cfg == sink_en) {
1009                rc = wled_auto_detection_at_init(wled);
1010                return rc;
1011        }
1012
1013        rc = regmap_update_bits(wled->regmap,
1014                                wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
1015                                WLED4_SINK_REG_CURR_SINK_MASK, 0);
1016        if (rc < 0)
1017                return rc;
1018
1019        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
1020                                WLED3_CTRL_REG_MOD_EN,
1021                                WLED3_CTRL_REG_MOD_EN_MASK, 0);
1022        if (rc < 0)
1023                return rc;
1024
1025        /* Per sink/string configuration */
1026        for (i = 0; i < wled->cfg.num_strings; i++) {
1027                j = wled->cfg.enabled_strings[i];
1028
1029                addr = wled->sink_addr +
1030                                WLED4_SINK_REG_STR_MOD_EN(j);
1031                rc = regmap_update_bits(wled->regmap, addr,
1032                                        WLED4_SINK_REG_STR_MOD_MASK,
1033                                        WLED4_SINK_REG_STR_MOD_MASK);
1034                if (rc < 0)
1035                        return rc;
1036
1037                addr = wled->sink_addr +
1038                                WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
1039                rc = regmap_update_bits(wled->regmap, addr,
1040                                        WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
1041                                        wled->cfg.string_i_limit);
1042                if (rc < 0)
1043                        return rc;
1044        }
1045
1046        rc = wled4_cabc_config(wled, wled->cfg.cabc);
1047        if (rc < 0)
1048                return rc;
1049
1050        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
1051                                WLED3_CTRL_REG_MOD_EN,
1052                                WLED3_CTRL_REG_MOD_EN_MASK,
1053                                WLED3_CTRL_REG_MOD_EN_MASK);
1054        if (rc < 0)
1055                return rc;
1056
1057        rc = regmap_update_bits(wled->regmap,
1058                                wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
1059                                WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
1060        if (rc < 0)
1061                return rc;
1062
1063        rc = wled->wled_sync_toggle(wled);
1064        if (rc < 0) {
1065                dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc);
1066                return rc;
1067        }
1068
1069        rc = wled_auto_detection_at_init(wled);
1070
1071        return rc;
1072}
1073
1074static const struct wled_config wled4_config_defaults = {
1075        .boost_i_limit = 4,
1076        .string_i_limit = 10,
1077        .ovp = 1,
1078        .num_strings = 4,
1079        .switch_freq = 11,
1080        .cabc = false,
1081        .external_pfet = false,
1082        .auto_detection_enabled = false,
1083};
1084
1085static int wled5_setup(struct wled *wled)
1086{
1087        int rc, temp, i, j, offset;
1088        u8 sink_en = 0;
1089        u16 addr;
1090        u32 val;
1091
1092        rc = regmap_update_bits(wled->regmap,
1093                                wled->ctrl_addr + WLED3_CTRL_REG_OVP,
1094                                WLED5_CTRL_REG_OVP_MASK, wled->cfg.ovp);
1095        if (rc < 0)
1096                return rc;
1097
1098        rc = regmap_update_bits(wled->regmap,
1099                                wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
1100                                WLED3_CTRL_REG_ILIMIT_MASK,
1101                                wled->cfg.boost_i_limit);
1102        if (rc < 0)
1103                return rc;
1104
1105        rc = regmap_update_bits(wled->regmap,
1106                                wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
1107                                WLED3_CTRL_REG_FREQ_MASK,
1108                                wled->cfg.switch_freq);
1109        if (rc < 0)
1110                return rc;
1111
1112        /* Per sink/string configuration */
1113        for (i = 0; i < wled->cfg.num_strings; ++i) {
1114                j = wled->cfg.enabled_strings[i];
1115                addr = wled->sink_addr +
1116                                WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
1117                rc = regmap_update_bits(wled->regmap, addr,
1118                                        WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
1119                                        wled->cfg.string_i_limit);
1120                if (rc < 0)
1121                        return rc;
1122
1123                addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j);
1124                rc = regmap_update_bits(wled->regmap, addr,
1125                                        WLED5_SINK_REG_SRC_SEL_MASK,
1126                                        wled->cfg.mod_sel == MOD_A ?
1127                                        WLED5_SINK_REG_SRC_SEL_MOD_A :
1128                                        WLED5_SINK_REG_SRC_SEL_MOD_B);
1129
1130                temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
1131                sink_en |= 1 << temp;
1132        }
1133
1134        rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false);
1135        if (rc < 0)
1136                return rc;
1137
1138        /* Enable one of the modulators A or B based on mod_sel */
1139        addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN;
1140        val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
1141        rc = regmap_update_bits(wled->regmap, addr,
1142                                WLED5_SINK_REG_MOD_EN_MASK, val);
1143        if (rc < 0)
1144                return rc;
1145
1146        addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN;
1147        val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
1148        rc = regmap_update_bits(wled->regmap, addr,
1149                                WLED5_SINK_REG_MOD_EN_MASK, val);
1150        if (rc < 0)
1151                return rc;
1152
1153        offset = (wled->cfg.mod_sel == MOD_A) ?
1154                  WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL :
1155                  WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL;
1156
1157        addr = wled->sink_addr + offset;
1158        val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ?
1159                 WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
1160                 WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B;
1161        rc = regmap_write(wled->regmap, addr, val);
1162        if (rc < 0)
1163                return rc;
1164
1165        rc = regmap_update_bits(wled->regmap,
1166                                wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
1167                                WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
1168        if (rc < 0)
1169                return rc;
1170
1171        /* This updates only FSC configuration in WLED5 */
1172        rc = wled->wled_sync_toggle(wled);
1173        if (rc < 0) {
1174                pr_err("Failed to toggle sync reg rc:%d\n", rc);
1175                return rc;
1176        }
1177
1178        rc = wled_auto_detection_at_init(wled);
1179        if (rc < 0)
1180                return rc;
1181
1182        return 0;
1183}
1184
1185static const struct wled_config wled5_config_defaults = {
1186        .boost_i_limit = 5,
1187        .string_i_limit = 10,
1188        .ovp = 4,
1189        .num_strings = 4,
1190        .switch_freq = 11,
1191        .mod_sel = 0,
1192        .cabc_sel = 0,
1193        .cabc = false,
1194        .external_pfet = false,
1195        .auto_detection_enabled = false,
1196};
1197
1198static const u32 wled3_boost_i_limit_values[] = {
1199        105, 385, 525, 805, 980, 1260, 1400, 1680,
1200};
1201
1202static const struct wled_var_cfg wled3_boost_i_limit_cfg = {
1203        .values = wled3_boost_i_limit_values,
1204        .size = ARRAY_SIZE(wled3_boost_i_limit_values),
1205};
1206
1207static const u32 wled4_boost_i_limit_values[] = {
1208        105, 280, 450, 620, 970, 1150, 1300, 1500,
1209};
1210
1211static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
1212        .values = wled4_boost_i_limit_values,
1213        .size = ARRAY_SIZE(wled4_boost_i_limit_values),
1214};
1215
1216static inline u32 wled5_boost_i_limit_values_fn(u32 idx)
1217{
1218        return 525 + (idx * 175);
1219}
1220
1221static const struct wled_var_cfg wled5_boost_i_limit_cfg = {
1222        .fn = wled5_boost_i_limit_values_fn,
1223        .size = 8,
1224};
1225
1226static const u32 wled3_ovp_values[] = {
1227        35, 32, 29, 27,
1228};
1229
1230static const struct wled_var_cfg wled3_ovp_cfg = {
1231        .values = wled3_ovp_values,
1232        .size = ARRAY_SIZE(wled3_ovp_values),
1233};
1234
1235static const u32 wled4_ovp_values[] = {
1236        31100, 29600, 19600, 18100,
1237};
1238
1239static const struct wled_var_cfg wled4_ovp_cfg = {
1240        .values = wled4_ovp_values,
1241        .size = ARRAY_SIZE(wled4_ovp_values),
1242};
1243
1244static inline u32 wled5_ovp_values_fn(u32 idx)
1245{
1246        /*
1247         * 0000 - 38.5 V
1248         * 0001 - 37 V ..
1249         * 1111 - 16 V
1250         */
1251        return 38500 - (idx * 1500);
1252}
1253
1254static const struct wled_var_cfg wled5_ovp_cfg = {
1255        .fn = wled5_ovp_values_fn,
1256        .size = 16,
1257};
1258
1259static u32 wled3_num_strings_values_fn(u32 idx)
1260{
1261        return idx + 1;
1262}
1263
1264static const struct wled_var_cfg wled3_num_strings_cfg = {
1265        .fn = wled3_num_strings_values_fn,
1266        .size = 3,
1267};
1268
1269static const struct wled_var_cfg wled4_num_strings_cfg = {
1270        .fn = wled3_num_strings_values_fn,
1271        .size = 4,
1272};
1273
1274static u32 wled3_switch_freq_values_fn(u32 idx)
1275{
1276        return 19200 / (2 * (1 + idx));
1277}
1278
1279static const struct wled_var_cfg wled3_switch_freq_cfg = {
1280        .fn = wled3_switch_freq_values_fn,
1281        .size = 16,
1282};
1283
1284static const struct wled_var_cfg wled3_string_i_limit_cfg = {
1285        .size = 26,
1286};
1287
1288static const u32 wled4_string_i_limit_values[] = {
1289        0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000,
1290        22500, 25000, 27500, 30000,
1291};
1292
1293static const struct wled_var_cfg wled4_string_i_limit_cfg = {
1294        .values = wled4_string_i_limit_values,
1295        .size = ARRAY_SIZE(wled4_string_i_limit_values),
1296};
1297
1298static const struct wled_var_cfg wled5_mod_sel_cfg = {
1299        .size = 2,
1300};
1301
1302static const struct wled_var_cfg wled5_cabc_sel_cfg = {
1303        .size = 4,
1304};
1305
1306static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
1307{
1308        if (idx >= cfg->size)
1309                return UINT_MAX;
1310        if (cfg->fn)
1311                return cfg->fn(idx);
1312        if (cfg->values)
1313                return cfg->values[idx];
1314        return idx;
1315}
1316
1317static int wled_configure(struct wled *wled)
1318{
1319        struct wled_config *cfg = &wled->cfg;
1320        struct device *dev = wled->dev;
1321        const __be32 *prop_addr;
1322        u32 size, val, c;
1323        int rc, i, j, string_len;
1324
1325        const struct wled_u32_opts *u32_opts = NULL;
1326        const struct wled_u32_opts wled3_opts[] = {
1327                {
1328                        .name = "qcom,current-boost-limit",
1329                        .val_ptr = &cfg->boost_i_limit,
1330                        .cfg = &wled3_boost_i_limit_cfg,
1331                },
1332                {
1333                        .name = "qcom,current-limit",
1334                        .val_ptr = &cfg->string_i_limit,
1335                        .cfg = &wled3_string_i_limit_cfg,
1336                },
1337                {
1338                        .name = "qcom,ovp",
1339                        .val_ptr = &cfg->ovp,
1340                        .cfg = &wled3_ovp_cfg,
1341                },
1342                {
1343                        .name = "qcom,switching-freq",
1344                        .val_ptr = &cfg->switch_freq,
1345                        .cfg = &wled3_switch_freq_cfg,
1346                },
1347                {
1348                        .name = "qcom,num-strings",
1349                        .val_ptr = &cfg->num_strings,
1350                        .cfg = &wled3_num_strings_cfg,
1351                },
1352        };
1353
1354        const struct wled_u32_opts wled4_opts[] = {
1355                {
1356                        .name = "qcom,current-boost-limit",
1357                        .val_ptr = &cfg->boost_i_limit,
1358                        .cfg = &wled4_boost_i_limit_cfg,
1359                },
1360                {
1361                        .name = "qcom,current-limit-microamp",
1362                        .val_ptr = &cfg->string_i_limit,
1363                        .cfg = &wled4_string_i_limit_cfg,
1364                },
1365                {
1366                        .name = "qcom,ovp-millivolt",
1367                        .val_ptr = &cfg->ovp,
1368                        .cfg = &wled4_ovp_cfg,
1369                },
1370                {
1371                        .name = "qcom,switching-freq",
1372                        .val_ptr = &cfg->switch_freq,
1373                        .cfg = &wled3_switch_freq_cfg,
1374                },
1375                {
1376                        .name = "qcom,num-strings",
1377                        .val_ptr = &cfg->num_strings,
1378                        .cfg = &wled4_num_strings_cfg,
1379                },
1380        };
1381
1382        const struct wled_u32_opts wled5_opts[] = {
1383                {
1384                        .name = "qcom,current-boost-limit",
1385                        .val_ptr = &cfg->boost_i_limit,
1386                        .cfg = &wled5_boost_i_limit_cfg,
1387                },
1388                {
1389                        .name = "qcom,current-limit-microamp",
1390                        .val_ptr = &cfg->string_i_limit,
1391                        .cfg = &wled4_string_i_limit_cfg,
1392                },
1393                {
1394                        .name = "qcom,ovp-millivolt",
1395                        .val_ptr = &cfg->ovp,
1396                        .cfg = &wled5_ovp_cfg,
1397                },
1398                {
1399                        .name = "qcom,switching-freq",
1400                        .val_ptr = &cfg->switch_freq,
1401                        .cfg = &wled3_switch_freq_cfg,
1402                },
1403                {
1404                        .name = "qcom,num-strings",
1405                        .val_ptr = &cfg->num_strings,
1406                        .cfg = &wled4_num_strings_cfg,
1407                },
1408                {
1409                        .name = "qcom,modulator-sel",
1410                        .val_ptr = &cfg->mod_sel,
1411                        .cfg = &wled5_mod_sel_cfg,
1412                },
1413                {
1414                        .name = "qcom,cabc-sel",
1415                        .val_ptr = &cfg->cabc_sel,
1416                        .cfg = &wled5_cabc_sel_cfg,
1417                },
1418        };
1419
1420        const struct wled_bool_opts bool_opts[] = {
1421                { "qcom,cs-out", &cfg->cs_out_en, },
1422                { "qcom,ext-gen", &cfg->ext_gen, },
1423                { "qcom,cabc", &cfg->cabc, },
1424                { "qcom,external-pfet", &cfg->external_pfet, },
1425                { "qcom,auto-string-detection", &cfg->auto_detection_enabled, },
1426        };
1427
1428        prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
1429        if (!prop_addr) {
1430                dev_err(wled->dev, "invalid IO resources\n");
1431                return -EINVAL;
1432        }
1433        wled->ctrl_addr = be32_to_cpu(*prop_addr);
1434
1435        rc = of_property_read_string(dev->of_node, "label", &wled->name);
1436        if (rc)
1437                wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
1438
1439        switch (wled->version) {
1440        case 3:
1441                u32_opts = wled3_opts;
1442                size = ARRAY_SIZE(wled3_opts);
1443                *cfg = wled3_config_defaults;
1444                wled->wled_set_brightness = wled3_set_brightness;
1445                wled->wled_sync_toggle = wled3_sync_toggle;
1446                wled->max_string_count = 3;
1447                wled->sink_addr = wled->ctrl_addr;
1448                break;
1449
1450        case 4:
1451                u32_opts = wled4_opts;
1452                size = ARRAY_SIZE(wled4_opts);
1453                *cfg = wled4_config_defaults;
1454                wled->wled_set_brightness = wled4_set_brightness;
1455                wled->wled_sync_toggle = wled3_sync_toggle;
1456                wled->wled_cabc_config = wled4_cabc_config;
1457                wled->wled_ovp_delay = wled4_ovp_delay;
1458                wled->wled_auto_detection_required =
1459                                        wled4_auto_detection_required;
1460                wled->max_string_count = 4;
1461
1462                prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
1463                if (!prop_addr) {
1464                        dev_err(wled->dev, "invalid IO resources\n");
1465                        return -EINVAL;
1466                }
1467                wled->sink_addr = be32_to_cpu(*prop_addr);
1468                break;
1469
1470        case 5:
1471                u32_opts = wled5_opts;
1472                size = ARRAY_SIZE(wled5_opts);
1473                *cfg = wled5_config_defaults;
1474                wled->wled_set_brightness = wled5_set_brightness;
1475                wled->wled_sync_toggle = wled3_sync_toggle;
1476                wled->wled_cabc_config = wled5_cabc_config;
1477                wled->wled_ovp_delay = wled5_ovp_delay;
1478                wled->wled_auto_detection_required =
1479                                        wled5_auto_detection_required;
1480                wled->max_string_count = 4;
1481
1482                prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
1483                if (!prop_addr) {
1484                        dev_err(wled->dev, "invalid IO resources\n");
1485                        return -EINVAL;
1486                }
1487                wled->sink_addr = be32_to_cpu(*prop_addr);
1488                break;
1489
1490        default:
1491                dev_err(wled->dev, "Invalid WLED version\n");
1492                return -EINVAL;
1493        }
1494
1495        for (i = 0; i < size; ++i) {
1496                rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
1497                if (rc == -EINVAL) {
1498                        continue;
1499                } else if (rc) {
1500                        dev_err(dev, "error reading '%s'\n", u32_opts[i].name);
1501                        return rc;
1502                }
1503
1504                c = UINT_MAX;
1505                for (j = 0; c != val; j++) {
1506                        c = wled_values(u32_opts[i].cfg, j);
1507                        if (c == UINT_MAX) {
1508                                dev_err(dev, "invalid value for '%s'\n",
1509                                        u32_opts[i].name);
1510                                return -EINVAL;
1511                        }
1512
1513                        if (c == val)
1514                                break;
1515                }
1516
1517                dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
1518                *u32_opts[i].val_ptr = j;
1519        }
1520
1521        for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
1522                if (of_property_read_bool(dev->of_node, bool_opts[i].name))
1523                        *bool_opts[i].val_ptr = true;
1524        }
1525
1526        cfg->num_strings = cfg->num_strings + 1;
1527
1528        string_len = of_property_count_elems_of_size(dev->of_node,
1529                                                     "qcom,enabled-strings",
1530                                                     sizeof(u32));
1531        if (string_len > 0)
1532                of_property_read_u32_array(dev->of_node,
1533                                                "qcom,enabled-strings",
1534                                                wled->cfg.enabled_strings,
1535                                                sizeof(u32));
1536
1537        return 0;
1538}
1539
1540static int wled_configure_short_irq(struct wled *wled,
1541                                    struct platform_device *pdev)
1542{
1543        int rc;
1544
1545        if (!wled->has_short_detect)
1546                return 0;
1547
1548        rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
1549                                WLED4_CTRL_REG_SHORT_PROTECT,
1550                                WLED4_CTRL_REG_SHORT_EN_MASK,
1551                                WLED4_CTRL_REG_SHORT_EN_MASK);
1552        if (rc < 0)
1553                return rc;
1554
1555        wled->short_irq = platform_get_irq_byname(pdev, "short");
1556        if (wled->short_irq < 0) {
1557                dev_dbg(&pdev->dev, "short irq is not used\n");
1558                return 0;
1559        }
1560
1561        rc = devm_request_threaded_irq(wled->dev, wled->short_irq,
1562                                       NULL, wled_short_irq_handler,
1563                                       IRQF_ONESHOT,
1564                                       "wled_short_irq", wled);
1565        if (rc < 0)
1566                dev_err(wled->dev, "Unable to request short_irq (err:%d)\n",
1567                        rc);
1568
1569        return rc;
1570}
1571
1572static int wled_configure_ovp_irq(struct wled *wled,
1573                                  struct platform_device *pdev)
1574{
1575        int rc;
1576        u32 val;
1577
1578        wled->ovp_irq = platform_get_irq_byname(pdev, "ovp");
1579        if (wled->ovp_irq < 0) {
1580                dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n");
1581                return 0;
1582        }
1583
1584        rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL,
1585                                       wled_ovp_irq_handler, IRQF_ONESHOT,
1586                                       "wled_ovp_irq", wled);
1587        if (rc < 0) {
1588                dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n",
1589                        rc);
1590                wled->ovp_irq = 0;
1591                return 0;
1592        }
1593
1594        rc = regmap_read(wled->regmap, wled->ctrl_addr +
1595                         WLED3_CTRL_REG_MOD_EN, &val);
1596        if (rc < 0)
1597                return rc;
1598
1599        /* Keep OVP irq disabled until module is enabled */
1600        if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
1601                disable_irq(wled->ovp_irq);
1602
1603        return 0;
1604}
1605
1606static const struct backlight_ops wled_ops = {
1607        .update_status = wled_update_status,
1608};
1609
1610static int wled_probe(struct platform_device *pdev)
1611{
1612        struct backlight_properties props;
1613        struct backlight_device *bl;
1614        struct wled *wled;
1615        struct regmap *regmap;
1616        u32 val;
1617        int rc;
1618
1619        regmap = dev_get_regmap(pdev->dev.parent, NULL);
1620        if (!regmap) {
1621                dev_err(&pdev->dev, "Unable to get regmap\n");
1622                return -EINVAL;
1623        }
1624
1625        wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
1626        if (!wled)
1627                return -ENOMEM;
1628
1629        wled->regmap = regmap;
1630        wled->dev = &pdev->dev;
1631
1632        wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev);
1633        if (!wled->version) {
1634                dev_err(&pdev->dev, "Unknown device version\n");
1635                return -ENODEV;
1636        }
1637
1638        mutex_init(&wled->lock);
1639        rc = wled_configure(wled);
1640        if (rc)
1641                return rc;
1642
1643        val = WLED3_SINK_REG_BRIGHT_MAX;
1644        of_property_read_u32(pdev->dev.of_node, "max-brightness", &val);
1645        wled->max_brightness = val;
1646
1647        switch (wled->version) {
1648        case 3:
1649                wled->cfg.auto_detection_enabled = false;
1650                rc = wled3_setup(wled);
1651                if (rc) {
1652                        dev_err(&pdev->dev, "wled3_setup failed\n");
1653                        return rc;
1654                }
1655                break;
1656
1657        case 4:
1658                wled->has_short_detect = true;
1659                rc = wled4_setup(wled);
1660                if (rc) {
1661                        dev_err(&pdev->dev, "wled4_setup failed\n");
1662                        return rc;
1663                }
1664                break;
1665
1666        case 5:
1667                wled->has_short_detect = true;
1668                if (wled->cfg.cabc_sel)
1669                        wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B;
1670
1671                rc = wled5_setup(wled);
1672                if (rc) {
1673                        dev_err(&pdev->dev, "wled5_setup failed\n");
1674                        return rc;
1675                }
1676                break;
1677
1678        default:
1679                dev_err(wled->dev, "Invalid WLED version\n");
1680                break;
1681        }
1682
1683        INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work);
1684
1685        rc = wled_configure_short_irq(wled, pdev);
1686        if (rc < 0)
1687                return rc;
1688
1689        rc = wled_configure_ovp_irq(wled, pdev);
1690        if (rc < 0)
1691                return rc;
1692
1693        val = WLED_DEFAULT_BRIGHTNESS;
1694        of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
1695
1696        memset(&props, 0, sizeof(struct backlight_properties));
1697        props.type = BACKLIGHT_RAW;
1698        props.brightness = val;
1699        props.max_brightness = wled->max_brightness;
1700        bl = devm_backlight_device_register(&pdev->dev, wled->name,
1701                                            &pdev->dev, wled,
1702                                            &wled_ops, &props);
1703        return PTR_ERR_OR_ZERO(bl);
1704};
1705
1706static int wled_remove(struct platform_device *pdev)
1707{
1708        struct wled *wled = platform_get_drvdata(pdev);
1709
1710        mutex_destroy(&wled->lock);
1711        cancel_delayed_work_sync(&wled->ovp_work);
1712        disable_irq(wled->short_irq);
1713        disable_irq(wled->ovp_irq);
1714
1715        return 0;
1716}
1717
1718static const struct of_device_id wled_match_table[] = {
1719        { .compatible = "qcom,pm8941-wled", .data = (void *)3 },
1720        { .compatible = "qcom,pmi8994-wled", .data = (void *)4 },
1721        { .compatible = "qcom,pmi8998-wled", .data = (void *)4 },
1722        { .compatible = "qcom,pm660l-wled", .data = (void *)4 },
1723        { .compatible = "qcom,pm8150l-wled", .data = (void *)5 },
1724        {}
1725};
1726MODULE_DEVICE_TABLE(of, wled_match_table);
1727
1728static struct platform_driver wled_driver = {
1729        .probe = wled_probe,
1730        .remove = wled_remove,
1731        .driver = {
1732                .name = "qcom,wled",
1733                .of_match_table = wled_match_table,
1734        },
1735};
1736
1737module_platform_driver(wled_driver);
1738
1739MODULE_DESCRIPTION("Qualcomm WLED driver");
1740MODULE_LICENSE("GPL v2");
1741