xref: /linux/drivers/leds/leds-lp5569.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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