diff options
-rw-r--r-- | drivers/extcon/Kconfig | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 93 | ||||
-rw-r--r-- | include/linux/mfd/arizona/pdata.h | 3 |
3 files changed, 97 insertions, 1 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 07122a9ef36e..9377050e5335 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig | |||
@@ -47,7 +47,7 @@ config EXTCON_MAX8997 | |||
47 | 47 | ||
48 | config EXTCON_ARIZONA | 48 | config EXTCON_ARIZONA |
49 | tristate "Wolfson Arizona EXTCON support" | 49 | tristate "Wolfson Arizona EXTCON support" |
50 | depends on MFD_ARIZONA && INPUT | 50 | depends on MFD_ARIZONA && INPUT && SND_SOC |
51 | help | 51 | help |
52 | Say Y here to enable support for external accessory detection | 52 | Say Y here to enable support for external accessory detection |
53 | with Wolfson Arizona devices. These are audio CODECs with | 53 | with Wolfson Arizona devices. These are audio CODECs with |
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index d7e1047ad68e..aa724314677a 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
@@ -27,6 +27,8 @@ | |||
27 | #include <linux/regulator/consumer.h> | 27 | #include <linux/regulator/consumer.h> |
28 | #include <linux/extcon.h> | 28 | #include <linux/extcon.h> |
29 | 29 | ||
30 | #include <sound/soc.h> | ||
31 | |||
30 | #include <linux/mfd/arizona/core.h> | 32 | #include <linux/mfd/arizona/core.h> |
31 | #include <linux/mfd/arizona/pdata.h> | 33 | #include <linux/mfd/arizona/pdata.h> |
32 | #include <linux/mfd/arizona/registers.h> | 34 | #include <linux/mfd/arizona/registers.h> |
@@ -113,6 +115,52 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) | |||
113 | dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); | 115 | dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); |
114 | } | 116 | } |
115 | 117 | ||
118 | static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info) | ||
119 | { | ||
120 | switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) { | ||
121 | case 1: | ||
122 | return "MICBIAS1"; | ||
123 | case 2: | ||
124 | return "MICBIAS2"; | ||
125 | case 3: | ||
126 | return "MICBIAS3"; | ||
127 | default: | ||
128 | return "MICVDD"; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info) | ||
133 | { | ||
134 | struct arizona *arizona = info->arizona; | ||
135 | const char *widget = arizona_extcon_get_micbias(info); | ||
136 | struct snd_soc_dapm_context *dapm = arizona->dapm; | ||
137 | int ret; | ||
138 | |||
139 | mutex_lock(&dapm->card->dapm_mutex); | ||
140 | |||
141 | ret = snd_soc_dapm_force_enable_pin(dapm, widget); | ||
142 | if (ret != 0) | ||
143 | dev_warn(arizona->dev, "Failed to enable %s: %d\n", | ||
144 | widget, ret); | ||
145 | |||
146 | mutex_unlock(&dapm->card->dapm_mutex); | ||
147 | |||
148 | snd_soc_dapm_sync(dapm); | ||
149 | |||
150 | if (!arizona->pdata.micd_force_micbias) { | ||
151 | mutex_lock(&dapm->card->dapm_mutex); | ||
152 | |||
153 | ret = snd_soc_dapm_disable_pin(arizona->dapm, widget); | ||
154 | if (ret != 0) | ||
155 | dev_warn(arizona->dev, "Failed to disable %s: %d\n", | ||
156 | widget, ret); | ||
157 | |||
158 | mutex_unlock(&dapm->card->dapm_mutex); | ||
159 | |||
160 | snd_soc_dapm_sync(dapm); | ||
161 | } | ||
162 | } | ||
163 | |||
116 | static void arizona_start_mic(struct arizona_extcon_info *info) | 164 | static void arizona_start_mic(struct arizona_extcon_info *info) |
117 | { | 165 | { |
118 | struct arizona *arizona = info->arizona; | 166 | struct arizona *arizona = info->arizona; |
@@ -122,6 +170,15 @@ static void arizona_start_mic(struct arizona_extcon_info *info) | |||
122 | /* Microphone detection can't use idle mode */ | 170 | /* Microphone detection can't use idle mode */ |
123 | pm_runtime_get(info->dev); | 171 | pm_runtime_get(info->dev); |
124 | 172 | ||
173 | if (info->detecting) { | ||
174 | ret = regulator_allow_bypass(info->micvdd, false); | ||
175 | if (ret != 0) { | ||
176 | dev_err(arizona->dev, | ||
177 | "Failed to regulate MICVDD: %d\n", | ||
178 | ret); | ||
179 | } | ||
180 | } | ||
181 | |||
125 | ret = regulator_enable(info->micvdd); | 182 | ret = regulator_enable(info->micvdd); |
126 | if (ret != 0) { | 183 | if (ret != 0) { |
127 | dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", | 184 | dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", |
@@ -138,6 +195,8 @@ static void arizona_start_mic(struct arizona_extcon_info *info) | |||
138 | ARIZONA_ACCESSORY_DETECT_MODE_1, | 195 | ARIZONA_ACCESSORY_DETECT_MODE_1, |
139 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | 196 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); |
140 | 197 | ||
198 | arizona_extcon_pulse_micbias(info); | ||
199 | |||
141 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, | 200 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, |
142 | ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, | 201 | ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, |
143 | &change); | 202 | &change); |
@@ -150,18 +209,39 @@ static void arizona_start_mic(struct arizona_extcon_info *info) | |||
150 | static void arizona_stop_mic(struct arizona_extcon_info *info) | 209 | static void arizona_stop_mic(struct arizona_extcon_info *info) |
151 | { | 210 | { |
152 | struct arizona *arizona = info->arizona; | 211 | struct arizona *arizona = info->arizona; |
212 | const char *widget = arizona_extcon_get_micbias(info); | ||
213 | struct snd_soc_dapm_context *dapm = arizona->dapm; | ||
153 | bool change; | 214 | bool change; |
215 | int ret; | ||
154 | 216 | ||
155 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, | 217 | regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, |
156 | ARIZONA_MICD_ENA, 0, | 218 | ARIZONA_MICD_ENA, 0, |
157 | &change); | 219 | &change); |
158 | 220 | ||
221 | mutex_lock(&dapm->card->dapm_mutex); | ||
222 | |||
223 | ret = snd_soc_dapm_disable_pin(dapm, widget); | ||
224 | if (ret != 0) | ||
225 | dev_warn(arizona->dev, | ||
226 | "Failed to disable %s: %d\n", | ||
227 | widget, ret); | ||
228 | |||
229 | mutex_unlock(&dapm->card->dapm_mutex); | ||
230 | |||
231 | snd_soc_dapm_sync(dapm); | ||
232 | |||
159 | if (info->micd_reva) { | 233 | if (info->micd_reva) { |
160 | regmap_write(arizona->regmap, 0x80, 0x3); | 234 | regmap_write(arizona->regmap, 0x80, 0x3); |
161 | regmap_write(arizona->regmap, 0x294, 2); | 235 | regmap_write(arizona->regmap, 0x294, 2); |
162 | regmap_write(arizona->regmap, 0x80, 0x0); | 236 | regmap_write(arizona->regmap, 0x80, 0x0); |
163 | } | 237 | } |
164 | 238 | ||
239 | ret = regulator_allow_bypass(info->micvdd, true); | ||
240 | if (ret != 0) { | ||
241 | dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", | ||
242 | ret); | ||
243 | } | ||
244 | |||
165 | if (change) { | 245 | if (change) { |
166 | regulator_disable(info->micvdd); | 246 | regulator_disable(info->micvdd); |
167 | pm_runtime_mark_last_busy(info->dev); | 247 | pm_runtime_mark_last_busy(info->dev); |
@@ -564,6 +644,8 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) | |||
564 | 644 | ||
565 | info->hpdet_active = true; | 645 | info->hpdet_active = true; |
566 | 646 | ||
647 | arizona_extcon_pulse_micbias(info); | ||
648 | |||
567 | ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); | 649 | ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); |
568 | if (ret != 0) | 650 | if (ret != 0) |
569 | dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); | 651 | dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); |
@@ -649,6 +731,13 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
649 | dev_err(arizona->dev, "Headset report failed: %d\n", | 731 | dev_err(arizona->dev, "Headset report failed: %d\n", |
650 | ret); | 732 | ret); |
651 | 733 | ||
734 | /* Don't need to regulate for button detection */ | ||
735 | ret = regulator_allow_bypass(info->micvdd, false); | ||
736 | if (ret != 0) { | ||
737 | dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", | ||
738 | ret); | ||
739 | } | ||
740 | |||
652 | info->mic = true; | 741 | info->mic = true; |
653 | info->detecting = false; | 742 | info->detecting = false; |
654 | goto handled; | 743 | goto handled; |
@@ -716,6 +805,7 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
716 | input_report_key(info->input, | 805 | input_report_key(info->input, |
717 | arizona_lvl_to_key[i].report, 0); | 806 | arizona_lvl_to_key[i].report, 0); |
718 | input_sync(info->input); | 807 | input_sync(info->input); |
808 | arizona_extcon_pulse_micbias(info); | ||
719 | } | 809 | } |
720 | 810 | ||
721 | handled: | 811 | handled: |
@@ -817,6 +907,9 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
817 | int jack_irq_fall, jack_irq_rise; | 907 | int jack_irq_fall, jack_irq_rise; |
818 | int ret, mode, i; | 908 | int ret, mode, i; |
819 | 909 | ||
910 | if (!arizona->dapm || !arizona->dapm->card) | ||
911 | return -EPROBE_DEFER; | ||
912 | |||
820 | pdata = dev_get_platdata(arizona->dev); | 913 | pdata = dev_get_platdata(arizona->dev); |
821 | 914 | ||
822 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 915 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 2f5f08e10b79..f8241753415c 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h | |||
@@ -117,6 +117,9 @@ struct arizona_pdata { | |||
117 | /** Mic detect debounce level */ | 117 | /** Mic detect debounce level */ |
118 | int micd_dbtime; | 118 | int micd_dbtime; |
119 | 119 | ||
120 | /** Force MICBIAS on for mic detect */ | ||
121 | bool micd_force_micbias; | ||
122 | |||
120 | /** Headset polarity configurations */ | 123 | /** Headset polarity configurations */ |
121 | struct arizona_micd_config *micd_configs; | 124 | struct arizona_micd_config *micd_configs; |
122 | int num_micd_configs; | 125 | int num_micd_configs; |