diff --git a/Documentation/devicetree/bindings/sound/fsa8500.txt b/Documentation/devicetree/bindings/sound/fsa8500.txt
index 8936d547f511c80bc64285ab8098fbf17f4a5c6a..427a46f75e40d8a380cb217750e7d3927efe1912 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 ce3ddc2dba917041940d9b545fc4a9dadfebe7e5..0f402f8bca75ec359be06293013c6ac2dfeea148 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 309aae1f64f8f97e8c4ef7c13ae05c3605d93144..427c36cba4a39cbe17a49bdc864cbcf21d3b6813 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 fe0ee97b3a745af41e3b17b22126d06a1946d3d9..22169028741a5012619610a850906333f0ca1cba 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) {