1e081c49eSBaolin Wang // SPDX-License-Identifier: GPL-2.0
2e081c49eSBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc.
3e081c49eSBaolin Wang
4e081c49eSBaolin Wang #include <linux/leds.h>
5e081c49eSBaolin Wang #include <linux/module.h>
6e081c49eSBaolin Wang #include <linux/of.h>
7e081c49eSBaolin Wang #include <linux/platform_device.h>
8e081c49eSBaolin Wang #include <linux/regmap.h>
9e081c49eSBaolin Wang
10e081c49eSBaolin Wang /* PMIC global control register definition */
11e081c49eSBaolin Wang #define SC27XX_MODULE_EN0 0xc08
12e081c49eSBaolin Wang #define SC27XX_CLK_EN0 0xc18
13e081c49eSBaolin Wang #define SC27XX_RGB_CTRL 0xebc
14e081c49eSBaolin Wang
15e081c49eSBaolin Wang #define SC27XX_BLTC_EN BIT(9)
16e081c49eSBaolin Wang #define SC27XX_RTC_EN BIT(7)
17e081c49eSBaolin Wang #define SC27XX_RGB_PD BIT(0)
18e081c49eSBaolin Wang
19e081c49eSBaolin Wang /* Breathing light controller register definition */
20e081c49eSBaolin Wang #define SC27XX_LEDS_CTRL 0x00
21e081c49eSBaolin Wang #define SC27XX_LEDS_PRESCALE 0x04
22e081c49eSBaolin Wang #define SC27XX_LEDS_DUTY 0x08
23e081c49eSBaolin Wang #define SC27XX_LEDS_CURVE0 0x0c
24e081c49eSBaolin Wang #define SC27XX_LEDS_CURVE1 0x10
25e081c49eSBaolin Wang
26e081c49eSBaolin Wang #define SC27XX_CTRL_SHIFT 4
27e081c49eSBaolin Wang #define SC27XX_LED_RUN BIT(0)
28e081c49eSBaolin Wang #define SC27XX_LED_TYPE BIT(1)
29e081c49eSBaolin Wang
30e081c49eSBaolin Wang #define SC27XX_DUTY_SHIFT 8
31e081c49eSBaolin Wang #define SC27XX_DUTY_MASK GENMASK(15, 0)
32e081c49eSBaolin Wang #define SC27XX_MOD_MASK GENMASK(7, 0)
33e081c49eSBaolin Wang
348dbac65fSBaolin Wang #define SC27XX_CURVE_SHIFT 8
358dbac65fSBaolin Wang #define SC27XX_CURVE_L_MASK GENMASK(7, 0)
368dbac65fSBaolin Wang #define SC27XX_CURVE_H_MASK GENMASK(15, 8)
378dbac65fSBaolin Wang
38e081c49eSBaolin Wang #define SC27XX_LEDS_OFFSET 0x10
39e081c49eSBaolin Wang #define SC27XX_LEDS_MAX 3
408dbac65fSBaolin Wang #define SC27XX_LEDS_PATTERN_CNT 4
418dbac65fSBaolin Wang /* Stage duration step, in milliseconds */
428dbac65fSBaolin Wang #define SC27XX_LEDS_STEP 125
438dbac65fSBaolin Wang /* Minimum and maximum duration, in milliseconds */
448dbac65fSBaolin Wang #define SC27XX_DELTA_T_MIN SC27XX_LEDS_STEP
458dbac65fSBaolin Wang #define SC27XX_DELTA_T_MAX (SC27XX_LEDS_STEP * 255)
46e081c49eSBaolin Wang
47e081c49eSBaolin Wang struct sc27xx_led {
485fdf85a0SJacek Anaszewski struct fwnode_handle *fwnode;
49e081c49eSBaolin Wang struct led_classdev ldev;
50e081c49eSBaolin Wang struct sc27xx_led_priv *priv;
51e081c49eSBaolin Wang u8 line;
52e081c49eSBaolin Wang bool active;
53e081c49eSBaolin Wang };
54e081c49eSBaolin Wang
55e081c49eSBaolin Wang struct sc27xx_led_priv {
56e081c49eSBaolin Wang struct sc27xx_led leds[SC27XX_LEDS_MAX];
57e081c49eSBaolin Wang struct regmap *regmap;
58e081c49eSBaolin Wang struct mutex lock;
59e081c49eSBaolin Wang u32 base;
60e081c49eSBaolin Wang };
61e081c49eSBaolin Wang
62e081c49eSBaolin Wang #define to_sc27xx_led(ldev) \
63e081c49eSBaolin Wang container_of(ldev, struct sc27xx_led, ldev)
64e081c49eSBaolin Wang
sc27xx_led_init(struct regmap * regmap)65e081c49eSBaolin Wang static int sc27xx_led_init(struct regmap *regmap)
66e081c49eSBaolin Wang {
67e081c49eSBaolin Wang int err;
68e081c49eSBaolin Wang
69e081c49eSBaolin Wang err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
70e081c49eSBaolin Wang SC27XX_BLTC_EN);
71e081c49eSBaolin Wang if (err)
72e081c49eSBaolin Wang return err;
73e081c49eSBaolin Wang
74e081c49eSBaolin Wang err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
75e081c49eSBaolin Wang SC27XX_RTC_EN);
76e081c49eSBaolin Wang if (err)
77e081c49eSBaolin Wang return err;
78e081c49eSBaolin Wang
79e081c49eSBaolin Wang return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
80e081c49eSBaolin Wang }
81e081c49eSBaolin Wang
sc27xx_led_get_offset(struct sc27xx_led * leds)82e081c49eSBaolin Wang static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
83e081c49eSBaolin Wang {
84e081c49eSBaolin Wang return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
85e081c49eSBaolin Wang }
86e081c49eSBaolin Wang
sc27xx_led_enable(struct sc27xx_led * leds,enum led_brightness value)87e081c49eSBaolin Wang static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
88e081c49eSBaolin Wang {
89e081c49eSBaolin Wang u32 base = sc27xx_led_get_offset(leds);
90e081c49eSBaolin Wang u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
91e081c49eSBaolin Wang u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
92e081c49eSBaolin Wang struct regmap *regmap = leds->priv->regmap;
93e081c49eSBaolin Wang int err;
94e081c49eSBaolin Wang
95e081c49eSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
96e081c49eSBaolin Wang SC27XX_DUTY_MASK,
97e081c49eSBaolin Wang (value << SC27XX_DUTY_SHIFT) |
98e081c49eSBaolin Wang SC27XX_MOD_MASK);
99e081c49eSBaolin Wang if (err)
100e081c49eSBaolin Wang return err;
101e081c49eSBaolin Wang
102e081c49eSBaolin Wang return regmap_update_bits(regmap, ctrl_base,
103e081c49eSBaolin Wang (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
104e081c49eSBaolin Wang (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
105e081c49eSBaolin Wang }
106e081c49eSBaolin Wang
sc27xx_led_disable(struct sc27xx_led * leds)107e081c49eSBaolin Wang static int sc27xx_led_disable(struct sc27xx_led *leds)
108e081c49eSBaolin Wang {
109e081c49eSBaolin Wang struct regmap *regmap = leds->priv->regmap;
110e081c49eSBaolin Wang u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
111e081c49eSBaolin Wang u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
112e081c49eSBaolin Wang
113e081c49eSBaolin Wang return regmap_update_bits(regmap, ctrl_base,
114e081c49eSBaolin Wang (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
115e081c49eSBaolin Wang }
116e081c49eSBaolin Wang
sc27xx_led_set(struct led_classdev * ldev,enum led_brightness value)117e081c49eSBaolin Wang static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
118e081c49eSBaolin Wang {
119e081c49eSBaolin Wang struct sc27xx_led *leds = to_sc27xx_led(ldev);
120e081c49eSBaolin Wang int err;
121e081c49eSBaolin Wang
122e081c49eSBaolin Wang mutex_lock(&leds->priv->lock);
123e081c49eSBaolin Wang
124e081c49eSBaolin Wang if (value == LED_OFF)
125e081c49eSBaolin Wang err = sc27xx_led_disable(leds);
126e081c49eSBaolin Wang else
127e081c49eSBaolin Wang err = sc27xx_led_enable(leds, value);
128e081c49eSBaolin Wang
129e081c49eSBaolin Wang mutex_unlock(&leds->priv->lock);
130e081c49eSBaolin Wang
131e081c49eSBaolin Wang return err;
132e081c49eSBaolin Wang }
133e081c49eSBaolin Wang
sc27xx_led_clamp_align_delta_t(u32 * delta_t)1348dbac65fSBaolin Wang static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
1358dbac65fSBaolin Wang {
1368dbac65fSBaolin Wang u32 v, offset, t = *delta_t;
1378dbac65fSBaolin Wang
1388dbac65fSBaolin Wang v = t + SC27XX_LEDS_STEP / 2;
1398dbac65fSBaolin Wang v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
1408dbac65fSBaolin Wang offset = v - SC27XX_DELTA_T_MIN;
1418dbac65fSBaolin Wang offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
1428dbac65fSBaolin Wang
1438dbac65fSBaolin Wang *delta_t = SC27XX_DELTA_T_MIN + offset;
1448dbac65fSBaolin Wang }
1458dbac65fSBaolin Wang
sc27xx_led_pattern_clear(struct led_classdev * ldev)1468dbac65fSBaolin Wang static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
1478dbac65fSBaolin Wang {
1488dbac65fSBaolin Wang struct sc27xx_led *leds = to_sc27xx_led(ldev);
1498dbac65fSBaolin Wang struct regmap *regmap = leds->priv->regmap;
1508dbac65fSBaolin Wang u32 base = sc27xx_led_get_offset(leds);
1518dbac65fSBaolin Wang u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
1528dbac65fSBaolin Wang u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
1538dbac65fSBaolin Wang int err;
1548dbac65fSBaolin Wang
1558dbac65fSBaolin Wang mutex_lock(&leds->priv->lock);
1568dbac65fSBaolin Wang
1578dbac65fSBaolin Wang /* Reset the rise, high, fall and low time to zero. */
1588dbac65fSBaolin Wang regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
1598dbac65fSBaolin Wang regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
1608dbac65fSBaolin Wang
1618dbac65fSBaolin Wang err = regmap_update_bits(regmap, ctrl_base,
1628dbac65fSBaolin Wang (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
1638dbac65fSBaolin Wang
1648dbac65fSBaolin Wang ldev->brightness = LED_OFF;
1658dbac65fSBaolin Wang
1668dbac65fSBaolin Wang mutex_unlock(&leds->priv->lock);
1678dbac65fSBaolin Wang
1688dbac65fSBaolin Wang return err;
1698dbac65fSBaolin Wang }
1708dbac65fSBaolin Wang
sc27xx_led_pattern_set(struct led_classdev * ldev,struct led_pattern * pattern,u32 len,int repeat)1718dbac65fSBaolin Wang static int sc27xx_led_pattern_set(struct led_classdev *ldev,
1728dbac65fSBaolin Wang struct led_pattern *pattern,
1738dbac65fSBaolin Wang u32 len, int repeat)
1748dbac65fSBaolin Wang {
1758dbac65fSBaolin Wang struct sc27xx_led *leds = to_sc27xx_led(ldev);
1768dbac65fSBaolin Wang u32 base = sc27xx_led_get_offset(leds);
1778dbac65fSBaolin Wang u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
1788dbac65fSBaolin Wang u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
1798dbac65fSBaolin Wang struct regmap *regmap = leds->priv->regmap;
1808dbac65fSBaolin Wang int err;
1818dbac65fSBaolin Wang
1828dbac65fSBaolin Wang /*
1838dbac65fSBaolin Wang * Must contain 4 tuples to configure the rise time, high time, fall
1848dbac65fSBaolin Wang * time and low time to enable the breathing mode.
1858dbac65fSBaolin Wang */
1868dbac65fSBaolin Wang if (len != SC27XX_LEDS_PATTERN_CNT)
1878dbac65fSBaolin Wang return -EINVAL;
1888dbac65fSBaolin Wang
1898dbac65fSBaolin Wang mutex_lock(&leds->priv->lock);
1908dbac65fSBaolin Wang
1918dbac65fSBaolin Wang sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
1928dbac65fSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
1938dbac65fSBaolin Wang SC27XX_CURVE_L_MASK,
1948dbac65fSBaolin Wang pattern[0].delta_t / SC27XX_LEDS_STEP);
1958dbac65fSBaolin Wang if (err)
1968dbac65fSBaolin Wang goto out;
1978dbac65fSBaolin Wang
1988dbac65fSBaolin Wang sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
1998dbac65fSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
2008dbac65fSBaolin Wang SC27XX_CURVE_L_MASK,
2018dbac65fSBaolin Wang pattern[1].delta_t / SC27XX_LEDS_STEP);
2028dbac65fSBaolin Wang if (err)
2038dbac65fSBaolin Wang goto out;
2048dbac65fSBaolin Wang
2058dbac65fSBaolin Wang sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
2068dbac65fSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
2078dbac65fSBaolin Wang SC27XX_CURVE_H_MASK,
2088dbac65fSBaolin Wang (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
2098dbac65fSBaolin Wang SC27XX_CURVE_SHIFT);
2108dbac65fSBaolin Wang if (err)
2118dbac65fSBaolin Wang goto out;
2128dbac65fSBaolin Wang
2138dbac65fSBaolin Wang sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
2148dbac65fSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
2158dbac65fSBaolin Wang SC27XX_CURVE_H_MASK,
2168dbac65fSBaolin Wang (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
2178dbac65fSBaolin Wang SC27XX_CURVE_SHIFT);
2188dbac65fSBaolin Wang if (err)
2198dbac65fSBaolin Wang goto out;
2208dbac65fSBaolin Wang
2218dbac65fSBaolin Wang err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
2228dbac65fSBaolin Wang SC27XX_DUTY_MASK,
2238dbac65fSBaolin Wang (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
2248dbac65fSBaolin Wang SC27XX_MOD_MASK);
2258dbac65fSBaolin Wang if (err)
2268dbac65fSBaolin Wang goto out;
2278dbac65fSBaolin Wang
2288dbac65fSBaolin Wang /* Enable the LED breathing mode */
2298dbac65fSBaolin Wang err = regmap_update_bits(regmap, ctrl_base,
2308dbac65fSBaolin Wang SC27XX_LED_RUN << ctrl_shift,
2318dbac65fSBaolin Wang SC27XX_LED_RUN << ctrl_shift);
2328dbac65fSBaolin Wang if (!err)
2338dbac65fSBaolin Wang ldev->brightness = pattern[1].brightness;
2348dbac65fSBaolin Wang
2358dbac65fSBaolin Wang out:
2368dbac65fSBaolin Wang mutex_unlock(&leds->priv->lock);
2378dbac65fSBaolin Wang
2388dbac65fSBaolin Wang return err;
2398dbac65fSBaolin Wang }
2408dbac65fSBaolin Wang
sc27xx_led_register(struct device * dev,struct sc27xx_led_priv * priv)241e081c49eSBaolin Wang static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
242e081c49eSBaolin Wang {
243e081c49eSBaolin Wang int i, err;
244e081c49eSBaolin Wang
245e081c49eSBaolin Wang err = sc27xx_led_init(priv->regmap);
246e081c49eSBaolin Wang if (err)
247e081c49eSBaolin Wang return err;
248e081c49eSBaolin Wang
249e081c49eSBaolin Wang for (i = 0; i < SC27XX_LEDS_MAX; i++) {
250e081c49eSBaolin Wang struct sc27xx_led *led = &priv->leds[i];
2515fdf85a0SJacek Anaszewski struct led_init_data init_data = {};
252e081c49eSBaolin Wang
253e081c49eSBaolin Wang if (!led->active)
254e081c49eSBaolin Wang continue;
255e081c49eSBaolin Wang
256e081c49eSBaolin Wang led->line = i;
257e081c49eSBaolin Wang led->priv = priv;
258e081c49eSBaolin Wang led->ldev.brightness_set_blocking = sc27xx_led_set;
2598dbac65fSBaolin Wang led->ldev.pattern_set = sc27xx_led_pattern_set;
2608dbac65fSBaolin Wang led->ldev.pattern_clear = sc27xx_led_pattern_clear;
2618dbac65fSBaolin Wang led->ldev.default_trigger = "pattern";
262e081c49eSBaolin Wang
2635fdf85a0SJacek Anaszewski init_data.fwnode = led->fwnode;
2645fdf85a0SJacek Anaszewski init_data.devicename = "sc27xx";
2655fdf85a0SJacek Anaszewski init_data.default_label = ":";
2665fdf85a0SJacek Anaszewski
2675fdf85a0SJacek Anaszewski err = devm_led_classdev_register_ext(dev, &led->ldev,
2685fdf85a0SJacek Anaszewski &init_data);
269e081c49eSBaolin Wang if (err)
270e081c49eSBaolin Wang return err;
271e081c49eSBaolin Wang }
272e081c49eSBaolin Wang
273e081c49eSBaolin Wang return 0;
274e081c49eSBaolin Wang }
275e081c49eSBaolin Wang
sc27xx_led_probe(struct platform_device * pdev)276e081c49eSBaolin Wang static int sc27xx_led_probe(struct platform_device *pdev)
277e081c49eSBaolin Wang {
278e081c49eSBaolin Wang struct device *dev = &pdev->dev;
2798853c95eSMarek Behún struct device_node *np = dev_of_node(dev), *child;
280e081c49eSBaolin Wang struct sc27xx_led_priv *priv;
281e081c49eSBaolin Wang u32 base, count, reg;
282e081c49eSBaolin Wang int err;
283e081c49eSBaolin Wang
284*99a013c8SMarek Behún count = of_get_available_child_count(np);
285e081c49eSBaolin Wang if (!count || count > SC27XX_LEDS_MAX)
286e081c49eSBaolin Wang return -EINVAL;
287e081c49eSBaolin Wang
288e081c49eSBaolin Wang err = of_property_read_u32(np, "reg", &base);
289e081c49eSBaolin Wang if (err) {
290e081c49eSBaolin Wang dev_err(dev, "fail to get reg of property\n");
291e081c49eSBaolin Wang return err;
292e081c49eSBaolin Wang }
293e081c49eSBaolin Wang
294e081c49eSBaolin Wang priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
295e081c49eSBaolin Wang if (!priv)
296e081c49eSBaolin Wang return -ENOMEM;
297e081c49eSBaolin Wang
298e081c49eSBaolin Wang platform_set_drvdata(pdev, priv);
299e081c49eSBaolin Wang mutex_init(&priv->lock);
300e081c49eSBaolin Wang priv->base = base;
301e081c49eSBaolin Wang priv->regmap = dev_get_regmap(dev->parent, NULL);
30243926c27SWei Yongjun if (!priv->regmap) {
30343926c27SWei Yongjun err = -ENODEV;
304e081c49eSBaolin Wang dev_err(dev, "failed to get regmap: %d\n", err);
305e081c49eSBaolin Wang return err;
306e081c49eSBaolin Wang }
307e081c49eSBaolin Wang
308*99a013c8SMarek Behún for_each_available_child_of_node(np, child) {
309e081c49eSBaolin Wang err = of_property_read_u32(child, "reg", ®);
310e081c49eSBaolin Wang if (err) {
311e081c49eSBaolin Wang of_node_put(child);
312e081c49eSBaolin Wang mutex_destroy(&priv->lock);
313e081c49eSBaolin Wang return err;
314e081c49eSBaolin Wang }
315e081c49eSBaolin Wang
316e081c49eSBaolin Wang if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
317e081c49eSBaolin Wang of_node_put(child);
318e081c49eSBaolin Wang mutex_destroy(&priv->lock);
319e081c49eSBaolin Wang return -EINVAL;
320e081c49eSBaolin Wang }
321e081c49eSBaolin Wang
3225fdf85a0SJacek Anaszewski priv->leds[reg].fwnode = of_fwnode_handle(child);
323e081c49eSBaolin Wang priv->leds[reg].active = true;
324e081c49eSBaolin Wang }
325e081c49eSBaolin Wang
326e081c49eSBaolin Wang err = sc27xx_led_register(dev, priv);
327e081c49eSBaolin Wang if (err)
328e081c49eSBaolin Wang mutex_destroy(&priv->lock);
329e081c49eSBaolin Wang
330e081c49eSBaolin Wang return err;
331e081c49eSBaolin Wang }
332e081c49eSBaolin Wang
sc27xx_led_remove(struct platform_device * pdev)333e081c49eSBaolin Wang static int sc27xx_led_remove(struct platform_device *pdev)
334e081c49eSBaolin Wang {
335e081c49eSBaolin Wang struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
336e081c49eSBaolin Wang
337e081c49eSBaolin Wang mutex_destroy(&priv->lock);
338e081c49eSBaolin Wang return 0;
339e081c49eSBaolin Wang }
340e081c49eSBaolin Wang
341e081c49eSBaolin Wang static const struct of_device_id sc27xx_led_of_match[] = {
342e081c49eSBaolin Wang { .compatible = "sprd,sc2731-bltc", },
343e081c49eSBaolin Wang { }
344e081c49eSBaolin Wang };
345e081c49eSBaolin Wang MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
346e081c49eSBaolin Wang
347e081c49eSBaolin Wang static struct platform_driver sc27xx_led_driver = {
348e081c49eSBaolin Wang .driver = {
349e081c49eSBaolin Wang .name = "sprd-bltc",
350e081c49eSBaolin Wang .of_match_table = sc27xx_led_of_match,
351e081c49eSBaolin Wang },
352e081c49eSBaolin Wang .probe = sc27xx_led_probe,
353e081c49eSBaolin Wang .remove = sc27xx_led_remove,
354e081c49eSBaolin Wang };
355e081c49eSBaolin Wang
356e081c49eSBaolin Wang module_platform_driver(sc27xx_led_driver);
357e081c49eSBaolin Wang
358e081c49eSBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
359e081c49eSBaolin Wang MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
3608dbac65fSBaolin Wang MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
361e081c49eSBaolin Wang MODULE_LICENSE("GPL v2");
362