From ab28f4de28a9afea80bee928f6872f366430ab68 Mon Sep 17 00:00:00 2001
From: Vladimir Karpovich <vkarpovich@motorola.com>
Date: Mon, 18 Aug 2014 15:43:12 -0500
Subject: [PATCH] asoc: fsa8500: Add multi-button headset support

Assign different key codes to buttons detected by FSA8500 and
make the codes configurable in device tree.

Change-Id: I8b7d9d6c089271b8194759d2385d15a5742a9138
Signed-off-by: Vladimir Karpovich <vkarpovich@motorola.com>
---
 .../devicetree/bindings/sound/fsa8500.txt     |  17 ++-
 include/linux/fsa8500.h                       |   6 +
 sound/soc/codecs/fsa8500-core.h               |   3 +
 sound/soc/codecs/fsa8500.c                    | 138 ++++++++++++++----
 4 files changed, 133 insertions(+), 31 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/fsa8500.txt b/Documentation/devicetree/bindings/sound/fsa8500.txt
index 8936d547f511..427a46f75e40 100644
--- a/Documentation/devicetree/bindings/sound/fsa8500.txt
+++ b/Documentation/devicetree/bindings/sound/fsa8500.txt
@@ -15,7 +15,8 @@ Required properties:
 Optional properties:
 
 -fsa8500-always-on-micbias: Keep bias always on.
-
+-fsa8500-keymap         : headset keys code map.
+			  Default :1-BTN with key code 0xE2 - KEY_MEDIA
 
 Example:
 
@@ -28,5 +29,19 @@ Example:
 		hs_det_micvdd-supply = <&pma8084_l18>;
 		fsa8500-init-regs = <0x0C 0xA1>,
 				    <0x0F 0xF9>;
+				/* SND_JACK_BTN_0 - KEY_MEDIA */
+		fsa8500-keymap = <0x4000000 0xE2>,
+				/* SND_JACK_BTN_1 - KEY_MEDIA */
+				 <0x2000000 0xE2>,
+				/* SND_JACK_BTN_2 - KEY_MEDIA */
+				 <0x1000000 0xE2>,
+				/* SND_JACK_BTN_3 - KEY_MEDIA */
+				 <0x0800000 0xE2>,
+				/* SND_JACK_BTN_4 - KEY_SEARCH */
+				 <0x0400000 0xD9>,
+				/* SND_JACK_BTN_5 - KEY_VOLUMEDOWN */
+				 <0x0200000 0x72>,
+				/* SND_JACK_BTN_6 - KEY_VOLUMEUP */
+				 <0x0100000 0x73>;
 	};
 };
diff --git a/include/linux/fsa8500.h b/include/linux/fsa8500.h
index ce3ddc2dba91..0f402f8bca75 100644
--- a/include/linux/fsa8500.h
+++ b/include/linux/fsa8500.h
@@ -23,11 +23,17 @@ struct fsa8500_regs {
 	u8 reg;
 	u8 value;
 };
+struct fsa8500_keymap {
+	u32 soc_btn;
+	u32 keycode;
+};
 struct fsa8500_platform_data {
 	int irq_gpio;
 	int alwayson_micbias;
 	int init_regs_num;
 	struct fsa8500_regs *init_regs;
+	int num_keys;
+	struct fsa8500_keymap *keymap;
 };
 
 #endif  /* __FSA8500_H__ */
diff --git a/sound/soc/codecs/fsa8500-core.h b/sound/soc/codecs/fsa8500-core.h
index 309aae1f64f8..427c36cba4a3 100644
--- a/sound/soc/codecs/fsa8500-core.h
+++ b/sound/soc/codecs/fsa8500-core.h
@@ -69,6 +69,9 @@
 #define FSA8500_MIC_DISABLED		0
 #define FSA8500_MIC_ENABLED		1
 
+/* number supported keys */
+#define FSA8500_NUM_KEYS		7
+
 extern int fsa8500_hs_detect(struct snd_soc_codec *codec);
 
 extern void fsa8500_hp_event(int event);
diff --git a/sound/soc/codecs/fsa8500.c b/sound/soc/codecs/fsa8500.c
index fe0ee97b3a74..22169028741a 100644
--- a/sound/soc/codecs/fsa8500.c
+++ b/sound/soc/codecs/fsa8500.c
@@ -50,9 +50,13 @@
 
 #define FSA8500_JACK_MASK (SND_JACK_HEADSET | SND_JACK_HEADPHONE| \
 				SND_JACK_LINEOUT | SND_JACK_UNSUPPORTED)
-#define FSA8500_JACK_BUTTON_MASK (SND_JACK_BTN_0)
+#define FSA8500_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+				SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+				SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+				SND_JACK_BTN_6 | SND_JACK_BTN_7)
 
 #define VDD_UA_ON_LOAD	10000
+#define	SND_JACK_BTN_SHIFT	20
 
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
@@ -444,6 +448,7 @@ static int fsa8500_get_hs_acc_type(struct fsa8500_data *fsa8500)
 
 static int fsa8500_report_hs(struct fsa8500_data *fsa8500)
 {
+	unsigned int status;
 	if ((fsa8500->button_jack == NULL) || (fsa8500->hs_jack == NULL)) {
 		pr_err("fsa8500: Something plugged in, report it later\n");
 		return -EINVAL;
@@ -481,28 +486,36 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500)
 
 	/* Key Short press */
 	if (fsa8500->irq_status[1] & 0x7F) {
-		pr_debug("%s:report short button press & release\n", __func__);
+		status = fsa8500->irq_status[1] & 0x7F;
+		pr_debug("%s:report key 0x%x short press & release\n",
+					__func__, status);
 		snd_soc_jack_report_no_dapm(fsa8500->button_jack,
-			SND_JACK_BTN_0, fsa8500->button_jack->jack->type);
+					status<<SND_JACK_BTN_SHIFT,
+					status<<SND_JACK_BTN_SHIFT);
 
 		snd_soc_jack_report_no_dapm(fsa8500->button_jack,
-			0, fsa8500->button_jack->jack->type);
+					0, status<<SND_JACK_BTN_SHIFT);
 	}
 
 	/* Key Long press */
 	if (fsa8500->irq_status[2] & 0x7F) {
-		pr_debug("%s:report long button press\n", __func__);
+		status = fsa8500->irq_status[2] & 0x7F;
+		pr_debug("%s:report key 0x%x long press\n", __func__, status);
 		snd_soc_jack_report_no_dapm(fsa8500->button_jack,
-			SND_JACK_BTN_0, fsa8500->button_jack->jack->type);
-		fsa8500->button_pressed = 1;
+					status<<SND_JACK_BTN_SHIFT,
+					status<<SND_JACK_BTN_SHIFT);
+
+		fsa8500->button_pressed |= status;
 	}
 
 	/* Key Long release */
 	if (fsa8500->irq_status[3] & 0x7F) {
-		pr_debug("%s:report long button release\n", __func__);
+		status = fsa8500->irq_status[3] & 0x7F;
+		pr_debug("%s:report key %d release\n", __func__, status);
 		snd_soc_jack_report_no_dapm(fsa8500->button_jack,
-			0, fsa8500->button_jack->jack->type);
-		fsa8500->button_pressed = 0;
+					0, status<<SND_JACK_BTN_SHIFT);
+
+		fsa8500->button_pressed &= ~status;
 	}
 
 	return 0;
@@ -510,7 +523,8 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500)
 
 int fsa8500_hs_detect(struct snd_soc_codec *codec)
 {
-	int ret = -EINVAL;
+	int ret = -EINVAL, i;
+	struct fsa8500_platform_data *pdata;
 	struct fsa8500_data *fsa8500;
 
 	if (fsa8500_client == NULL)
@@ -521,6 +535,8 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec)
 	if (fsa8500 == NULL)
 		return ret;
 
+	pdata = fsa8500_client->dev.platform_data;
+
 	if (fsa8500->hs_jack == NULL) {
 		ret = snd_soc_jack_new(codec, "Headset Jack",
 					FSA8500_JACK_MASK, &hs_jack);
@@ -541,12 +557,25 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec)
 		fsa8500->button_jack = &button_jack;
 	}
 
-	ret = snd_jack_set_key(fsa8500->button_jack->jack,
+	if (pdata->num_keys && pdata->keymap) {
+		for (i = 0; i < pdata->num_keys; i++) {
+			ret = snd_jack_set_key(fsa8500->button_jack->jack,
+					pdata->keymap[i].soc_btn,
+					pdata->keymap[i].keycode);
+			if (ret) {
+				pr_err("%s: Failed to set code for 0x%x -- 0x%x\n",
+					__func__, pdata->keymap[i].soc_btn,
+					pdata->keymap[i].keycode);
+				return ret;
+			}
+		}
+	} else {
+		ret = snd_jack_set_key(fsa8500->button_jack->jack,
 					SND_JACK_BTN_0, KEY_MEDIA);
-
-	if (ret) {
-		pr_err("%s: Failed to set code for btn-0\n", __func__);
-		return ret;
+		if (ret) {
+			pr_err("%s: Failed to set code for btn-0\n", __func__);
+			return ret;
+		}
 	}
 
 	mutex_lock(&fsa8500->lock);
@@ -621,13 +650,36 @@ static int fsa8500_parse_dt_regs_array(const u32 *arr,
 	return len;
 }
 
+static int fsa8500_parse_dt_keymap(const u32 *arr,
+	struct fsa8500_keymap *keymap, int count)
+{
+	u32 len = 0, btn, code;
+	int i;
+
+	if (!arr)
+		return 0;
+
+	for (i = 0; i < count*2; i += 2) {
+		btn = be32_to_cpu(arr[i]);
+		code = be32_to_cpu(arr[i + 1]);
+		if (btn & 0xff00000) {
+			keymap->soc_btn = btn;
+			keymap->keycode = code;
+			len++;
+			pr_debug("%s: key: 0x%x=0x%x\n", __func__, btn, code);
+			keymap++;
+		}
+	}
+	return len;
+}
+
 static struct fsa8500_platform_data *
 fsa8500_of_init(struct i2c_client *client)
 {
 	struct fsa8500_platform_data *pdata;
 	struct device_node *np = client->dev.of_node;
-	const u32 *regs_arr;
-	int regs_len;
+	const u32 *regs_arr, *keymap;
+	int regs_len, keymap_len;
 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
 		dev_err(&client->dev, "pdata allocation failure\n");
@@ -643,19 +695,45 @@ fsa8500_of_init(struct i2c_client *client)
 	if (!regs_arr || (regs_len & 1)) {
 		pr_warn("fsa8500: No init registers setting\n");
 		regs_len = 0;
-	}
-
-	regs_len /= 2 * sizeof(u32);
-	if (regs_len > FSA8500_MAX_REGISTER_VAL)
+	} else {
+		regs_len /= 2 * sizeof(u32);
+		if (regs_len > FSA8500_MAX_REGISTER_VAL)
 			regs_len = FSA8500_MAX_REGISTER_VAL;
 
-	pdata->init_regs = devm_kzalloc(&client->dev,
+		pdata->init_regs = devm_kzalloc(&client->dev,
 					sizeof(struct fsa8500_regs) * regs_len,
 					GFP_KERNEL);
-	if (!pdata->init_regs)
-		return NULL;
-	pdata->init_regs_num = fsa8500_parse_dt_regs_array(regs_arr,
-					pdata->init_regs, regs_len);
+
+		if (!pdata->init_regs) {
+			pr_warn("fsa8500: init_regs allocation failure\n");
+			return pdata;
+		}
+		pdata->init_regs_num = fsa8500_parse_dt_regs_array(regs_arr,
+						pdata->init_regs, regs_len);
+	}
+
+
+	keymap = of_get_property(np, "fsa8500-keymap",
+			&keymap_len);
+	if (!keymap || (keymap_len & 1)) {
+		pr_warn("fsa8500: No keymap setting\n");
+		keymap_len = 0;
+	} else {
+		keymap_len /= 2 * sizeof(u32);
+		if (keymap_len > FSA8500_NUM_KEYS)
+			keymap_len = FSA8500_NUM_KEYS;
+
+		pdata->keymap = devm_kzalloc(&client->dev,
+				sizeof(struct fsa8500_keymap) * keymap_len,
+				GFP_KERNEL);
+		if (!pdata->keymap) {
+			pr_warn("fsa8500: keymap allocation failure\n");
+			return pdata;
+		}
+		pdata->num_keys = fsa8500_parse_dt_keymap(keymap,
+						pdata->keymap, keymap_len);
+	}
+
 	return pdata;
 }
 #else
@@ -679,9 +757,9 @@ static int fsa8500_probe(struct i2c_client *client,
 	}
 
 	if (client->dev.of_node)
-			fsa8500_pdata = fsa8500_of_init(client);
-	else
-			fsa8500_pdata = client->dev.platform_data;
+			client->dev.platform_data = fsa8500_of_init(client);
+
+	fsa8500_pdata = client->dev.platform_data;
 
 	/* Check platform data */
 	if (fsa8500_pdata == NULL) {
-- 
GitLab