summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongwoo Lee <dwoo08.lee@samsung.com>2016-10-21 16:51:40 +0900
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:54:35 +0900
commit287de690cbe05fa894e510b9482f47943227f259 (patch)
tree465ec3007942aee541ae8e0c1a55fc7ff9a4c8a2
parent141d04287de37ac7f6827577db839bf7f34f0ec1 (diff)
ASoC: max98090: fix interrupt handling problem
The max98090_interrupt handler currently will read the device status register. After that, it will act only when the JDET bit is set. However, when max98090_interrupt handler is triggered and jack status register has really changed, the JDET bit of device status register is not set as expected for unknown reason. Because of this, the jack status cannot be reported. This patch adds a workaround solution for jack detection, by adding the second chance to recognize the reason of interrupt generation. If the interrupt is generated without setting any values on device status register, jack status register is compared to the current jack states. If the jack status is changed, the JDET bit is manually set. Change-Id: I74f6fb54fb9cc47e292f1f8e116032faae89c063 Reported-by: Huang Chao <chao7.huang@samsung.com> Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
-rw-r--r--sound/soc/codecs/max98090.c60
1 files changed, 58 insertions, 2 deletions
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 3e33ef2acf3c..9f9f5ee42d6e 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2270,6 +2270,39 @@ static void max98090_jack_work(struct work_struct *work)
snd_soc_dapm_sync(dapm);
}
+static int max98090_jack_status_changed(int prev_state, int status)
+{
+ int disconnected;
+
+ /*
+ * Jack status register includes two bits LSNS and JKSNS, and they
+ * indicate the jack state as below:
+ *
+ * | LSNS | JKSNS | STATE |
+ * | 1 | 1 | No jack |
+ * | 0 | 1 | Headset(MIC) |
+ * | 0 | 0 | Headphone |
+ * | 1 | 0 | Not Possible |
+ *
+ * It means that the jack is in disconnected state only if both LSNS
+ * and JKSNS are set, otherwise it is in connected state. Leveraging
+ * this, the change can be recognized by comparing the current state
+ * with the previous.
+ */
+ disconnected =
+ (status & M98090_LSNS_MASK) && (status & M98090_JKSNS_MASK);
+
+ /* jack_state is transited from "Connected" into "No Jack" */
+ if (prev_state != M98090_JACK_STATE_NO_HEADSET && disconnected)
+ return true;
+ /* jack_state is transited from "No Jack" into "Connected" */
+ else if (prev_state == M98090_JACK_STATE_NO_HEADSET && !disconnected)
+ return true;
+ /* if no transition in jack_state */
+ else
+ return false;
+}
+
static irqreturn_t max98090_interrupt(int irq, void *data)
{
struct max98090_priv *max98090 = data;
@@ -2307,8 +2340,31 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
active &= mask;
- if (!active)
- return IRQ_NONE;
+ /* As described in datasheet, if this handler is triggered by jack
+ * detection interrupt, the JDET bit of DEVICE STATUS register
+ * should be set. However, it doesn't work for unknown reason.
+ *
+ * To this end, if interrupt is generated but status register is
+ * not set, check the change in jack status and set JDET bit forcibly.
+ */
+ if (!active) {
+ int jack_status;
+
+ ret = regmap_read(max98090->regmap,
+ M98090_REG_JACK_STATUS, &jack_status);
+ if (ret != 0) {
+ dev_err(max98090->codec->dev,
+ "failed to read M98090_REG_JACK_STATUS: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (max98090_jack_status_changed(max98090->jack_state,
+ jack_status))
+ active |= M98090_JDET_MASK;
+ else
+ return IRQ_NONE;
+ }
if (active & M98090_CLD_MASK)
dev_err(codec->dev, "M98090_CLD_MASK\n");