Skip to content

Commit 34452ac

Browse files
committed
video: backlight: Add support for Richtek RT8555 Backlight
The Richtek RT8555 is a 6 channel LED driver that supports up to 35mA/LED. This is a backlight driver that drives ILED1. Signed-off-by: Michael Abood <[email protected]>
1 parent 01ef533 commit 34452ac

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

Diff for: drivers/video/backlight/Kconfig

+9
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,15 @@ config BACKLIGHT_RT4831
297297
It's commonly used to drive the display WLED. There're four channels
298298
inisde, and each channel can provide up to 30mA current.
299299

300+
config BACKLIGHT_RT8555
301+
tristate "Richtek RT8555 Backlight Driver"
302+
depends on I2C
303+
select REGMAP_I2C
304+
help
305+
This enables support for Richtek RT8555 Backlight driver.
306+
It's commonly used to drive the display WLED. There are six channels
307+
inisde, and each channel can provide up to 35mA current.
308+
300309
config BACKLIGHT_SAHARA
301310
tristate "Tabletkiosk Sahara Touch-iT Backlight Driver"
302311
depends on X86

Diff for: drivers/video/backlight/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
5050
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
5151
obj-$(CONFIG_BACKLIGHT_QCOM_WLED) += qcom-wled.o
5252
obj-$(CONFIG_BACKLIGHT_RT4831) += rt4831-backlight.o
53+
obj-$(CONFIG_BACKLIGHT_RT8555) += rt8555-backlight.o
5354
obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o
5455
obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o
5556
obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o

Diff for: drivers/video/backlight/rt8555-backlight.c

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/err.h>
4+
#include <linux/backlight.h>
5+
#include <linux/bitops.h>
6+
#include <linux/kernel.h>
7+
#include <linux/module.h>
8+
#include <linux/gpio/consumer.h>
9+
#include <linux/platform_device.h>
10+
#include <linux/regmap.h>
11+
12+
#define RT8555_MAX_BRIGHTNESS 1023
13+
14+
/* Pulled from datashet */
15+
#define RT8555_DEFAULT_CURRENT_LIMIT 0x92
16+
#define RT8555_DEFAULT_LED_DRIVER_HEADROOM 0x00
17+
18+
#define RT8555_REG_CFG0 0x00
19+
#define RT8555_REG_CFG1 0x01
20+
#define RT8555_REG_CFG8 0x08
21+
22+
#define RT8555_REG_ILED_CURRENT_LIMIT 0x02
23+
#define RT8555_REG_ILED1_LSB 0x04
24+
#define RT8555_REG_ILED1_MSB 0x05
25+
26+
#define RT8555_DIM_MODE_MASK BIT(0)
27+
#define RT8555_BRIGHTNESS_SOURCE_MASK BIT(1)
28+
#define RT8555_CHANGE_DUTY_MASK GENMASK(3, 2)
29+
#define RT8555_LED_DRIVER_HEADROOM_MASK GENMASK(3, 2)
30+
#define RT8555_MIX_26K_MASK BIT(7)
31+
#define RT8555_EN10BIT_MASK BIT(7)
32+
#define RT8555_LSB_MASK GENMASK(7, 0)
33+
34+
struct rt8555_priv {
35+
struct device *dev;
36+
struct regmap *regmap;
37+
struct backlight_device *bl;
38+
struct gpio_desc *enable;
39+
40+
int change_duty;
41+
int driver_headroom;
42+
int current_limit;
43+
bool pwm_dim_mode;
44+
};
45+
46+
static int rt8555_bl_enable(struct rt8555_priv *priv)
47+
{
48+
int ret;
49+
50+
if (!IS_ERR_OR_NULL(priv->enable)) {
51+
gpiod_set_value(priv->enable, 1);
52+
53+
/* Wait for RT8555 to power on */
54+
usleep_range(10000, 20000);
55+
}
56+
57+
/* Set 10 bit mode */
58+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG1, RT8555_EN10BIT_MASK, 0xFF);
59+
if (ret)
60+
return ret;
61+
62+
/* Depend on i2c for brightness */
63+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG0,
64+
RT8555_BRIGHTNESS_SOURCE_MASK, 0xFF);
65+
if (ret)
66+
return ret;
67+
68+
/* Whether we use PWM or mixed mode for dimming */
69+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG0, RT8555_DIM_MODE_MASK,
70+
priv->pwm_dim_mode ? 0x00 : 0xFF);
71+
if (ret)
72+
return ret;
73+
74+
/* "Change Duty" for Mixed Mode */
75+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG0, RT8555_CHANGE_DUTY_MASK,
76+
priv->change_duty << 2);
77+
if (ret)
78+
return ret;
79+
80+
/* Mixed mode frequency source, use fixed 26khz clock instead of PWM pin */
81+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG0, RT8555_MIX_26K_MASK, 0xFF);
82+
if (ret)
83+
return ret;
84+
85+
ret = regmap_update_bits(priv->regmap, RT8555_REG_CFG8, RT8555_LED_DRIVER_HEADROOM_MASK,
86+
priv->driver_headroom << 2);
87+
if (ret)
88+
return ret;
89+
90+
ret = regmap_write(priv->regmap, RT8555_REG_ILED_CURRENT_LIMIT, priv->current_limit);
91+
if (ret)
92+
return ret;
93+
94+
return ret;
95+
}
96+
97+
static int rt8555_bl_is_disabled(struct rt8555_priv *priv)
98+
{
99+
if (!IS_ERR_OR_NULL(priv->enable))
100+
return gpiod_get_value(priv->enable) == 0;
101+
else
102+
return 0;
103+
}
104+
105+
static int rt8555_bl_update_status(struct backlight_device *bl_dev)
106+
{
107+
struct rt8555_priv *priv = bl_get_data(bl_dev);
108+
unsigned int brightness = backlight_get_brightness(bl_dev);
109+
int ret;
110+
__le16 brightness_le = cpu_to_le16(brightness);
111+
112+
/* Enable the IC (if disabled) before setting the brightness */
113+
if (brightness && !IS_ERR_OR_NULL(priv->enable)) {
114+
if (rt8555_bl_is_disabled(priv)) {
115+
ret = rt8555_bl_enable(priv);
116+
if (ret) {
117+
dev_err(priv->dev, "rt8555_bl_enable failed with error %d", ret);
118+
return ret;
119+
}
120+
}
121+
}
122+
123+
ret = regmap_raw_write(priv->regmap, RT8555_REG_ILED1_LSB, &brightness_le, 2);
124+
125+
/* Disable the IC after setting it to 0 */
126+
if (brightness == 0)
127+
if (!IS_ERR_OR_NULL(priv->enable))
128+
gpiod_set_value(priv->enable, 0);
129+
130+
return 0;
131+
}
132+
133+
static int rt8555_bl_get_brightness(struct backlight_device *bl_dev)
134+
{
135+
struct rt8555_priv *priv = bl_get_data(bl_dev);
136+
int ret;
137+
__le16 brightness;
138+
139+
/*
140+
* If the RT8555 is disabled, there's no reason to turn it on just to read
141+
* it back
142+
*/
143+
if (rt8555_bl_is_disabled(priv))
144+
return 0;
145+
146+
ret = regmap_raw_read(priv->regmap, RT8555_REG_ILED1_LSB, &brightness, 2);
147+
if (ret)
148+
return ret;
149+
150+
return le16_to_cpu(brightness);
151+
}
152+
153+
static const struct backlight_ops rt8555_bl_ops = {
154+
.options = BL_CORE_SUSPENDRESUME,
155+
.update_status = rt8555_bl_update_status,
156+
.get_brightness = rt8555_bl_get_brightness,
157+
};
158+
159+
static const struct regmap_config rt8555_regmap_config = {
160+
.reg_bits = 8,
161+
.val_bits = 8,
162+
};
163+
164+
static int rt8555_bl_probe(struct i2c_client *client)
165+
{
166+
struct rt8555_priv *priv;
167+
struct backlight_properties bl_props = {
168+
.type = BACKLIGHT_RAW, .scale = BACKLIGHT_SCALE_LINEAR
169+
};
170+
int ret;
171+
u32 brightness;
172+
173+
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
174+
if (!priv)
175+
return -ENOMEM;
176+
177+
priv->dev = &client->dev;
178+
179+
priv->enable = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH);
180+
181+
priv->regmap = devm_regmap_init_i2c(client, &rt8555_regmap_config);
182+
if (!priv->regmap) {
183+
dev_err(&client->dev, "Failed to init regmap\n");
184+
return -ENODEV;
185+
}
186+
187+
ret = device_property_read_u32(&client->dev, "max-brightness", &brightness);
188+
if (ret)
189+
brightness = RT8555_MAX_BRIGHTNESS;
190+
191+
bl_props.max_brightness = min_t(u32, brightness, RT8555_MAX_BRIGHTNESS);
192+
193+
ret = device_property_read_u32(&client->dev, "default-brightness", &brightness);
194+
if (ret)
195+
brightness = bl_props.max_brightness;
196+
197+
bl_props.brightness = min_t(u32, brightness, bl_props.max_brightness);
198+
199+
priv->pwm_dim_mode = device_property_read_bool(&client->dev, "use-pwm-dimming-mode");
200+
201+
ret = device_property_read_u32(&client->dev, "change-duty", &priv->change_duty);
202+
if (ret)
203+
priv->change_duty = 3;
204+
205+
/* i2c register is only 2 bits, clamp value */
206+
priv->change_duty = clamp(priv->change_duty, 0, 3);
207+
208+
ret = device_property_read_u32(&client->dev, "driver-headroom", &priv->driver_headroom);
209+
if (ret)
210+
priv->driver_headroom = RT8555_DEFAULT_LED_DRIVER_HEADROOM;
211+
212+
priv->driver_headroom = clamp(priv->driver_headroom, 0, 3);
213+
214+
ret = device_property_read_u32(&client->dev, "current-limit", &priv->current_limit);
215+
if (ret)
216+
priv->current_limit = RT8555_DEFAULT_CURRENT_LIMIT;
217+
218+
priv->bl = devm_backlight_device_register(&client->dev, client->name, &client->dev, priv,
219+
&rt8555_bl_ops, &bl_props);
220+
if (IS_ERR(priv->bl)) {
221+
dev_err(&client->dev, "Failed to register backlight\n");
222+
return PTR_ERR(priv->bl);
223+
}
224+
225+
ret = rt8555_bl_enable(priv);
226+
if (ret) {
227+
dev_err(priv->dev, "rt8555_bl_enable failed with error %d",
228+
ret);
229+
return ret;
230+
}
231+
232+
backlight_update_status(priv->bl);
233+
i2c_set_clientdata(client, priv);
234+
235+
return 0;
236+
}
237+
238+
static void rt8555_bl_remove(struct i2c_client *client)
239+
{
240+
struct rt8555_priv *priv = i2c_get_clientdata(client);
241+
struct backlight_device *bl_dev = priv->bl;
242+
243+
bl_dev->props.brightness = 0;
244+
backlight_update_status(priv->bl);
245+
246+
if (!IS_ERR_OR_NULL(priv->enable))
247+
gpiod_set_value(priv->enable, 0);
248+
}
249+
250+
static const struct of_device_id __maybe_unused rt8555_bl_of_match[] = {
251+
{ .compatible = "richtek,rt8555-backlight", },
252+
{}
253+
};
254+
MODULE_DEVICE_TABLE(of, rt8555_bl_of_match);
255+
256+
static struct i2c_driver rt8555_bl_driver = {
257+
.driver = {
258+
.name = "rt8555-backlight",
259+
.of_match_table = rt8555_bl_of_match
260+
},
261+
.probe_new = rt8555_bl_probe,
262+
.shutdown = rt8555_bl_remove
263+
};
264+
module_i2c_driver(rt8555_bl_driver);
265+
MODULE_AUTHOR("Michael Abood <[email protected]>");
266+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)