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