Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2018 Spreadtrum Communications Inc.
  3
  4#include <linux/leds.h>
  5#include <linux/module.h>
  6#include <linux/of.h>
  7#include <linux/platform_device.h>
  8#include <linux/regmap.h>
  9
 10/* PMIC global control register definition */
 11#define SC27XX_MODULE_EN0	0xc08
 12#define SC27XX_CLK_EN0		0xc18
 13#define SC27XX_RGB_CTRL		0xebc
 14
 15#define SC27XX_BLTC_EN		BIT(9)
 16#define SC27XX_RTC_EN		BIT(7)
 17#define SC27XX_RGB_PD		BIT(0)
 18
 19/* Breathing light controller register definition */
 20#define SC27XX_LEDS_CTRL	0x00
 21#define SC27XX_LEDS_PRESCALE	0x04
 22#define SC27XX_LEDS_DUTY	0x08
 23#define SC27XX_LEDS_CURVE0	0x0c
 24#define SC27XX_LEDS_CURVE1	0x10
 25
 26#define SC27XX_CTRL_SHIFT	4
 27#define SC27XX_LED_RUN		BIT(0)
 28#define SC27XX_LED_TYPE		BIT(1)
 29
 30#define SC27XX_DUTY_SHIFT	8
 31#define SC27XX_DUTY_MASK	GENMASK(15, 0)
 32#define SC27XX_MOD_MASK		GENMASK(7, 0)
 33
 34#define SC27XX_CURVE_SHIFT	8
 35#define SC27XX_CURVE_L_MASK	GENMASK(7, 0)
 36#define SC27XX_CURVE_H_MASK	GENMASK(15, 8)
 37
 38#define SC27XX_LEDS_OFFSET	0x10
 39#define SC27XX_LEDS_MAX		3
 40#define SC27XX_LEDS_PATTERN_CNT	4
 41/* Stage duration step, in milliseconds */
 42#define SC27XX_LEDS_STEP	125
 43/* Minimum and maximum duration, in milliseconds */
 44#define SC27XX_DELTA_T_MIN	SC27XX_LEDS_STEP
 45#define SC27XX_DELTA_T_MAX	(SC27XX_LEDS_STEP * 255)
 46
 47struct sc27xx_led {
 48	struct fwnode_handle *fwnode;
 49	struct led_classdev ldev;
 50	struct sc27xx_led_priv *priv;
 51	u8 line;
 52	bool active;
 53};
 54
 55struct sc27xx_led_priv {
 56	struct sc27xx_led leds[SC27XX_LEDS_MAX];
 57	struct regmap *regmap;
 58	struct mutex lock;
 59	u32 base;
 60};
 61
 62#define to_sc27xx_led(ldev) \
 63	container_of(ldev, struct sc27xx_led, ldev)
 64
 65static int sc27xx_led_init(struct regmap *regmap)
 66{
 67	int err;
 68
 69	err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
 70				 SC27XX_BLTC_EN);
 71	if (err)
 72		return err;
 73
 74	err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
 75				 SC27XX_RTC_EN);
 76	if (err)
 77		return err;
 78
 79	return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
 80}
 81
 82static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
 83{
 84	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
 85}
 86
 87static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
 88{
 89	u32 base = sc27xx_led_get_offset(leds);
 90	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
 91	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
 92	struct regmap *regmap = leds->priv->regmap;
 93	int err;
 94
 95	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
 96				 SC27XX_DUTY_MASK,
 97				 (value << SC27XX_DUTY_SHIFT) |
 98				 SC27XX_MOD_MASK);
 99	if (err)
100		return err;
101
102	return regmap_update_bits(regmap, ctrl_base,
103			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
104			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
105}
106
107static int sc27xx_led_disable(struct sc27xx_led *leds)
108{
109	struct regmap *regmap = leds->priv->regmap;
110	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
111	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
112
113	return regmap_update_bits(regmap, ctrl_base,
114			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
115}
116
117static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
118{
119	struct sc27xx_led *leds = to_sc27xx_led(ldev);
120	int err;
121
122	mutex_lock(&leds->priv->lock);
123
124	if (value == LED_OFF)
125		err = sc27xx_led_disable(leds);
126	else
127		err = sc27xx_led_enable(leds, value);
128
129	mutex_unlock(&leds->priv->lock);
130
131	return err;
132}
133
134static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
135{
136	u32 v, offset, t = *delta_t;
137
138	v = t + SC27XX_LEDS_STEP / 2;
139	v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
140	offset = v - SC27XX_DELTA_T_MIN;
141	offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
142
143	*delta_t = SC27XX_DELTA_T_MIN + offset;
144}
145
146static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
147{
148	struct sc27xx_led *leds = to_sc27xx_led(ldev);
149	struct regmap *regmap = leds->priv->regmap;
150	u32 base = sc27xx_led_get_offset(leds);
151	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
152	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
153	int err;
154
155	mutex_lock(&leds->priv->lock);
156
157	/* Reset the rise, high, fall and low time to zero. */
158	regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
159	regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
160
161	err = regmap_update_bits(regmap, ctrl_base,
162			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
163
164	ldev->brightness = LED_OFF;
165
166	mutex_unlock(&leds->priv->lock);
167
168	return err;
169}
170
171static int sc27xx_led_pattern_set(struct led_classdev *ldev,
172				  struct led_pattern *pattern,
173				  u32 len, int repeat)
174{
175	struct sc27xx_led *leds = to_sc27xx_led(ldev);
176	u32 base = sc27xx_led_get_offset(leds);
177	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
178	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
179	struct regmap *regmap = leds->priv->regmap;
180	int err;
181
182	/*
183	 * Must contain 4 tuples to configure the rise time, high time, fall
184	 * time and low time to enable the breathing mode.
185	 */
186	if (len != SC27XX_LEDS_PATTERN_CNT)
187		return -EINVAL;
188
189	mutex_lock(&leds->priv->lock);
190
191	sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
192	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
193				 SC27XX_CURVE_L_MASK,
194				 pattern[0].delta_t / SC27XX_LEDS_STEP);
195	if (err)
196		goto out;
197
198	sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
199	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
200				 SC27XX_CURVE_L_MASK,
201				 pattern[1].delta_t / SC27XX_LEDS_STEP);
202	if (err)
203		goto out;
204
205	sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
206	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
207				 SC27XX_CURVE_H_MASK,
208				 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
209				 SC27XX_CURVE_SHIFT);
210	if (err)
211		goto out;
212
213	sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
214	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
215				 SC27XX_CURVE_H_MASK,
216				 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
217				 SC27XX_CURVE_SHIFT);
218	if (err)
219		goto out;
220
221	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
222				 SC27XX_DUTY_MASK,
223				 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
224				 SC27XX_MOD_MASK);
225	if (err)
226		goto out;
227
228	/* Enable the LED breathing mode */
229	err = regmap_update_bits(regmap, ctrl_base,
230				 SC27XX_LED_RUN << ctrl_shift,
231				 SC27XX_LED_RUN << ctrl_shift);
232	if (!err)
233		ldev->brightness = pattern[1].brightness;
234
235out:
236	mutex_unlock(&leds->priv->lock);
237
238	return err;
239}
240
241static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
242{
243	int i, err;
244
245	err = sc27xx_led_init(priv->regmap);
246	if (err)
247		return err;
248
249	for (i = 0; i < SC27XX_LEDS_MAX; i++) {
250		struct sc27xx_led *led = &priv->leds[i];
251		struct led_init_data init_data = {};
252
253		if (!led->active)
254			continue;
255
256		led->line = i;
257		led->priv = priv;
258		led->ldev.brightness_set_blocking = sc27xx_led_set;
259		led->ldev.pattern_set = sc27xx_led_pattern_set;
260		led->ldev.pattern_clear = sc27xx_led_pattern_clear;
261		led->ldev.default_trigger = "pattern";
262
263		init_data.fwnode = led->fwnode;
264		init_data.devicename = "sc27xx";
265		init_data.default_label = ":";
266
267		err = devm_led_classdev_register_ext(dev, &led->ldev,
268						     &init_data);
269		if (err)
270			return err;
271	}
272
273	return 0;
274}
275
276static int sc27xx_led_probe(struct platform_device *pdev)
277{
278	struct device *dev = &pdev->dev;
279	struct device_node *np = dev_of_node(dev);
280	struct sc27xx_led_priv *priv;
281	u32 base, count, reg;
282	int err;
283
284	count = of_get_available_child_count(np);
285	if (!count || count > SC27XX_LEDS_MAX)
286		return -EINVAL;
287
288	err = of_property_read_u32(np, "reg", &base);
289	if (err) {
290		dev_err(dev, "fail to get reg of property\n");
291		return err;
292	}
293
294	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
295	if (!priv)
296		return -ENOMEM;
297
298	platform_set_drvdata(pdev, priv);
 
299	priv->base = base;
300	priv->regmap = dev_get_regmap(dev->parent, NULL);
301	if (!priv->regmap) {
302		err = -ENODEV;
303		dev_err(dev, "failed to get regmap: %d\n", err);
304		return err;
305	}
306
307	for_each_available_child_of_node_scoped(np, child) {
308		err = of_property_read_u32(child, "reg", &reg);
309		if (err)
 
 
310			return err;
 
311
312		if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active)
 
 
313			return -EINVAL;
 
314
315		priv->leds[reg].fwnode = of_fwnode_handle(child);
316		priv->leds[reg].active = true;
317	}
318
319	mutex_init(&priv->lock);
320
321	err = sc27xx_led_register(dev, priv);
322	if (err)
323		mutex_destroy(&priv->lock);
324
325	return err;
326}
327
328static void sc27xx_led_remove(struct platform_device *pdev)
329{
330	struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
331
332	mutex_destroy(&priv->lock);
 
333}
334
335static const struct of_device_id sc27xx_led_of_match[] = {
336	{ .compatible = "sprd,sc2731-bltc", },
337	{ }
338};
339MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
340
341static struct platform_driver sc27xx_led_driver = {
342	.driver = {
343		.name = "sprd-bltc",
344		.of_match_table = sc27xx_led_of_match,
345	},
346	.probe = sc27xx_led_probe,
347	.remove = sc27xx_led_remove,
348};
349
350module_platform_driver(sc27xx_led_driver);
351
352MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
353MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
354MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
355MODULE_LICENSE("GPL v2");
v5.14.15
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2018 Spreadtrum Communications Inc.
  3
  4#include <linux/leds.h>
  5#include <linux/module.h>
  6#include <linux/of.h>
  7#include <linux/platform_device.h>
  8#include <linux/regmap.h>
  9
 10/* PMIC global control register definition */
 11#define SC27XX_MODULE_EN0	0xc08
 12#define SC27XX_CLK_EN0		0xc18
 13#define SC27XX_RGB_CTRL		0xebc
 14
 15#define SC27XX_BLTC_EN		BIT(9)
 16#define SC27XX_RTC_EN		BIT(7)
 17#define SC27XX_RGB_PD		BIT(0)
 18
 19/* Breathing light controller register definition */
 20#define SC27XX_LEDS_CTRL	0x00
 21#define SC27XX_LEDS_PRESCALE	0x04
 22#define SC27XX_LEDS_DUTY	0x08
 23#define SC27XX_LEDS_CURVE0	0x0c
 24#define SC27XX_LEDS_CURVE1	0x10
 25
 26#define SC27XX_CTRL_SHIFT	4
 27#define SC27XX_LED_RUN		BIT(0)
 28#define SC27XX_LED_TYPE		BIT(1)
 29
 30#define SC27XX_DUTY_SHIFT	8
 31#define SC27XX_DUTY_MASK	GENMASK(15, 0)
 32#define SC27XX_MOD_MASK		GENMASK(7, 0)
 33
 34#define SC27XX_CURVE_SHIFT	8
 35#define SC27XX_CURVE_L_MASK	GENMASK(7, 0)
 36#define SC27XX_CURVE_H_MASK	GENMASK(15, 8)
 37
 38#define SC27XX_LEDS_OFFSET	0x10
 39#define SC27XX_LEDS_MAX		3
 40#define SC27XX_LEDS_PATTERN_CNT	4
 41/* Stage duration step, in milliseconds */
 42#define SC27XX_LEDS_STEP	125
 43/* Minimum and maximum duration, in milliseconds */
 44#define SC27XX_DELTA_T_MIN	SC27XX_LEDS_STEP
 45#define SC27XX_DELTA_T_MAX	(SC27XX_LEDS_STEP * 255)
 46
 47struct sc27xx_led {
 48	struct fwnode_handle *fwnode;
 49	struct led_classdev ldev;
 50	struct sc27xx_led_priv *priv;
 51	u8 line;
 52	bool active;
 53};
 54
 55struct sc27xx_led_priv {
 56	struct sc27xx_led leds[SC27XX_LEDS_MAX];
 57	struct regmap *regmap;
 58	struct mutex lock;
 59	u32 base;
 60};
 61
 62#define to_sc27xx_led(ldev) \
 63	container_of(ldev, struct sc27xx_led, ldev)
 64
 65static int sc27xx_led_init(struct regmap *regmap)
 66{
 67	int err;
 68
 69	err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
 70				 SC27XX_BLTC_EN);
 71	if (err)
 72		return err;
 73
 74	err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
 75				 SC27XX_RTC_EN);
 76	if (err)
 77		return err;
 78
 79	return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
 80}
 81
 82static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
 83{
 84	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
 85}
 86
 87static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
 88{
 89	u32 base = sc27xx_led_get_offset(leds);
 90	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
 91	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
 92	struct regmap *regmap = leds->priv->regmap;
 93	int err;
 94
 95	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
 96				 SC27XX_DUTY_MASK,
 97				 (value << SC27XX_DUTY_SHIFT) |
 98				 SC27XX_MOD_MASK);
 99	if (err)
100		return err;
101
102	return regmap_update_bits(regmap, ctrl_base,
103			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
104			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
105}
106
107static int sc27xx_led_disable(struct sc27xx_led *leds)
108{
109	struct regmap *regmap = leds->priv->regmap;
110	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
111	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
112
113	return regmap_update_bits(regmap, ctrl_base,
114			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
115}
116
117static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
118{
119	struct sc27xx_led *leds = to_sc27xx_led(ldev);
120	int err;
121
122	mutex_lock(&leds->priv->lock);
123
124	if (value == LED_OFF)
125		err = sc27xx_led_disable(leds);
126	else
127		err = sc27xx_led_enable(leds, value);
128
129	mutex_unlock(&leds->priv->lock);
130
131	return err;
132}
133
134static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
135{
136	u32 v, offset, t = *delta_t;
137
138	v = t + SC27XX_LEDS_STEP / 2;
139	v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
140	offset = v - SC27XX_DELTA_T_MIN;
141	offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
142
143	*delta_t = SC27XX_DELTA_T_MIN + offset;
144}
145
146static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
147{
148	struct sc27xx_led *leds = to_sc27xx_led(ldev);
149	struct regmap *regmap = leds->priv->regmap;
150	u32 base = sc27xx_led_get_offset(leds);
151	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
152	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
153	int err;
154
155	mutex_lock(&leds->priv->lock);
156
157	/* Reset the rise, high, fall and low time to zero. */
158	regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
159	regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
160
161	err = regmap_update_bits(regmap, ctrl_base,
162			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
163
164	ldev->brightness = LED_OFF;
165
166	mutex_unlock(&leds->priv->lock);
167
168	return err;
169}
170
171static int sc27xx_led_pattern_set(struct led_classdev *ldev,
172				  struct led_pattern *pattern,
173				  u32 len, int repeat)
174{
175	struct sc27xx_led *leds = to_sc27xx_led(ldev);
176	u32 base = sc27xx_led_get_offset(leds);
177	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
178	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
179	struct regmap *regmap = leds->priv->regmap;
180	int err;
181
182	/*
183	 * Must contain 4 tuples to configure the rise time, high time, fall
184	 * time and low time to enable the breathing mode.
185	 */
186	if (len != SC27XX_LEDS_PATTERN_CNT)
187		return -EINVAL;
188
189	mutex_lock(&leds->priv->lock);
190
191	sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
192	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
193				 SC27XX_CURVE_L_MASK,
194				 pattern[0].delta_t / SC27XX_LEDS_STEP);
195	if (err)
196		goto out;
197
198	sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
199	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
200				 SC27XX_CURVE_L_MASK,
201				 pattern[1].delta_t / SC27XX_LEDS_STEP);
202	if (err)
203		goto out;
204
205	sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
206	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
207				 SC27XX_CURVE_H_MASK,
208				 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
209				 SC27XX_CURVE_SHIFT);
210	if (err)
211		goto out;
212
213	sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
214	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
215				 SC27XX_CURVE_H_MASK,
216				 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
217				 SC27XX_CURVE_SHIFT);
218	if (err)
219		goto out;
220
221	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
222				 SC27XX_DUTY_MASK,
223				 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
224				 SC27XX_MOD_MASK);
225	if (err)
226		goto out;
227
228	/* Enable the LED breathing mode */
229	err = regmap_update_bits(regmap, ctrl_base,
230				 SC27XX_LED_RUN << ctrl_shift,
231				 SC27XX_LED_RUN << ctrl_shift);
232	if (!err)
233		ldev->brightness = pattern[1].brightness;
234
235out:
236	mutex_unlock(&leds->priv->lock);
237
238	return err;
239}
240
241static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
242{
243	int i, err;
244
245	err = sc27xx_led_init(priv->regmap);
246	if (err)
247		return err;
248
249	for (i = 0; i < SC27XX_LEDS_MAX; i++) {
250		struct sc27xx_led *led = &priv->leds[i];
251		struct led_init_data init_data = {};
252
253		if (!led->active)
254			continue;
255
256		led->line = i;
257		led->priv = priv;
258		led->ldev.brightness_set_blocking = sc27xx_led_set;
259		led->ldev.pattern_set = sc27xx_led_pattern_set;
260		led->ldev.pattern_clear = sc27xx_led_pattern_clear;
261		led->ldev.default_trigger = "pattern";
262
263		init_data.fwnode = led->fwnode;
264		init_data.devicename = "sc27xx";
265		init_data.default_label = ":";
266
267		err = devm_led_classdev_register_ext(dev, &led->ldev,
268						     &init_data);
269		if (err)
270			return err;
271	}
272
273	return 0;
274}
275
276static int sc27xx_led_probe(struct platform_device *pdev)
277{
278	struct device *dev = &pdev->dev;
279	struct device_node *np = dev_of_node(dev), *child;
280	struct sc27xx_led_priv *priv;
281	u32 base, count, reg;
282	int err;
283
284	count = of_get_available_child_count(np);
285	if (!count || count > SC27XX_LEDS_MAX)
286		return -EINVAL;
287
288	err = of_property_read_u32(np, "reg", &base);
289	if (err) {
290		dev_err(dev, "fail to get reg of property\n");
291		return err;
292	}
293
294	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
295	if (!priv)
296		return -ENOMEM;
297
298	platform_set_drvdata(pdev, priv);
299	mutex_init(&priv->lock);
300	priv->base = base;
301	priv->regmap = dev_get_regmap(dev->parent, NULL);
302	if (!priv->regmap) {
303		err = -ENODEV;
304		dev_err(dev, "failed to get regmap: %d\n", err);
305		return err;
306	}
307
308	for_each_available_child_of_node(np, child) {
309		err = of_property_read_u32(child, "reg", &reg);
310		if (err) {
311			of_node_put(child);
312			mutex_destroy(&priv->lock);
313			return err;
314		}
315
316		if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
317			of_node_put(child);
318			mutex_destroy(&priv->lock);
319			return -EINVAL;
320		}
321
322		priv->leds[reg].fwnode = of_fwnode_handle(child);
323		priv->leds[reg].active = true;
324	}
325
 
 
326	err = sc27xx_led_register(dev, priv);
327	if (err)
328		mutex_destroy(&priv->lock);
329
330	return err;
331}
332
333static int sc27xx_led_remove(struct platform_device *pdev)
334{
335	struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
336
337	mutex_destroy(&priv->lock);
338	return 0;
339}
340
341static const struct of_device_id sc27xx_led_of_match[] = {
342	{ .compatible = "sprd,sc2731-bltc", },
343	{ }
344};
345MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
346
347static struct platform_driver sc27xx_led_driver = {
348	.driver = {
349		.name = "sprd-bltc",
350		.of_match_table = sc27xx_led_of_match,
351	},
352	.probe = sc27xx_led_probe,
353	.remove = sc27xx_led_remove,
354};
355
356module_platform_driver(sc27xx_led_driver);
357
358MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
359MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
360MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
361MODULE_LICENSE("GPL v2");