130c6743cSChristian Marangi // SPDX-License-Identifier: GPL-2.0-only
230c6743cSChristian Marangi /*
330c6743cSChristian Marangi * Copyright (C) 2024 Christian Marangi <ansuelsmth@gmail.com>
430c6743cSChristian Marangi */
530c6743cSChristian Marangi
630c6743cSChristian Marangi #include <linux/bitfield.h>
74137d94fSChristian Marangi #include <linux/cleanup.h>
830c6743cSChristian Marangi #include <linux/delay.h>
930c6743cSChristian Marangi #include <linux/firmware.h>
1030c6743cSChristian Marangi #include <linux/i2c.h>
11*b0eed397SChristian Marangi #include <linux/iopoll.h>
1230c6743cSChristian Marangi #include <linux/leds.h>
1330c6743cSChristian Marangi #include <linux/module.h>
1430c6743cSChristian Marangi #include <linux/mutex.h>
1530c6743cSChristian Marangi #include <linux/of.h>
1630c6743cSChristian Marangi #include <linux/platform_data/leds-lp55xx.h>
1730c6743cSChristian Marangi #include <linux/slab.h>
1830c6743cSChristian Marangi #include <dt-bindings/leds/leds-lp55xx.h>
1930c6743cSChristian Marangi
2030c6743cSChristian Marangi #include "leds-lp55xx-common.h"
2130c6743cSChristian Marangi
2230c6743cSChristian Marangi #define LP5569_MAX_LEDS 9
2330c6743cSChristian Marangi
2430c6743cSChristian Marangi /* Memory is used like this:
2530c6743cSChristian Marangi * 0x00 engine 1 program (4 pages)
2630c6743cSChristian Marangi * 0x40 engine 2 program (4 pages)
2730c6743cSChristian Marangi * 0x80 engine 3 program (4 pages)
2830c6743cSChristian Marangi * 0xc0 engine 1 muxing info (1 page)
2930c6743cSChristian Marangi * 0xd0 engine 2 muxing info (1 page)
3030c6743cSChristian Marangi * 0xe0 engine 3 muxing info (1 page)
3130c6743cSChristian Marangi */
3230c6743cSChristian Marangi #define LP5569_PAGES_PER_ENGINE 4
3330c6743cSChristian Marangi
3430c6743cSChristian Marangi #define LP5569_REG_ENABLE 0x00
3530c6743cSChristian Marangi #define LP5569_ENABLE BIT(6)
3630c6743cSChristian Marangi
3730c6743cSChristian Marangi #define LP5569_REG_EXEC_CTRL 0x01
3830c6743cSChristian Marangi #define LP5569_MODE_ENG_SHIFT 2
3930c6743cSChristian Marangi
4030c6743cSChristian Marangi #define LP5569_REG_OP_MODE 0x02
4130c6743cSChristian Marangi #define LP5569_EXEC_ENG_SHIFT 2
4230c6743cSChristian Marangi
4330c6743cSChristian Marangi #define LP5569_REG_ENABLE_LEDS_MSB 0x04
4430c6743cSChristian Marangi #define LP5569_REG_ENABLE_LEDS_LSB 0x05
4530c6743cSChristian Marangi #define LP5569_REG_LED_CTRL_BASE 0x07
4630c6743cSChristian Marangi #define LP5569_FADER_MAPPING_MASK GENMASK(7, 5)
4730c6743cSChristian Marangi #define LP5569_REG_LED_PWM_BASE 0x16
4830c6743cSChristian Marangi #define LP5569_REG_LED_CURRENT_BASE 0x22
4930c6743cSChristian Marangi #define LP5569_REG_MISC 0x2F
5030c6743cSChristian Marangi #define LP5569_AUTO_INC BIT(6)
5130c6743cSChristian Marangi #define LP5569_PWR_SAVE BIT(5)
5230c6743cSChristian Marangi #define LP5569_CP_MODE_MASK GENMASK(4, 3)
5330c6743cSChristian Marangi #define LP5569_PWM_PWR_SAVE BIT(2)
5430c6743cSChristian Marangi #define LP5569_INTERNAL_CLK BIT(0)
5530c6743cSChristian Marangi #define LP5569_REG_MISC2 0x33
5630c6743cSChristian Marangi #define LP5569_LED_SHORT_TEST BIT(4)
5730c6743cSChristian Marangi #define LP5569_LED_OPEN_TEST BIT(3)
5830c6743cSChristian Marangi #define LP5569_REG_STATUS 0x3C
5930c6743cSChristian Marangi #define LP5569_MASK_BUSY BIT(7)
6030c6743cSChristian Marangi #define LP5569_STARTUP_BUSY BIT(6)
6130c6743cSChristian Marangi #define LP5569_ENGINE_BUSY BIT(5)
6230c6743cSChristian Marangi #define LP5569_ENGINE1_INT BIT(2)
6330c6743cSChristian Marangi #define LP5569_ENGINE2_INT BIT(1)
6430c6743cSChristian Marangi #define LP5569_ENGINE3_INT BIT(0)
6530c6743cSChristian Marangi #define LP5569_ENG_STATUS_MASK (LP5569_ENGINE1_INT | LP5569_ENGINE2_INT | \
6630c6743cSChristian Marangi LP5569_ENGINE3_INT)
6730c6743cSChristian Marangi #define LP5569_REG_IO_CONTROL 0x3D
6830c6743cSChristian Marangi #define LP5569_CLK_OUTPUT BIT(3)
6930c6743cSChristian Marangi #define LP5569_REG_RESET 0x3F
7030c6743cSChristian Marangi #define LP5569_RESET 0xFF
7130c6743cSChristian Marangi #define LP5569_REG_MASTER_FADER_BASE 0x46
7230c6743cSChristian Marangi #define LP5569_REG_CH1_PROG_START 0x4B
7330c6743cSChristian Marangi #define LP5569_REG_CH2_PROG_START 0x4C
7430c6743cSChristian Marangi #define LP5569_REG_CH3_PROG_START 0x4D
7530c6743cSChristian Marangi #define LP5569_REG_PROG_PAGE_SEL 0x4F
7630c6743cSChristian Marangi #define LP5569_REG_PROG_MEM 0x50
7730c6743cSChristian Marangi #define LP5569_REG_LED_FAULT1 0x81
7830c6743cSChristian Marangi #define LP5569_LED_FAULT8 BIT(0)
7930c6743cSChristian Marangi #define LP5569_REG_LED_FAULT2 0x82
8030c6743cSChristian Marangi #define LP5569_LED_FAULT7 BIT(7)
8130c6743cSChristian Marangi #define LP5569_LED_FAULT6 BIT(6)
8230c6743cSChristian Marangi #define LP5569_LED_FAULT5 BIT(5)
8330c6743cSChristian Marangi #define LP5569_LED_FAULT4 BIT(4)
8430c6743cSChristian Marangi #define LP5569_LED_FAULT3 BIT(3)
8530c6743cSChristian Marangi #define LP5569_LED_FAULT2 BIT(2)
8630c6743cSChristian Marangi #define LP5569_LED_FAULT1 BIT(1)
8730c6743cSChristian Marangi #define LP5569_LED_FAULT0 BIT(0)
8830c6743cSChristian Marangi
8930c6743cSChristian Marangi #define LP5569_ENG1_PROG_ADDR 0x0
9030c6743cSChristian Marangi #define LP5569_ENG2_PROG_ADDR 0x40
9130c6743cSChristian Marangi #define LP5569_ENG3_PROG_ADDR 0x80
9230c6743cSChristian Marangi #define LP5569_ENG1_MUX_ADDR 0xc0
9330c6743cSChristian Marangi #define LP5569_ENG2_MUX_ADDR 0xd0
9430c6743cSChristian Marangi #define LP5569_ENG3_MUX_ADDR 0xe0
9530c6743cSChristian Marangi
96*b0eed397SChristian Marangi #define LP5569_STARTUP_SLEEP 500
97*b0eed397SChristian Marangi
9830c6743cSChristian Marangi #define LEDn_STATUS_FAULT(n, status) ((status) >> (n) & BIT(0))
9930c6743cSChristian Marangi
10030c6743cSChristian Marangi #define LP5569_DEFAULT_CONFIG \
10130c6743cSChristian Marangi (LP5569_AUTO_INC | LP5569_PWR_SAVE | LP5569_PWM_PWR_SAVE)
10230c6743cSChristian Marangi
lp5569_run_engine(struct lp55xx_chip * chip,bool start)10330c6743cSChristian Marangi static void lp5569_run_engine(struct lp55xx_chip *chip, bool start)
10430c6743cSChristian Marangi {
10530c6743cSChristian Marangi if (!start) {
10630c6743cSChristian Marangi lp55xx_stop_engine(chip);
10730c6743cSChristian Marangi lp55xx_turn_off_channels(chip);
10830c6743cSChristian Marangi return;
10930c6743cSChristian Marangi }
11030c6743cSChristian Marangi
11130c6743cSChristian Marangi lp55xx_run_engine_common(chip);
11230c6743cSChristian Marangi }
11330c6743cSChristian Marangi
lp5569_init_program_engine(struct lp55xx_chip * chip)11430c6743cSChristian Marangi static int lp5569_init_program_engine(struct lp55xx_chip *chip)
11530c6743cSChristian Marangi {
11630c6743cSChristian Marangi int i;
11730c6743cSChristian Marangi int j;
11830c6743cSChristian Marangi int ret;
11930c6743cSChristian Marangi u8 status;
12030c6743cSChristian Marangi /* Precompiled pattern per ENGINE setting LED MUX start and stop addresses */
12130c6743cSChristian Marangi static const u8 pattern[][LP55xx_BYTES_PER_PAGE] = {
12230c6743cSChristian Marangi { 0x9c, LP5569_ENG1_MUX_ADDR, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
12330c6743cSChristian Marangi { 0x9c, LP5569_ENG2_MUX_ADDR, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
12430c6743cSChristian Marangi { 0x9c, LP5569_ENG3_MUX_ADDR, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
12530c6743cSChristian Marangi };
12630c6743cSChristian Marangi
12730c6743cSChristian Marangi /* Setup each ENGINE program start address */
12830c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_CH1_PROG_START, LP5569_ENG1_PROG_ADDR);
12930c6743cSChristian Marangi if (ret)
13030c6743cSChristian Marangi return ret;
13130c6743cSChristian Marangi
13230c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_CH2_PROG_START, LP5569_ENG2_PROG_ADDR);
13330c6743cSChristian Marangi if (ret)
13430c6743cSChristian Marangi return ret;
13530c6743cSChristian Marangi
13630c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_CH3_PROG_START, LP5569_ENG3_PROG_ADDR);
13730c6743cSChristian Marangi if (ret)
13830c6743cSChristian Marangi return ret;
13930c6743cSChristian Marangi
14030c6743cSChristian Marangi /* Write precompiled pattern for LED MUX address space for each ENGINE */
14130c6743cSChristian Marangi for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
14230c6743cSChristian Marangi chip->engine_idx = i;
14330c6743cSChristian Marangi lp55xx_load_engine(chip);
14430c6743cSChristian Marangi
14530c6743cSChristian Marangi for (j = 0; j < LP55xx_BYTES_PER_PAGE; j++) {
14630c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_PROG_MEM + j,
14730c6743cSChristian Marangi pattern[i - 1][j]);
14830c6743cSChristian Marangi if (ret)
14930c6743cSChristian Marangi goto out;
15030c6743cSChristian Marangi }
15130c6743cSChristian Marangi }
15230c6743cSChristian Marangi
15330c6743cSChristian Marangi lp5569_run_engine(chip, true);
15430c6743cSChristian Marangi
15530c6743cSChristian Marangi /* Let the programs run for couple of ms and check the engine status */
15630c6743cSChristian Marangi usleep_range(3000, 6000);
15730c6743cSChristian Marangi lp55xx_read(chip, LP5569_REG_STATUS, &status);
15830c6743cSChristian Marangi status = FIELD_GET(LP5569_ENG_STATUS_MASK, status);
15930c6743cSChristian Marangi
16030c6743cSChristian Marangi if (status != LP5569_ENG_STATUS_MASK) {
16130c6743cSChristian Marangi dev_err(&chip->cl->dev,
16230c6743cSChristian Marangi "could not configure LED engine, status = 0x%.2x\n",
16330c6743cSChristian Marangi status);
16430c6743cSChristian Marangi ret = -EINVAL;
16530c6743cSChristian Marangi }
16630c6743cSChristian Marangi
16730c6743cSChristian Marangi out:
16830c6743cSChristian Marangi lp55xx_stop_all_engine(chip);
16930c6743cSChristian Marangi return ret;
17030c6743cSChristian Marangi }
17130c6743cSChristian Marangi
lp5569_post_init_device(struct lp55xx_chip * chip)17230c6743cSChristian Marangi static int lp5569_post_init_device(struct lp55xx_chip *chip)
17330c6743cSChristian Marangi {
17430c6743cSChristian Marangi int ret;
175*b0eed397SChristian Marangi u8 val;
17630c6743cSChristian Marangi
17730c6743cSChristian Marangi val = LP5569_DEFAULT_CONFIG;
17830c6743cSChristian Marangi val |= FIELD_PREP(LP5569_CP_MODE_MASK, chip->pdata->charge_pump_mode);
1792a498d62SChristian Marangi ret = lp55xx_write(chip, LP5569_REG_MISC, val);
1802a498d62SChristian Marangi if (ret)
1812a498d62SChristian Marangi return ret;
18230c6743cSChristian Marangi
18330c6743cSChristian Marangi if (chip->pdata->clock_mode == LP55XX_CLOCK_INT) {
1842a498d62SChristian Marangi /* Internal clock MUST be configured before CLK output */
1852a498d62SChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_MISC,
1862a498d62SChristian Marangi LP5569_INTERNAL_CLK,
1872a498d62SChristian Marangi LP5569_INTERNAL_CLK);
1882a498d62SChristian Marangi if (ret)
1892a498d62SChristian Marangi return ret;
1902a498d62SChristian Marangi
19130c6743cSChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_IO_CONTROL,
19230c6743cSChristian Marangi LP5569_CLK_OUTPUT,
19330c6743cSChristian Marangi LP5569_CLK_OUTPUT);
19430c6743cSChristian Marangi if (ret)
19530c6743cSChristian Marangi return ret;
19630c6743cSChristian Marangi }
19730c6743cSChristian Marangi
198*b0eed397SChristian Marangi ret = lp55xx_write(chip, LP5569_REG_ENABLE, LP5569_ENABLE);
199*b0eed397SChristian Marangi if (ret)
200*b0eed397SChristian Marangi return ret;
201*b0eed397SChristian Marangi
202*b0eed397SChristian Marangi read_poll_timeout(lp55xx_read, ret, !(val & LP5569_STARTUP_BUSY),
203*b0eed397SChristian Marangi LP5569_STARTUP_SLEEP, LP5569_STARTUP_SLEEP * 10, false,
204*b0eed397SChristian Marangi chip, LP5569_REG_STATUS, &val);
20530c6743cSChristian Marangi
20630c6743cSChristian Marangi return lp5569_init_program_engine(chip);
20730c6743cSChristian Marangi }
20830c6743cSChristian Marangi
lp5569_led_open_test(struct lp55xx_led * led,char * buf)20930c6743cSChristian Marangi static ssize_t lp5569_led_open_test(struct lp55xx_led *led, char *buf)
21030c6743cSChristian Marangi {
21130c6743cSChristian Marangi struct lp55xx_chip *chip = led->chip;
21230c6743cSChristian Marangi struct lp55xx_platform_data *pdata = chip->pdata;
21330c6743cSChristian Marangi bool leds_fault[LP5569_MAX_LEDS];
21430c6743cSChristian Marangi struct lp55xx_led *led_tmp = led;
21530c6743cSChristian Marangi int i, ret, pos = 0;
21630c6743cSChristian Marangi u8 status;
21730c6743cSChristian Marangi
21830c6743cSChristian Marangi /* Set in STANDBY state */
21930c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_ENABLE, 0);
22030c6743cSChristian Marangi if (ret)
22130c6743cSChristian Marangi goto exit;
22230c6743cSChristian Marangi
22330c6743cSChristian Marangi /* Wait 1ms for device to enter STANDBY state */
22430c6743cSChristian Marangi usleep_range(1000, 2000);
22530c6743cSChristian Marangi
22630c6743cSChristian Marangi /* Set Charge Pump to 1.5x */
22730c6743cSChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_MISC,
22830c6743cSChristian Marangi FIELD_PREP(LP5569_CP_MODE_MASK, LP55XX_CP_BOOST),
22930c6743cSChristian Marangi LP5569_CP_MODE_MASK);
23030c6743cSChristian Marangi if (ret)
23130c6743cSChristian Marangi goto exit;
23230c6743cSChristian Marangi
23330c6743cSChristian Marangi /* Enable LED Open Test */
23430c6743cSChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_MISC2, LP5569_LED_OPEN_TEST,
23530c6743cSChristian Marangi LP5569_LED_OPEN_TEST);
23630c6743cSChristian Marangi if (ret)
23730c6743cSChristian Marangi goto exit;
23830c6743cSChristian Marangi
23930c6743cSChristian Marangi /* Put Device in NORMAL state */
24030c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_ENABLE, LP5569_ENABLE);
24130c6743cSChristian Marangi if (ret)
24230c6743cSChristian Marangi goto exit;
24330c6743cSChristian Marangi
24430c6743cSChristian Marangi /* Wait 500 us for device to enter NORMAL state */
24530c6743cSChristian Marangi usleep_range(500, 750);
24630c6743cSChristian Marangi
24730c6743cSChristian Marangi /* Enable LED and set to 100% brightness */
24830c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
24930c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_LED_PWM_BASE + led_tmp->chan_nr,
25030c6743cSChristian Marangi LED_FULL);
25130c6743cSChristian Marangi if (ret)
25230c6743cSChristian Marangi goto exit;
25330c6743cSChristian Marangi
25430c6743cSChristian Marangi led_tmp++;
25530c6743cSChristian Marangi }
25630c6743cSChristian Marangi
25730c6743cSChristian Marangi /* Wait 500 us for device to fill status regs */
25830c6743cSChristian Marangi usleep_range(500, 750);
25930c6743cSChristian Marangi
26030c6743cSChristian Marangi /* Parse status led fault 1 regs */
26130c6743cSChristian Marangi ret = lp55xx_read(chip, LP5569_REG_LED_FAULT1, &status);
26230c6743cSChristian Marangi if (ret < 0)
26330c6743cSChristian Marangi goto exit;
26430c6743cSChristian Marangi
26530c6743cSChristian Marangi for (i = 0; i < 8; i++)
26630c6743cSChristian Marangi leds_fault[i] = !!((status >> i) & 0x1);
26730c6743cSChristian Marangi
26830c6743cSChristian Marangi /* Parse status led fault 2 regs */
26930c6743cSChristian Marangi ret = lp55xx_read(chip, LP5569_REG_LED_FAULT2, &status);
27030c6743cSChristian Marangi if (ret < 0)
27130c6743cSChristian Marangi goto exit;
27230c6743cSChristian Marangi
27330c6743cSChristian Marangi for (i = 0; i < 1; i++)
27430c6743cSChristian Marangi leds_fault[i + 8] = !!((status >> i) & 0x1);
27530c6743cSChristian Marangi
27630c6743cSChristian Marangi /* Report LED fault */
27730c6743cSChristian Marangi led_tmp = led;
27830c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
27930c6743cSChristian Marangi if (leds_fault[led_tmp->chan_nr])
2806f2fdde9SChristian Marangi pos += sysfs_emit_at(buf, pos, "LED %d OPEN FAIL\n",
28130c6743cSChristian Marangi led_tmp->chan_nr);
28230c6743cSChristian Marangi
28330c6743cSChristian Marangi led_tmp++;
28430c6743cSChristian Marangi }
28530c6743cSChristian Marangi
28630c6743cSChristian Marangi ret = pos;
28730c6743cSChristian Marangi
28830c6743cSChristian Marangi exit:
28930c6743cSChristian Marangi /* Disable LED Open Test */
29030c6743cSChristian Marangi lp55xx_update_bits(chip, LP5569_REG_MISC2, LP5569_LED_OPEN_TEST, 0);
29130c6743cSChristian Marangi
29230c6743cSChristian Marangi led_tmp = led;
29330c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
29430c6743cSChristian Marangi lp55xx_write(chip, LP5569_REG_LED_PWM_BASE + led_tmp->chan_nr, 0);
29530c6743cSChristian Marangi
29630c6743cSChristian Marangi led_tmp++;
29730c6743cSChristian Marangi }
29830c6743cSChristian Marangi
29930c6743cSChristian Marangi return ret;
30030c6743cSChristian Marangi }
30130c6743cSChristian Marangi
lp5569_led_short_test(struct lp55xx_led * led,char * buf)30230c6743cSChristian Marangi static ssize_t lp5569_led_short_test(struct lp55xx_led *led, char *buf)
30330c6743cSChristian Marangi {
30430c6743cSChristian Marangi struct lp55xx_chip *chip = led->chip;
30530c6743cSChristian Marangi struct lp55xx_platform_data *pdata = chip->pdata;
30630c6743cSChristian Marangi bool leds_fault[LP5569_MAX_LEDS];
30730c6743cSChristian Marangi struct lp55xx_led *led_tmp = led;
30830c6743cSChristian Marangi int i, ret, pos = 0;
30930c6743cSChristian Marangi u8 status;
31030c6743cSChristian Marangi
31130c6743cSChristian Marangi /* Set in STANDBY state */
31230c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_ENABLE, 0);
31330c6743cSChristian Marangi if (ret)
31430c6743cSChristian Marangi goto exit;
31530c6743cSChristian Marangi
31630c6743cSChristian Marangi /* Wait 1ms for device to enter STANDBY state */
31730c6743cSChristian Marangi usleep_range(1000, 2000);
31830c6743cSChristian Marangi
31930c6743cSChristian Marangi /* Set Charge Pump to 1x */
32030c6743cSChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_MISC,
32130c6743cSChristian Marangi FIELD_PREP(LP5569_CP_MODE_MASK, LP55XX_CP_BYPASS),
32230c6743cSChristian Marangi LP5569_CP_MODE_MASK);
32330c6743cSChristian Marangi if (ret)
32430c6743cSChristian Marangi goto exit;
32530c6743cSChristian Marangi
32630c6743cSChristian Marangi /* Enable LED and set to 100% brightness and current to 100% (25.5mA) */
32730c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
32830c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_LED_PWM_BASE + led_tmp->chan_nr,
32930c6743cSChristian Marangi LED_FULL);
33030c6743cSChristian Marangi if (ret)
33130c6743cSChristian Marangi goto exit;
33230c6743cSChristian Marangi
33330c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_LED_CURRENT_BASE + led_tmp->chan_nr,
33430c6743cSChristian Marangi LED_FULL);
33530c6743cSChristian Marangi if (ret)
33630c6743cSChristian Marangi goto exit;
33730c6743cSChristian Marangi
33830c6743cSChristian Marangi led_tmp++;
33930c6743cSChristian Marangi }
34030c6743cSChristian Marangi
34130c6743cSChristian Marangi /* Put Device in NORMAL state */
34230c6743cSChristian Marangi ret = lp55xx_write(chip, LP5569_REG_ENABLE, LP5569_ENABLE);
34330c6743cSChristian Marangi if (ret)
34430c6743cSChristian Marangi goto exit;
34530c6743cSChristian Marangi
34630c6743cSChristian Marangi /* Wait 500 us for device to enter NORMAL state */
34730c6743cSChristian Marangi usleep_range(500, 750);
34830c6743cSChristian Marangi
34930c6743cSChristian Marangi /* Enable LED Shorted Test */
35030c6743cSChristian Marangi ret = lp55xx_update_bits(chip, LP5569_REG_MISC2, LP5569_LED_OPEN_TEST,
35130c6743cSChristian Marangi LP5569_LED_SHORT_TEST);
35230c6743cSChristian Marangi if (ret)
35330c6743cSChristian Marangi goto exit;
35430c6743cSChristian Marangi
35530c6743cSChristian Marangi /* Wait 500 us for device to fill status regs */
35630c6743cSChristian Marangi usleep_range(500, 750);
35730c6743cSChristian Marangi
35830c6743cSChristian Marangi /* Parse status led fault 1 regs */
35930c6743cSChristian Marangi ret = lp55xx_read(chip, LP5569_REG_LED_FAULT1, &status);
36030c6743cSChristian Marangi if (ret < 0)
36130c6743cSChristian Marangi goto exit;
36230c6743cSChristian Marangi
36330c6743cSChristian Marangi for (i = 0; i < 8; i++)
36430c6743cSChristian Marangi leds_fault[i] = !!LEDn_STATUS_FAULT(i, status);
36530c6743cSChristian Marangi
36630c6743cSChristian Marangi /* Parse status led fault 2 regs */
36730c6743cSChristian Marangi ret = lp55xx_read(chip, LP5569_REG_LED_FAULT2, &status);
36830c6743cSChristian Marangi if (ret < 0)
36930c6743cSChristian Marangi goto exit;
37030c6743cSChristian Marangi
37130c6743cSChristian Marangi for (i = 0; i < 1; i++)
37230c6743cSChristian Marangi leds_fault[i + 8] = !!LEDn_STATUS_FAULT(i, status);
37330c6743cSChristian Marangi
37430c6743cSChristian Marangi /* Report LED fault */
37530c6743cSChristian Marangi led_tmp = led;
37630c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
37730c6743cSChristian Marangi if (leds_fault[led_tmp->chan_nr])
3786f2fdde9SChristian Marangi pos += sysfs_emit_at(buf, pos, "LED %d SHORTED FAIL\n",
37930c6743cSChristian Marangi led_tmp->chan_nr);
38030c6743cSChristian Marangi
38130c6743cSChristian Marangi led_tmp++;
38230c6743cSChristian Marangi }
38330c6743cSChristian Marangi
38430c6743cSChristian Marangi ret = pos;
38530c6743cSChristian Marangi
38630c6743cSChristian Marangi exit:
38730c6743cSChristian Marangi /* Disable LED Shorted Test */
38830c6743cSChristian Marangi lp55xx_update_bits(chip, LP5569_REG_MISC2, LP5569_LED_SHORT_TEST, 0);
38930c6743cSChristian Marangi
39030c6743cSChristian Marangi led_tmp = led;
39130c6743cSChristian Marangi for (i = 0; i < pdata->num_channels; i++) {
39230c6743cSChristian Marangi lp55xx_write(chip, LP5569_REG_LED_PWM_BASE + led_tmp->chan_nr, 0);
39330c6743cSChristian Marangi
39430c6743cSChristian Marangi led_tmp++;
39530c6743cSChristian Marangi }
39630c6743cSChristian Marangi
39730c6743cSChristian Marangi return ret;
39830c6743cSChristian Marangi }
39930c6743cSChristian Marangi
lp5569_selftest(struct device * dev,struct device_attribute * attr,char * buf)40030c6743cSChristian Marangi static ssize_t lp5569_selftest(struct device *dev,
40130c6743cSChristian Marangi struct device_attribute *attr,
40230c6743cSChristian Marangi char *buf)
40330c6743cSChristian Marangi {
40430c6743cSChristian Marangi struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
40530c6743cSChristian Marangi struct lp55xx_chip *chip = led->chip;
40630c6743cSChristian Marangi int i, pos = 0;
40730c6743cSChristian Marangi
4084137d94fSChristian Marangi guard(mutex)(&chip->lock);
40930c6743cSChristian Marangi
41030c6743cSChristian Marangi /* Test LED Open */
41130c6743cSChristian Marangi pos = lp5569_led_open_test(led, buf);
41230c6743cSChristian Marangi if (pos < 0)
4134137d94fSChristian Marangi return sprintf(buf, "FAIL\n");
41430c6743cSChristian Marangi
41530c6743cSChristian Marangi /* Test LED Shorted */
4166f2fdde9SChristian Marangi pos += lp5569_led_short_test(led, buf);
41730c6743cSChristian Marangi if (pos < 0)
4184137d94fSChristian Marangi return sprintf(buf, "FAIL\n");
41930c6743cSChristian Marangi
42030c6743cSChristian Marangi for (i = 0; i < chip->pdata->num_channels; i++) {
42130c6743cSChristian Marangi /* Restore current */
42230c6743cSChristian Marangi lp55xx_write(chip, LP5569_REG_LED_CURRENT_BASE + led->chan_nr,
42330c6743cSChristian Marangi led->led_current);
42430c6743cSChristian Marangi
42530c6743cSChristian Marangi /* Restore brightness */
42630c6743cSChristian Marangi lp55xx_write(chip, LP5569_REG_LED_PWM_BASE + led->chan_nr,
42730c6743cSChristian Marangi led->brightness);
42830c6743cSChristian Marangi led++;
42930c6743cSChristian Marangi }
43030c6743cSChristian Marangi
4314137d94fSChristian Marangi return pos == 0 ? sysfs_emit(buf, "OK\n") : pos;
43230c6743cSChristian Marangi }
43330c6743cSChristian Marangi
43430c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_MODE(1);
43530c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_MODE(2);
43630c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_MODE(3);
43730c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LEDS(1);
43830c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LEDS(2);
43930c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LEDS(3);
44030c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LOAD(1);
44130c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LOAD(2);
44230c6743cSChristian Marangi LP55XX_DEV_ATTR_ENGINE_LOAD(3);
44330c6743cSChristian Marangi static LP55XX_DEV_ATTR_RO(selftest, lp5569_selftest);
44430c6743cSChristian Marangi LP55XX_DEV_ATTR_MASTER_FADER(1);
44530c6743cSChristian Marangi LP55XX_DEV_ATTR_MASTER_FADER(2);
44630c6743cSChristian Marangi LP55XX_DEV_ATTR_MASTER_FADER(3);
44730c6743cSChristian Marangi static LP55XX_DEV_ATTR_RW(master_fader_leds, lp55xx_show_master_fader_leds,
44830c6743cSChristian Marangi lp55xx_store_master_fader_leds);
44930c6743cSChristian Marangi
45030c6743cSChristian Marangi static struct attribute *lp5569_attributes[] = {
45130c6743cSChristian Marangi &dev_attr_engine1_mode.attr,
45230c6743cSChristian Marangi &dev_attr_engine2_mode.attr,
45330c6743cSChristian Marangi &dev_attr_engine3_mode.attr,
45430c6743cSChristian Marangi &dev_attr_engine1_load.attr,
45530c6743cSChristian Marangi &dev_attr_engine2_load.attr,
45630c6743cSChristian Marangi &dev_attr_engine3_load.attr,
45730c6743cSChristian Marangi &dev_attr_engine1_leds.attr,
45830c6743cSChristian Marangi &dev_attr_engine2_leds.attr,
45930c6743cSChristian Marangi &dev_attr_engine3_leds.attr,
46030c6743cSChristian Marangi &dev_attr_selftest.attr,
46130c6743cSChristian Marangi &dev_attr_master_fader1.attr,
46230c6743cSChristian Marangi &dev_attr_master_fader2.attr,
46330c6743cSChristian Marangi &dev_attr_master_fader3.attr,
46430c6743cSChristian Marangi &dev_attr_master_fader_leds.attr,
46530c6743cSChristian Marangi NULL,
46630c6743cSChristian Marangi };
46730c6743cSChristian Marangi
46830c6743cSChristian Marangi static const struct attribute_group lp5569_group = {
46930c6743cSChristian Marangi .attrs = lp5569_attributes,
47030c6743cSChristian Marangi };
47130c6743cSChristian Marangi
47230c6743cSChristian Marangi /* Chip specific configurations */
47330c6743cSChristian Marangi static struct lp55xx_device_config lp5569_cfg = {
47430c6743cSChristian Marangi .reg_op_mode = {
47530c6743cSChristian Marangi .addr = LP5569_REG_OP_MODE,
47630c6743cSChristian Marangi .shift = LP5569_MODE_ENG_SHIFT,
47730c6743cSChristian Marangi },
47830c6743cSChristian Marangi .reg_exec = {
47930c6743cSChristian Marangi .addr = LP5569_REG_EXEC_CTRL,
48030c6743cSChristian Marangi .shift = LP5569_EXEC_ENG_SHIFT,
48130c6743cSChristian Marangi },
48230c6743cSChristian Marangi .reset = {
48330c6743cSChristian Marangi .addr = LP5569_REG_RESET,
48430c6743cSChristian Marangi .val = LP5569_RESET,
48530c6743cSChristian Marangi },
48630c6743cSChristian Marangi .enable = {
48730c6743cSChristian Marangi .addr = LP5569_REG_ENABLE,
48830c6743cSChristian Marangi .val = LP5569_ENABLE,
48930c6743cSChristian Marangi },
49030c6743cSChristian Marangi .prog_mem_base = {
49130c6743cSChristian Marangi .addr = LP5569_REG_PROG_MEM,
49230c6743cSChristian Marangi },
49330c6743cSChristian Marangi .reg_led_pwm_base = {
49430c6743cSChristian Marangi .addr = LP5569_REG_LED_PWM_BASE,
49530c6743cSChristian Marangi },
49630c6743cSChristian Marangi .reg_led_current_base = {
49730c6743cSChristian Marangi .addr = LP5569_REG_LED_CURRENT_BASE,
49830c6743cSChristian Marangi },
49930c6743cSChristian Marangi .reg_master_fader_base = {
50030c6743cSChristian Marangi .addr = LP5569_REG_MASTER_FADER_BASE,
50130c6743cSChristian Marangi },
50230c6743cSChristian Marangi .reg_led_ctrl_base = {
50330c6743cSChristian Marangi .addr = LP5569_REG_LED_CTRL_BASE,
50430c6743cSChristian Marangi },
50530c6743cSChristian Marangi .pages_per_engine = LP5569_PAGES_PER_ENGINE,
50630c6743cSChristian Marangi .max_channel = LP5569_MAX_LEDS,
50730c6743cSChristian Marangi .post_init_device = lp5569_post_init_device,
50830c6743cSChristian Marangi .brightness_fn = lp55xx_led_brightness,
50930c6743cSChristian Marangi .multicolor_brightness_fn = lp55xx_multicolor_brightness,
51030c6743cSChristian Marangi .set_led_current = lp55xx_set_led_current,
51130c6743cSChristian Marangi .firmware_cb = lp55xx_firmware_loaded_cb,
51230c6743cSChristian Marangi .run_engine = lp5569_run_engine,
51330c6743cSChristian Marangi .dev_attr_group = &lp5569_group,
51430c6743cSChristian Marangi };
51530c6743cSChristian Marangi
51630c6743cSChristian Marangi static const struct i2c_device_id lp5569_id[] = {
51730c6743cSChristian Marangi { "lp5569", .driver_data = (kernel_ulong_t)&lp5569_cfg, },
51830c6743cSChristian Marangi { }
51930c6743cSChristian Marangi };
52030c6743cSChristian Marangi
52130c6743cSChristian Marangi MODULE_DEVICE_TABLE(i2c, lp5569_id);
52230c6743cSChristian Marangi
52330c6743cSChristian Marangi static const struct of_device_id of_lp5569_leds_match[] = {
52430c6743cSChristian Marangi { .compatible = "ti,lp5569", .data = &lp5569_cfg, },
52530c6743cSChristian Marangi {},
52630c6743cSChristian Marangi };
52730c6743cSChristian Marangi
52830c6743cSChristian Marangi MODULE_DEVICE_TABLE(of, of_lp5569_leds_match);
52930c6743cSChristian Marangi
53030c6743cSChristian Marangi static struct i2c_driver lp5569_driver = {
53130c6743cSChristian Marangi .driver = {
5329c5fd279SChristian Marangi .name = "lp5569",
53330c6743cSChristian Marangi .of_match_table = of_lp5569_leds_match,
53430c6743cSChristian Marangi },
53530c6743cSChristian Marangi .probe = lp55xx_probe,
53630c6743cSChristian Marangi .remove = lp55xx_remove,
53730c6743cSChristian Marangi .id_table = lp5569_id,
53830c6743cSChristian Marangi };
53930c6743cSChristian Marangi
54030c6743cSChristian Marangi module_i2c_driver(lp5569_driver);
54130c6743cSChristian Marangi
54230c6743cSChristian Marangi MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
54330c6743cSChristian Marangi MODULE_DESCRIPTION("LP5569 LED engine");
54430c6743cSChristian Marangi MODULE_LICENSE("GPL");
545