aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-10 18:55:43 -0500
committerChanwoo Choi <cw00.choi@samsung.com>2013-01-15 01:42:17 -0500
commit4f340333822de79b3439bddccdbf3846f9794e42 (patch)
tree03ef9d0ad533f531dce5ba38e79d3cb8ecb936b2 /drivers/extcon
parent92a49871b378b1e523a95da569cd38efdd06eee5 (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.c341
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
36struct arizona_extcon_info { 42struct 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
78static const char *arizona_cable[] = { 89static 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
115static 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
124static 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
134static 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
254static 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
312done:
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
330out:
331 mutex_unlock(&info->lock);
332
333 return IRQ_HANDLED;
334}
335
336static 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
378err:
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
103static void arizona_start_mic(struct arizona_extcon_info *info) 399static 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
849err_hpdet:
850 arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
552err_micdet: 851err_micdet:
553 arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); 852 arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
554err_fall_wake: 853err_fall_wake: