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