18325642dSAndreas Kemnade // SPDX-License-Identifier: GPL-2.0-only
28325642dSAndreas Kemnade /*
38325642dSAndreas Kemnade * Copyright (C) 2023 Andreas Kemnade
48325642dSAndreas Kemnade *
58325642dSAndreas Kemnade * Datasheet:
68325642dSAndreas Kemnade * https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf
78325642dSAndreas Kemnade *
88325642dSAndreas Kemnade * If LED brightness cannot be controlled independently due to shared
98325642dSAndreas Kemnade * brightness registers, max_brightness is set to 1 and only on/off
108325642dSAndreas Kemnade * is possible for the affected LED pair.
118325642dSAndreas Kemnade */
128325642dSAndreas Kemnade
138325642dSAndreas Kemnade #include <linux/i2c.h>
148325642dSAndreas Kemnade #include <linux/leds.h>
158325642dSAndreas Kemnade #include <linux/module.h>
168325642dSAndreas Kemnade #include <linux/mod_devicetable.h>
178325642dSAndreas Kemnade #include <linux/property.h>
188325642dSAndreas Kemnade #include <linux/regmap.h>
198325642dSAndreas Kemnade #include <linux/slab.h>
208325642dSAndreas Kemnade
218325642dSAndreas Kemnade #define BD2606_MAX_LEDS 6
228325642dSAndreas Kemnade #define BD2606_MAX_BRIGHTNESS 63
238325642dSAndreas Kemnade #define BD2606_REG_PWRCNT 3
248325642dSAndreas Kemnade #define ldev_to_led(c) container_of(c, struct bd2606mvv_led, ldev)
258325642dSAndreas Kemnade
268325642dSAndreas Kemnade struct bd2606mvv_led {
278325642dSAndreas Kemnade unsigned int led_no;
288325642dSAndreas Kemnade struct led_classdev ldev;
298325642dSAndreas Kemnade struct bd2606mvv_priv *priv;
308325642dSAndreas Kemnade };
318325642dSAndreas Kemnade
328325642dSAndreas Kemnade struct bd2606mvv_priv {
338325642dSAndreas Kemnade struct bd2606mvv_led leds[BD2606_MAX_LEDS];
348325642dSAndreas Kemnade struct regmap *regmap;
358325642dSAndreas Kemnade };
368325642dSAndreas Kemnade
378325642dSAndreas Kemnade static int
bd2606mvv_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)388325642dSAndreas Kemnade bd2606mvv_brightness_set(struct led_classdev *led_cdev,
398325642dSAndreas Kemnade enum led_brightness brightness)
408325642dSAndreas Kemnade {
418325642dSAndreas Kemnade struct bd2606mvv_led *led = ldev_to_led(led_cdev);
428325642dSAndreas Kemnade struct bd2606mvv_priv *priv = led->priv;
438325642dSAndreas Kemnade int err;
448325642dSAndreas Kemnade
458325642dSAndreas Kemnade if (brightness == 0)
468325642dSAndreas Kemnade return regmap_update_bits(priv->regmap,
478325642dSAndreas Kemnade BD2606_REG_PWRCNT,
488325642dSAndreas Kemnade 1 << led->led_no,
498325642dSAndreas Kemnade 0);
508325642dSAndreas Kemnade
518325642dSAndreas Kemnade /* shared brightness register */
528325642dSAndreas Kemnade err = regmap_write(priv->regmap, led->led_no / 2,
538325642dSAndreas Kemnade led_cdev->max_brightness == 1 ?
548325642dSAndreas Kemnade BD2606_MAX_BRIGHTNESS : brightness);
558325642dSAndreas Kemnade if (err)
568325642dSAndreas Kemnade return err;
578325642dSAndreas Kemnade
588325642dSAndreas Kemnade return regmap_update_bits(priv->regmap,
598325642dSAndreas Kemnade BD2606_REG_PWRCNT,
608325642dSAndreas Kemnade 1 << led->led_no,
618325642dSAndreas Kemnade 1 << led->led_no);
628325642dSAndreas Kemnade }
638325642dSAndreas Kemnade
648325642dSAndreas Kemnade static const struct regmap_config bd2606mvv_regmap = {
658325642dSAndreas Kemnade .reg_bits = 8,
668325642dSAndreas Kemnade .val_bits = 8,
678325642dSAndreas Kemnade .max_register = 0x3,
688325642dSAndreas Kemnade };
698325642dSAndreas Kemnade
bd2606mvv_probe(struct i2c_client * client)708325642dSAndreas Kemnade static int bd2606mvv_probe(struct i2c_client *client)
718325642dSAndreas Kemnade {
728325642dSAndreas Kemnade struct device *dev = &client->dev;
738325642dSAndreas Kemnade struct bd2606mvv_priv *priv;
748325642dSAndreas Kemnade struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };
758325642dSAndreas Kemnade int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };
768325642dSAndreas Kemnade int err, reg;
77*bbf297b4SJavier Carrasco int i, j;
788325642dSAndreas Kemnade
79*bbf297b4SJavier Carrasco if (!dev_fwnode(dev))
808325642dSAndreas Kemnade return -ENODEV;
818325642dSAndreas Kemnade
828325642dSAndreas Kemnade priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
838325642dSAndreas Kemnade if (!priv)
848325642dSAndreas Kemnade return -ENOMEM;
858325642dSAndreas Kemnade
868325642dSAndreas Kemnade priv->regmap = devm_regmap_init_i2c(client, &bd2606mvv_regmap);
878325642dSAndreas Kemnade if (IS_ERR(priv->regmap)) {
888325642dSAndreas Kemnade err = PTR_ERR(priv->regmap);
898325642dSAndreas Kemnade dev_err(dev, "Failed to allocate register map: %d\n", err);
908325642dSAndreas Kemnade return err;
918325642dSAndreas Kemnade }
928325642dSAndreas Kemnade
938325642dSAndreas Kemnade i2c_set_clientdata(client, priv);
948325642dSAndreas Kemnade
95*bbf297b4SJavier Carrasco device_for_each_child_node_scoped(dev, child) {
968325642dSAndreas Kemnade struct bd2606mvv_led *led;
978325642dSAndreas Kemnade
988325642dSAndreas Kemnade err = fwnode_property_read_u32(child, "reg", ®);
99*bbf297b4SJavier Carrasco if (err)
1008325642dSAndreas Kemnade return err;
101*bbf297b4SJavier Carrasco
102*bbf297b4SJavier Carrasco if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg])
1038325642dSAndreas Kemnade return -EINVAL;
104*bbf297b4SJavier Carrasco
1058325642dSAndreas Kemnade led = &priv->leds[reg];
106*bbf297b4SJavier Carrasco led_fwnodes[reg] = fwnode_handle_get(child);
1078325642dSAndreas Kemnade active_pairs[reg / 2]++;
1088325642dSAndreas Kemnade led->priv = priv;
1098325642dSAndreas Kemnade led->led_no = reg;
1108325642dSAndreas Kemnade led->ldev.brightness_set_blocking = bd2606mvv_brightness_set;
1118325642dSAndreas Kemnade led->ldev.max_brightness = BD2606_MAX_BRIGHTNESS;
1128325642dSAndreas Kemnade }
1138325642dSAndreas Kemnade
1148325642dSAndreas Kemnade for (i = 0; i < BD2606_MAX_LEDS; i++) {
1158325642dSAndreas Kemnade struct led_init_data init_data = {};
1168325642dSAndreas Kemnade
1178325642dSAndreas Kemnade if (!led_fwnodes[i])
1188325642dSAndreas Kemnade continue;
1198325642dSAndreas Kemnade
1208325642dSAndreas Kemnade init_data.fwnode = led_fwnodes[i];
1218325642dSAndreas Kemnade /* Check whether brightness can be independently adjusted. */
1228325642dSAndreas Kemnade if (active_pairs[i / 2] == 2)
1238325642dSAndreas Kemnade priv->leds[i].ldev.max_brightness = 1;
1248325642dSAndreas Kemnade
1258325642dSAndreas Kemnade err = devm_led_classdev_register_ext(dev,
1268325642dSAndreas Kemnade &priv->leds[i].ldev,
1278325642dSAndreas Kemnade &init_data);
1288325642dSAndreas Kemnade if (err < 0) {
129*bbf297b4SJavier Carrasco for (j = i; j < BD2606_MAX_LEDS; j++)
130*bbf297b4SJavier Carrasco fwnode_handle_put(led_fwnodes[j]);
1318325642dSAndreas Kemnade return dev_err_probe(dev, err,
1328325642dSAndreas Kemnade "couldn't register LED %s\n",
1338325642dSAndreas Kemnade priv->leds[i].ldev.name);
1348325642dSAndreas Kemnade }
1358325642dSAndreas Kemnade }
1368325642dSAndreas Kemnade return 0;
1378325642dSAndreas Kemnade }
1388325642dSAndreas Kemnade
1398325642dSAndreas Kemnade static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match[] = {
1408325642dSAndreas Kemnade { .compatible = "rohm,bd2606mvv", },
1418325642dSAndreas Kemnade {},
1428325642dSAndreas Kemnade };
1438325642dSAndreas Kemnade MODULE_DEVICE_TABLE(of, of_bd2606mvv_leds_match);
1448325642dSAndreas Kemnade
1458325642dSAndreas Kemnade static struct i2c_driver bd2606mvv_driver = {
1468325642dSAndreas Kemnade .driver = {
1478325642dSAndreas Kemnade .name = "leds-bd2606mvv",
1488325642dSAndreas Kemnade .of_match_table = of_match_ptr(of_bd2606mvv_leds_match),
1498325642dSAndreas Kemnade },
150d9ff8a8eSUwe Kleine-König .probe = bd2606mvv_probe,
1518325642dSAndreas Kemnade };
1528325642dSAndreas Kemnade
1538325642dSAndreas Kemnade module_i2c_driver(bd2606mvv_driver);
1548325642dSAndreas Kemnade
1558325642dSAndreas Kemnade MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");
1568325642dSAndreas Kemnade MODULE_DESCRIPTION("BD2606 LED driver");
1578325642dSAndreas Kemnade MODULE_LICENSE("GPL");
158