]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/extcon/extcon-arizona.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[karo-tx-linux.git] / drivers / extcon / extcon-arizona.c
index c18cf14067c661b90ec286a9c175ce960e74aeb0..7a1b4a7791baf9e1fb3f0932903736014582559d 100644 (file)
@@ -42,6 +42,7 @@
 #define ARIZONA_HPDET_MAX 10000
 
 #define HPDET_DEBOUNCE 500
+#define DEFAULT_MICD_TIMEOUT 2000
 
 struct arizona_extcon_info {
        struct device *dev;
@@ -59,10 +60,14 @@ struct arizona_extcon_info {
        const struct arizona_micd_range *micd_ranges;
        int num_micd_ranges;
 
+       int micd_timeout;
+
        bool micd_reva;
        bool micd_clamp;
 
        struct delayed_work hpdet_work;
+       struct delayed_work micd_detect_work;
+       struct delayed_work micd_timeout_work;
 
        bool hpdet_active;
        bool hpdet_done;
@@ -459,7 +464,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
        return val;
 }
 
-static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
+static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
+                              bool *mic)
 {
        struct arizona *arizona = info->arizona;
        int id_gpio = arizona->pdata.hpdet_id_gpio;
@@ -471,32 +477,8 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
        if (arizona->pdata.hpdet_acc_id) {
                info->hpdet_res[info->num_hpdet_res++] = *reading;
 
-               /*
-                * If the impedence is too high don't measure the
-                * second ground.
-                */
-               if (info->num_hpdet_res == 1 && *reading >= 45) {
-                       dev_dbg(arizona->dev, "Skipping ground flip\n");
-                       info->hpdet_res[info->num_hpdet_res++] = *reading;
-               }
-
-               if (info->num_hpdet_res == 1) {
-                       dev_dbg(arizona->dev, "Flipping ground\n");
-
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_ACCESSORY_DETECT_MODE_1,
-                                          ARIZONA_ACCDET_SRC,
-                                          ~info->micd_modes[0].src);
-
-                       regmap_update_bits(arizona->regmap,
-                                          ARIZONA_HEADPHONE_DETECT_1,
-                                          ARIZONA_HP_POLL, ARIZONA_HP_POLL);
-                       return -EAGAIN;
-               }
-
                /* Only check the mic directly if we didn't already ID it */
-               if (id_gpio && info->num_hpdet_res == 2 &&
-                   !((info->hpdet_res[0] > info->hpdet_res[1] * 2))) {
+               if (id_gpio && info->num_hpdet_res == 1) {
                        dev_dbg(arizona->dev, "Measuring mic\n");
 
                        regmap_update_bits(arizona->regmap,
@@ -515,10 +497,8 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
                }
 
                /* OK, got both.  Now, compare... */
-               dev_dbg(arizona->dev, "HPDET measured %d %d %d\n",
-                       info->hpdet_res[0], info->hpdet_res[1],
-                       info->hpdet_res[2]);
-
+               dev_dbg(arizona->dev, "HPDET measured %d %d\n",
+                       info->hpdet_res[0], info->hpdet_res[1]);
 
                /* Take the headphone impedance for the main report */
                *reading = info->hpdet_res[0];
@@ -534,13 +514,11 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
                }
 
                /*
-                * Either the two grounds measure differently or we
-                * measure the mic as high impedance.
+                * If we measure the mic as 
                 */
-               if ((info->hpdet_res[0] > info->hpdet_res[1] * 2) ||
-                   (id_gpio && info->hpdet_res[2] > 1257)) {
+               if (!id_gpio || info->hpdet_res[1] > 50) {
                        dev_dbg(arizona->dev, "Detected mic\n");
-                       info->mic = true;
+                       *mic = true;
                        info->detecting = true;
                } else {
                        dev_dbg(arizona->dev, "Detected headphone\n");
@@ -563,6 +541,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        int id_gpio = arizona->pdata.hpdet_id_gpio;
        int report = ARIZONA_CABLE_HEADPHONE;
        int ret, reading;
+       bool mic = false;
 
        mutex_lock(&info->lock);
 
@@ -598,7 +577,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
                           ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
                           0);
 
-       ret = arizona_hpdet_do_id(info, &reading);
+       ret = arizona_hpdet_do_id(info, &reading, &mic);
        if (ret == -EAGAIN) {
                goto out;
        } else if (ret < 0) {
@@ -628,7 +607,7 @@ done:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* If we have a mic then reenable MICDET */
-       if (info->mic)
+       if (mic || info->mic)
                arizona_start_mic(info);
 
        if (info->hpdet_active) {
@@ -703,6 +682,8 @@ err:
 static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
 {
        struct arizona *arizona = info->arizona;
+       int hp_reading = 32;
+       bool mic;
        int ret;
 
        dev_dbg(arizona->dev, "Starting identification via HPDET\n");
@@ -724,12 +705,18 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
                goto err;
        }
 
-       ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
-                                ARIZONA_HP_POLL, ARIZONA_HP_POLL);
-       if (ret != 0) {
-               dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
-                       ret);
-               goto err;
+       if (arizona->pdata.hpdet_acc_id_line) {
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_HEADPHONE_DETECT_1,
+                                        ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+               if (ret != 0) {
+                       dev_err(arizona->dev,
+                               "Can't start HPDETL measurement: %d\n",
+                               ret);
+                       goto err;
+               }
+       } else {
+               arizona_hpdet_do_id(info, &hp_reading, &mic);
        }
 
        return;
@@ -748,13 +735,35 @@ err:
        info->hpdet_active = false;
 }
 
-static irqreturn_t arizona_micdet(int irq, void *data)
+static void arizona_micd_timeout_work(struct work_struct *work)
 {
-       struct arizona_extcon_info *info = data;
+       struct arizona_extcon_info *info = container_of(work,
+                                                       struct arizona_extcon_info,
+                                                       micd_timeout_work.work);
+
+       mutex_lock(&info->lock);
+
+       dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
+       arizona_identify_headphone(info);
+
+       info->detecting = false;
+
+       arizona_stop_mic(info);
+
+       mutex_unlock(&info->lock);
+}
+
+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;
        unsigned int val = 0, lvl;
        int ret, i, key;
 
+       cancel_delayed_work_sync(&info->micd_timeout_work);
+
        mutex_lock(&info->lock);
 
        for (i = 0; i < 10 && !(val & 0x7fc); i++) {
@@ -762,7 +771,7 @@ static irqreturn_t arizona_micdet(int irq, void *data)
                if (ret != 0) {
                        dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
                        mutex_unlock(&info->lock);
-                       return IRQ_NONE;
+                       return;
                }
 
                dev_dbg(arizona->dev, "MICDET: %x\n", val);
@@ -770,14 +779,14 @@ static irqreturn_t arizona_micdet(int irq, void *data)
                if (!(val & ARIZONA_MICD_VALID)) {
                        dev_warn(arizona->dev, "Microphone detection state invalid\n");
                        mutex_unlock(&info->lock);
-                       return IRQ_NONE;
+                       return;
                }
        }
 
        if (i == 10 && !(val & 0x7fc)) {
                dev_err(arizona->dev, "Failed to get valid MICDET value\n");
                mutex_unlock(&info->lock);
-               return IRQ_NONE;
+               return;
        }
 
        /* Due to jack detect this should never happen */
@@ -848,6 +857,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
                        lvl = val & ARIZONA_MICD_LVL_MASK;
                        lvl >>= ARIZONA_MICD_LVL_SHIFT;
 
+                       for (i = 0; i < info->num_micd_ranges; i++)
+                               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) {
@@ -876,8 +889,33 @@ static irqreturn_t arizona_micdet(int irq, void *data)
        }
 
 handled:
+       if (info->detecting)
+               schedule_delayed_work(&info->micd_timeout_work,
+                                     msecs_to_jiffies(info->micd_timeout));
+
        pm_runtime_mark_last_busy(info->dev);
        mutex_unlock(&info->lock);
+}
+
+static irqreturn_t arizona_micdet(int irq, void *data)
+{
+       struct arizona_extcon_info *info = data;
+       struct arizona *arizona = info->arizona;
+       int debounce = arizona->pdata.micd_detect_debounce;
+
+       cancel_delayed_work_sync(&info->micd_detect_work);
+       cancel_delayed_work_sync(&info->micd_timeout_work);
+
+       mutex_lock(&info->lock);
+       if (!info->detecting)
+               debounce = 0;
+       mutex_unlock(&info->lock);
+
+       if (debounce)
+               schedule_delayed_work(&info->micd_detect_work,
+                                     msecs_to_jiffies(debounce));
+       else
+               arizona_micd_detect(&info->micd_detect_work.work);
 
        return IRQ_HANDLED;
 }
@@ -898,10 +936,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        struct arizona_extcon_info *info = data;
        struct arizona *arizona = info->arizona;
        unsigned int val, present, mask;
-       bool cancelled;
+       bool cancelled_hp, cancelled_mic;
        int ret, i;
 
-       cancelled = cancel_delayed_work_sync(&info->hpdet_work);
+       cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
+       cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
 
        pm_runtime_get_sync(info->dev);
 
@@ -927,10 +966,14 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        val &= mask;
        if (val == info->last_jackdet) {
                dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
-               if (cancelled)
+               if (cancelled_hp)
                        schedule_delayed_work(&info->hpdet_work,
                                              msecs_to_jiffies(HPDET_DEBOUNCE));
 
+               if (cancelled_mic)
+                       schedule_delayed_work(&info->micd_timeout_work,
+                                             msecs_to_jiffies(info->micd_timeout));
+
                goto out;
        }
        info->last_jackdet = val;
@@ -986,6 +1029,11 @@ 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;
+
        /* 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 |
@@ -1055,6 +1103,8 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        info->dev = &pdev->dev;
        info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
        INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
+       INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
+       INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
        platform_set_drvdata(pdev, info);
 
        switch (arizona->type) {