These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / extcon / extcon-arizona.c
index a0ed35b..e4890dd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * extcon-arizona.c - Extcon driver Wolfson Arizona devices
  *
- *  Copyright (C) 2012 Wolfson Microelectronics plc
+ *  Copyright (C) 2012-2014 Wolfson Microelectronics plc
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
+#include <linux/gpio/consumer.h>
 #include <linux/gpio.h>
 #include <linux/input.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/property.h>
 #include <linux/regulator/consumer.h>
 #include <linux/extcon.h>
 
 #include <linux/mfd/arizona/core.h>
 #include <linux/mfd/arizona/pdata.h>
 #include <linux/mfd/arizona/registers.h>
+#include <dt-bindings/mfd/arizona.h>
 
 #define ARIZONA_MAX_MICD_RANGE 8
 
-#define ARIZONA_ACCDET_MODE_MIC 0
-#define ARIZONA_ACCDET_MODE_HPL 1
-#define ARIZONA_ACCDET_MODE_HPR 2
-
 #define ARIZONA_MICD_CLAMP_MODE_JDL      0x4
 #define ARIZONA_MICD_CLAMP_MODE_JDH      0x5
 #define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
 #define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
 
+#define ARIZONA_TST_CAP_DEFAULT 0x3
+#define ARIZONA_TST_CAP_CLAMP   0x1
+
 #define ARIZONA_HPDET_MAX 10000
 
 #define HPDET_DEBOUNCE 500
 #define DEFAULT_MICD_TIMEOUT 2000
 
+#define QUICK_HEADPHONE_MAX_OHM 3
+#define MICROPHONE_MIN_OHM      1257
+#define MICROPHONE_MAX_OHM      30000
+
+#define MICD_DBTIME_TWO_READINGS 2
+#define MICD_DBTIME_FOUR_READINGS 4
+
 #define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
                         ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
                         ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
@@ -94,9 +103,11 @@ struct arizona_extcon_info {
        bool detecting;
        int jack_flips;
 
-       int hpdet_ip;
+       int hpdet_ip_version;
 
        struct extcon_dev *edev;
+
+       struct gpio_desc *micd_pol_gpio;
 };
 
 static const struct arizona_micd_config micd_default_modes[] = {
@@ -113,25 +124,23 @@ static const struct arizona_micd_range micd_default_ranges[] = {
        { .max = 430, .key = BTN_5 },
 };
 
+/* The number of levels in arizona_micd_levels valid for button thresholds */
+#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64
+
 static const int arizona_micd_levels[] = {
        3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
        49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
        105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
        270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
-       1257,
+       1257, 30000,
 };
 
-#define ARIZONA_CABLE_MECHANICAL 0
-#define ARIZONA_CABLE_MICROPHONE 1
-#define ARIZONA_CABLE_HEADPHONE  2
-#define ARIZONA_CABLE_LINEOUT    3
-
-static const char *arizona_cable[] = {
-       "Mechanical",
-       "Microphone",
-       "Headphone",
-       "Line-out",
-       NULL,
+static const unsigned int arizona_cable[] = {
+       EXTCON_MECHANICAL,
+       EXTCON_JACK_MICROPHONE,
+       EXTCON_JACK_HEADPHONE,
+       EXTCON_JACK_LINE_OUT,
+       EXTCON_NONE,
 };
 
 static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
@@ -141,16 +150,33 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
 {
        struct arizona *arizona = info->arizona;
        unsigned int mask = 0, val = 0;
+       unsigned int cap_sel = 0;
        int ret;
 
        switch (arizona->type) {
+       case WM8998:
+       case WM1814:
+               mask = 0;
+               break;
        case WM5110:
+       case WM8280:
                mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
                       ARIZONA_HP1L_SHRTI;
-               if (clamp)
+               if (clamp) {
                        val = ARIZONA_HP1L_SHRTO;
-               else
+                       cap_sel = ARIZONA_TST_CAP_CLAMP;
+               } else {
                        val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
+                       cap_sel = ARIZONA_TST_CAP_DEFAULT;
+               }
+
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_HP_TEST_CTRL_1,
+                                        ARIZONA_HP1_TST_CAP_SEL_MASK,
+                                        cap_sel);
+               if (ret != 0)
+                       dev_warn(arizona->dev,
+                                "Failed to set TST_CAP_SEL: %d\n", ret);
                break;
        default:
                mask = ARIZONA_RMV_SHRT_HP1L;
@@ -175,17 +201,19 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
                                 ret);
        }
 
-       ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
-                                mask, val);
-       if (ret != 0)
-               dev_warn(arizona->dev, "Failed to do clamp: %d\n",
+       if (mask) {
+               ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
+                                        mask, val);
+               if (ret != 0)
+                       dev_warn(arizona->dev, "Failed to do clamp: %d\n",
                                 ret);
 
-       ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
-                                mask, val);
-       if (ret != 0)
-               dev_warn(arizona->dev, "Failed to do clamp: %d\n",
-                        ret);
+               ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
+                                        mask, val);
+               if (ret != 0)
+                       dev_warn(arizona->dev, "Failed to do clamp: %d\n",
+                                ret);
+       }
 
        /* Restore the desired state while not doing the clamp */
        if (!clamp) {
@@ -211,6 +239,10 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
        if (arizona->pdata.micd_pol_gpio > 0)
                gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
                                        info->micd_modes[mode].gpio);
+       else
+               gpiod_set_value_cansleep(info->micd_pol_gpio,
+                                        info->micd_modes[mode].gpio);
+
        regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
                           ARIZONA_MICD_BIAS_SRC_MASK,
                           info->micd_modes[mode].bias <<
@@ -266,6 +298,7 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
        struct arizona *arizona = info->arizona;
        bool change;
        int ret;
+       unsigned int mode;
 
        /* Microphone detection can't use idle mode */
        pm_runtime_get(info->dev);
@@ -291,9 +324,14 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
                regmap_write(arizona->regmap, 0x80, 0x0);
        }
 
+       if (info->detecting && arizona->pdata.micd_software_compare)
+               mode = ARIZONA_ACCDET_MODE_ADC;
+       else
+               mode = ARIZONA_ACCDET_MODE_MIC;
+
        regmap_update_bits(arizona->regmap,
                           ARIZONA_ACCESSORY_DETECT_MODE_1,
-                          ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
+                          ARIZONA_ACCDET_MODE_MASK, mode);
 
        arizona_extcon_pulse_micbias(info);
 
@@ -380,7 +418,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                return ret;
        }
 
-       switch (info->hpdet_ip) {
+       switch (info->hpdet_ip_version) {
        case 0:
                if (!(val & ARIZONA_HP_DONE)) {
                        dev_err(arizona->dev, "HPDET did not complete: %x\n",
@@ -439,9 +477,6 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                           arizona_hpdet_b_ranges[range].factor_a);
                break;
 
-       default:
-               dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
-                        info->hpdet_ip);
        case 2:
                if (!(val & ARIZONA_HP_DONE_B)) {
                        dev_err(arizona->dev, "HPDET did not complete: %x\n",
@@ -478,6 +513,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                                arizona_hpdet_c_ranges[range].min);
                        val = arizona_hpdet_c_ranges[range].min;
                }
+               break;
+
+       default:
+               dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
+                        info->hpdet_ip_version);
+               return -EINVAL;
        }
 
        dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
@@ -559,7 +600,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        struct arizona_extcon_info *info = data;
        struct arizona *arizona = info->arizona;
        int id_gpio = arizona->pdata.hpdet_id_gpio;
-       int report = ARIZONA_CABLE_HEADPHONE;
+       unsigned int report = EXTCON_JACK_HEADPHONE;
        int ret, reading;
        bool mic = false;
 
@@ -573,7 +614,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        }
 
        /* If the cable was removed while measuring ignore the result */
-       ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
+       ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
        if (ret < 0) {
                dev_err(arizona->dev, "Failed to check cable state: %d\n",
                        ret);
@@ -604,9 +645,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
 
        /* Report high impedence cables as line outputs */
        if (reading >= 5000)
-               report = ARIZONA_CABLE_LINEOUT;
+               report = EXTCON_JACK_LINE_OUT;
        else
-               report = ARIZONA_CABLE_HEADPHONE;
+               report = EXTCON_JACK_HEADPHONE;
 
        ret = extcon_set_cable_state_(info->edev, report, true);
        if (ret != 0)
@@ -670,9 +711,9 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
        ret = regmap_update_bits(arizona->regmap,
                                 ARIZONA_ACCESSORY_DETECT_MODE_1,
                                 ARIZONA_ACCDET_MODE_MASK,
-                                ARIZONA_ACCDET_MODE_HPL);
+                                arizona->pdata.hpdet_channel);
        if (ret != 0) {
-               dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret);
+               dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
                goto err;
        }
 
@@ -691,8 +732,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev,
-                                     ARIZONA_CABLE_HEADPHONE, true);
+       ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
        if (ret != 0)
                dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
 
@@ -722,9 +762,9 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
                                 ARIZONA_ACCESSORY_DETECT_MODE_1,
                                 ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
                                 info->micd_modes[0].src |
-                                ARIZONA_ACCDET_MODE_HPL);
+                                arizona->pdata.hpdet_channel);
        if (ret != 0) {
-               dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret);
+               dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
                goto err;
        }
 
@@ -749,8 +789,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev,
-                                     ARIZONA_CABLE_HEADPHONE, true);
+       ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
        if (ret != 0)
                dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
 
@@ -766,10 +805,11 @@ static void arizona_micd_timeout_work(struct work_struct *work)
        mutex_lock(&info->lock);
 
        dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
-       arizona_identify_headphone(info);
 
        info->detecting = false;
 
+       arizona_identify_headphone(info);
+
        arizona_stop_mic(info);
 
        mutex_unlock(&info->lock);
@@ -789,7 +829,7 @@ static void arizona_micd_detect(struct work_struct *work)
        mutex_lock(&info->lock);
 
        /* If the cable was removed while measuring ignore the result */
-       ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
+       ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
        if (ret < 0) {
                dev_err(arizona->dev, "Failed to check cable state: %d\n",
                                ret);
@@ -801,6 +841,37 @@ static void arizona_micd_detect(struct work_struct *work)
                return;
        }
 
+       if (info->detecting && arizona->pdata.micd_software_compare) {
+               /* Must disable MICD before we read the ADCVAL */
+               regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                                  ARIZONA_MICD_ENA, 0);
+               ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
+               if (ret != 0) {
+                       dev_err(arizona->dev,
+                               "Failed to read MICDET_ADCVAL: %d\n",
+                               ret);
+                       mutex_unlock(&info->lock);
+                       return;
+               }
+
+               dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
+
+               val &= ARIZONA_MICDET_ADCVAL_MASK;
+               if (val < ARRAY_SIZE(arizona_micd_levels))
+                       val = arizona_micd_levels[val];
+               else
+                       val = INT_MAX;
+
+               if (val <= QUICK_HEADPHONE_MAX_OHM)
+                       val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
+               else if (val <= MICROPHONE_MIN_OHM)
+                       val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
+               else if (val <= MICROPHONE_MAX_OHM)
+                       val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
+               else
+                       val = ARIZONA_MICD_LVL_8;
+       }
+
        for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
                ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
                if (ret != 0) {
@@ -829,17 +900,22 @@ static void arizona_micd_detect(struct work_struct *work)
        /* Due to jack detect this should never happen */
        if (!(val & ARIZONA_MICD_STS)) {
                dev_warn(arizona->dev, "Detected open circuit\n");
+               info->mic = false;
+               arizona_stop_mic(info);
                info->detecting = false;
+               arizona_identify_headphone(info);
                goto handled;
        }
 
        /* If we got a high impedence we should have a headset, report it. */
        if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
+               info->mic = true;
+               info->detecting = false;
+
                arizona_identify_headphone(info);
 
                ret = extcon_set_cable_state_(info->edev,
-                                             ARIZONA_CABLE_MICROPHONE, true);
-
+                                             EXTCON_JACK_MICROPHONE, true);
                if (ret != 0)
                        dev_err(arizona->dev, "Headset report failed: %d\n",
                                ret);
@@ -851,8 +927,6 @@ static void arizona_micd_detect(struct work_struct *work)
                                ret);
                }
 
-               info->mic = true;
-               info->detecting = false;
                goto handled;
        }
 
@@ -865,10 +939,11 @@ static void arizona_micd_detect(struct work_struct *work)
        if (info->detecting && (val & MICD_LVL_1_TO_7)) {
                if (info->jack_flips >= info->micd_num_modes * 10) {
                        dev_dbg(arizona->dev, "Detected HP/line\n");
-                       arizona_identify_headphone(info);
 
                        info->detecting = false;
 
+                       arizona_identify_headphone(info);
+
                        arizona_stop_mic(info);
                } else {
                        info->micd_mode++;
@@ -925,10 +1000,17 @@ static void arizona_micd_detect(struct work_struct *work)
        }
 
 handled:
-       if (info->detecting)
+       if (info->detecting) {
+               if (arizona->pdata.micd_software_compare)
+                       regmap_update_bits(arizona->regmap,
+                                          ARIZONA_MIC_DETECT_1,
+                                          ARIZONA_MICD_ENA,
+                                          ARIZONA_MICD_ENA);
+
                queue_delayed_work(system_power_efficient_wq,
                                   &info->micd_timeout_work,
                                   msecs_to_jiffies(info->micd_timeout));
+       }
 
        pm_runtime_mark_last_busy(info->dev);
        mutex_unlock(&info->lock);
@@ -984,12 +1066,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
 
        mutex_lock(&info->lock);
 
-       if (arizona->pdata.jd_gpio5) {
+       if (info->micd_clamp) {
                mask = ARIZONA_MICD_CLAMP_STS;
-               if (arizona->pdata.jd_invert)
-                       present = ARIZONA_MICD_CLAMP_STS;
-               else
-                       present = 0;
+               present = 0;
        } else {
                mask = ARIZONA_JD1_STS;
                if (arizona->pdata.jd_invert)
@@ -1030,7 +1109,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        if (info->last_jackdet == present) {
                dev_dbg(arizona->dev, "Detected jack\n");
                ret = extcon_set_cable_state_(info->edev,
-                                             ARIZONA_CABLE_MECHANICAL, true);
+                                             EXTCON_MECHANICAL, true);
 
                if (ret != 0)
                        dev_err(arizona->dev, "Mechanical report failed: %d\n",
@@ -1048,9 +1127,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                           msecs_to_jiffies(HPDET_DEBOUNCE));
                }
 
-               regmap_update_bits(arizona->regmap,
-                                  ARIZONA_JACK_DETECT_DEBOUNCE,
-                                  ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, 0);
+               if (info->micd_clamp || !arizona->pdata.jd_invert)
+                       regmap_update_bits(arizona->regmap,
+                                          ARIZONA_JACK_DETECT_DEBOUNCE,
+                                          ARIZONA_MICD_CLAMP_DB |
+                                          ARIZONA_JD1_DB, 0);
        } else {
                dev_dbg(arizona->dev, "Detected jack removal\n");
 
@@ -1120,6 +1201,44 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,
        regmap_update_bits(arizona->regmap, reg, mask, level);
 }
 
+static int arizona_extcon_device_get_pdata(struct arizona *arizona)
+{
+       struct arizona_pdata *pdata = &arizona->pdata;
+       unsigned int val = ARIZONA_ACCDET_MODE_HPL;
+
+       device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
+       switch (val) {
+       case ARIZONA_ACCDET_MODE_HPL:
+       case ARIZONA_ACCDET_MODE_HPR:
+               pdata->hpdet_channel = val;
+               break;
+       default:
+               dev_err(arizona->dev,
+                       "Wrong wlf,hpdet-channel DT value %d\n", val);
+               pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
+       }
+
+       device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce",
+                                &pdata->micd_detect_debounce);
+
+       device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time",
+                                &pdata->micd_bias_start_time);
+
+       device_property_read_u32(arizona->dev, "wlf,micd-rate",
+                                &pdata->micd_rate);
+
+       device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
+                                &pdata->micd_dbtime);
+
+       device_property_read_u32(arizona->dev, "wlf,micd-timeout",
+                                &pdata->micd_timeout);
+
+       pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
+                                               "wlf,micd-force-micbias");
+
+       return 0;
+}
+
 static int arizona_extcon_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -1137,6 +1256,9 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        if (!info)
                return -ENOMEM;
 
+       if (!dev_get_platdata(arizona->dev))
+               arizona_extcon_device_get_pdata(arizona);
+
        info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
        if (IS_ERR(info->micvdd)) {
                ret = PTR_ERR(info->micvdd);
@@ -1161,7 +1283,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                        break;
                default:
                        info->micd_clamp = true;
-                       info->hpdet_ip = 1;
+                       info->hpdet_ip_version = 1;
                        break;
                }
                break;
@@ -1172,10 +1294,15 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                        break;
                default:
                        info->micd_clamp = true;
-                       info->hpdet_ip = 2;
+                       info->hpdet_ip_version = 2;
                        break;
                }
                break;
+       case WM8998:
+       case WM1814:
+               info->micd_clamp = true;
+               info->hpdet_ip_version = 2;
+               break;
        default:
                break;
        }
@@ -1185,7 +1312,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to allocate extcon device\n");
                return -ENOMEM;
        }
-       info->edev->name = "Headset Jack";
 
        ret = devm_extcon_dev_register(&pdev->dev, info->edev);
        if (ret < 0) {
@@ -1212,6 +1338,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
        }
 
+       if (arizona->pdata.gpsw > 0)
+               regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
+                               ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
+
        if (arizona->pdata.micd_pol_gpio > 0) {
                if (info->micd_modes[0].gpio)
                        mode = GPIOF_OUT_INIT_HIGH;
@@ -1227,6 +1357,27 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                                arizona->pdata.micd_pol_gpio, ret);
                        goto err_register;
                }
+       } else {
+               if (info->micd_modes[0].gpio)
+                       mode = GPIOD_OUT_HIGH;
+               else
+                       mode = GPIOD_OUT_LOW;
+
+               /* We can't use devm here because we need to do the get
+                * against the MFD device, as that is where the of_node
+                * will reside, but if we devm against that the GPIO
+                * will not be freed if the extcon driver is unloaded.
+                */
+               info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
+                                                        "wlf,micd-pol",
+                                                        GPIOD_OUT_LOW);
+               if (IS_ERR(info->micd_pol_gpio)) {
+                       ret = PTR_ERR(info->micd_pol_gpio);
+                       dev_err(arizona->dev,
+                               "Failed to get microphone polarity GPIO: %d\n",
+                               ret);
+                       goto err_register;
+               }
        }
 
        if (arizona->pdata.hpdet_id_gpio > 0) {
@@ -1237,7 +1388,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                if (ret != 0) {
                        dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
                                arizona->pdata.hpdet_id_gpio, ret);
-                       goto err_register;
+                       goto err_gpio;
                }
        }
 
@@ -1253,13 +1404,22 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                                   arizona->pdata.micd_rate
                                   << ARIZONA_MICD_RATE_SHIFT);
 
-       if (arizona->pdata.micd_dbtime)
+       switch (arizona->pdata.micd_dbtime) {
+       case MICD_DBTIME_FOUR_READINGS:
                regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
                                   ARIZONA_MICD_DBTIME_MASK,
-                                  arizona->pdata.micd_dbtime
-                                  << ARIZONA_MICD_DBTIME_SHIFT);
+                                  ARIZONA_MICD_DBTIME);
+               break;
+       case MICD_DBTIME_TWO_READINGS:
+               regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                                  ARIZONA_MICD_DBTIME_MASK, 0);
+               break;
+       default:
+               break;
+       }
 
-       BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40);
+       BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) <
+                    ARIZONA_NUM_MICD_BUTTON_LEVELS);
 
        if (arizona->pdata.num_micd_ranges) {
                info->micd_ranges = pdata->micd_ranges;
@@ -1281,7 +1441,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                                dev_err(arizona->dev,
                                        "MICD ranges must be sorted\n");
                                ret = -EINVAL;
-                               goto err_input;
+                               goto err_gpio;
                        }
                }
        }
@@ -1292,15 +1452,15 @@ static int arizona_extcon_probe(struct platform_device *pdev)
 
        /* Set up all the buttons the user specified */
        for (i = 0; i < info->num_micd_ranges; i++) {
-               for (j = 0; j < ARRAY_SIZE(arizona_micd_levels); j++)
+               for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++)
                        if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
                                break;
 
-               if (j == ARRAY_SIZE(arizona_micd_levels)) {
+               if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
                        dev_err(arizona->dev, "Unsupported MICD level %d\n",
                                info->micd_ranges[i].max);
                        ret = -EINVAL;
-                       goto err_input;
+                       goto err_gpio;
                }
 
                dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
@@ -1360,7 +1520,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        pm_runtime_idle(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
 
-       if (arizona->pdata.jd_gpio5) {
+       if (info->micd_clamp) {
                jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
                jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
        } else {
@@ -1373,7 +1533,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
                        ret);
-               goto err_input;
+               goto err_gpio;
        }
 
        ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
@@ -1444,7 +1604,8 @@ err_rise_wake:
        arizona_set_irq_wake(arizona, jack_irq_rise, 0);
 err_rise:
        arizona_free_irq(arizona, jack_irq_rise, info);
-err_input:
+err_gpio:
+       gpiod_put(info->micd_pol_gpio);
 err_register:
        pm_runtime_disable(&pdev->dev);
        return ret;
@@ -1456,13 +1617,15 @@ static int arizona_extcon_remove(struct platform_device *pdev)
        struct arizona *arizona = info->arizona;
        int jack_irq_rise, jack_irq_fall;
 
+       gpiod_put(info->micd_pol_gpio);
+
        pm_runtime_disable(&pdev->dev);
 
        regmap_update_bits(arizona->regmap,
                           ARIZONA_MICD_CLAMP_CONTROL,
                           ARIZONA_MICD_CLAMP_MODE_MASK, 0);
 
-       if (arizona->pdata.jd_gpio5) {
+       if (info->micd_clamp) {
                jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
                jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
        } else {