Skip to content

Commit 8325642

Browse files
akemnadelag-linaro
authored andcommitted
leds: bd2606mvv: Driver for the Rohm 6 Channel i2c LED driver
The device provides 6 channels which can be individually turned off and on but groups of two channels share a common brightness register. Limitation: The GPIO to enable the device is not used yet. Signed-off-by: Andreas Kemnade <andreas@kemnade.info> Reviewed-by: Matti Vaittinen <mazziesaccount@gmail.com> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Lee Jones <lee@kernel.org> Link: https://lore.kernel.org/r/20230419111806.1100437-3-andreas@kemnade.info
1 parent 36cd9fb commit 8325642

3 files changed

Lines changed: 175 additions & 0 deletions

File tree

drivers/leds/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,20 @@ config LEDS_REGULATOR
551551
help
552552
This option enables support for regulator driven LEDs.
553553

554+
config LEDS_BD2606MVV
555+
tristate "LED driver for BD2606MVV"
556+
depends on LEDS_CLASS
557+
depends on I2C
558+
select REGMAP_I2C
559+
help
560+
This option enables support for BD2606MVV LED driver chips
561+
accessed via the I2C bus. It supports setting brightness, with
562+
the limitiation that there are groups of two channels sharing
563+
a brightness setting, but not the on/off setting.
564+
565+
To compile this driver as a module, choose M here: the module will
566+
be called leds-bd2606mvv.
567+
554568
config LEDS_BD2802
555569
tristate "LED driver for BD2802 RGB LED"
556570
depends on LEDS_CLASS

drivers/leds/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o
1717
obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o
1818
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
1919
obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
20+
obj-$(CONFIG_LEDS_BD2606MVV) += leds-bd2606mvv.o
2021
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
2122
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
2223
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o

drivers/leds/leds-bd2606mvv.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2023 Andreas Kemnade
4+
*
5+
* Datasheet:
6+
* https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf
7+
*
8+
* If LED brightness cannot be controlled independently due to shared
9+
* brightness registers, max_brightness is set to 1 and only on/off
10+
* is possible for the affected LED pair.
11+
*/
12+
13+
#include <linux/i2c.h>
14+
#include <linux/leds.h>
15+
#include <linux/module.h>
16+
#include <linux/mod_devicetable.h>
17+
#include <linux/property.h>
18+
#include <linux/regmap.h>
19+
#include <linux/slab.h>
20+
21+
#define BD2606_MAX_LEDS 6
22+
#define BD2606_MAX_BRIGHTNESS 63
23+
#define BD2606_REG_PWRCNT 3
24+
#define ldev_to_led(c) container_of(c, struct bd2606mvv_led, ldev)
25+
26+
struct bd2606mvv_led {
27+
unsigned int led_no;
28+
struct led_classdev ldev;
29+
struct bd2606mvv_priv *priv;
30+
};
31+
32+
struct bd2606mvv_priv {
33+
struct bd2606mvv_led leds[BD2606_MAX_LEDS];
34+
struct regmap *regmap;
35+
};
36+
37+
static int
38+
bd2606mvv_brightness_set(struct led_classdev *led_cdev,
39+
enum led_brightness brightness)
40+
{
41+
struct bd2606mvv_led *led = ldev_to_led(led_cdev);
42+
struct bd2606mvv_priv *priv = led->priv;
43+
int err;
44+
45+
if (brightness == 0)
46+
return regmap_update_bits(priv->regmap,
47+
BD2606_REG_PWRCNT,
48+
1 << led->led_no,
49+
0);
50+
51+
/* shared brightness register */
52+
err = regmap_write(priv->regmap, led->led_no / 2,
53+
led_cdev->max_brightness == 1 ?
54+
BD2606_MAX_BRIGHTNESS : brightness);
55+
if (err)
56+
return err;
57+
58+
return regmap_update_bits(priv->regmap,
59+
BD2606_REG_PWRCNT,
60+
1 << led->led_no,
61+
1 << led->led_no);
62+
}
63+
64+
static const struct regmap_config bd2606mvv_regmap = {
65+
.reg_bits = 8,
66+
.val_bits = 8,
67+
.max_register = 0x3,
68+
};
69+
70+
static int bd2606mvv_probe(struct i2c_client *client)
71+
{
72+
struct fwnode_handle *np, *child;
73+
struct device *dev = &client->dev;
74+
struct bd2606mvv_priv *priv;
75+
struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };
76+
int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };
77+
int err, reg;
78+
int i;
79+
80+
np = dev_fwnode(dev);
81+
if (!np)
82+
return -ENODEV;
83+
84+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
85+
if (!priv)
86+
return -ENOMEM;
87+
88+
priv->regmap = devm_regmap_init_i2c(client, &bd2606mvv_regmap);
89+
if (IS_ERR(priv->regmap)) {
90+
err = PTR_ERR(priv->regmap);
91+
dev_err(dev, "Failed to allocate register map: %d\n", err);
92+
return err;
93+
}
94+
95+
i2c_set_clientdata(client, priv);
96+
97+
fwnode_for_each_available_child_node(np, child) {
98+
struct bd2606mvv_led *led;
99+
100+
err = fwnode_property_read_u32(child, "reg", &reg);
101+
if (err) {
102+
fwnode_handle_put(child);
103+
return err;
104+
}
105+
if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg]) {
106+
fwnode_handle_put(child);
107+
return -EINVAL;
108+
}
109+
led = &priv->leds[reg];
110+
led_fwnodes[reg] = child;
111+
active_pairs[reg / 2]++;
112+
led->priv = priv;
113+
led->led_no = reg;
114+
led->ldev.brightness_set_blocking = bd2606mvv_brightness_set;
115+
led->ldev.max_brightness = BD2606_MAX_BRIGHTNESS;
116+
}
117+
118+
for (i = 0; i < BD2606_MAX_LEDS; i++) {
119+
struct led_init_data init_data = {};
120+
121+
if (!led_fwnodes[i])
122+
continue;
123+
124+
init_data.fwnode = led_fwnodes[i];
125+
/* Check whether brightness can be independently adjusted. */
126+
if (active_pairs[i / 2] == 2)
127+
priv->leds[i].ldev.max_brightness = 1;
128+
129+
err = devm_led_classdev_register_ext(dev,
130+
&priv->leds[i].ldev,
131+
&init_data);
132+
if (err < 0) {
133+
fwnode_handle_put(child);
134+
return dev_err_probe(dev, err,
135+
"couldn't register LED %s\n",
136+
priv->leds[i].ldev.name);
137+
}
138+
}
139+
return 0;
140+
}
141+
142+
static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match[] = {
143+
{ .compatible = "rohm,bd2606mvv", },
144+
{},
145+
};
146+
MODULE_DEVICE_TABLE(of, of_bd2606mvv_leds_match);
147+
148+
static struct i2c_driver bd2606mvv_driver = {
149+
.driver = {
150+
.name = "leds-bd2606mvv",
151+
.of_match_table = of_match_ptr(of_bd2606mvv_leds_match),
152+
},
153+
.probe_new = bd2606mvv_probe,
154+
};
155+
156+
module_i2c_driver(bd2606mvv_driver);
157+
158+
MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");
159+
MODULE_DESCRIPTION("BD2606 LED driver");
160+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)