]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/extcon/extcon-arizona.c
Merge remote-tracking branch 'char-misc/char-misc-next'
[karo-tx-linux.git] / drivers / extcon / extcon-arizona.c
index 4b9f09cc38d879899b18314509d20a7292f81fa6..e4890dd4fefd6ee52ca5a411b7b3aa4dc0905175 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
 #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
 
@@ -117,19 +124,22 @@ 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,
 };
 
 static const unsigned int arizona_cable[] = {
        EXTCON_MECHANICAL,
-       EXTCON_MICROPHONE,
-       EXTCON_HEADPHONE,
-       EXTCON_LINE_OUT,
+       EXTCON_JACK_MICROPHONE,
+       EXTCON_JACK_HEADPHONE,
+       EXTCON_JACK_LINE_OUT,
        EXTCON_NONE,
 };
 
@@ -140,17 +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) {
@@ -270,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);
@@ -295,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);
 
@@ -443,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_version);
        case 2:
                if (!(val & ARIZONA_HP_DONE_B)) {
                        dev_err(arizona->dev, "HPDET did not complete: %x\n",
@@ -482,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);
@@ -563,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;
-       unsigned int report = EXTCON_HEADPHONE;
+       unsigned int report = EXTCON_JACK_HEADPHONE;
        int ret, reading;
        bool mic = false;
 
@@ -608,9 +645,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
 
        /* Report high impedence cables as line outputs */
        if (reading >= 5000)
-               report = EXTCON_LINE_OUT;
+               report = EXTCON_JACK_LINE_OUT;
        else
-               report = EXTCON_HEADPHONE;
+               report = EXTCON_JACK_HEADPHONE;
 
        ret = extcon_set_cable_state_(info->edev, report, true);
        if (ret != 0)
@@ -695,7 +732,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev, EXTCON_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);
 
@@ -752,7 +789,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev, EXTCON_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);
 
@@ -804,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) {
@@ -847,7 +915,7 @@ static void arizona_micd_detect(struct work_struct *work)
                arizona_identify_headphone(info);
 
                ret = extcon_set_cable_state_(info->edev,
-                                             EXTCON_MICROPHONE, true);
+                                             EXTCON_JACK_MICROPHONE, true);
                if (ret != 0)
                        dev_err(arizona->dev, "Headset report failed: %d\n",
                                ret);
@@ -932,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);
@@ -991,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)
@@ -1055,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");
 
@@ -1224,6 +1298,11 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                        break;
                }
                break;
+       case WM8998:
+       case WM1814:
+               info->micd_clamp = true;
+               info->hpdet_ip_version = 2;
+               break;
        default:
                break;
        }
@@ -1259,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;
@@ -1335,7 +1418,8 @@ static int arizona_extcon_probe(struct platform_device *pdev)
                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;
@@ -1368,11 +1452,11 @@ 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;
@@ -1436,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 {
@@ -1541,7 +1625,7 @@ static int arizona_extcon_remove(struct platform_device *pdev)
                           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 {