extcon: arizona: Fix some issues when HPDET IRQ fires after the jack has been unplugged
[ Upstream commit c309a3e8793f7e01c4a4ec7960658380572cb576 ]
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 <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Tested-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
b10f94efbd
commit
ae066d1687
@@ -602,7 +602,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);
|
||||
@@ -615,12 +615,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;
|
||||
}
|
||||
@@ -673,7 +672,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* 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) {
|
||||
@@ -681,7 +680,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user