brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 61cbefa Raw
268 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * LED driver for WM8350 driven LEDS.4 *5 * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.6 */7 8#include <linux/kernel.h>9#include <linux/platform_device.h>10#include <linux/leds.h>11#include <linux/err.h>12#include <linux/mfd/wm8350/pmic.h>13#include <linux/regulator/consumer.h>14#include <linux/slab.h>15#include <linux/module.h>16 17/* Microamps */18static const int isink_cur[] = {19	4,20	5,21	6,22	7,23	8,24	10,25	11,26	14,27	16,28	19,29	23,30	27,31	32,32	39,33	46,34	54,35	65,36	77,37	92,38	109,39	130,40	154,41	183,42	218,43	259,44	308,45	367,46	436,47	518,48	616,49	733,50	872,51	1037,52	1233,53	1466,54	1744,55	2073,56	2466,57	2933,58	3487,59	4147,60	4932,61	5865,62	6975,63	8294,64	9864,65	11730,66	13949,67	16589,68	19728,69	23460,70	27899,71	33178,72	39455,73	46920,74	55798,75	66355,76	78910,77	93840,78	111596,79	132710,80	157820,81	187681,82	22319183};84 85#define to_wm8350_led(led_cdev) \86	container_of(led_cdev, struct wm8350_led, cdev)87 88static int wm8350_led_enable(struct wm8350_led *led)89{90	int ret = 0;91 92	if (led->enabled)93		return ret;94 95	ret = regulator_enable(led->isink);96	if (ret != 0) {97		dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);98		return ret;99	}100 101	ret = regulator_enable(led->dcdc);102	if (ret != 0) {103		dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);104		regulator_disable(led->isink);105		return ret;106	}107 108	led->enabled = 1;109 110	return ret;111}112 113static int wm8350_led_disable(struct wm8350_led *led)114{115	int ret = 0;116 117	if (!led->enabled)118		return ret;119 120	ret = regulator_disable(led->dcdc);121	if (ret != 0) {122		dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);123		return ret;124	}125 126	ret = regulator_disable(led->isink);127	if (ret != 0) {128		dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);129		ret = regulator_enable(led->dcdc);130		if (ret != 0)131			dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",132				ret);133		return ret;134	}135 136	led->enabled = 0;137 138	return ret;139}140 141static int wm8350_led_set(struct led_classdev *led_cdev,142			   enum led_brightness value)143{144	struct wm8350_led *led = to_wm8350_led(led_cdev);145	unsigned long flags;146	int ret;147	int uA;148 149	led->value = value;150 151	spin_lock_irqsave(&led->value_lock, flags);152 153	if (led->value == LED_OFF) {154		spin_unlock_irqrestore(&led->value_lock, flags);155		return wm8350_led_disable(led);156	}157 158	/* This scales linearly into the index of valid current159	 * settings which results in a linear scaling of perceived160	 * brightness due to the non-linear current settings provided161	 * by the hardware.162	 */163	uA = (led->max_uA_index * led->value) / LED_FULL;164	spin_unlock_irqrestore(&led->value_lock, flags);165	BUG_ON(uA >= ARRAY_SIZE(isink_cur));166 167	ret = regulator_set_current_limit(led->isink, isink_cur[uA],168					  isink_cur[uA]);169	if (ret != 0) {170		dev_err(led->cdev.dev, "Failed to set %duA: %d\n",171			isink_cur[uA], ret);172		return ret;173	}174 175	return wm8350_led_enable(led);176}177 178static void wm8350_led_shutdown(struct platform_device *pdev)179{180	struct wm8350_led *led = platform_get_drvdata(pdev);181 182	led->value = LED_OFF;183	wm8350_led_disable(led);184}185 186static int wm8350_led_probe(struct platform_device *pdev)187{188	struct regulator *isink, *dcdc;189	struct wm8350_led *led;190	struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);191	int i;192 193	if (pdata == NULL) {194		dev_err(&pdev->dev, "no platform data\n");195		return -ENODEV;196	}197 198	if (pdata->max_uA < isink_cur[0]) {199		dev_err(&pdev->dev, "Invalid maximum current %duA\n",200			pdata->max_uA);201		return -EINVAL;202	}203 204	isink = devm_regulator_get(&pdev->dev, "led_isink");205	if (IS_ERR(isink)) {206		dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__);207		return PTR_ERR(isink);208	}209 210	dcdc = devm_regulator_get(&pdev->dev, "led_vcc");211	if (IS_ERR(dcdc)) {212		dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__);213		return PTR_ERR(dcdc);214	}215 216	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);217	if (led == NULL)218		return -ENOMEM;219 220	led->cdev.brightness_set_blocking = wm8350_led_set;221	led->cdev.default_trigger = pdata->default_trigger;222	led->cdev.name = pdata->name;223	led->cdev.flags |= LED_CORE_SUSPENDRESUME;224	led->enabled = regulator_is_enabled(isink);225	led->isink = isink;226	led->dcdc = dcdc;227 228	for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)229		if (isink_cur[i] >= pdata->max_uA)230			break;231	led->max_uA_index = i;232	if (pdata->max_uA != isink_cur[i])233		dev_warn(&pdev->dev,234			 "Maximum current %duA is not directly supported,"235			 " check platform data\n",236			 pdata->max_uA);237 238	spin_lock_init(&led->value_lock);239	led->value = LED_OFF;240	platform_set_drvdata(pdev, led);241 242	return led_classdev_register(&pdev->dev, &led->cdev);243}244 245static void wm8350_led_remove(struct platform_device *pdev)246{247	struct wm8350_led *led = platform_get_drvdata(pdev);248 249	led_classdev_unregister(&led->cdev);250	wm8350_led_disable(led);251}252 253static struct platform_driver wm8350_led_driver = {254	.driver = {255		   .name = "wm8350-led",256		   },257	.probe = wm8350_led_probe,258	.remove_new = wm8350_led_remove,259	.shutdown = wm8350_led_shutdown,260};261 262module_platform_driver(wm8350_led_driver);263 264MODULE_AUTHOR("Mark Brown");265MODULE_DESCRIPTION("WM8350 LED driver");266MODULE_LICENSE("GPL");267MODULE_ALIAS("platform:wm8350-led");268