]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/extcon/extcon-arizona.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
[linux.git] / drivers / extcon / extcon-arizona.c
index e970134c95fabd3295df2f5f3a4cc29ca1d4384a..7401733db08bb7a8fbc77bfcd0c9dfdde95a1def 100644 (file)
@@ -77,8 +77,6 @@ struct arizona_extcon_info {
        const struct arizona_micd_range *micd_ranges;
        int num_micd_ranges;
 
-       int micd_timeout;
-
        bool micd_reva;
        bool micd_clamp;
 
@@ -310,9 +308,13 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
        }
 
        if (info->micd_reva) {
-               regmap_write(arizona->regmap, 0x80, 0x3);
-               regmap_write(arizona->regmap, 0x294, 0);
-               regmap_write(arizona->regmap, 0x80, 0x0);
+               const struct reg_sequence reva[] = {
+                       { 0x80,  0x3 },
+                       { 0x294, 0x0 },
+                       { 0x80,  0x0 },
+               };
+
+               regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
        }
 
        if (info->detecting && arizona->pdata.micd_software_compare)
@@ -361,9 +363,13 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
        snd_soc_dapm_sync(dapm);
 
        if (info->micd_reva) {
-               regmap_write(arizona->regmap, 0x80, 0x3);
-               regmap_write(arizona->regmap, 0x294, 2);
-               regmap_write(arizona->regmap, 0x80, 0x0);
+               const struct reg_sequence reva[] = {
+                       { 0x80,  0x3 },
+                       { 0x294, 0x2 },
+                       { 0x80,  0x0 },
+               };
+
+               regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
        }
 
        ret = regulator_allow_bypass(info->micvdd, true);
@@ -527,67 +533,65 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
        struct arizona *arizona = info->arizona;
        int id_gpio = arizona->pdata.hpdet_id_gpio;
 
+       if (!arizona->pdata.hpdet_acc_id)
+               return 0;
+
        /*
         * If we're using HPDET for accessory identification we need
         * to take multiple measurements, step through them in sequence.
         */
-       if (arizona->pdata.hpdet_acc_id) {
-               info->hpdet_res[info->num_hpdet_res++] = *reading;
+       info->hpdet_res[info->num_hpdet_res++] = *reading;
 
-               /* Only check the mic directly if we didn't already ID it */
-               if (id_gpio && info->num_hpdet_res == 1) {
-                       dev_dbg(arizona->dev, "Measuring mic\n");
+       /* Only check the mic directly if we didn't already ID it */
+       if (id_gpio && info->num_hpdet_res == 1) {
+               dev_dbg(arizona->dev, "Measuring mic\n");
 
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_ACCESSORY_DETECT_MODE_1,
-                                          ARIZONA_ACCDET_MODE_MASK |
-                                          ARIZONA_ACCDET_SRC,
-                                          ARIZONA_ACCDET_MODE_HPR |
-                                          info->micd_modes[0].src);
+               regmap_update_bits(arizona->regmap,
+                                  ARIZONA_ACCESSORY_DETECT_MODE_1,
+                                  ARIZONA_ACCDET_MODE_MASK |
+                                  ARIZONA_ACCDET_SRC,
+                                  ARIZONA_ACCDET_MODE_HPR |
+                                  info->micd_modes[0].src);
 
-                       gpio_set_value_cansleep(id_gpio, 1);
+               gpio_set_value_cansleep(id_gpio, 1);
 
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_HEADPHONE_DETECT_1,
-                                          ARIZONA_HP_POLL, ARIZONA_HP_POLL);
-                       return -EAGAIN;
-               }
-
-               /* OK, got both.  Now, compare... */
-               dev_dbg(arizona->dev, "HPDET measured %d %d\n",
-                       info->hpdet_res[0], info->hpdet_res[1]);
+               regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+                                  ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+               return -EAGAIN;
+       }
 
-               /* Take the headphone impedance for the main report */
-               *reading = info->hpdet_res[0];
+       /* OK, got both.  Now, compare... */
+       dev_dbg(arizona->dev, "HPDET measured %d %d\n",
+               info->hpdet_res[0], info->hpdet_res[1]);
 
-               /* Sometimes we get false readings due to slow insert */
-               if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
-                       dev_dbg(arizona->dev, "Retrying high impedance\n");
-                       info->num_hpdet_res = 0;
-                       info->hpdet_retried = true;
-                       arizona_start_hpdet_acc_id(info);
-                       pm_runtime_put(info->dev);
-                       return -EAGAIN;
-               }
+       /* Take the headphone impedance for the main report */
+       *reading = info->hpdet_res[0];
 
-               /*
-                * If we measure the mic as high impedance
-                */
-               if (!id_gpio || info->hpdet_res[1] > 50) {
-                       dev_dbg(arizona->dev, "Detected mic\n");
-                       *mic = true;
-                       info->detecting = true;
-               } else {
-                       dev_dbg(arizona->dev, "Detected headphone\n");
-               }
+       /* Sometimes we get false readings due to slow insert */
+       if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
+               dev_dbg(arizona->dev, "Retrying high impedance\n");
+               info->num_hpdet_res = 0;
+               info->hpdet_retried = true;
+               arizona_start_hpdet_acc_id(info);
+               pm_runtime_put(info->dev);
+               return -EAGAIN;
+       }
 
-               /* Make sure everything is reset back to the real polarity */
-               regmap_update_bits(arizona->regmap,
-                                  ARIZONA_ACCESSORY_DETECT_MODE_1,
-                                  ARIZONA_ACCDET_SRC,
-                                  info->micd_modes[0].src);
+       /*
+        * If we measure the mic as high impedance
+        */
+       if (!id_gpio || info->hpdet_res[1] > 50) {
+               dev_dbg(arizona->dev, "Detected mic\n");
+               *mic = true;
+               info->detecting = true;
+       } else {
+               dev_dbg(arizona->dev, "Detected headphone\n");
        }
 
+       /* Make sure everything is reset back to the real polarity */
+       regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+                          ARIZONA_ACCDET_SRC, info->micd_modes[0].src);
+
        return 0;
 }
 
@@ -662,11 +666,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        if (id_gpio)
                gpio_set_value_cansleep(id_gpio, 0);
 
-       /* Revert back to MICDET mode */
-       regmap_update_bits(arizona->regmap,
-                          ARIZONA_ACCESSORY_DETECT_MODE_1,
-                          ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
-
        /* If we have a mic then reenable MICDET */
        if (mic || info->mic)
                arizona_start_mic(info);
@@ -699,8 +698,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
 
        info->hpdet_active = true;
 
-       if (info->mic)
-               arizona_stop_mic(info);
+       arizona_stop_mic(info);
 
        arizona_extcon_hp_clamp(info, true);
 
@@ -724,8 +722,8 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
        return;
 
 err:
-       regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
-                          ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
+       arizona_extcon_hp_clamp(info, false);
+       pm_runtime_put_autosuspend(info->dev);
 
        /* Just report headphone */
        ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
@@ -781,9 +779,6 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
        return;
 
 err:
-       regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
-                          ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
-
        /* Just report headphone */
        ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
        if (ret != 0)
@@ -806,75 +801,58 @@ static void arizona_micd_timeout_work(struct work_struct *work)
 
        arizona_identify_headphone(info);
 
-       arizona_stop_mic(info);
-
        mutex_unlock(&info->lock);
 }
 
-static void arizona_micd_detect(struct work_struct *work)
+static int arizona_micd_adc_read(struct arizona_extcon_info *info)
 {
-       struct arizona_extcon_info *info = container_of(work,
-                                               struct arizona_extcon_info,
-                                               micd_detect_work.work);
        struct arizona *arizona = info->arizona;
-       unsigned int val = 0, lvl;
-       int ret, i, key;
-
-       cancel_delayed_work_sync(&info->micd_timeout_work);
+       unsigned int val;
+       int ret;
 
-       mutex_lock(&info->lock);
+       /* Must disable MICD before we read the ADCVAL */
+       regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                          ARIZONA_MICD_ENA, 0);
 
-       /* If the cable was removed while measuring ignore the result */
-       ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
-       if (ret < 0) {
-               dev_err(arizona->dev, "Failed to check cable state: %d\n",
-                               ret);
-               mutex_unlock(&info->lock);
-               return;
-       } else if (!ret) {
-               dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
-               mutex_unlock(&info->lock);
-               return;
+       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);
+               return ret;
        }
 
-       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);
 
-               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;
 
-               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;
-       }
+       return val;
+}
+
+static int arizona_micd_read(struct arizona_extcon_info *info)
+{
+       struct arizona *arizona = info->arizona;
+       unsigned int val = 0;
+       int ret, i;
 
        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) {
                        dev_err(arizona->dev,
                                "Failed to read MICDET: %d\n", ret);
-                       mutex_unlock(&info->lock);
-                       return;
+                       return ret;
                }
 
                dev_dbg(arizona->dev, "MICDET: %x\n", val);
@@ -882,29 +860,44 @@ static void arizona_micd_detect(struct work_struct *work)
                if (!(val & ARIZONA_MICD_VALID)) {
                        dev_warn(arizona->dev,
                                 "Microphone detection state invalid\n");
-                       mutex_unlock(&info->lock);
-                       return;
+                       return -EINVAL;
                }
        }
 
        if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
                dev_err(arizona->dev, "Failed to get valid MICDET value\n");
-               mutex_unlock(&info->lock);
-               return;
+               return -EINVAL;
        }
 
+       return val;
+}
+
+static int arizona_micdet_reading(void *priv)
+{
+       struct arizona_extcon_info *info = priv;
+       struct arizona *arizona = info->arizona;
+       int ret, val;
+
+       if (info->detecting && arizona->pdata.micd_software_compare)
+               ret = arizona_micd_adc_read(info);
+       else
+               ret = arizona_micd_read(info);
+       if (ret < 0)
+               return ret;
+
+       val = ret;
+
        /* 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;
+               return 0;
        }
 
        /* If we got a high impedence we should have a headset, report it. */
-       if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
+       if (val & ARIZONA_MICD_LVL_8) {
                info->mic = true;
                info->detecting = false;
 
@@ -923,7 +916,7 @@ static void arizona_micd_detect(struct work_struct *work)
                                ret);
                }
 
-               goto handled;
+               return 0;
        }
 
        /* If we detected a lower impedence during initial startup
@@ -932,15 +925,13 @@ static void arizona_micd_detect(struct work_struct *work)
         * plain headphones.  If both polarities report a low
         * impedence then give up and report headphones.
         */
-       if (info->detecting && (val & MICD_LVL_1_TO_7)) {
+       if (val & MICD_LVL_1_TO_7) {
                if (info->jack_flips >= info->micd_num_modes * 10) {
                        dev_dbg(arizona->dev, "Detected HP/line\n");
 
                        info->detecting = false;
 
                        arizona_identify_headphone(info);
-
-                       arizona_stop_mic(info);
                } else {
                        info->micd_mode++;
                        if (info->micd_mode == info->micd_num_modes)
@@ -948,11 +939,43 @@ static void arizona_micd_detect(struct work_struct *work)
                        arizona_extcon_set_mode(info, info->micd_mode);
 
                        info->jack_flips++;
+
+                       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(arizona->pdata.micd_timeout));
                }
 
-               goto handled;
+               return 0;
        }
 
+       /*
+        * If we're still detecting and we detect a short then we've
+        * got a headphone.
+        */
+       dev_dbg(arizona->dev, "Headphone detected\n");
+       info->detecting = false;
+
+       arizona_identify_headphone(info);
+
+       return 0;
+}
+
+static int arizona_button_reading(void *priv)
+{
+       struct arizona_extcon_info *info = priv;
+       struct arizona *arizona = info->arizona;
+       int val, key, lvl, i;
+
+       val = arizona_micd_read(info);
+       if (val < 0)
+               return val;
+
        /*
         * If we're still detecting and we detect a short then we've
         * got a headphone.  Otherwise it's a button press.
@@ -968,20 +991,13 @@ static void arizona_micd_detect(struct work_struct *work)
                                input_report_key(info->input,
                                                 info->micd_ranges[i].key, 0);
 
-                       WARN_ON(!lvl);
-                       WARN_ON(ffs(lvl) - 1 >= info->num_micd_ranges);
                        if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
                                key = info->micd_ranges[ffs(lvl) - 1].key;
                                input_report_key(info->input, key, 1);
                                input_sync(info->input);
+                       } else {
+                               dev_err(arizona->dev, "Button out of range\n");
                        }
-
-               } else if (info->detecting) {
-                       dev_dbg(arizona->dev, "Headphone detected\n");
-                       info->detecting = false;
-                       arizona_stop_mic(info);
-
-                       arizona_identify_headphone(info);
                } else {
                        dev_warn(arizona->dev, "Button with no mic: %x\n",
                                 val);
@@ -995,19 +1011,39 @@ static void arizona_micd_detect(struct work_struct *work)
                arizona_extcon_pulse_micbias(info);
        }
 
-handled:
-       if (info->detecting) {
-               if (arizona->pdata.micd_software_compare)
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_MIC_DETECT_1,
-                                          ARIZONA_MICD_ENA,
-                                          ARIZONA_MICD_ENA);
+       return 0;
+}
 
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->micd_timeout_work,
-                                  msecs_to_jiffies(info->micd_timeout));
+static void arizona_micd_detect(struct work_struct *work)
+{
+       struct arizona_extcon_info *info = container_of(work,
+                                               struct arizona_extcon_info,
+                                               micd_detect_work.work);
+       struct arizona *arizona = info->arizona;
+       int ret;
+
+       cancel_delayed_work_sync(&info->micd_timeout_work);
+
+       mutex_lock(&info->lock);
+
+       /* If the cable was removed while measuring ignore the result */
+       ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
+       if (ret < 0) {
+               dev_err(arizona->dev, "Failed to check cable state: %d\n",
+                               ret);
+               mutex_unlock(&info->lock);
+               return;
+       } else if (!ret) {
+               dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
+               mutex_unlock(&info->lock);
+               return;
        }
 
+       if (info->detecting)
+               arizona_micdet_reading(info);
+       else
+               arizona_button_reading(info);
+
        pm_runtime_mark_last_busy(info->dev);
        mutex_unlock(&info->lock);
 }
@@ -1125,7 +1161,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                           msecs_to_jiffies(HPDET_DEBOUNCE));
 
                if (cancelled_mic) {
-                       int micd_timeout = info->micd_timeout;
+                       int micd_timeout = arizona->pdata.micd_timeout;
 
                        queue_delayed_work(system_power_efficient_wq,
                                           &info->micd_timeout_work,
@@ -1145,11 +1181,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                        dev_err(arizona->dev, "Mechanical report failed: %d\n",
                                ret);
 
-               if (!arizona->pdata.hpdet_acc_id) {
-                       info->detecting = true;
-                       info->mic = false;
-                       info->jack_flips = 0;
+               info->detecting = true;
+               info->mic = false;
+               info->jack_flips = 0;
 
+               if (!arizona->pdata.hpdet_acc_id) {
                        arizona_start_mic(info);
                } else {
                        queue_delayed_work(system_power_efficient_wq,
@@ -1202,11 +1238,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                   ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
        }
 
-       if (arizona->pdata.micd_timeout)
-               info->micd_timeout = arizona->pdata.micd_timeout;
-       else
-               info->micd_timeout = DEFAULT_MICD_TIMEOUT;
-
 out:
        /* Clear trig_sts to make sure DCVDD is not forced up */
        regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
@@ -1435,6 +1466,9 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        info->input->name = "Headset";
        info->input->phys = "arizona/extcon";
 
+       if (!pdata->micd_timeout)
+               pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
+
        if (pdata->num_micd_configs) {
                info->micd_modes = pdata->micd_configs;
                info->micd_num_modes = pdata->num_micd_configs;