xref: /linux/drivers/video/backlight/sy7758.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*110d6769SKancyJoe // SPDX-License-Identifier: GPL-2.0-only
2*110d6769SKancyJoe /*
3*110d6769SKancyJoe  * Silergy SY7758 6-channel High Efficiency LED Driver
4*110d6769SKancyJoe  *
5*110d6769SKancyJoe  * Copyright (C) 2025 Kancy Joe <kancy2333@outlook.com>
6*110d6769SKancyJoe  * Copyright (C) 2026 Linaro Limited
7*110d6769SKancyJoe  * Author: Neil Armstrong <neil.armstrong@linaro.org>
8*110d6769SKancyJoe  */
9*110d6769SKancyJoe #include <linux/backlight.h>
10*110d6769SKancyJoe #include <linux/module.h>
11*110d6769SKancyJoe #include <linux/i2c.h>
12*110d6769SKancyJoe #include <linux/of.h>
13*110d6769SKancyJoe #include <linux/err.h>
14*110d6769SKancyJoe #include <linux/bits.h>
15*110d6769SKancyJoe #include <linux/delay.h>
16*110d6769SKancyJoe #include <linux/regmap.h>
17*110d6769SKancyJoe #include <linux/bitfield.h>
18*110d6769SKancyJoe #include <linux/gpio/consumer.h>
19*110d6769SKancyJoe #include <linux/regulator/consumer.h>
20*110d6769SKancyJoe 
21*110d6769SKancyJoe #define DEFAULT_BRIGHTNESS	1024
22*110d6769SKancyJoe #define MAX_BRIGHTNESS		4080
23*110d6769SKancyJoe #define REG_MAX			0xAE
24*110d6769SKancyJoe 
25*110d6769SKancyJoe /* Registers */
26*110d6769SKancyJoe #define REG_DEV_CTL		0x01
27*110d6769SKancyJoe #define REG_DEV_ID		0x03
28*110d6769SKancyJoe #define REG_BRT_12BIT_L		0x10
29*110d6769SKancyJoe #define REG_BRT_12BIT_H		0x11
30*110d6769SKancyJoe 
31*110d6769SKancyJoe /* OTP memory */
32*110d6769SKancyJoe #define REG_OTP_CFG0		0xA0
33*110d6769SKancyJoe #define REG_OTP_CFG1		0xA1
34*110d6769SKancyJoe #define REG_OTP_CFG2		0xA2
35*110d6769SKancyJoe #define REG_OTP_CFG5		0xA5
36*110d6769SKancyJoe #define REG_OTP_CFG9		0xA9
37*110d6769SKancyJoe 
38*110d6769SKancyJoe /* Fields */
39*110d6769SKancyJoe #define BIT_DEV_CTL_FAST	BIT(7)
40*110d6769SKancyJoe #define MSK_DEV_CTL_BRT_MODE	GENMASK(2, 1)
41*110d6769SKancyJoe #define BIT_DEV_CTL_BL_CTLB	BIT(0)
42*110d6769SKancyJoe 
43*110d6769SKancyJoe #define MSK_BRT_12BIT_L		GENMASK(7, 0)
44*110d6769SKancyJoe #define MSK_BRT_12BIT_H		GENMASK(3, 0)
45*110d6769SKancyJoe 
46*110d6769SKancyJoe #define MSK_CFG0_CURRENT_LOW	GENMASK(7, 0)
47*110d6769SKancyJoe 
48*110d6769SKancyJoe #define BIT_CFG1_PDET_STDBY	BIT(7)
49*110d6769SKancyJoe #define MSK_CFG1_CURRENT_MAX	GENMASK(6, 4)
50*110d6769SKancyJoe #define MSK_CFG1_CURRENT_HIGH	GENMASK(3, 0)
51*110d6769SKancyJoe 
52*110d6769SKancyJoe #define BIT_CFG2_UVLO_EN	BIT(5)
53*110d6769SKancyJoe #define BIT_CFG2_UVLO_TH	BIT(4)
54*110d6769SKancyJoe #define BIT_CFG2_BL_ON		BIT(3)
55*110d6769SKancyJoe #define BIT_CFG2_ISET_EN	BIT(2)
56*110d6769SKancyJoe #define BIT_CFG2_BST_ESET_EN	BIT(1)
57*110d6769SKancyJoe 
58*110d6769SKancyJoe #define BIT_CFG5_PWM_DIRECT	BIT(7)
59*110d6769SKancyJoe #define MSK_CFG5_PS_MODE	GENMASK(6, 4)
60*110d6769SKancyJoe #define MSK_CFG5_PWM_FREQ	GENMASK(3, 0)
61*110d6769SKancyJoe 
62*110d6769SKancyJoe #define MSK_CFG9_VBST_MAX	GENMASK(7, 5)
63*110d6769SKancyJoe #define BIT_CFG9_JUMP_EN	BIT(4)
64*110d6769SKancyJoe #define MSK_CFG9_JUMP_TH	GENMASK(3, 2)
65*110d6769SKancyJoe #define MSK_CFG9_JUMP_VOLTAGE	GENMASK(1, 0)
66*110d6769SKancyJoe 
67*110d6769SKancyJoe struct sy7758 {
68*110d6769SKancyJoe 	struct i2c_client *client;
69*110d6769SKancyJoe 	struct regmap *regmap;
70*110d6769SKancyJoe 	struct gpio_desc *gpio;
71*110d6769SKancyJoe 	struct backlight_device *bl;
72*110d6769SKancyJoe };
73*110d6769SKancyJoe 
74*110d6769SKancyJoe static const struct regmap_config sy7758_regmap_config = {
75*110d6769SKancyJoe 	.reg_bits = 8,
76*110d6769SKancyJoe 	.val_bits = 8,
77*110d6769SKancyJoe 	.max_register = REG_MAX,
78*110d6769SKancyJoe };
79*110d6769SKancyJoe 
80*110d6769SKancyJoe static int sy7758_backlight_update_status(struct backlight_device *backlight_dev)
81*110d6769SKancyJoe {
82*110d6769SKancyJoe 	struct sy7758 *sydev = bl_get_data(backlight_dev);
83*110d6769SKancyJoe 	unsigned int brightness = backlight_get_brightness(backlight_dev);
84*110d6769SKancyJoe 	int ret;
85*110d6769SKancyJoe 
86*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_BRT_12BIT_L,
87*110d6769SKancyJoe 			   FIELD_PREP(MSK_BRT_12BIT_L,
88*110d6769SKancyJoe 				      brightness & 0xff));
89*110d6769SKancyJoe 	if (ret)
90*110d6769SKancyJoe 		return ret;
91*110d6769SKancyJoe 
92*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_BRT_12BIT_H,
93*110d6769SKancyJoe 			   FIELD_PREP(MSK_BRT_12BIT_H,
94*110d6769SKancyJoe 				      (brightness >> 8) & 0xf));
95*110d6769SKancyJoe 	if (ret)
96*110d6769SKancyJoe 		return ret;
97*110d6769SKancyJoe 
98*110d6769SKancyJoe 	return 0;
99*110d6769SKancyJoe }
100*110d6769SKancyJoe 
101*110d6769SKancyJoe static const struct backlight_ops sy7758_backlight_ops = {
102*110d6769SKancyJoe 	.options = BL_CORE_SUSPENDRESUME,
103*110d6769SKancyJoe 	.update_status = sy7758_backlight_update_status,
104*110d6769SKancyJoe };
105*110d6769SKancyJoe 
106*110d6769SKancyJoe static int sy7758_init(struct sy7758 *sydev)
107*110d6769SKancyJoe {
108*110d6769SKancyJoe 	int ret = 0;
109*110d6769SKancyJoe 
110*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_DEV_CTL,
111*110d6769SKancyJoe 			   BIT_DEV_CTL_FAST | BIT_DEV_CTL_BL_CTLB |
112*110d6769SKancyJoe 			   FIELD_PREP(MSK_DEV_CTL_BRT_MODE, 2));
113*110d6769SKancyJoe 	if (ret)
114*110d6769SKancyJoe 		return ret;
115*110d6769SKancyJoe 
116*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_BRT_12BIT_L,
117*110d6769SKancyJoe 			   FIELD_PREP(MSK_BRT_12BIT_L,
118*110d6769SKancyJoe 				      DEFAULT_BRIGHTNESS & 0xff));
119*110d6769SKancyJoe 	if (ret)
120*110d6769SKancyJoe 		return ret;
121*110d6769SKancyJoe 
122*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_BRT_12BIT_H,
123*110d6769SKancyJoe 			   FIELD_PREP(MSK_BRT_12BIT_H,
124*110d6769SKancyJoe 				      (DEFAULT_BRIGHTNESS >> 8)));
125*110d6769SKancyJoe 	if (ret)
126*110d6769SKancyJoe 		return ret;
127*110d6769SKancyJoe 
128*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_OTP_CFG5,
129*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG5_PS_MODE, 6) |
130*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG5_PWM_FREQ, 4));
131*110d6769SKancyJoe 	if (ret)
132*110d6769SKancyJoe 		return ret;
133*110d6769SKancyJoe 
134*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_OTP_CFG0,
135*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG0_CURRENT_LOW, 85));
136*110d6769SKancyJoe 	if (ret)
137*110d6769SKancyJoe 		return ret;
138*110d6769SKancyJoe 
139*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_OTP_CFG1,
140*110d6769SKancyJoe 			   BIT_CFG1_PDET_STDBY |
141*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG1_CURRENT_MAX, 1) |
142*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG1_CURRENT_HIGH, 10));
143*110d6769SKancyJoe 	if (ret)
144*110d6769SKancyJoe 		return ret;
145*110d6769SKancyJoe 
146*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_OTP_CFG9,
147*110d6769SKancyJoe 			   FIELD_PREP(MSK_CFG9_VBST_MAX, 4));
148*110d6769SKancyJoe 	if (ret)
149*110d6769SKancyJoe 		return ret;
150*110d6769SKancyJoe 
151*110d6769SKancyJoe 	ret = regmap_write(sydev->regmap, REG_OTP_CFG2,
152*110d6769SKancyJoe 			   BIT_CFG2_BL_ON | BIT_CFG2_UVLO_EN);
153*110d6769SKancyJoe 	if (ret)
154*110d6769SKancyJoe 		return ret;
155*110d6769SKancyJoe 
156*110d6769SKancyJoe 	return 0;
157*110d6769SKancyJoe }
158*110d6769SKancyJoe 
159*110d6769SKancyJoe static int sy7758_probe(struct i2c_client *client)
160*110d6769SKancyJoe {
161*110d6769SKancyJoe 	struct backlight_properties props = { };
162*110d6769SKancyJoe 	struct device *dev = &client->dev;
163*110d6769SKancyJoe 	struct sy7758 *sydev;
164*110d6769SKancyJoe 	unsigned int dev_id;
165*110d6769SKancyJoe 	int ret;
166*110d6769SKancyJoe 
167*110d6769SKancyJoe 	sydev = devm_kzalloc(dev, sizeof(*sydev), GFP_KERNEL);
168*110d6769SKancyJoe 	if (!sydev)
169*110d6769SKancyJoe 		return -ENOMEM;
170*110d6769SKancyJoe 
171*110d6769SKancyJoe 	i2c_set_clientdata(client, sydev);
172*110d6769SKancyJoe 
173*110d6769SKancyJoe 	/* Initialize regmap */
174*110d6769SKancyJoe 	sydev->client = client;
175*110d6769SKancyJoe 	sydev->regmap = devm_regmap_init_i2c(client, &sy7758_regmap_config);
176*110d6769SKancyJoe 	if (IS_ERR(sydev->regmap))
177*110d6769SKancyJoe 		return dev_err_probe(dev, PTR_ERR(sydev->regmap),
178*110d6769SKancyJoe 				     "failed to init regmap\n");
179*110d6769SKancyJoe 
180*110d6769SKancyJoe 	/* Get and enable regulator */
181*110d6769SKancyJoe 	ret = devm_regulator_get_enable(dev, "vdd");
182*110d6769SKancyJoe 	if (ret)
183*110d6769SKancyJoe 		return dev_err_probe(dev, ret, "failed to get regulator\n");
184*110d6769SKancyJoe 
185*110d6769SKancyJoe 	fsleep(100);
186*110d6769SKancyJoe 
187*110d6769SKancyJoe 	/* Get enable GPIO and set to high */
188*110d6769SKancyJoe 	sydev->gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
189*110d6769SKancyJoe 	if (IS_ERR(sydev->gpio))
190*110d6769SKancyJoe 		return dev_err_probe(dev, PTR_ERR(sydev->gpio),
191*110d6769SKancyJoe 				     "failed to get enable GPIO\n");
192*110d6769SKancyJoe 
193*110d6769SKancyJoe 	/* Let some time for HW to settle */
194*110d6769SKancyJoe 	fsleep(10000);
195*110d6769SKancyJoe 
196*110d6769SKancyJoe 	/* try read and check device id */
197*110d6769SKancyJoe 	ret = regmap_read(sydev->regmap, REG_DEV_ID, &dev_id);
198*110d6769SKancyJoe 	if (ret < 0)
199*110d6769SKancyJoe 		return dev_err_probe(dev, ret, "failed to read device id\n");
200*110d6769SKancyJoe 	if (dev_id != 0x63) {
201*110d6769SKancyJoe 		dev_err(dev, "unexpected device id: 0x%02x\n", dev_id);
202*110d6769SKancyJoe 		return -ENODEV;
203*110d6769SKancyJoe 	}
204*110d6769SKancyJoe 
205*110d6769SKancyJoe 	/* Initialize and set default brightness */
206*110d6769SKancyJoe 	ret = sy7758_init(sydev);
207*110d6769SKancyJoe 	if (ret)
208*110d6769SKancyJoe 		return ret;
209*110d6769SKancyJoe 
210*110d6769SKancyJoe 	props.type = BACKLIGHT_RAW;
211*110d6769SKancyJoe 	props.max_brightness = MAX_BRIGHTNESS;
212*110d6769SKancyJoe 	props.brightness = DEFAULT_BRIGHTNESS;
213*110d6769SKancyJoe 	props.scale = BACKLIGHT_SCALE_LINEAR;
214*110d6769SKancyJoe 
215*110d6769SKancyJoe 	sydev->bl = devm_backlight_device_register(dev, "sy7758-backlight",
216*110d6769SKancyJoe 						   dev, sydev, &sy7758_backlight_ops,
217*110d6769SKancyJoe 						   &props);
218*110d6769SKancyJoe 	if (IS_ERR(sydev->bl))
219*110d6769SKancyJoe 		return dev_err_probe(dev, PTR_ERR(sydev->bl),
220*110d6769SKancyJoe 				     "failed to register backlight device\n");
221*110d6769SKancyJoe 
222*110d6769SKancyJoe 	return backlight_update_status(sydev->bl);
223*110d6769SKancyJoe }
224*110d6769SKancyJoe 
225*110d6769SKancyJoe static void sy7758_remove(struct i2c_client *client)
226*110d6769SKancyJoe {
227*110d6769SKancyJoe 	struct sy7758 *sydev = i2c_get_clientdata(client);
228*110d6769SKancyJoe 
229*110d6769SKancyJoe 	backlight_disable(sydev->bl);
230*110d6769SKancyJoe }
231*110d6769SKancyJoe 
232*110d6769SKancyJoe static const struct i2c_device_id sy7758_ids[] = {
233*110d6769SKancyJoe 	{ "sy7758" },
234*110d6769SKancyJoe 	{ }
235*110d6769SKancyJoe };
236*110d6769SKancyJoe MODULE_DEVICE_TABLE(i2c, sy7758_ids);
237*110d6769SKancyJoe 
238*110d6769SKancyJoe static const struct of_device_id sy7758_match_table[] = {
239*110d6769SKancyJoe 	{ .compatible = "silergy,sy7758", },
240*110d6769SKancyJoe 	{ },
241*110d6769SKancyJoe };
242*110d6769SKancyJoe MODULE_DEVICE_TABLE(of, sy7758_match_table);
243*110d6769SKancyJoe 
244*110d6769SKancyJoe static struct i2c_driver sy7758_driver = {
245*110d6769SKancyJoe 	.driver = {
246*110d6769SKancyJoe 		.name = "sy7758",
247*110d6769SKancyJoe 		.of_match_table = sy7758_match_table,
248*110d6769SKancyJoe 	},
249*110d6769SKancyJoe 	.probe = sy7758_probe,
250*110d6769SKancyJoe 	.remove = sy7758_remove,
251*110d6769SKancyJoe 	.id_table = sy7758_ids,
252*110d6769SKancyJoe };
253*110d6769SKancyJoe 
254*110d6769SKancyJoe module_i2c_driver(sy7758_driver);
255*110d6769SKancyJoe 
256*110d6769SKancyJoe MODULE_DESCRIPTION("Silergy SY7758 Backlight Driver");
257*110d6769SKancyJoe MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>");
258*110d6769SKancyJoe MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
259*110d6769SKancyJoe MODULE_LICENSE("GPL");
260