aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-10 18:55:51 -0500
committerChanwoo Choi <cw00.choi@samsung.com>2013-01-15 01:42:18 -0500
commitdd235eea4ed75b1599dd9a53bb618fe5befeb731 (patch)
treefa179be072d4865c3590599c14f02ee761c8326e /drivers/extcon
parent9b1270c71fb5f4783c72cae7e458b2cf8c657f84 (diff)
extcon: arizona: Support HPDET based accessory identification
The accessory detection functionality in Arizona devices is flexible and supports several system designs in addition to the default one implemented by the existing driver. One such design uses the HPDET feature to determine what kind of accessory is present by comparing measurements taken with the two headphone grounds available on the device, implement that if selected by platform data. 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.c159
1 files changed, 147 insertions, 12 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index dab4584a4ad5..ba9525ee4706 100644
--- a/drivers/extcon/extcon-arizona.c
+++ b/drivers/extcon/extcon-arizona.c
@@ -55,6 +55,9 @@ struct arizona_extcon_info {
55 55
56 bool hpdet_active; 56 bool hpdet_active;
57 57
58 int num_hpdet_res;
59 unsigned int hpdet_res[2];
60
58 bool mic; 61 bool mic;
59 bool detecting; 62 bool detecting;
60 int jack_flips; 63 int jack_flips;
@@ -65,8 +68,8 @@ struct arizona_extcon_info {
65}; 68};
66 69
67static const struct arizona_micd_config micd_default_modes[] = { 70static const struct arizona_micd_config micd_default_modes[] = {
68 { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
69 { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, 71 { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
72 { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
70}; 73};
71 74
72static struct { 75static struct {
@@ -118,10 +121,6 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
118 bool change; 121 bool change;
119 int ret; 122 int ret;
120 123
121 info->detecting = true;
122 info->mic = false;
123 info->jack_flips = 0;
124
125 /* Microphone detection can't use idle mode */ 124 /* Microphone detection can't use idle mode */
126 pm_runtime_get(info->dev); 125 pm_runtime_get(info->dev);
127 126
@@ -311,12 +310,80 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
311 return val; 310 return val;
312} 311}
313 312
313static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
314{
315 struct arizona *arizona = info->arizona;
316 int ret;
317
318 /*
319 * If we're using HPDET for accessory identification we need
320 * to take multiple measurements, step through them in sequence.
321 */
322 if (arizona->pdata.hpdet_acc_id) {
323 info->hpdet_res[info->num_hpdet_res++] = *reading;
324
325 /*
326 * If the impedence is too high don't measure the
327 * second ground.
328 */
329 if (info->num_hpdet_res == 1 && *reading >= 45) {
330 dev_dbg(arizona->dev, "Skipping ground flip\n");
331 info->hpdet_res[info->num_hpdet_res++] = *reading;
332 }
333
334 if (info->num_hpdet_res == 1) {
335 dev_dbg(arizona->dev, "Flipping ground\n");
336
337 regmap_update_bits(arizona->regmap,
338 ARIZONA_ACCESSORY_DETECT_MODE_1,
339 ARIZONA_ACCDET_SRC,
340 ~info->micd_modes[0].src);
341 regmap_update_bits(arizona->regmap,
342 ARIZONA_HEADPHONE_DETECT_1,
343 ARIZONA_HP_POLL, 0);
344 regmap_update_bits(arizona->regmap,
345 ARIZONA_HEADPHONE_DETECT_1,
346 ARIZONA_HP_POLL, ARIZONA_HP_POLL);
347 return -EAGAIN;
348 }
349
350 /* OK, got both. Now, compare... */
351 dev_dbg(arizona->dev, "HPDET measured %d %d\n",
352 info->hpdet_res[0], info->hpdet_res[1]);
353
354 if (info->hpdet_res[0] > info->hpdet_res[1] * 2) {
355 dev_dbg(arizona->dev, "Detected mic\n");
356 info->mic = true;
357 ret = extcon_set_cable_state_(&info->edev,
358 ARIZONA_CABLE_MICROPHONE,
359 true);
360 if (ret != 0) {
361 dev_err(arizona->dev,
362 "Failed to report mic: %d\n", ret);
363 }
364
365 /* Take the headphone impedance for the main report */
366 *reading = info->hpdet_res[1];
367 } else {
368 dev_dbg(arizona->dev, "Detected headphone\n");
369 }
370
371 /* Make sure everything is reset back to the real polarity */
372 regmap_update_bits(arizona->regmap,
373 ARIZONA_ACCESSORY_DETECT_MODE_1,
374 ARIZONA_ACCDET_SRC,
375 info->micd_modes[0].src);
376 }
377
378 return 0;
379}
380
314static irqreturn_t arizona_hpdet_irq(int irq, void *data) 381static irqreturn_t arizona_hpdet_irq(int irq, void *data)
315{ 382{
316 struct arizona_extcon_info *info = data; 383 struct arizona_extcon_info *info = data;
317 struct arizona *arizona = info->arizona; 384 struct arizona *arizona = info->arizona;
318 int report = ARIZONA_CABLE_HEADPHONE; 385 int report = ARIZONA_CABLE_HEADPHONE;
319 int ret; 386 int ret, reading;
320 387
321 mutex_lock(&info->lock); 388 mutex_lock(&info->lock);
322 389
@@ -344,14 +411,23 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
344 } else if (ret < 0) { 411 } else if (ret < 0) {
345 goto done; 412 goto done;
346 } 413 }
414 reading = ret;
347 415
348 /* Reset back to starting range */ 416 /* Reset back to starting range */
349 regmap_update_bits(arizona->regmap, 417 regmap_update_bits(arizona->regmap,
350 ARIZONA_HEADPHONE_DETECT_1, 418 ARIZONA_HEADPHONE_DETECT_1,
351 ARIZONA_HP_IMPEDANCE_RANGE_MASK, 0); 419 ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
420 0);
421
422 ret = arizona_hpdet_do_id(info, &reading);
423 if (ret == -EAGAIN) {
424 goto out;
425 } else if (ret < 0) {
426 goto done;
427 }
352 428
353 /* Report high impedence cables as line outputs */ 429 /* Report high impedence cables as line outputs */
354 if (ret >= 5000) 430 if (reading >= 5000)
355 report = ARIZONA_CABLE_LINEOUT; 431 report = ARIZONA_CABLE_LINEOUT;
356 else 432 else
357 report = ARIZONA_CABLE_HEADPHONE; 433 report = ARIZONA_CABLE_HEADPHONE;
@@ -370,8 +446,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
370 dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); 446 dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret);
371 447
372done: 448done:
373 regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
374 ARIZONA_HP_POLL, 0);
375 449
376 /* Revert back to MICDET mode */ 450 /* Revert back to MICDET mode */
377 regmap_update_bits(arizona->regmap, 451 regmap_update_bits(arizona->regmap,
@@ -451,8 +525,58 @@ err:
451 525
452 info->hpdet_active = false; 526 info->hpdet_active = false;
453} 527}
528
529static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
530{
531 struct arizona *arizona = info->arizona;
532 int ret;
533
534 dev_dbg(arizona->dev, "Starting identification via HPDET\n");
535
536 /* Make sure we keep the device enabled during the measurement */
537 pm_runtime_get(info->dev);
538
539 info->hpdet_active = true;
540
541 ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000);
542 if (ret != 0)
543 dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
544
545 ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000);
546 if (ret != 0)
547 dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
548
549 ret = regmap_update_bits(arizona->regmap,
550 ARIZONA_ACCESSORY_DETECT_MODE_1,
551 ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
552 info->micd_modes[0].src |
553 ARIZONA_ACCDET_MODE_HPL);
554 if (ret != 0) {
555 dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret);
556 goto err;
454 } 557 }
455 558
559 ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
560 ARIZONA_HP_POLL, ARIZONA_HP_POLL);
561 if (ret != 0) {
562 dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
563 ret);
564 goto err;
565 }
566
567 return;
568
569err:
570 regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
571 ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
572
573 /* Just report headphone */
574 ret = extcon_update_state(&info->edev,
575 1 << ARIZONA_CABLE_HEADPHONE,
576 1 << ARIZONA_CABLE_HEADPHONE);
577 if (ret != 0)
578 dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
579
456 info->hpdet_active = false; 580 info->hpdet_active = false;
457} 581}
458 582
@@ -612,12 +736,24 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
612 dev_err(arizona->dev, "Mechanical report failed: %d\n", 736 dev_err(arizona->dev, "Mechanical report failed: %d\n",
613 ret); 737 ret);
614 738
615 arizona_start_mic(info); 739 if (!arizona->pdata.hpdet_acc_id) {
740 info->detecting = true;
741 info->mic = false;
742 info->jack_flips = 0;
743
744 arizona_start_mic(info);
745 } else {
746 arizona_start_hpdet_acc_id(info);
747 }
616 } else { 748 } else {
617 dev_dbg(arizona->dev, "Detected jack removal\n"); 749 dev_dbg(arizona->dev, "Detected jack removal\n");
618 750
619 arizona_stop_mic(info); 751 arizona_stop_mic(info);
620 752
753 info->num_hpdet_res = 0;
754 for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
755 info->hpdet_res[i] = 0;
756 info->mic = false;
621 757
622 for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) 758 for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
623 input_report_key(info->input, 759 input_report_key(info->input,
@@ -665,7 +801,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
665 mutex_init(&info->lock); 801 mutex_init(&info->lock);
666 info->arizona = arizona; 802 info->arizona = arizona;
667 info->dev = &pdev->dev; 803 info->dev = &pdev->dev;
668 info->detecting = true;
669 platform_set_drvdata(pdev, info); 804 platform_set_drvdata(pdev, info);
670 805
671 switch (arizona->type) { 806 switch (arizona->type) {