aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-led-driver-sc27xx22
-rw-r--r--drivers/leds/leds-sc27xx-bltc.c121
2 files changed, 143 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-sc27xx b/Documentation/ABI/testing/sysfs-class-led-driver-sc27xx
new file mode 100644
index 000000000000..45b1e605d355
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-led-driver-sc27xx
@@ -0,0 +1,22 @@
1What: /sys/class/leds/<led>/hw_pattern
2Date: September 2018
3KernelVersion: 4.20
4Description:
5 Specify a hardware pattern for the SC27XX LED. For the SC27XX
6 LED controller, it only supports 4 stages to make a single
7 hardware pattern, which is used to configure the rise time,
8 high time, fall time and low time for the breathing mode.
9
10 For the breathing mode, the SC27XX LED only expects one brightness
11 for the high stage. To be compatible with the hardware pattern
12 format, we should set brightness as 0 for rise stage, fall
13 stage and low stage.
14
15 Min stage duration: 125 ms
16 Max stage duration: 31875 ms
17
18 Since the stage duration step is 125 ms, the duration should be
19 a multiplier of 125, like 125ms, 250ms, 375ms, 500ms ... 31875ms.
20
21 Thus the format of the hardware pattern values should be:
22 "0 rise_duration brightness high_duration 0 fall_duration 0 low_duration".
diff --git a/drivers/leds/leds-sc27xx-bltc.c b/drivers/leds/leds-sc27xx-bltc.c
index 9d9b7aab843f..fecf27fb1cdc 100644
--- a/drivers/leds/leds-sc27xx-bltc.c
+++ b/drivers/leds/leds-sc27xx-bltc.c
@@ -32,8 +32,18 @@
32#define SC27XX_DUTY_MASK GENMASK(15, 0) 32#define SC27XX_DUTY_MASK GENMASK(15, 0)
33#define SC27XX_MOD_MASK GENMASK(7, 0) 33#define SC27XX_MOD_MASK GENMASK(7, 0)
34 34
35#define SC27XX_CURVE_SHIFT 8
36#define SC27XX_CURVE_L_MASK GENMASK(7, 0)
37#define SC27XX_CURVE_H_MASK GENMASK(15, 8)
38
35#define SC27XX_LEDS_OFFSET 0x10 39#define SC27XX_LEDS_OFFSET 0x10
36#define SC27XX_LEDS_MAX 3 40#define SC27XX_LEDS_MAX 3
41#define SC27XX_LEDS_PATTERN_CNT 4
42/* Stage duration step, in milliseconds */
43#define SC27XX_LEDS_STEP 125
44/* Minimum and maximum duration, in milliseconds */
45#define SC27XX_DELTA_T_MIN SC27XX_LEDS_STEP
46#define SC27XX_DELTA_T_MAX (SC27XX_LEDS_STEP * 255)
37 47
38struct sc27xx_led { 48struct sc27xx_led {
39 char name[LED_MAX_NAME_SIZE]; 49 char name[LED_MAX_NAME_SIZE];
@@ -122,6 +132,113 @@ static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
122 return err; 132 return err;
123} 133}
124 134
135static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
136{
137 u32 v, offset, t = *delta_t;
138
139 v = t + SC27XX_LEDS_STEP / 2;
140 v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
141 offset = v - SC27XX_DELTA_T_MIN;
142 offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
143
144 *delta_t = SC27XX_DELTA_T_MIN + offset;
145}
146
147static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
148{
149 struct sc27xx_led *leds = to_sc27xx_led(ldev);
150 struct regmap *regmap = leds->priv->regmap;
151 u32 base = sc27xx_led_get_offset(leds);
152 u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
153 u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
154 int err;
155
156 mutex_lock(&leds->priv->lock);
157
158 /* Reset the rise, high, fall and low time to zero. */
159 regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
160 regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
161
162 err = regmap_update_bits(regmap, ctrl_base,
163 (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
164
165 ldev->brightness = LED_OFF;
166
167 mutex_unlock(&leds->priv->lock);
168
169 return err;
170}
171
172static int sc27xx_led_pattern_set(struct led_classdev *ldev,
173 struct led_pattern *pattern,
174 u32 len, int repeat)
175{
176 struct sc27xx_led *leds = to_sc27xx_led(ldev);
177 u32 base = sc27xx_led_get_offset(leds);
178 u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
179 u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
180 struct regmap *regmap = leds->priv->regmap;
181 int err;
182
183 /*
184 * Must contain 4 tuples to configure the rise time, high time, fall
185 * time and low time to enable the breathing mode.
186 */
187 if (len != SC27XX_LEDS_PATTERN_CNT)
188 return -EINVAL;
189
190 mutex_lock(&leds->priv->lock);
191
192 sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
193 err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
194 SC27XX_CURVE_L_MASK,
195 pattern[0].delta_t / SC27XX_LEDS_STEP);
196 if (err)
197 goto out;
198
199 sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
200 err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
201 SC27XX_CURVE_L_MASK,
202 pattern[1].delta_t / SC27XX_LEDS_STEP);
203 if (err)
204 goto out;
205
206 sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
207 err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
208 SC27XX_CURVE_H_MASK,
209 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
210 SC27XX_CURVE_SHIFT);
211 if (err)
212 goto out;
213
214 sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
215 err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
216 SC27XX_CURVE_H_MASK,
217 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
218 SC27XX_CURVE_SHIFT);
219 if (err)
220 goto out;
221
222 err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
223 SC27XX_DUTY_MASK,
224 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
225 SC27XX_MOD_MASK);
226 if (err)
227 goto out;
228
229 /* Enable the LED breathing mode */
230 err = regmap_update_bits(regmap, ctrl_base,
231 SC27XX_LED_RUN << ctrl_shift,
232 SC27XX_LED_RUN << ctrl_shift);
233 if (!err)
234 ldev->brightness = pattern[1].brightness;
235
236out:
237 mutex_unlock(&leds->priv->lock);
238
239 return err;
240}
241
125static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv) 242static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
126{ 243{
127 int i, err; 244 int i, err;
@@ -140,6 +257,9 @@ static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
140 led->priv = priv; 257 led->priv = priv;
141 led->ldev.name = led->name; 258 led->ldev.name = led->name;
142 led->ldev.brightness_set_blocking = sc27xx_led_set; 259 led->ldev.brightness_set_blocking = sc27xx_led_set;
260 led->ldev.pattern_set = sc27xx_led_pattern_set;
261 led->ldev.pattern_clear = sc27xx_led_pattern_clear;
262 led->ldev.default_trigger = "pattern";
143 263
144 err = devm_led_classdev_register(dev, &led->ldev); 264 err = devm_led_classdev_register(dev, &led->ldev);
145 if (err) 265 if (err)
@@ -241,4 +361,5 @@ module_platform_driver(sc27xx_led_driver);
241 361
242MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver"); 362MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
243MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>"); 363MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
364MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
244MODULE_LICENSE("GPL v2"); 365MODULE_LICENSE("GPL v2");