diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-01-10 18:55:43 -0500 |
---|---|---|
committer | Chanwoo Choi <cw00.choi@samsung.com> | 2013-01-15 01:42:17 -0500 |
commit | 4f340333822de79b3439bddccdbf3846f9794e42 (patch) | |
tree | 03ef9d0ad533f531dce5ba38e79d3cb8ecb936b2 /drivers/extcon | |
parent | 92a49871b378b1e523a95da569cd38efdd06eee5 (diff) |
extcon: arizona: Enable basic headphone identification
Use the headphone detection to identify if the accessory is a headphone or
line load. There are two different revisions of the IP with different
register layouts, support both.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 341 |
1 files changed, 320 insertions, 21 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index b5190a811601..cc258a8842ef 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
@@ -31,8 +31,14 @@ | |||
31 | #include <linux/mfd/arizona/pdata.h> | 31 | #include <linux/mfd/arizona/pdata.h> |
32 | #include <linux/mfd/arizona/registers.h> | 32 | #include <linux/mfd/arizona/registers.h> |
33 | 33 | ||
34 | #define ARIZONA_DEFAULT_HP 32 | ||
35 | |||
34 | #define ARIZONA_NUM_BUTTONS 6 | 36 | #define ARIZONA_NUM_BUTTONS 6 |
35 | 37 | ||
38 | #define ARIZONA_ACCDET_MODE_MIC 0 | ||
39 | #define ARIZONA_ACCDET_MODE_HPL 1 | ||
40 | #define ARIZONA_ACCDET_MODE_HPR 2 | ||
41 | |||
36 | struct arizona_extcon_info { | 42 | struct arizona_extcon_info { |
37 | struct device *dev; | 43 | struct device *dev; |
38 | struct arizona *arizona; | 44 | struct arizona *arizona; |
@@ -47,10 +53,14 @@ struct arizona_extcon_info { | |||
47 | bool micd_reva; | 53 | bool micd_reva; |
48 | bool micd_clamp; | 54 | bool micd_clamp; |
49 | 55 | ||
56 | bool hpdet_active; | ||
57 | |||
50 | bool mic; | 58 | bool mic; |
51 | bool detecting; | 59 | bool detecting; |
52 | int jack_flips; | 60 | int jack_flips; |
53 | 61 | ||
62 | int hpdet_ip; | ||
63 | |||
54 | struct extcon_dev edev; | 64 | struct extcon_dev edev; |
55 | }; | 65 | }; |
56 | 66 | ||
@@ -74,11 +84,13 @@ static struct { | |||
74 | #define ARIZONA_CABLE_MECHANICAL 0 | 84 | #define ARIZONA_CABLE_MECHANICAL 0 |
75 | #define ARIZONA_CABLE_MICROPHONE 1 | 85 | #define ARIZONA_CABLE_MICROPHONE 1 |
76 | #define ARIZONA_CABLE_HEADPHONE 2 | 86 | #define ARIZONA_CABLE_HEADPHONE 2 |
87 | #define ARIZONA_CABLE_LINEOUT 3 | ||
77 | 88 | ||
78 | static const char *arizona_cable[] = { | 89 | static const char *arizona_cable[] = { |
79 | "Mechanical", | 90 | "Mechanical", |
80 | "Microphone", | 91 | "Microphone", |
81 | "Headphone", | 92 | "Headphone", |
93 | "Line-out", | ||
82 | NULL, | 94 | NULL, |
83 | }; | 95 | }; |
84 | 96 | ||
@@ -100,6 +112,290 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) | |||
100 | dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); | 112 | dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); |
101 | } | 113 | } |
102 | 114 | ||
115 | static struct { | ||
116 | unsigned int factor_a; | ||
117 | unsigned int factor_b; | ||
118 | } arizona_hpdet_b_ranges[] = { | ||
119 | { 5528, 362464 }, | ||
120 | { 11084, 6186851 }, | ||
121 | { 11065, 65460395 }, | ||
122 | }; | ||
123 | |||
124 | static struct { | ||
125 | int min; | ||
126 | int max; | ||
127 | } arizona_hpdet_c_ranges[] = { | ||
128 | { 0, 30 }, | ||
129 | { 8, 100 }, | ||
130 | { 100, 1000 }, | ||
131 | { 1000, 10000 }, | ||
132 | }; | ||
133 | |||
134 | static int arizona_hpdet_read(struct arizona_extcon_info *info) | ||
135 | { | ||
136 | struct arizona *arizona = info->arizona; | ||
137 | unsigned int val, range; | ||
138 | int ret; | ||
139 | |||
140 | ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val); | ||
141 | if (ret != 0) { | ||
142 | dev_err(arizona->dev, "Failed to read HPDET status: %d\n", | ||
143 | ret); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | switch (info->hpdet_ip) { | ||
148 | case 0: | ||
149 | if (!(val & ARIZONA_HP_DONE)) { | ||
150 | dev_err(arizona->dev, "HPDET did not complete: %x\n", | ||
151 | val); | ||
152 | val = ARIZONA_DEFAULT_HP; | ||
153 | } | ||
154 | |||
155 | val &= ARIZONA_HP_LVL_MASK; | ||
156 | break; | ||
157 | |||
158 | case 1: | ||
159 | if (!(val & ARIZONA_HP_DONE_B)) { | ||
160 | dev_err(arizona->dev, "HPDET did not complete: %x\n", | ||
161 | val); | ||
162 | return ARIZONA_DEFAULT_HP; | ||
163 | } | ||
164 | |||
165 | ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val); | ||
166 | if (ret != 0) { | ||
167 | dev_err(arizona->dev, "Failed to read HP value: %d\n", | ||
168 | ret); | ||
169 | return ARIZONA_DEFAULT_HP; | ||
170 | } | ||
171 | |||
172 | regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, | ||
173 | &range); | ||
174 | range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) | ||
175 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; | ||
176 | |||
177 | if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && | ||
178 | (val < 100 || val > 0x3fb)) { | ||
179 | range++; | ||
180 | dev_dbg(arizona->dev, "Moving to HPDET range %d\n", | ||
181 | range); | ||
182 | regmap_update_bits(arizona->regmap, | ||
183 | ARIZONA_HEADPHONE_DETECT_1, | ||
184 | ARIZONA_HP_IMPEDANCE_RANGE_MASK, | ||
185 | range << | ||
186 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); | ||
187 | return -EAGAIN; | ||
188 | } | ||
189 | |||
190 | /* If we go out of range report top of range */ | ||
191 | if (val < 100 || val > 0x3fb) { | ||
192 | dev_dbg(arizona->dev, "Measurement out of range\n"); | ||
193 | return 10000; | ||
194 | } | ||
195 | |||
196 | dev_dbg(arizona->dev, "HPDET read %d in range %d\n", | ||
197 | val, range); | ||
198 | |||
199 | val = arizona_hpdet_b_ranges[range].factor_b | ||
200 | / ((val * 100) - | ||
201 | arizona_hpdet_b_ranges[range].factor_a); | ||
202 | break; | ||
203 | |||
204 | default: | ||
205 | dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", | ||
206 | info->hpdet_ip); | ||
207 | case 2: | ||
208 | if (!(val & ARIZONA_HP_DONE_B)) { | ||
209 | dev_err(arizona->dev, "HPDET did not complete: %x\n", | ||
210 | val); | ||
211 | return ARIZONA_DEFAULT_HP; | ||
212 | } | ||
213 | |||
214 | val &= ARIZONA_HP_LVL_B_MASK; | ||
215 | |||
216 | regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, | ||
217 | &range); | ||
218 | range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) | ||
219 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; | ||
220 | |||
221 | /* Skip up or down a range? */ | ||
222 | if (range && (val < arizona_hpdet_c_ranges[range].min)) { | ||
223 | range--; | ||
224 | dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", | ||
225 | arizona_hpdet_c_ranges[range].min, | ||
226 | arizona_hpdet_c_ranges[range].max); | ||
227 | regmap_update_bits(arizona->regmap, | ||
228 | ARIZONA_HEADPHONE_DETECT_1, | ||
229 | ARIZONA_HP_IMPEDANCE_RANGE_MASK, | ||
230 | range << | ||
231 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); | ||
232 | return -EAGAIN; | ||
233 | } | ||
234 | |||
235 | if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 && | ||
236 | (val >= arizona_hpdet_c_ranges[range].max)) { | ||
237 | range++; | ||
238 | dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", | ||
239 | arizona_hpdet_c_ranges[range].min, | ||
240 | arizona_hpdet_c_ranges[range].max); | ||
241 | regmap_update_bits(arizona->regmap, | ||
242 | ARIZONA_HEADPHONE_DETECT_1, | ||
243 | ARIZONA_HP_IMPEDANCE_RANGE_MASK, | ||
244 | range << | ||
245 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); | ||
246 | return -EAGAIN; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); | ||
251 | return val; | ||
252 | } | ||
253 | |||
254 | static irqreturn_t arizona_hpdet_irq(int irq, void *data) | ||
255 | { | ||
256 | struct arizona_extcon_info *info = data; | ||
257 | struct arizona *arizona = info->arizona; | ||
258 | int report = ARIZONA_CABLE_HEADPHONE; | ||
259 | int ret; | ||
260 | |||
261 | mutex_lock(&info->lock); | ||
262 | |||
263 | /* If we got a spurious IRQ for some reason then ignore it */ | ||
264 | if (!info->hpdet_active) { | ||
265 | dev_warn(arizona->dev, "Spurious HPDET IRQ\n"); | ||
266 | mutex_unlock(&info->lock); | ||
267 | return IRQ_NONE; | ||
268 | } | ||
269 | |||
270 | /* If the cable was removed while measuring ignore the result */ | ||
271 | ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); | ||
272 | if (ret < 0) { | ||
273 | dev_err(arizona->dev, "Failed to check cable state: %d\n", | ||
274 | ret); | ||
275 | goto out; | ||
276 | } else if (!ret) { | ||
277 | dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n"); | ||
278 | goto done; | ||
279 | } | ||
280 | |||
281 | ret = arizona_hpdet_read(info); | ||
282 | if (ret == -EAGAIN) { | ||
283 | goto out; | ||
284 | } else if (ret < 0) { | ||
285 | goto done; | ||
286 | } | ||
287 | |||
288 | /* Reset back to starting range */ | ||
289 | regmap_update_bits(arizona->regmap, | ||
290 | ARIZONA_HEADPHONE_DETECT_1, | ||
291 | ARIZONA_HP_IMPEDANCE_RANGE_MASK, 0); | ||
292 | |||
293 | /* Report high impedence cables as line outputs */ | ||
294 | if (ret >= 5000) | ||
295 | report = ARIZONA_CABLE_LINEOUT; | ||
296 | else | ||
297 | report = ARIZONA_CABLE_HEADPHONE; | ||
298 | |||
299 | ret = extcon_set_cable_state_(&info->edev, report, true); | ||
300 | if (ret != 0) | ||
301 | dev_err(arizona->dev, "Failed to report HP/line: %d\n", | ||
302 | ret); | ||
303 | |||
304 | ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0); | ||
305 | if (ret != 0) | ||
306 | dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); | ||
307 | |||
308 | ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0); | ||
309 | if (ret != 0) | ||
310 | dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); | ||
311 | |||
312 | done: | ||
313 | regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, | ||
314 | ARIZONA_HP_POLL, 0); | ||
315 | |||
316 | /* Revert back to MICDET mode */ | ||
317 | regmap_update_bits(arizona->regmap, | ||
318 | ARIZONA_ACCESSORY_DETECT_MODE_1, | ||
319 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | ||
320 | |||
321 | /* If we have a mic then reenable MICDET */ | ||
322 | if (info->mic) | ||
323 | arizona_start_mic(info); | ||
324 | |||
325 | if (info->hpdet_active) { | ||
326 | pm_runtime_put_autosuspend(info->dev); | ||
327 | info->hpdet_active = false; | ||
328 | } | ||
329 | |||
330 | out: | ||
331 | mutex_unlock(&info->lock); | ||
332 | |||
333 | return IRQ_HANDLED; | ||
334 | } | ||
335 | |||
336 | static void arizona_identify_headphone(struct arizona_extcon_info *info) | ||
337 | { | ||
338 | struct arizona *arizona = info->arizona; | ||
339 | int ret; | ||
340 | |||
341 | dev_dbg(arizona->dev, "Starting HPDET\n"); | ||
342 | |||
343 | /* Make sure we keep the device enabled during the measurement */ | ||
344 | pm_runtime_get(info->dev); | ||
345 | |||
346 | info->hpdet_active = true; | ||
347 | |||
348 | if (info->mic) | ||
349 | arizona_stop_mic(info); | ||
350 | |||
351 | ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); | ||
352 | if (ret != 0) | ||
353 | dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); | ||
354 | |||
355 | ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000); | ||
356 | if (ret != 0) | ||
357 | dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); | ||
358 | |||
359 | ret = regmap_update_bits(arizona->regmap, | ||
360 | ARIZONA_ACCESSORY_DETECT_MODE_1, | ||
361 | ARIZONA_ACCDET_MODE_MASK, | ||
362 | ARIZONA_ACCDET_MODE_HPL); | ||
363 | if (ret != 0) { | ||
364 | dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret); | ||
365 | goto err; | ||
366 | } | ||
367 | |||
368 | ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, | ||
369 | ARIZONA_HP_POLL, ARIZONA_HP_POLL); | ||
370 | if (ret != 0) { | ||
371 | dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", | ||
372 | ret); | ||
373 | goto err; | ||
374 | } | ||
375 | |||
376 | return; | ||
377 | |||
378 | err: | ||
379 | regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, | ||
380 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | ||
381 | |||
382 | /* Just report headphone */ | ||
383 | ret = extcon_update_state(&info->edev, | ||
384 | 1 << ARIZONA_CABLE_HEADPHONE, | ||
385 | 1 << ARIZONA_CABLE_HEADPHONE); | ||
386 | if (ret != 0) | ||
387 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); | ||
388 | |||
389 | if (info->mic) | ||
390 | arizona_start_mic(info); | ||
391 | |||
392 | info->hpdet_active = false; | ||
393 | } | ||
394 | } | ||
395 | |||
396 | info->hpdet_active = false; | ||
397 | } | ||
398 | |||
103 | static void arizona_start_mic(struct arizona_extcon_info *info) | 399 | static void arizona_start_mic(struct arizona_extcon_info *info) |
104 | { | 400 | { |
105 | struct arizona *arizona = info->arizona; | 401 | struct arizona *arizona = info->arizona; |
@@ -125,6 +421,10 @@ static void arizona_start_mic(struct arizona_extcon_info *info) | |||
125 | regmap_write(arizona->regmap, 0x80, 0x0); | 421 | regmap_write(arizona->regmap, 0x80, 0x0); |
126 | } | 422 | } |
127 | 423 | ||
424 | regmap_update_bits(arizona->regmap, | ||
425 | ARIZONA_ACCESSORY_DETECT_MODE_1, | ||
426 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | ||
427 | |||
128 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, | 428 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, |
129 | ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, | 429 | ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, |
130 | &change); | 430 | &change); |
@@ -189,11 +489,11 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
189 | 489 | ||
190 | /* If we got a high impedence we should have a headset, report it. */ | 490 | /* If we got a high impedence we should have a headset, report it. */ |
191 | if (info->detecting && (val & 0x400)) { | 491 | if (info->detecting && (val & 0x400)) { |
492 | arizona_identify_headphone(info); | ||
493 | |||
192 | ret = extcon_update_state(&info->edev, | 494 | ret = extcon_update_state(&info->edev, |
193 | 1 << ARIZONA_CABLE_MICROPHONE | | 495 | 1 << ARIZONA_CABLE_MICROPHONE, |
194 | 1 << ARIZONA_CABLE_HEADPHONE, | 496 | 1 << ARIZONA_CABLE_MICROPHONE); |
195 | 1 << ARIZONA_CABLE_MICROPHONE | | ||
196 | 1 << ARIZONA_CABLE_HEADPHONE); | ||
197 | 497 | ||
198 | if (ret != 0) | 498 | if (ret != 0) |
199 | dev_err(arizona->dev, "Headset report failed: %d\n", | 499 | dev_err(arizona->dev, "Headset report failed: %d\n", |
@@ -214,17 +514,12 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
214 | info->jack_flips++; | 514 | info->jack_flips++; |
215 | 515 | ||
216 | if (info->jack_flips >= info->micd_num_modes) { | 516 | if (info->jack_flips >= info->micd_num_modes) { |
217 | dev_dbg(arizona->dev, "Detected headphone\n"); | 517 | dev_dbg(arizona->dev, "Detected HP/line\n"); |
518 | arizona_identify_headphone(info); | ||
519 | |||
218 | info->detecting = false; | 520 | info->detecting = false; |
219 | arizona_stop_mic(info); | ||
220 | 521 | ||
221 | ret = extcon_set_cable_state_(&info->edev, | 522 | arizona_stop_mic(info); |
222 | ARIZONA_CABLE_HEADPHONE, | ||
223 | true); | ||
224 | if (ret != 0) | ||
225 | dev_err(arizona->dev, | ||
226 | "Headphone report failed: %d\n", | ||
227 | ret); | ||
228 | } else { | 523 | } else { |
229 | info->micd_mode++; | 524 | info->micd_mode++; |
230 | if (info->micd_mode == info->micd_num_modes) | 525 | if (info->micd_mode == info->micd_num_modes) |
@@ -260,13 +555,7 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
260 | info->detecting = false; | 555 | info->detecting = false; |
261 | arizona_stop_mic(info); | 556 | arizona_stop_mic(info); |
262 | 557 | ||
263 | ret = extcon_set_cable_state_(&info->edev, | 558 | arizona_identify_headphone(info); |
264 | ARIZONA_CABLE_HEADPHONE, | ||
265 | true); | ||
266 | if (ret != 0) | ||
267 | dev_err(arizona->dev, | ||
268 | "Headphone report failed: %d\n", | ||
269 | ret); | ||
270 | } else { | 559 | } else { |
271 | dev_warn(arizona->dev, "Button with no mic: %x\n", | 560 | dev_warn(arizona->dev, "Button with no mic: %x\n", |
272 | val); | 561 | val); |
@@ -387,6 +676,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
387 | break; | 676 | break; |
388 | default: | 677 | default: |
389 | info->micd_clamp = true; | 678 | info->micd_clamp = true; |
679 | info->hpdet_ip = 1; | ||
390 | break; | 680 | break; |
391 | } | 681 | } |
392 | break; | 682 | break; |
@@ -524,6 +814,13 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
524 | goto err_fall_wake; | 814 | goto err_fall_wake; |
525 | } | 815 | } |
526 | 816 | ||
817 | ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET, | ||
818 | "HPDET", arizona_hpdet_irq, info); | ||
819 | if (ret != 0) { | ||
820 | dev_err(&pdev->dev, "Failed to get HPDET IRQ: %d\n", ret); | ||
821 | goto err_micdet; | ||
822 | } | ||
823 | |||
527 | regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, | 824 | regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, |
528 | ARIZONA_MICD_RATE_MASK, | 825 | ARIZONA_MICD_RATE_MASK, |
529 | 8 << ARIZONA_MICD_RATE_SHIFT); | 826 | 8 << ARIZONA_MICD_RATE_SHIFT); |
@@ -544,11 +841,13 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
544 | ret = input_register_device(info->input); | 841 | ret = input_register_device(info->input); |
545 | if (ret) { | 842 | if (ret) { |
546 | dev_err(&pdev->dev, "Can't register input device: %d\n", ret); | 843 | dev_err(&pdev->dev, "Can't register input device: %d\n", ret); |
547 | goto err_micdet; | 844 | goto err_hpdet; |
548 | } | 845 | } |
549 | 846 | ||
550 | return 0; | 847 | return 0; |
551 | 848 | ||
849 | err_hpdet: | ||
850 | arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info); | ||
552 | err_micdet: | 851 | err_micdet: |
553 | arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); | 852 | arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); |
554 | err_fall_wake: | 853 | err_fall_wake: |