brintos

brintos / linux-shallow public Read only

0
0
Text · 3.9 KiB · c1181a3 Raw
158 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2023 Andreas Kemnade4 *5 * Datasheet:6 * https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf7 *8 * If LED brightness cannot be controlled independently due to shared9 * brightness registers, max_brightness is set to 1 and only on/off10 * 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 622#define BD2606_MAX_BRIGHTNESS 6323#define BD2606_REG_PWRCNT 324#define ldev_to_led(c)	container_of(c, struct bd2606mvv_led, ldev)25 26struct bd2606mvv_led {27	unsigned int led_no;28	struct led_classdev ldev;29	struct bd2606mvv_priv *priv;30};31 32struct bd2606mvv_priv {33	struct bd2606mvv_led leds[BD2606_MAX_LEDS];34	struct regmap *regmap;35};36 37static int38bd2606mvv_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 64static const struct regmap_config bd2606mvv_regmap = {65	.reg_bits = 8,66	.val_bits = 8,67	.max_register = 0x3,68};69 70static int bd2606mvv_probe(struct i2c_client *client)71{72	struct device *dev = &client->dev;73	struct bd2606mvv_priv *priv;74	struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };75	int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };76	int err, reg;77	int i, j;78 79	if (!dev_fwnode(dev))80		return -ENODEV;81 82	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);83	if (!priv)84		return -ENOMEM;85 86	priv->regmap = devm_regmap_init_i2c(client, &bd2606mvv_regmap);87	if (IS_ERR(priv->regmap)) {88		err = PTR_ERR(priv->regmap);89		dev_err(dev, "Failed to allocate register map: %d\n", err);90		return err;91	}92 93	i2c_set_clientdata(client, priv);94 95	device_for_each_child_node_scoped(dev, child) {96		struct bd2606mvv_led *led;97 98		err = fwnode_property_read_u32(child, "reg", &reg);99		if (err)100			return err;101 102		if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg])103			return -EINVAL;104 105		led = &priv->leds[reg];106		led_fwnodes[reg] = fwnode_handle_get(child);107		active_pairs[reg / 2]++;108		led->priv = priv;109		led->led_no = reg;110		led->ldev.brightness_set_blocking = bd2606mvv_brightness_set;111		led->ldev.max_brightness = BD2606_MAX_BRIGHTNESS;112	}113 114	for (i = 0; i < BD2606_MAX_LEDS; i++) {115		struct led_init_data init_data = {};116 117		if (!led_fwnodes[i])118			continue;119 120		init_data.fwnode = led_fwnodes[i];121		/* Check whether brightness can be independently adjusted. */122		if (active_pairs[i / 2] == 2)123			priv->leds[i].ldev.max_brightness = 1;124 125		err = devm_led_classdev_register_ext(dev,126						     &priv->leds[i].ldev,127						     &init_data);128		if (err < 0) {129			for (j = i; j < BD2606_MAX_LEDS; j++)130				fwnode_handle_put(led_fwnodes[j]);131			return dev_err_probe(dev, err,132					     "couldn't register LED %s\n",133					     priv->leds[i].ldev.name);134		}135	}136	return 0;137}138 139static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match[] = {140	{ .compatible = "rohm,bd2606mvv", },141	{},142};143MODULE_DEVICE_TABLE(of, of_bd2606mvv_leds_match);144 145static struct i2c_driver bd2606mvv_driver = {146	.driver   = {147		.name    = "leds-bd2606mvv",148		.of_match_table = of_match_ptr(of_bd2606mvv_leds_match),149	},150	.probe = bd2606mvv_probe,151};152 153module_i2c_driver(bd2606mvv_driver);154 155MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");156MODULE_DESCRIPTION("BD2606 LED driver");157MODULE_LICENSE("GPL");158