Skip to content
Snippets Groups Projects
Commit ab28f4de authored by Vladimir Karpovich's avatar Vladimir Karpovich Committed by Iliyan Malchev
Browse files

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: default avatarVladimir Karpovich <vkarpovich@motorola.com>
parent a33be419
No related branches found
No related tags found
No related merge requests found
...@@ -15,7 +15,8 @@ Required properties: ...@@ -15,7 +15,8 @@ Required properties:
Optional properties: Optional properties:
-fsa8500-always-on-micbias: Keep bias always on. -fsa8500-always-on-micbias: Keep bias always on.
-fsa8500-keymap : headset keys code map.
Default :1-BTN with key code 0xE2 - KEY_MEDIA
Example: Example:
...@@ -28,5 +29,19 @@ Example: ...@@ -28,5 +29,19 @@ Example:
hs_det_micvdd-supply = <&pma8084_l18>; hs_det_micvdd-supply = <&pma8084_l18>;
fsa8500-init-regs = <0x0C 0xA1>, fsa8500-init-regs = <0x0C 0xA1>,
<0x0F 0xF9>; <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>;
}; };
}; };
...@@ -23,11 +23,17 @@ struct fsa8500_regs { ...@@ -23,11 +23,17 @@ struct fsa8500_regs {
u8 reg; u8 reg;
u8 value; u8 value;
}; };
struct fsa8500_keymap {
u32 soc_btn;
u32 keycode;
};
struct fsa8500_platform_data { struct fsa8500_platform_data {
int irq_gpio; int irq_gpio;
int alwayson_micbias; int alwayson_micbias;
int init_regs_num; int init_regs_num;
struct fsa8500_regs *init_regs; struct fsa8500_regs *init_regs;
int num_keys;
struct fsa8500_keymap *keymap;
}; };
#endif /* __FSA8500_H__ */ #endif /* __FSA8500_H__ */
...@@ -69,6 +69,9 @@ ...@@ -69,6 +69,9 @@
#define FSA8500_MIC_DISABLED 0 #define FSA8500_MIC_DISABLED 0
#define FSA8500_MIC_ENABLED 1 #define FSA8500_MIC_ENABLED 1
/* number supported keys */
#define FSA8500_NUM_KEYS 7
extern int fsa8500_hs_detect(struct snd_soc_codec *codec); extern int fsa8500_hs_detect(struct snd_soc_codec *codec);
extern void fsa8500_hp_event(int event); extern void fsa8500_hp_event(int event);
......
...@@ -50,9 +50,13 @@ ...@@ -50,9 +50,13 @@
#define FSA8500_JACK_MASK (SND_JACK_HEADSET | SND_JACK_HEADPHONE| \ #define FSA8500_JACK_MASK (SND_JACK_HEADSET | SND_JACK_HEADPHONE| \
SND_JACK_LINEOUT | SND_JACK_UNSUPPORTED) 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 VDD_UA_ON_LOAD 10000
#define SND_JACK_BTN_SHIFT 20
static struct snd_soc_jack hs_jack; static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack; static struct snd_soc_jack button_jack;
...@@ -444,6 +448,7 @@ static int fsa8500_get_hs_acc_type(struct fsa8500_data *fsa8500) ...@@ -444,6 +448,7 @@ static int fsa8500_get_hs_acc_type(struct fsa8500_data *fsa8500)
static int fsa8500_report_hs(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)) { if ((fsa8500->button_jack == NULL) || (fsa8500->hs_jack == NULL)) {
pr_err("fsa8500: Something plugged in, report it later\n"); pr_err("fsa8500: Something plugged in, report it later\n");
return -EINVAL; return -EINVAL;
...@@ -481,28 +486,36 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500) ...@@ -481,28 +486,36 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500)
/* Key Short press */ /* Key Short press */
if (fsa8500->irq_status[1] & 0x7F) { 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_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, snd_soc_jack_report_no_dapm(fsa8500->button_jack,
0, fsa8500->button_jack->jack->type); 0, status<<SND_JACK_BTN_SHIFT);
} }
/* Key Long press */ /* Key Long press */
if (fsa8500->irq_status[2] & 0x7F) { 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_soc_jack_report_no_dapm(fsa8500->button_jack,
SND_JACK_BTN_0, fsa8500->button_jack->jack->type); status<<SND_JACK_BTN_SHIFT,
fsa8500->button_pressed = 1; status<<SND_JACK_BTN_SHIFT);
fsa8500->button_pressed |= status;
} }
/* Key Long release */ /* Key Long release */
if (fsa8500->irq_status[3] & 0x7F) { 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, snd_soc_jack_report_no_dapm(fsa8500->button_jack,
0, fsa8500->button_jack->jack->type); 0, status<<SND_JACK_BTN_SHIFT);
fsa8500->button_pressed = 0;
fsa8500->button_pressed &= ~status;
} }
return 0; return 0;
...@@ -510,7 +523,8 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500) ...@@ -510,7 +523,8 @@ static int fsa8500_report_hs(struct fsa8500_data *fsa8500)
int fsa8500_hs_detect(struct snd_soc_codec *codec) 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; struct fsa8500_data *fsa8500;
if (fsa8500_client == NULL) if (fsa8500_client == NULL)
...@@ -521,6 +535,8 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec) ...@@ -521,6 +535,8 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec)
if (fsa8500 == NULL) if (fsa8500 == NULL)
return ret; return ret;
pdata = fsa8500_client->dev.platform_data;
if (fsa8500->hs_jack == NULL) { if (fsa8500->hs_jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", ret = snd_soc_jack_new(codec, "Headset Jack",
FSA8500_JACK_MASK, &hs_jack); FSA8500_JACK_MASK, &hs_jack);
...@@ -541,13 +557,26 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec) ...@@ -541,13 +557,26 @@ int fsa8500_hs_detect(struct snd_soc_codec *codec)
fsa8500->button_jack = &button_jack; fsa8500->button_jack = &button_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, ret = snd_jack_set_key(fsa8500->button_jack->jack,
SND_JACK_BTN_0, KEY_MEDIA); SND_JACK_BTN_0, KEY_MEDIA);
if (ret) { if (ret) {
pr_err("%s: Failed to set code for btn-0\n", __func__); pr_err("%s: Failed to set code for btn-0\n", __func__);
return ret; return ret;
} }
}
mutex_lock(&fsa8500->lock); mutex_lock(&fsa8500->lock);
...@@ -621,13 +650,36 @@ static int fsa8500_parse_dt_regs_array(const u32 *arr, ...@@ -621,13 +650,36 @@ static int fsa8500_parse_dt_regs_array(const u32 *arr,
return len; 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 * static struct fsa8500_platform_data *
fsa8500_of_init(struct i2c_client *client) fsa8500_of_init(struct i2c_client *client)
{ {
struct fsa8500_platform_data *pdata; struct fsa8500_platform_data *pdata;
struct device_node *np = client->dev.of_node; struct device_node *np = client->dev.of_node;
const u32 *regs_arr; const u32 *regs_arr, *keymap;
int regs_len; int regs_len, keymap_len;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) { if (!pdata) {
dev_err(&client->dev, "pdata allocation failure\n"); dev_err(&client->dev, "pdata allocation failure\n");
...@@ -643,8 +695,7 @@ fsa8500_of_init(struct i2c_client *client) ...@@ -643,8 +695,7 @@ fsa8500_of_init(struct i2c_client *client)
if (!regs_arr || (regs_len & 1)) { if (!regs_arr || (regs_len & 1)) {
pr_warn("fsa8500: No init registers setting\n"); pr_warn("fsa8500: No init registers setting\n");
regs_len = 0; regs_len = 0;
} } else {
regs_len /= 2 * sizeof(u32); regs_len /= 2 * sizeof(u32);
if (regs_len > FSA8500_MAX_REGISTER_VAL) if (regs_len > FSA8500_MAX_REGISTER_VAL)
regs_len = FSA8500_MAX_REGISTER_VAL; regs_len = FSA8500_MAX_REGISTER_VAL;
...@@ -652,10 +703,37 @@ fsa8500_of_init(struct i2c_client *client) ...@@ -652,10 +703,37 @@ fsa8500_of_init(struct i2c_client *client)
pdata->init_regs = devm_kzalloc(&client->dev, pdata->init_regs = devm_kzalloc(&client->dev,
sizeof(struct fsa8500_regs) * regs_len, sizeof(struct fsa8500_regs) * regs_len,
GFP_KERNEL); GFP_KERNEL);
if (!pdata->init_regs)
return NULL; 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_num = fsa8500_parse_dt_regs_array(regs_arr,
pdata->init_regs, regs_len); 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; return pdata;
} }
#else #else
...@@ -679,8 +757,8 @@ static int fsa8500_probe(struct i2c_client *client, ...@@ -679,8 +757,8 @@ static int fsa8500_probe(struct i2c_client *client,
} }
if (client->dev.of_node) if (client->dev.of_node)
fsa8500_pdata = fsa8500_of_init(client); client->dev.platform_data = fsa8500_of_init(client);
else
fsa8500_pdata = client->dev.platform_data; fsa8500_pdata = client->dev.platform_data;
/* Check platform data */ /* Check platform data */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment