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) {
val = ARIZONA_HP1L_SHRTO;
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)
dev_warn(arizona->dev, "Failed to set TST_CAP_SEL: %d\n", ret); break; default:
mask = ARIZONA_RMV_SHRT_HP1L; if (clamp)
val = ARIZONA_RMV_SHRT_HP1L; break;
}
snd_soc_dapm_mutex_lock(arizona->dapm);
arizona->hpdet_clamp = clamp;
/* Keep the HP output stages disabled while doing the clamp */ if (clamp) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT1L_ENA |
ARIZONA_OUT1R_ENA, 0); if (ret)
dev_warn(arizona->dev, "Failed to disable headphone outputs: %d\n", ret);
}
if (mask) {
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
mask, val); if (ret)
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)
dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
}
/* Restore the desired state while not doing the clamp */ if (!clamp) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT1L_ENA |
ARIZONA_OUT1R_ENA, arizona->hp_ena); if (ret)
dev_warn(arizona->dev, "Failed to restore headphone outputs: %d\n", ret);
}
ret = snd_soc_component_force_enable_pin(component, widget); if (ret)
dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret);
snd_soc_dapm_sync(dapm);
if (!arizona->pdata.micd_force_micbias) {
ret = snd_soc_component_disable_pin(component, widget); if (ret)
dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val); if (ret) {
dev_err(arizona->dev, "Failed to read HPDET status: %d\n", ret); return ret;
}
switch (info->hpdet_ip_version) { case 0: if (!(val & ARIZONA_HP_DONE)) {
dev_err(arizona->dev, "HPDET did not complete: %x\n", val); return -EAGAIN;
}
val &= ARIZONA_HP_LVL_MASK; break;
case 1: if (!(val & ARIZONA_HP_DONE_B)) {
dev_err(arizona->dev, "HPDET did not complete: %x\n", val); return -EAGAIN;
}
ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val); if (ret) {
dev_err(arizona->dev, "Failed to read HP value: %d\n", ret); return -EAGAIN;
}
regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
&range);
range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
>> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
(val < arizona_hpdet_b_ranges[range].threshold ||
val >= ARIZONA_HPDET_B_RANGE_MAX)) {
range++;
dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range);
regmap_update_bits(arizona->regmap,
ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_IMPEDANCE_RANGE_MASK,
range <<
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); return -EAGAIN;
}
/* If we go out of range report top of range */ if (val < arizona_hpdet_b_ranges[range].threshold ||
val >= ARIZONA_HPDET_B_RANGE_MAX) {
dev_dbg(arizona->dev, "Measurement out of range\n"); return ARIZONA_HPDET_MAX;
}
dev_dbg(arizona->dev, "HPDET read %d in range %d\n", val, range);
val = arizona_hpdet_b_ranges[range].factor_b
/ ((val * 100) -
arizona_hpdet_b_ranges[range].factor_a); break;
case 2: if (!(val & ARIZONA_HP_DONE_B)) {
dev_err(arizona->dev, "HPDET did not complete: %x\n", val); return -EAGAIN;
}
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 a range, or report? */ if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
(val >= arizona_hpdet_c_ranges[range].max)) {
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;
}
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;
} break;
staticint arizona_hpdet_do_id(struct arizona_priv *info, int *reading, bool *mic)
{ 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.
*/
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");
/* Take the headphone impedance for the main report */
*reading = info->hpdet_res[0];
/* 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(arizona->dev); return -EAGAIN;
}
/* * 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;
}
static irqreturn_t arizona_hpdet_irq(int irq, void *data)
{ struct arizona_priv *info = data; struct arizona *arizona = info->arizona; int id_gpio = arizona->pdata.hpdet_id_gpio; int ret, reading, state, report; bool mic = false;
mutex_lock(&info->lock);
/* If we got a spurious IRQ for some reason then ignore it */ if (!info->hpdet_active) {
dev_warn(arizona->dev, "Spurious HPDET IRQ\n");
mutex_unlock(&info->lock); return IRQ_NONE;
}
/* If the cable was removed while measuring ignore the result */
state = info->jack->status & SND_JACK_MECHANICAL; if (!state) {
dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n"); goto done;
}
ret = arizona_hpdet_read(info); if (ret == -EAGAIN) goto out; elseif (ret < 0) goto done;
reading = ret;
/* Reset back to starting range */
regmap_update_bits(arizona->regmap,
ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
0);
ret = arizona_hpdet_do_id(info, &reading, &mic); if (ret == -EAGAIN) goto out; elseif (ret < 0) goto done;
/* Report high impedence cables as line outputs */ if (reading >= 5000)
report = SND_JACK_LINEOUT; else
report = SND_JACK_HEADPHONE;
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_hp_clamp(info, false);
if (id_gpio)
gpio_set_value_cansleep(id_gpio, 0);
/* If we have a mic then reenable MICDET */ if (state && (mic || info->mic))
arizona_start_mic(info);
if (info->hpdet_active) {
pm_runtime_put_autosuspend(arizona->dev);
info->hpdet_active = false;
}
/* Do not set hp_det done when the cable has been unplugged */ if (state)
info->hpdet_done = true;
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;
info->detecting = false;
arizona_identify_headphone(info); return 0;
}
/* If we got a high impedence we should have a headset, report it. */ if (val & ARIZONA_MICD_LVL_8) {
info->mic = true;
info->detecting = false;
/* Don't need to regulate for button detection */
ret = regulator_allow_bypass(info->micvdd, true); if (ret)
dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
return 0;
}
/* If we detected a lower impedence during initial startup * then we probably have the wrong polarity, flip it. Don't * do this for the lowest impedences to speed up detection of * plain headphones. If both polarities report a low * impedence then give up and report headphones.
*/ if (val & MICD_LVL_1_TO_7) { if (info->jack_flips >= info->micd_num_modes * 10) {
dev_dbg(arizona->dev, "Detected HP/line\n");
/* * 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;
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) { if (info->mic) {
dev_dbg(arizona->dev, "Mic button detected\n");
lvl = val & ARIZONA_MICD_LVL_MASK;
lvl >>= ARIZONA_MICD_LVL_SHIFT;
/* If the cable was removed while measuring ignore the result */ if (!(info->jack->status & SND_JACK_MECHANICAL)) {
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);
staticint arizona_hpdet_wait(struct arizona_priv *info)
{ struct arizona *arizona = info->arizona; unsignedint val; int i, ret;
for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) {
ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
&val); if (ret) {
dev_err(arizona->dev, "Failed to read HPDET state: %d\n", ret); return ret;
}
switch (info->hpdet_ip_version) { case 0: if (val & ARIZONA_HP_DONE) return 0; break; default: if (val & ARIZONA_HP_DONE_B) return 0; break;
}
msleep(ARIZONA_HPDET_WAIT_DELAY_MS);
}
dev_warn(arizona->dev, "HPDET did not appear to complete\n");
/* * If the jack was removed during a headphone detection we * need to wait for the headphone detection to finish, as * it can not be aborted. We don't want to be able to start * a new headphone detection from a fresh insert until this * one is finished.
*/
arizona_hpdet_wait(info);
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 |
ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
ARIZONA_JD1_FALL_TRIG_STS |
ARIZONA_JD1_RISE_TRIG_STS);
mutex_unlock(&info->lock);
pm_runtime_put_autosuspend(arizona->dev);
return IRQ_HANDLED;
}
/* Map a level onto a slot in the register bank */ staticvoid arizona_micd_set_level(struct arizona *arizona, int index, unsignedint level)
{ int reg; unsignedint mask;
/* We can't use devm here because we need to do the get * against the MFD device, as that is where the of_node * will reside, but if we devm against that the GPIO * will not be freed if the extcon driver is unloaded.
*/
info->micd_pol_gpio = gpiod_get_optional(arizona->dev, "wlf,micd-pol",
mode); if (IS_ERR(info->micd_pol_gpio)) {
ret = PTR_ERR(info->micd_pol_gpio);
dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); return ret;
}
}
if (arizona->pdata.hpdet_id_gpio > 0) {
ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio,
GPIOF_OUT_INIT_LOW, "HPDET"); if (ret != 0) {
dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
arizona->pdata.hpdet_id_gpio, ret);
gpiod_put(info->micd_pol_gpio); return ret;
}
}
if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_BUTTONS) {
dev_err(arizona->dev, "Too many MICD ranges: %d > %d\n",
arizona->pdata.num_micd_ranges, ARIZONA_MAX_MICD_BUTTONS); return -EINVAL;
}
if (info->num_micd_ranges > 1) { for (i = 1; i < info->num_micd_ranges; i++) { if (info->micd_ranges[i - 1].max >
info->micd_ranges[i].max) {
dev_err(arizona->dev, "MICD ranges must be sorted\n"); return -EINVAL;
}
}
}
/* Disable all buttons by default */
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
ARIZONA_MICD_LVL_SEL_MASK, 0x81);
/* Set up all the buttons the user specified */ for (i = 0; i < info->num_micd_ranges; i++) { for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++) if (arizona_micd_levels[j] >= info->micd_ranges[i].max) break;
dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
arizona_micd_levels[j], i);
arizona_micd_set_level(arizona, i, j);
/* SND_JACK_BTN_# masks start with the most significant bit */
info->micd_button_mask |= SND_JACK_BTN_0 >> i;
snd_jack_set_key(jack->jack, SND_JACK_BTN_0 >> i,
info->micd_ranges[i].key);
/* Enable reporting of that range */
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
1 << i, 1 << i);
}
/* Set all the remaining keys to a maximum */ for (; i < ARIZONA_MAX_MICD_RANGE; i++)
arizona_micd_set_level(arizona, i, 0x3f);
/* * If we have a clamp use it, activating in conjunction with * GPIO5 if that is connected for jack detect operation.
*/ if (info->micd_clamp) { if (arizona->pdata.jd_gpio5) { /* Put the GPIO into input mode with optional pull */
val = 0xc101; if (arizona->pdata.jd_gpio5_nopull)
val &= ~ARIZONA_GPN_PU;
ret = arizona_request_irq(arizona, jack_irq_rise, "JACKDET rise", arizona_jackdet, info); if (ret != 0) {
dev_err(arizona->dev, "Failed to get JACKDET rise IRQ: %d\n", ret); goto err_pm;
}
ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1); if (ret != 0) {
dev_err(arizona->dev, "Failed to set JD rise IRQ wake: %d\n", ret); goto err_rise;
}
ret = arizona_request_irq(arizona, jack_irq_fall, "JACKDET fall", arizona_jackdet, info); if (ret != 0) {
dev_err(arizona->dev, "Failed to get JD fall IRQ: %d\n", ret); goto err_rise_wake;
}
ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1); if (ret != 0) {
dev_err(arizona->dev, "Failed to set JD fall IRQ wake: %d\n", ret); goto err_fall;
}
ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET, "MICDET", arizona_micdet, info); if (ret != 0) {
dev_err(arizona->dev, "Failed to get MICDET IRQ: %d\n", ret); goto err_fall_wake;
}
ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET, "HPDET", arizona_hpdet_irq, info); if (ret != 0) {
dev_err(arizona->dev, "Failed to get HPDET IRQ: %d\n", ret); goto err_micdet;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.