diff options
Diffstat (limited to 'drivers/extcon/extcon-arizona.c')
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 354 |
1 files changed, 194 insertions, 160 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e970134c95fa..7401733db08b 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -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 @@ done: 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,13 +939,45 @@ 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. */ if (val & MICD_LVL_0_TO_7) { @@ -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; |