Skip to content

Commit

Permalink
extcon: arizona: Fix some issues when HPDET IRQ fires after the jack …
Browse files Browse the repository at this point in the history
…has been unplugged

When the jack is partially inserted and then removed again it may be
removed while the hpdet code is running. In this case the following
may happen:

1. The "JACKDET rise" or ""JACKDET fall" IRQ triggers
2. arizona_jackdet runs and takes info->lock
3. The "HPDET" IRQ triggers
4. arizona_hpdet_irq runs, blocks on info->lock
5. arizona_jackdet calls arizona_stop_mic() and clears info->hpdet_done
6. arizona_jackdet releases info->lock
7. arizona_hpdet_irq now can continue running and:
7.1 Calls arizona_start_mic() (if a mic was detected)
7.2 sets info->hpdet_done

Step 7 is undesirable / a bug:
7.1 causes the device to stay in a high power-state (with MICVDD enabled)
7.2 causes hpdet to not run on the next jack insertion, which in turn
    causes the EXTCON_JACK_HEADPHONE state to never get set

This fixes both issues by skipping these 2 steps when arizona_hpdet_irq
runs after the jack has been unplugged.

Signed-off-by: Hans de Goede <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Acked-by: Charles Keepax <[email protected]>
Tested-by: Charles Keepax <[email protected]>
Acked-by: Chanwoo Choi <[email protected]>
Signed-off-by: Lee Jones <[email protected]>
  • Loading branch information
jwrdegoede authored and Lee Jones committed Mar 18, 2021
1 parent 4e0b9ea commit c309a3e
Showing 1 changed file with 9 additions and 8 deletions.
17 changes: 9 additions & 8 deletions drivers/extcon/extcon-arizona.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
struct arizona *arizona = info->arizona;
int id_gpio = arizona->pdata.hpdet_id_gpio;
unsigned int report = EXTCON_JACK_HEADPHONE;
int ret, reading;
int ret, reading, state;
bool mic = false;

mutex_lock(&info->lock);
Expand All @@ -614,12 +614,11 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
}

/* 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);
state = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (state < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", state);
goto out;
} else if (!ret) {
} else if (!state) {
dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
goto done;
}
Expand Down Expand Up @@ -667,15 +666,17 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
gpio_set_value_cansleep(id_gpio, 0);

/* If we have a mic then reenable MICDET */
if (mic || info->mic)
if (state && (mic || info->mic))
arizona_start_mic(info);

if (info->hpdet_active) {
pm_runtime_put_autosuspend(info->dev);
info->hpdet_active = false;
}

info->hpdet_done = true;
/* Do not set hp_det done when the cable has been unplugged */
if (state)
info->hpdet_done = true;

out:
mutex_unlock(&info->lock);
Expand Down

0 comments on commit c309a3e

Please sign in to comment.