brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 848e929 Raw
205 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * leds-regulator.c - LED class driver for regulator driven LEDs.4 *5 * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>6 *7 * Inspired by leds-wm8350 driver.8 */9 10#include <linux/module.h>11#include <linux/mod_devicetable.h>12#include <linux/err.h>13#include <linux/slab.h>14#include <linux/leds.h>15#include <linux/leds-regulator.h>16#include <linux/platform_device.h>17#include <linux/regulator/consumer.h>18 19#define to_regulator_led(led_cdev) \20	container_of(led_cdev, struct regulator_led, cdev)21 22struct regulator_led {23	struct led_classdev cdev;24	int enabled;25	struct mutex mutex;26 27	struct regulator *vcc;28};29 30static inline int led_regulator_get_max_brightness(struct regulator *supply)31{32	int ret;33	int voltage = regulator_list_voltage(supply, 0);34 35	if (voltage <= 0)36		return 1;37 38	/* even if regulator can't change voltages,39	 * we still assume it can change status40	 * and the LED can be turned on and off.41	 */42	ret = regulator_set_voltage(supply, voltage, voltage);43	if (ret < 0)44		return 1;45 46	return regulator_count_voltages(supply);47}48 49static int led_regulator_get_voltage(struct regulator *supply,50		enum led_brightness brightness)51{52	if (brightness == 0)53		return -EINVAL;54 55	return regulator_list_voltage(supply, brightness - 1);56}57 58 59static void regulator_led_enable(struct regulator_led *led)60{61	int ret;62 63	if (led->enabled)64		return;65 66	ret = regulator_enable(led->vcc);67	if (ret != 0) {68		dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);69		return;70	}71 72	led->enabled = 1;73}74 75static void regulator_led_disable(struct regulator_led *led)76{77	int ret;78 79	if (!led->enabled)80		return;81 82	ret = regulator_disable(led->vcc);83	if (ret != 0) {84		dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);85		return;86	}87 88	led->enabled = 0;89}90 91static int regulator_led_brightness_set(struct led_classdev *led_cdev,92					 enum led_brightness value)93{94	struct regulator_led *led = to_regulator_led(led_cdev);95	int voltage;96	int ret = 0;97 98	mutex_lock(&led->mutex);99 100	if (value == LED_OFF) {101		regulator_led_disable(led);102		goto out;103	}104 105	if (led->cdev.max_brightness > 1) {106		voltage = led_regulator_get_voltage(led->vcc, value);107		dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",108				value, voltage);109 110		ret = regulator_set_voltage(led->vcc, voltage, voltage);111		if (ret != 0)112			dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",113				voltage, ret);114	}115 116	regulator_led_enable(led);117 118out:119	mutex_unlock(&led->mutex);120	return ret;121}122 123static int regulator_led_probe(struct platform_device *pdev)124{125	struct led_regulator_platform_data *pdata =126			dev_get_platdata(&pdev->dev);127	struct device *dev = &pdev->dev;128	struct led_init_data init_data = {};129	struct regulator_led *led;130	struct regulator *vcc;131	int ret = 0;132 133	vcc = devm_regulator_get_exclusive(dev, "vled");134	if (IS_ERR(vcc)) {135		dev_err(dev, "Cannot get vcc\n");136		return PTR_ERR(vcc);137	}138 139	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);140	if (led == NULL)141		return -ENOMEM;142 143	init_data.fwnode = dev->fwnode;144 145	led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);146	/* Legacy platform data label assignment */147	if (pdata) {148		if (pdata->brightness > led->cdev.max_brightness) {149			dev_err(dev, "Invalid default brightness %d\n",150				pdata->brightness);151			return -EINVAL;152		}153		led->cdev.brightness = pdata->brightness;154		init_data.default_label = pdata->name;155	}156 157	led->cdev.brightness_set_blocking = regulator_led_brightness_set;158	led->cdev.flags |= LED_CORE_SUSPENDRESUME;159	led->vcc = vcc;160 161	/* to handle correctly an already enabled regulator */162	if (regulator_is_enabled(led->vcc))163		led->enabled = 1;164 165	mutex_init(&led->mutex);166 167	platform_set_drvdata(pdev, led);168 169	ret = led_classdev_register_ext(dev, &led->cdev, &init_data);170	if (ret < 0)171		return ret;172 173	return 0;174}175 176static void regulator_led_remove(struct platform_device *pdev)177{178	struct regulator_led *led = platform_get_drvdata(pdev);179 180	led_classdev_unregister(&led->cdev);181	regulator_led_disable(led);182}183 184static const struct of_device_id regulator_led_of_match[] = {185	{ .compatible = "regulator-led", },186	{}187};188MODULE_DEVICE_TABLE(of, regulator_led_of_match);189 190static struct platform_driver regulator_led_driver = {191	.driver = {192		.name  = "leds-regulator",193		.of_match_table = regulator_led_of_match,194	},195	.probe  = regulator_led_probe,196	.remove_new = regulator_led_remove,197};198 199module_platform_driver(regulator_led_driver);200 201MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");202MODULE_DESCRIPTION("Regulator driven LED driver");203MODULE_LICENSE("GPL");204MODULE_ALIAS("platform:leds-regulator");205