extcon: max14577: Add extcon-max14577 driver to support MUIC device
[firefly-linux-kernel-4.4.55.git] / drivers / extcon / extcon-arizona.c
index e55713083c7887abe7baa60d24396126446bbf30..c20602f601ee22732635aa3fa04fe94e44ed6020 100644 (file)
 #define HPDET_DEBOUNCE 500
 #define DEFAULT_MICD_TIMEOUT 2000
 
+#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 | \
+                        ARIZONA_MICD_LVL_7)
+
+#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7)
+
+#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
+
 struct arizona_extcon_info {
        struct device *dev;
        struct arizona *arizona;
@@ -86,8 +95,8 @@ struct arizona_extcon_info {
 };
 
 static const struct arizona_micd_config micd_default_modes[] = {
-       { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
-       { 0,                  2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
+       { ARIZONA_ACCDET_SRC, 1, 0 },
+       { 0,                  2, 1 },
 };
 
 static const struct arizona_micd_range micd_default_ranges[] = {
@@ -182,7 +191,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
                                        info->micd_modes[mode].gpio);
        regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
                           ARIZONA_MICD_BIAS_SRC_MASK,
-                          info->micd_modes[mode].bias);
+                          info->micd_modes[mode].bias <<
+                          ARIZONA_MICD_BIAS_SRC_SHIFT);
        regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
                           ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
 
@@ -193,7 +203,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
 
 static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
 {
-       switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) {
+       switch (info->micd_modes[0].bias) {
        case 1:
                return "MICBIAS1";
        case 2:
@@ -388,7 +398,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                           >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
 
                if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
-                   (val < 100 || val > 0x3fb)) {
+                   (val < 100 || val >= 0x3fb)) {
                        range++;
                        dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
                                range);
@@ -401,7 +411,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                }
 
                /* If we go out of range report top of range */
-               if (val < 100 || val > 0x3fb) {
+               if (val < 100 || val >= 0x3fb) {
                        dev_dbg(arizona->dev, "Measurement out of range\n");
                        return ARIZONA_HPDET_MAX;
                }
@@ -425,26 +435,15 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                }
 
                val &= ARIZONA_HP_LVL_B_MASK;
+               /* Convert to ohms, the value is in 0.5 ohm increments */
+               val /= 2;
 
                regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
                            &range);
                range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
                           >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
 
-               /* Skip up or down a range? */
-               if (range && (val < arizona_hpdet_c_ranges[range].min)) {
-                       range--;
-                       dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n",
-                               arizona_hpdet_c_ranges[range].min,
-                               arizona_hpdet_c_ranges[range].max);
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_HEADPHONE_DETECT_1,
-                                          ARIZONA_HP_IMPEDANCE_RANGE_MASK,
-                                          range <<
-                                          ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
-                       return -EAGAIN;
-               }
-
+               /* Skip up a range, or report? */
                if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
                    (val >= arizona_hpdet_c_ranges[range].max)) {
                        range++;
@@ -458,6 +457,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                                           ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
                        return -EAGAIN;
                }
+
+               if (range && (val < arizona_hpdet_c_ranges[range].min)) {
+                       dev_dbg(arizona->dev, "Reporting range boundary %d\n",
+                               arizona_hpdet_c_ranges[range].min);
+                       val = arizona_hpdet_c_ranges[range].min;
+               }
        }
 
        dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
@@ -514,7 +519,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
                }
 
                /*
-                * If we measure the mic as 
+                * If we measure the mic as high impedance
                 */
                if (!id_gpio || info->hpdet_res[1] > 50) {
                        dev_dbg(arizona->dev, "Detected mic\n");
@@ -564,11 +569,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        }
 
        ret = arizona_hpdet_read(info);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
        reading = ret;
 
        /* Reset back to starting range */
@@ -578,11 +582,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
                           0);
 
        ret = arizona_hpdet_do_id(info, &reading, &mic);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
 
        /* Report high impedence cables as line outputs */
        if (reading >= 5000)
@@ -595,9 +598,15 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
                dev_err(arizona->dev, "Failed to report HP/line: %d\n",
                        ret);
 
+done:
+       /* Reset back to starting range */
+       regmap_update_bits(arizona->regmap,
+                          ARIZONA_HEADPHONE_DETECT_1,
+                          ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
+                          0);
+
        arizona_extcon_do_magic(info, 0);
 
-done:
        if (id_gpio)
                gpio_set_value_cansleep(id_gpio, 0);
 
@@ -738,8 +747,8 @@ err:
 static void arizona_micd_timeout_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       micd_timeout_work.work);
+                                               struct arizona_extcon_info,
+                                               micd_timeout_work.work);
 
        mutex_lock(&info->lock);
 
@@ -756,8 +765,8 @@ static void arizona_micd_timeout_work(struct work_struct *work)
 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_extcon_info,
+                                               micd_detect_work.work);
        struct arizona *arizona = info->arizona;
        unsigned int val = 0, lvl;
        int ret, i, key;
@@ -766,10 +775,24 @@ static void arizona_micd_detect(struct work_struct *work)
 
        mutex_lock(&info->lock);
 
-       for (i = 0; i < 10 && !(val & 0x7fc); i++) {
+       /* If the cable was removed while measuring ignore the result */
+       ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_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;
+       }
+
+       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);
+                       dev_err(arizona->dev,
+                               "Failed to read MICDET: %d\n", ret);
                        mutex_unlock(&info->lock);
                        return;
                }
@@ -777,13 +800,14 @@ static void arizona_micd_detect(struct work_struct *work)
                dev_dbg(arizona->dev, "MICDET: %x\n", val);
 
                if (!(val & ARIZONA_MICD_VALID)) {
-                       dev_warn(arizona->dev, "Microphone detection state invalid\n");
+                       dev_warn(arizona->dev,
+                                "Microphone detection state invalid\n");
                        mutex_unlock(&info->lock);
                        return;
                }
        }
 
-       if (i == 10 && !(val & 0x7fc)) {
+       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;
@@ -797,7 +821,7 @@ static void arizona_micd_detect(struct work_struct *work)
        }
 
        /* If we got a high impedence we should have a headset, report it. */
-       if (info->detecting && (val & 0x400)) {
+       if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
                arizona_identify_headphone(info);
 
                ret = extcon_update_state(&info->edev,
@@ -826,7 +850,7 @@ 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 & 0x3f8)) {
+       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);
@@ -850,7 +874,7 @@ static void arizona_micd_detect(struct work_struct *work)
         * If we're still detecting and we detect a short then we've
         * got a headphone.  Otherwise it's a button press.
         */
-       if (val & 0x3fc) {
+       if (val & MICD_LVL_0_TO_7) {
                if (info->mic) {
                        dev_dbg(arizona->dev, "Mic button detected\n");
 
@@ -925,8 +949,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
 static void arizona_hpdet_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       hpdet_work.work);
+                                               struct arizona_extcon_info,
+                                               hpdet_work.work);
 
        mutex_lock(&info->lock);
        arizona_start_hpdet_acc_id(info);
@@ -973,10 +997,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                           &info->hpdet_work,
                                           msecs_to_jiffies(HPDET_DEBOUNCE));
 
-               if (cancelled_mic)
+               if (cancelled_mic) {
+                       int micd_timeout = info->micd_timeout;
+
                        queue_delayed_work(system_power_efficient_wq,
                                           &info->micd_timeout_work,
-                                          msecs_to_jiffies(info->micd_timeout));
+                                          msecs_to_jiffies(micd_timeout));
+               }
 
                goto out;
        }
@@ -1039,6 +1066,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        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,
                     ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
@@ -1046,7 +1074,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                     ARIZONA_JD1_FALL_TRIG_STS |
                     ARIZONA_JD1_RISE_TRIG_STS);
 
-out:
        mutex_unlock(&info->lock);
 
        pm_runtime_mark_last_busy(info->dev);
@@ -1078,7 +1105,7 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,
 static int arizona_extcon_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
-       struct arizona_pdata *pdata;
+       struct arizona_pdata *pdata = &arizona->pdata;
        struct arizona_extcon_info *info;
        unsigned int val;
        int jack_irq_fall, jack_irq_rise;
@@ -1087,8 +1114,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        if (!arizona->dapm || !arizona->dapm->card)
                return -EPROBE_DEFER;
 
-       pdata = dev_get_platdata(arizona->dev);
-
        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info) {
                dev_err(&pdev->dev, "Failed to allocate memory\n");
@@ -1124,14 +1149,25 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                        break;
                }
                break;
+       case WM5110:
+               switch (arizona->rev) {
+               case 0 ... 2:
+                       break;
+               default:
+                       info->micd_clamp = true;
+                       info->hpdet_ip = 2;
+                       break;
+               }
+               break;
        default:
                break;
        }
 
        info->edev.name = "Headset Jack";
+       info->edev.dev.parent = arizona->dev;
        info->edev.supported_cable = arizona_cable;
 
-       ret = extcon_dev_register(&info->edev, arizona->dev);
+       ret = extcon_dev_register(&info->edev);
        if (ret < 0) {
                dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
                        ret);