diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-04-01 14:17:34 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-04-02 06:54:03 -0400 |
commit | 939c5671d11d86ae783f416b703c705647ac563b (patch) | |
tree | c1042dd76e6cbc81006361f5aae8001be44bf50c | |
parent | 9c2ba270eaa227c999af451e1c2c9bf0d24aa8e5 (diff) |
extcon: arizona: Time out if MICDET fails to report
In pathological cases the microphone detection may fail to report, for
example due to a failure to get a stable measurement. Provide a timeout
to cover such cases.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index a83ca27aa99f..e2d881a58ca6 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #define ARIZONA_HPDET_MAX 10000 | 42 | #define ARIZONA_HPDET_MAX 10000 |
43 | 43 | ||
44 | #define HPDET_DEBOUNCE 500 | 44 | #define HPDET_DEBOUNCE 500 |
45 | #define MICD_TIMEOUT 2000 | ||
45 | 46 | ||
46 | struct arizona_extcon_info { | 47 | struct arizona_extcon_info { |
47 | struct device *dev; | 48 | struct device *dev; |
@@ -63,6 +64,7 @@ struct arizona_extcon_info { | |||
63 | bool micd_clamp; | 64 | bool micd_clamp; |
64 | 65 | ||
65 | struct delayed_work hpdet_work; | 66 | struct delayed_work hpdet_work; |
67 | struct delayed_work micd_timeout_work; | ||
66 | 68 | ||
67 | bool hpdet_active; | 69 | bool hpdet_active; |
68 | bool hpdet_done; | 70 | bool hpdet_done; |
@@ -730,6 +732,24 @@ err: | |||
730 | info->hpdet_active = false; | 732 | info->hpdet_active = false; |
731 | } | 733 | } |
732 | 734 | ||
735 | static void arizona_micd_timeout_work(struct work_struct *work) | ||
736 | { | ||
737 | struct arizona_extcon_info *info = container_of(work, | ||
738 | struct arizona_extcon_info, | ||
739 | micd_timeout_work.work); | ||
740 | |||
741 | mutex_lock(&info->lock); | ||
742 | |||
743 | dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); | ||
744 | arizona_identify_headphone(info); | ||
745 | |||
746 | info->detecting = false; | ||
747 | |||
748 | arizona_stop_mic(info); | ||
749 | |||
750 | mutex_unlock(&info->lock); | ||
751 | } | ||
752 | |||
733 | static irqreturn_t arizona_micdet(int irq, void *data) | 753 | static irqreturn_t arizona_micdet(int irq, void *data) |
734 | { | 754 | { |
735 | struct arizona_extcon_info *info = data; | 755 | struct arizona_extcon_info *info = data; |
@@ -737,6 +757,8 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
737 | unsigned int val = 0, lvl; | 757 | unsigned int val = 0, lvl; |
738 | int ret, i, key; | 758 | int ret, i, key; |
739 | 759 | ||
760 | cancel_delayed_work_sync(&info->micd_timeout_work); | ||
761 | |||
740 | mutex_lock(&info->lock); | 762 | mutex_lock(&info->lock); |
741 | 763 | ||
742 | for (i = 0; i < 10 && !(val & 0x7fc); i++) { | 764 | for (i = 0; i < 10 && !(val & 0x7fc); i++) { |
@@ -858,6 +880,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
858 | } | 880 | } |
859 | 881 | ||
860 | handled: | 882 | handled: |
883 | if (info->detecting) | ||
884 | schedule_delayed_work(&info->micd_timeout_work, | ||
885 | msecs_to_jiffies(MICD_TIMEOUT)); | ||
886 | |||
861 | pm_runtime_mark_last_busy(info->dev); | 887 | pm_runtime_mark_last_busy(info->dev); |
862 | mutex_unlock(&info->lock); | 888 | mutex_unlock(&info->lock); |
863 | 889 | ||
@@ -880,10 +906,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data) | |||
880 | struct arizona_extcon_info *info = data; | 906 | struct arizona_extcon_info *info = data; |
881 | struct arizona *arizona = info->arizona; | 907 | struct arizona *arizona = info->arizona; |
882 | unsigned int val, present, mask; | 908 | unsigned int val, present, mask; |
883 | bool cancelled; | 909 | bool cancelled_hp, cancelled_mic; |
884 | int ret, i; | 910 | int ret, i; |
885 | 911 | ||
886 | cancelled = cancel_delayed_work_sync(&info->hpdet_work); | 912 | cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); |
913 | cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); | ||
887 | 914 | ||
888 | pm_runtime_get_sync(info->dev); | 915 | pm_runtime_get_sync(info->dev); |
889 | 916 | ||
@@ -909,10 +936,14 @@ static irqreturn_t arizona_jackdet(int irq, void *data) | |||
909 | val &= mask; | 936 | val &= mask; |
910 | if (val == info->last_jackdet) { | 937 | if (val == info->last_jackdet) { |
911 | dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); | 938 | dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); |
912 | if (cancelled) | 939 | if (cancelled_hp) |
913 | schedule_delayed_work(&info->hpdet_work, | 940 | schedule_delayed_work(&info->hpdet_work, |
914 | msecs_to_jiffies(HPDET_DEBOUNCE)); | 941 | msecs_to_jiffies(HPDET_DEBOUNCE)); |
915 | 942 | ||
943 | if (cancelled_mic) | ||
944 | schedule_delayed_work(&info->micd_timeout_work, | ||
945 | msecs_to_jiffies(MICD_TIMEOUT)); | ||
946 | |||
916 | goto out; | 947 | goto out; |
917 | } | 948 | } |
918 | info->last_jackdet = val; | 949 | info->last_jackdet = val; |
@@ -1037,6 +1068,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
1037 | info->dev = &pdev->dev; | 1068 | info->dev = &pdev->dev; |
1038 | info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS); | 1069 | info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS); |
1039 | INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work); | 1070 | INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work); |
1071 | INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work); | ||
1040 | platform_set_drvdata(pdev, info); | 1072 | platform_set_drvdata(pdev, info); |
1041 | 1073 | ||
1042 | switch (arizona->type) { | 1074 | switch (arizona->type) { |