ASoC: wcd9310: Add high impedance headphone detection support
If HPHL and HPHR have relatively high impedance, internal MBHC HPHL
trigger cannot trigger so results in headphone/headset detection
failure.
Utilize MIC trigger as well as HPHL trigger in order to detect headphone
and headset which have high impedance on headphone line.
CRs-fixed: 339390, 337590, 336847, 334339
Change-Id: Ibfd4ba254197233f2b5e253f5d1fa2a98c848c97
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 86c01ee..c1febcf 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -141,12 +141,30 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+{
+ if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
+ (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ } else {
+ handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ }
+}
+
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
{
int ret;
struct wcd9xxx *wcd9xxx = data;
u8 status[WCD9XXX_NUM_IRQ_REGS];
- unsigned int i;
+ int i;
wcd9xxx_lock_sleep(wcd9xxx);
ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
@@ -164,28 +182,22 @@
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*/
- for (i = 0; i < TABLA_NUM_IRQS; i++) {
- if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
- if ((i <= TABLA_IRQ_MBHC_INSERTION) &&
- (i >= TABLA_IRQ_MBHC_REMOVAL)) {
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(i), BYTE_BIT_MASK(i));
- if (wcd9xxx_get_intf_type() ==
- WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx,
- TABLA_A_INTR_MODE, 0x02);
- handle_nested_irq(wcd9xxx->irq_base + i);
- } else {
- handle_nested_irq(wcd9xxx->irq_base + i);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(i), BYTE_BIT_MASK(i));
- if (wcd9xxx_get_intf_type() ==
- WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx,
- TABLA_A_INTR_MODE, 0x02);
- }
- break;
- }
+ if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
+ BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
+ wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+
+ /* Since codec has only one hardware irq line which is shared by
+ * codec's different internal interrupts, so it's possible master irq
+ * handler dispatches multiple nested irq handlers after breaking
+ * order. Dispatch MBHC interrupts order to follow MBHC state
+ * machine's order */
+ for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ wcd9xxx_irq_dispatch(wcd9xxx, i);
+ }
+ for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ wcd9xxx_irq_dispatch(wcd9xxx, i);
}
wcd9xxx_unlock_sleep(wcd9xxx);